3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
21 objectPrototype = Object.prototype,
22 toString = objectPrototype.toString,
24 enumerablesTest = { toString: 1 },
27 if (typeof Ext === 'undefined') {
33 for (i in enumerablesTest) {
38 enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
39 'toLocaleString', 'toString', 'constructor'];
43 * An array containing extra enumerables for old browsers
44 * @property {String[]}
46 Ext.enumerables = enumerables;
49 * Copies all the properties of config to the specified object.
50 * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
51 * {@link Ext.Object#merge} instead.
52 * @param {Object} object The receiver of the properties
53 * @param {Object} config The source of the properties
54 * @param {Object} defaults A different object that will also be applied for default values
55 * @return {Object} returns obj
57 Ext.apply = function(object, config, defaults) {
59 Ext.apply(object, defaults);
62 if (object && config && typeof config === 'object') {
66 object[i] = config[i];
70 for (j = enumerables.length; j--;) {
72 if (config.hasOwnProperty(k)) {
73 object[k] = config[k];
82 Ext.buildSettings = Ext.apply({
85 }, Ext.buildSettings || {});
89 * A reusable empty function
91 emptyFn: function() {},
93 baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
96 * Copies all the properties of config to object if they don't already exist.
97 * @param {Object} object The receiver of the properties
98 * @param {Object} config The source of the properties
99 * @return {Object} returns obj
101 applyIf: function(object, config) {
105 for (property in config) {
106 if (object[property] === undefined) {
107 object[property] = config[property];
116 * Iterates either an array or an object. This method delegates to
117 * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
119 * @param {Object/Array} object The object or array to be iterated.
120 * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
121 * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
122 * type that is being iterated.
123 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
124 * Defaults to the object being iterated itself.
127 iterate: function(object, fn, scope) {
128 if (Ext.isEmpty(object)) {
132 if (scope === undefined) {
136 if (Ext.isIterable(object)) {
137 Ext.Array.each.call(Ext.Array, object, fn, scope);
140 Ext.Object.each.call(Ext.Object, object, fn, scope);
148 * This method deprecated. Use {@link Ext#define Ext.define} instead.
150 * @param {Function} superclass
151 * @param {Object} overrides
152 * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
153 * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
157 var objectConstructor = objectPrototype.constructor,
158 inlineOverrides = function(o) {
160 if (!o.hasOwnProperty(m)) {
167 return function(subclass, superclass, overrides) {
168 // First we check if the user passed in just the superClass with overrides
169 if (Ext.isObject(superclass)) {
170 overrides = superclass;
171 superclass = subclass;
172 subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
173 superclass.apply(this, arguments);
180 sourceMethod: 'extend',
181 msg: 'Attempting to extend from a class which has not been loaded on the page.'
185 // We create a new temporary class
186 var F = function() {},
187 subclassProto, superclassProto = superclass.prototype;
189 F.prototype = superclassProto;
190 subclassProto = subclass.prototype = new F();
191 subclassProto.constructor = subclass;
192 subclass.superclass = superclassProto;
194 if (superclassProto.constructor === objectConstructor) {
195 superclassProto.constructor = superclass;
198 subclass.override = function(overrides) {
199 Ext.override(subclass, overrides);
202 subclassProto.override = inlineOverrides;
203 subclassProto.proto = subclassProto;
205 subclass.override(overrides);
206 subclass.extend = function(o) {
207 return Ext.extend(subclass, o);
215 * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
217 Ext.define('My.cool.Class', {
223 Ext.override(My.cool.Class, {
225 alert('About to say...');
227 this.callOverridden();
231 var cool = new My.cool.Class();
232 cool.sayHi(); // alerts 'About to say...'
235 * Please note that `this.callOverridden()` only works if the class was previously
236 * created with {@link Ext#define)
238 * @param {Object} cls The class to override
239 * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
240 * containing one or more methods.
244 override: function(cls, overrides) {
245 if (cls.prototype.$className) {
246 return cls.override(overrides);
249 Ext.apply(cls.prototype, overrides);
254 // A full set of static methods to do type checking
258 * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
259 * value (second argument) otherwise.
261 * @param {Object} value The value to test
262 * @param {Object} defaultValue The value to return if the original value is empty
263 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
264 * @return {Object} value, if non-empty, else defaultValue
266 valueFrom: function(value, defaultValue, allowBlank){
267 return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
271 * Returns the type of the given variable in string format. List of possible values are:
273 * - `undefined`: If the given value is `undefined`
274 * - `null`: If the given value is `null`
275 * - `string`: If the given value is a string
276 * - `number`: If the given value is a number
277 * - `boolean`: If the given value is a boolean value
278 * - `date`: If the given value is a `Date` object
279 * - `function`: If the given value is a function reference
280 * - `object`: If the given value is an object
281 * - `array`: If the given value is an array
282 * - `regexp`: If the given value is a regular expression
283 * - `element`: If the given value is a DOM Element
284 * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
285 * - `whitespace`: If the given value is a DOM text node and contains only whitespace
287 * @param {Object} value
291 typeOf: function(value) {
292 if (value === null) {
296 var type = typeof value;
298 if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
302 var typeToString = toString.call(value);
304 switch(typeToString) {
305 case '[object Array]':
307 case '[object Date]':
309 case '[object Boolean]':
311 case '[object Number]':
313 case '[object RegExp]':
317 if (type === 'function') {
321 if (type === 'object') {
322 if (value.nodeType !== undefined) {
323 if (value.nodeType === 3) {
324 return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
336 sourceMethod: 'typeOf',
337 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
342 * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
346 * - a zero-length array
347 * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
349 * @param {Object} value The value to test
350 * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
354 isEmpty: function(value, allowEmptyString) {
355 return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
359 * Returns true if the passed value is a JavaScript Array, false otherwise.
361 * @param {Object} target The target to test
365 isArray: ('isArray' in Array) ? Array.isArray : function(value) {
366 return toString.call(value) === '[object Array]';
370 * Returns true if the passed value is a JavaScript Date object, false otherwise.
371 * @param {Object} object The object to test
374 isDate: function(value) {
375 return toString.call(value) === '[object Date]';
379 * Returns true if the passed value is a JavaScript Object, false otherwise.
380 * @param {Object} value The value to test
384 isObject: (toString.call(null) === '[object Object]') ?
386 // check ownerDocument here as well to exclude DOM nodes
387 return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
390 return toString.call(value) === '[object Object]';
394 * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
395 * @param {Object} value The value to test
398 isPrimitive: function(value) {
399 var type = typeof value;
401 return type === 'string' || type === 'number' || type === 'boolean';
405 * Returns true if the passed value is a JavaScript Function, false otherwise.
406 * @param {Object} value The value to test
411 // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
412 // Object.prorotype.toString (slower)
413 (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
414 return toString.call(value) === '[object Function]';
415 } : function(value) {
416 return typeof value === 'function';
420 * Returns true if the passed value is a number. Returns false for non-finite numbers.
421 * @param {Object} value The value to test
424 isNumber: function(value) {
425 return typeof value === 'number' && isFinite(value);
429 * Validates that a value is numeric.
430 * @param {Object} value Examples: 1, '1', '2.34'
431 * @return {Boolean} True if numeric, false otherwise
433 isNumeric: function(value) {
434 return !isNaN(parseFloat(value)) && isFinite(value);
438 * Returns true if the passed value is a string.
439 * @param {Object} value The value to test
442 isString: function(value) {
443 return typeof value === 'string';
447 * Returns true if the passed value is a boolean.
449 * @param {Object} value The value to test
452 isBoolean: function(value) {
453 return typeof value === 'boolean';
457 * Returns true if the passed value is an HTMLElement
458 * @param {Object} value The value to test
461 isElement: function(value) {
462 return value ? value.nodeType === 1 : false;
466 * Returns true if the passed value is a TextNode
467 * @param {Object} value The value to test
470 isTextNode: function(value) {
471 return value ? value.nodeName === "#text" : false;
475 * Returns true if the passed value is defined.
476 * @param {Object} value The value to test
479 isDefined: function(value) {
480 return typeof value !== 'undefined';
484 * Returns true if the passed value is iterable, false otherwise
485 * @param {Object} value The value to test
488 isIterable: function(value) {
489 return (value && typeof value !== 'string') ? value.length !== undefined : false;
496 * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
497 * @param {Object} item The variable to clone
498 * @return {Object} clone
500 clone: function(item) {
501 if (item === null || item === undefined) {
506 // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
508 if (item.nodeType && item.cloneNode) {
509 return item.cloneNode(true);
512 var type = toString.call(item);
515 if (type === '[object Date]') {
516 return new Date(item.getTime());
519 var i, j, k, clone, key;
522 if (type === '[object Array]') {
528 clone[i] = Ext.clone(item[i]);
532 else if (type === '[object Object]' && item.constructor === Object) {
536 clone[key] = Ext.clone(item[key]);
540 for (j = enumerables.length; j--;) {
547 return clone || item;
552 * Generate a unique reference of Ext in the global scope, useful for sandboxing
554 getUniqueGlobalNamespace: function() {
555 var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
557 if (uniqueGlobalNamespace === undefined) {
561 uniqueGlobalNamespace = 'ExtBox' + (++i);
562 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
564 Ext.global[uniqueGlobalNamespace] = Ext;
565 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
568 return uniqueGlobalNamespace;
574 functionFactory: function() {
575 var args = Array.prototype.slice.call(arguments);
577 if (args.length > 0) {
578 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
579 args[args.length - 1];
582 return Function.prototype.constructor.apply(Function.prototype, args);
587 * Old alias to {@link Ext#typeOf}
588 * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
592 Ext.type = Ext.typeOf;
597 * @author Jacky Nguyen <jacky@sencha.com>
598 * @docauthor Jacky Nguyen <jacky@sencha.com>
601 * A utility class that wrap around a string version number and provide convenient
602 * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
604 var version = new Ext.Version('1.0.2beta');
605 console.log("Version is " + version); // Version is 1.0.2beta
607 console.log(version.getMajor()); // 1
608 console.log(version.getMinor()); // 0
609 console.log(version.getPatch()); // 2
610 console.log(version.getBuild()); // 0
611 console.log(version.getRelease()); // beta
613 console.log(version.isGreaterThan('1.0.1')); // True
614 console.log(version.isGreaterThan('1.0.2alpha')); // True
615 console.log(version.isGreaterThan('1.0.2RC')); // False
616 console.log(version.isGreaterThan('1.0.2')); // False
617 console.log(version.isLessThan('1.0.2')); // True
619 console.log(version.match(1.0)); // True
620 console.log(version.match('1.0.2')); // True
626 // Current core version
627 var version = '4.0.7', Version;
628 Ext.Version = Version = Ext.extend(Object, {
631 * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
632 * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
633 * @return {Ext.Version} this
635 constructor: function(version) {
636 var parts, releaseStartIndex;
638 if (version instanceof Version) {
642 this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
644 releaseStartIndex = this.version.search(/([^\d\.])/);
646 if (releaseStartIndex !== -1) {
647 this.release = this.version.substr(releaseStartIndex, version.length);
648 this.shortVersion = this.version.substr(0, releaseStartIndex);
651 this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
653 parts = this.version.split('.');
655 this.major = parseInt(parts.shift() || 0, 10);
656 this.minor = parseInt(parts.shift() || 0, 10);
657 this.patch = parseInt(parts.shift() || 0, 10);
658 this.build = parseInt(parts.shift() || 0, 10);
664 * Override the native toString method
666 * @return {String} version
668 toString: function() {
673 * Override the native valueOf method
675 * @return {String} version
677 valueOf: function() {
682 * Returns the major component value
683 * @return {Number} major
685 getMajor: function() {
686 return this.major || 0;
690 * Returns the minor component value
691 * @return {Number} minor
693 getMinor: function() {
694 return this.minor || 0;
698 * Returns the patch component value
699 * @return {Number} patch
701 getPatch: function() {
702 return this.patch || 0;
706 * Returns the build component value
707 * @return {Number} build
709 getBuild: function() {
710 return this.build || 0;
714 * Returns the release component value
715 * @return {Number} release
717 getRelease: function() {
718 return this.release || '';
722 * Returns whether this version if greater than the supplied argument
723 * @param {String/Number} target The version to compare with
724 * @return {Boolean} True if this version if greater than the target, false otherwise
726 isGreaterThan: function(target) {
727 return Version.compare(this.version, target) === 1;
731 * Returns whether this version if smaller than the supplied argument
732 * @param {String/Number} target The version to compare with
733 * @return {Boolean} True if this version if smaller than the target, false otherwise
735 isLessThan: function(target) {
736 return Version.compare(this.version, target) === -1;
740 * Returns whether this version equals to the supplied argument
741 * @param {String/Number} target The version to compare with
742 * @return {Boolean} True if this version equals to the target, false otherwise
744 equals: function(target) {
745 return Version.compare(this.version, target) === 0;
749 * Returns whether this version matches the supplied argument. Example:
751 * var version = new Ext.Version('1.0.2beta');
752 * console.log(version.match(1)); // True
753 * console.log(version.match(1.0)); // True
754 * console.log(version.match('1.0.2')); // True
755 * console.log(version.match('1.0.2RC')); // False
757 * @param {String/Number} target The version to compare with
758 * @return {Boolean} True if this version matches the target, false otherwise
760 match: function(target) {
761 target = String(target);
762 return this.version.substr(0, target.length) === target;
766 * Returns this format: [major, minor, patch, build, release]. Useful for comparison
769 toArray: function() {
770 return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
774 * Returns shortVersion version without dots and release
777 getShortVersion: function() {
778 return this.shortVersion;
797 * Converts a version component to a comparable value
800 * @param {Object} value The value to convert
803 getComponentValue: function(value) {
804 return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
808 * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
809 * they are handled in the following order:
810 * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
813 * @param {String} current The current version to compare to
814 * @param {String} target The target version to compare to
815 * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
817 compare: function(current, target) {
818 var currentValue, targetValue, i;
820 current = new Version(current).toArray();
821 target = new Version(target).toArray();
823 for (i = 0; i < Math.max(current.length, target.length); i++) {
824 currentValue = this.getComponentValue(current[i]);
825 targetValue = this.getComponentValue(target[i]);
827 if (currentValue < targetValue) {
829 } else if (currentValue > targetValue) {
847 lastRegisteredVersion: null,
850 * Set version number for the given package name.
852 * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
853 * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
856 setVersion: function(packageName, version) {
857 Ext.versions[packageName] = new Version(version);
858 Ext.lastRegisteredVersion = Ext.versions[packageName];
864 * Get the version number of the supplied package name; will return the last registered version
865 * (last Ext.setVersion call) if there's no package name given.
867 * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
868 * @return {Ext.Version} The version
870 getVersion: function(packageName) {
871 if (packageName === undefined) {
872 return Ext.lastRegisteredVersion;
875 return Ext.versions[packageName];
879 * Create a closure for deprecated code.
881 // This means Ext.oldMethod is only supported in 4.0.0beta and older.
882 // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
883 // the closure will not be invoked
884 Ext.deprecate('extjs', '4.0.0beta', function() {
885 Ext.oldMethod = Ext.newMethod;
890 * @param {String} packageName The package name
891 * @param {String} since The last version before it's deprecated
892 * @param {Function} closure The callback function to be executed with the specified version is less than the current version
893 * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
896 deprecate: function(packageName, since, closure, scope) {
897 if (Version.compare(Ext.getVersion(packageName), since) < 1) {
901 }); // End Versioning
903 Ext.setVersion('core', version);
910 * A collection of useful static methods to deal with strings
915 trimRegex: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
917 formatRe: /\{(\d+)\}/g,
918 escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
921 * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages.
922 * @param {String} value The string to encode
923 * @return {String} The encoded text
926 htmlEncode: (function() {
932 }, keys = [], p, regex;
934 for (p in entities) {
938 regex = new RegExp('(' + keys.join('|') + ')', 'g');
940 return function(value) {
941 return (!value) ? value : String(value).replace(regex, function(match, capture) {
942 return entities[capture];
948 * Convert certain characters (&, <, >, and ") from their HTML character equivalents.
949 * @param {String} value The string to decode
950 * @return {String} The decoded text
953 htmlDecode: (function() {
959 }, keys = [], p, regex;
961 for (p in entities) {
965 regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
967 return function(value) {
968 return (!value) ? value : String(value).replace(regex, function(match, capture) {
969 if (capture in entities) {
970 return entities[capture];
972 return String.fromCharCode(parseInt(capture.substr(2), 10));
979 * Appends content to the query string of a URL, handling logic for whether to place
980 * a question mark or ampersand.
981 * @param {String} url The URL to append to.
982 * @param {String} string The content to append to the URL.
983 * @return (String) The resulting URL
985 urlAppend : function(url, string) {
986 if (!Ext.isEmpty(string)) {
987 return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
994 * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
997 alert('-' + s + '-'); //alerts "- foo bar -"
998 alert('-' + Ext.String.trim(s) + '-'); //alerts "-foo bar-"
1000 * @param {String} string The string to escape
1001 * @return {String} The trimmed string
1003 trim: function(string) {
1004 return string.replace(Ext.String.trimRegex, "");
1008 * Capitalize the given string
1009 * @param {String} string
1012 capitalize: function(string) {
1013 return string.charAt(0).toUpperCase() + string.substr(1);
1017 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1018 * @param {String} value The string to truncate
1019 * @param {Number} length The maximum length to allow before truncating
1020 * @param {Boolean} word True to try to find a common word break
1021 * @return {String} The converted text
1023 ellipsis: function(value, len, word) {
1024 if (value && value.length > len) {
1026 var vs = value.substr(0, len - 2),
1027 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1028 if (index !== -1 && index >= (len - 15)) {
1029 return vs.substr(0, index) + "...";
1032 return value.substr(0, len - 3) + "...";
1038 * Escapes the passed string for use in a regular expression
1039 * @param {String} string
1042 escapeRegex: function(string) {
1043 return string.replace(Ext.String.escapeRegexRe, "\\$1");
1047 * Escapes the passed string for ' and \
1048 * @param {String} string The string to escape
1049 * @return {String} The escaped string
1051 escape: function(string) {
1052 return string.replace(Ext.String.escapeRe, "\\$1");
1056 * Utility function that allows you to easily switch a string between two alternating values. The passed value
1057 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
1058 * they are already different, the first value passed in is returned. Note that this method returns the new value
1059 * but does not change the current string.
1061 // alternate sort directions
1062 sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1064 // instead of conditional logic:
1065 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1067 * @param {String} string The current string
1068 * @param {String} value The value to compare to the current string
1069 * @param {String} other The new value to use if the string already equals the first value passed in
1070 * @return {String} The new value
1072 toggle: function(string, value, other) {
1073 return string === value ? other : value;
1077 * Pads the left side of a string with a specified character. This is especially useful
1078 * for normalizing number and date strings. Example usage:
1081 var s = Ext.String.leftPad('123', 5, '0');
1082 // s now contains the string: '00123'
1084 * @param {String} string The original string
1085 * @param {Number} size The total length of the output string
1086 * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1087 * @return {String} The padded string
1089 leftPad: function(string, size, character) {
1090 var result = String(string);
1091 character = character || " ";
1092 while (result.length < size) {
1093 result = character + result;
1099 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
1100 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
1102 var cls = 'my-class', text = 'Some text';
1103 var s = Ext.String.format('<div class="{0}">{1}</div>', cls, text);
1104 // s now contains the string: '<div class="my-class">Some text</div>'
1106 * @param {String} string The tokenized string to be formatted
1107 * @param {String} value1 The value to replace token {0}
1108 * @param {String} value2 Etc...
1109 * @return {String} The formatted string
1111 format: function(format) {
1112 var args = Ext.Array.toArray(arguments, 1);
1113 return format.replace(Ext.String.formatRe, function(m, i) {
1119 * Returns a string with a specified number of repititions a given string pattern.
1120 * The pattern be separated by a different string.
1122 * var s = Ext.String.repeat('---', 4); // = '------------'
1123 * var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--'
1125 * @param {String} pattern The pattern to repeat.
1126 * @param {Number} count The number of times to repeat the pattern (may be 0).
1127 * @param {String} sep An option string to separate each pattern.
1129 repeat: function(pattern, count, sep) {
1130 for (var buf = [], i = count; i--; ) {
1133 return buf.join(sep || '');
1140 * A collection of useful static methods to deal with numbers
1146 var isToFixedBroken = (0.9).toFixed() !== '1';
1150 * Checks whether or not the passed number is within a desired range. If the number is already within the
1151 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1152 * exceeded. Note that this method returns the constrained value but does not change the current number.
1153 * @param {Number} number The number to check
1154 * @param {Number} min The minimum number in the range
1155 * @param {Number} max The maximum number in the range
1156 * @return {Number} The constrained value if outside the range, otherwise the current value
1158 constrain: function(number, min, max) {
1159 number = parseFloat(number);
1162 number = Math.max(number, min);
1165 number = Math.min(number, max);
1171 * Snaps the passed number between stopping points based upon a passed increment value.
1172 * @param {Number} value The unsnapped value.
1173 * @param {Number} increment The increment by which the value must move.
1174 * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
1175 * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
1176 * @return {Number} The value of the nearest snap target.
1178 snap : function(value, increment, minValue, maxValue) {
1179 var newValue = value,
1182 if (!(increment && value)) {
1185 m = value % increment;
1188 if (m * 2 >= increment) {
1189 newValue += increment;
1190 } else if (m * 2 < -increment) {
1191 newValue -= increment;
1194 return Ext.Number.constrain(newValue, minValue, maxValue);
1198 * Formats a number using fixed-point notation
1199 * @param {Number} value The number to format
1200 * @param {Number} precision The number of digits to show after the decimal point
1202 toFixed: function(value, precision) {
1203 if (isToFixedBroken) {
1204 precision = precision || 0;
1205 var pow = Math.pow(10, precision);
1206 return (Math.round(value * pow) / pow).toFixed(precision);
1209 return value.toFixed(precision);
1213 * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1216 Ext.Number.from('1.23', 1); // returns 1.23
1217 Ext.Number.from('abc', 1); // returns 1
1219 * @param {Object} value
1220 * @param {Number} defaultValue The value to return if the original value is non-numeric
1221 * @return {Number} value, if numeric, defaultValue otherwise
1223 from: function(value, defaultValue) {
1224 if (isFinite(value)) {
1225 value = parseFloat(value);
1228 return !isNaN(value) ? value : defaultValue;
1235 * @deprecated 4.0.0 Please use {@link Ext.Number#from} instead.
1238 * @alias Ext.Number#from
1240 Ext.num = function() {
1241 return Ext.Number.from.apply(this, arguments);
1246 * @author Jacky Nguyen <jacky@sencha.com>
1247 * @docauthor Jacky Nguyen <jacky@sencha.com>
1249 * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1253 var arrayPrototype = Array.prototype,
1254 slice = arrayPrototype.slice,
1255 supportsSplice = function () {
1260 if (!array.splice) {
1264 // This detects a bug in IE8 splice method:
1265 // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
1271 array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");
1273 lengthBefore = array.length; //41
1274 array.splice(13, 0, "XXX"); // add one element
1276 if (lengthBefore+1 != array.length) {
1283 supportsForEach = 'forEach' in arrayPrototype,
1284 supportsMap = 'map' in arrayPrototype,
1285 supportsIndexOf = 'indexOf' in arrayPrototype,
1286 supportsEvery = 'every' in arrayPrototype,
1287 supportsSome = 'some' in arrayPrototype,
1288 supportsFilter = 'filter' in arrayPrototype,
1289 supportsSort = function() {
1290 var a = [1,2,3,4,5].sort(function(){ return 0; });
1291 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1293 supportsSliceOnNodeList = true,
1297 // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1298 if (typeof document !== 'undefined') {
1299 slice.call(document.getElementsByTagName('body'));
1302 supportsSliceOnNodeList = false;
1305 function fixArrayIndex (array, index) {
1306 return (index < 0) ? Math.max(0, array.length + index)
1307 : Math.min(array.length, index);
1311 Does the same work as splice, but with a slightly more convenient signature. The splice
1312 method has bugs in IE8, so this is the implementation we use on that platform.
1314 The rippling of items in the array can be tricky. Consider two use cases:
1319 +---+---+---+---+---+---+---+---+
1320 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1321 +---+---+---+---+---+---+---+---+
1324 / / \/ \/ \ +--------------------------+
1325 / / /\ /\ +--------------------------+ \
1326 / / / \/ +--------------------------+ \ \
1327 / / / /+--------------------------+ \ \ \
1330 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1331 | 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
1332 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1336 In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
1337 that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
1338 must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
1340 function replaceSim (array, index, removeCount, insert) {
1341 var add = insert ? insert.length : 0,
1342 length = array.length,
1343 pos = fixArrayIndex(array, index);
1345 // we try to use Array.push when we can for efficiency...
1346 if (pos === length) {
1348 array.push.apply(array, insert);
1351 var remove = Math.min(removeCount, length - pos),
1352 tailOldPos = pos + remove,
1353 tailNewPos = tailOldPos + add - remove,
1354 tailCount = length - tailOldPos,
1355 lengthAfterRemove = length - remove,
1358 if (tailNewPos < tailOldPos) { // case A
1359 for (i = 0; i < tailCount; ++i) {
1360 array[tailNewPos+i] = array[tailOldPos+i];
1362 } else if (tailNewPos > tailOldPos) { // case B
1363 for (i = tailCount; i--; ) {
1364 array[tailNewPos+i] = array[tailOldPos+i];
1366 } // else, add == remove (nothing to do)
1368 if (add && pos === lengthAfterRemove) {
1369 array.length = lengthAfterRemove; // truncate array
1370 array.push.apply(array, insert);
1372 array.length = lengthAfterRemove + add; // reserves space
1373 for (i = 0; i < add; ++i) {
1374 array[pos+i] = insert[i];
1382 function replaceNative (array, index, removeCount, insert) {
1383 if (insert && insert.length) {
1384 if (index < array.length) {
1385 array.splice.apply(array, [index, removeCount].concat(insert));
1387 array.push.apply(array, insert);
1390 array.splice(index, removeCount);
1395 function eraseSim (array, index, removeCount) {
1396 return replaceSim(array, index, removeCount);
1399 function eraseNative (array, index, removeCount) {
1400 array.splice(index, removeCount);
1404 function spliceSim (array, index, removeCount) {
1405 var pos = fixArrayIndex(array, index),
1406 removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
1408 if (arguments.length < 4) {
1409 replaceSim(array, pos, removeCount);
1411 replaceSim(array, pos, removeCount, slice.call(arguments, 3));
1417 function spliceNative (array) {
1418 return array.splice.apply(array, slice.call(arguments, 1));
1421 var erase = supportsSplice ? eraseNative : eraseSim,
1422 replace = supportsSplice ? replaceNative : replaceSim,
1423 splice = supportsSplice ? spliceNative : spliceSim;
1425 // NOTE: from here on, use erase, replace or splice (not native methods)...
1427 ExtArray = Ext.Array = {
1429 * Iterates an array or an iterable value and invoke the given callback function for each item.
1431 * var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1433 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1434 * console.log(name);
1437 * var sum = function() {
1440 * Ext.Array.each(arguments, function(value) {
1447 * sum(1, 2, 3); // returns 6
1449 * The iteration can be stopped by returning false in the function callback.
1451 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1452 * if (name === 'Singapore') {
1453 * return false; // break here
1457 * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
1459 * @param {Array/NodeList/Object} iterable The value to be iterated. If this
1460 * argument is not iterable, the callback function is called once.
1461 * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1462 * the current `index`.
1463 * @param {Object} fn.item The item at the current `index` in the passed `array`
1464 * @param {Number} fn.index The current `index` within the `array`
1465 * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1466 * @param {Boolean} fn.return Return false to stop iteration.
1467 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1468 * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1470 * @return {Boolean} See description for the `fn` parameter.
1472 each: function(array, fn, scope, reverse) {
1473 array = ExtArray.from(array);
1478 if (reverse !== true) {
1479 for (i = 0; i < ln; i++) {
1480 if (fn.call(scope || array[i], array[i], i, array) === false) {
1486 for (i = ln - 1; i > -1; i--) {
1487 if (fn.call(scope || array[i], array[i], i, array) === false) {
1497 * Iterates an array and invoke the given callback function for each item. Note that this will simply
1498 * delegate to the native Array.prototype.forEach method if supported. It doesn't support stopping the
1499 * iteration by returning false in the callback function like {@link Ext.Array#each}. However, performance
1500 * could be much better in modern browsers comparing with {@link Ext.Array#each}
1502 * @param {Array} array The array to iterate
1503 * @param {Function} fn The callback function.
1504 * @param {Object} fn.item The item at the current `index` in the passed `array`
1505 * @param {Number} fn.index The current `index` within the `array`
1506 * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1507 * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1509 forEach: function(array, fn, scope) {
1510 if (supportsForEach) {
1511 return array.forEach(fn, scope);
1517 for (; i < ln; i++) {
1518 fn.call(scope, array[i], i, array);
1523 * Get the index of the provided `item` in the given `array`, a supplement for the
1524 * missing arrayPrototype.indexOf in Internet Explorer.
1526 * @param {Array} array The array to check
1527 * @param {Object} item The item to look for
1528 * @param {Number} from (Optional) The index at which to begin the search
1529 * @return {Number} The index of item in the array (or -1 if it is not found)
1531 indexOf: function(array, item, from) {
1532 if (supportsIndexOf) {
1533 return array.indexOf(item, from);
1536 var i, length = array.length;
1538 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1539 if (array[i] === item) {
1548 * Checks whether or not the given `array` contains the specified `item`
1550 * @param {Array} array The array to check
1551 * @param {Object} item The item to look for
1552 * @return {Boolean} True if the array contains the item, false otherwise
1554 contains: function(array, item) {
1555 if (supportsIndexOf) {
1556 return array.indexOf(item) !== -1;
1561 for (i = 0, ln = array.length; i < ln; i++) {
1562 if (array[i] === item) {
1571 * Converts any iterable (numeric indices and a length property) into a true array.
1574 * var args = Ext.Array.toArray(arguments),
1575 * fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1577 * alert(args.join(' '));
1578 * alert(fromSecondToLastArgs.join(' '));
1581 * test('just', 'testing', 'here'); // alerts 'just testing here';
1582 * // alerts 'testing here';
1584 * Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1585 * Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1586 * Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1588 * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
1590 * @param {Object} iterable the iterable object to be turned into a true Array.
1591 * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1592 * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1593 * index of the iterable value
1594 * @return {Array} array
1596 toArray: function(iterable, start, end){
1597 if (!iterable || !iterable.length) {
1601 if (typeof iterable === 'string') {
1602 iterable = iterable.split('');
1605 if (supportsSliceOnNodeList) {
1606 return slice.call(iterable, start || 0, end || iterable.length);
1613 end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1615 for (i = start; i < end; i++) {
1616 array.push(iterable[i]);
1623 * Plucks the value of a property from each item in the Array. Example:
1625 * Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1627 * @param {Array/NodeList} array The Array of items to pluck the value from.
1628 * @param {String} propertyName The property name to pluck from each element.
1629 * @return {Array} The value from each item in the Array.
1631 pluck: function(array, propertyName) {
1635 for (i = 0, ln = array.length; i < ln; i++) {
1638 ret.push(item[propertyName]);
1645 * Creates a new array with the results of calling a provided function on every element in this array.
1647 * @param {Array} array
1648 * @param {Function} fn Callback function for each item
1649 * @param {Object} scope Callback function scope
1650 * @return {Array} results
1652 map: function(array, fn, scope) {
1654 return array.map(fn, scope);
1661 for (; i < len; i++) {
1662 results[i] = fn.call(scope, array[i], i, array);
1669 * Executes the specified function for each array element until the function returns a falsy value.
1670 * If such an item is found, the function will return false immediately.
1671 * Otherwise, it will return true.
1673 * @param {Array} array
1674 * @param {Function} fn Callback function for each item
1675 * @param {Object} scope Callback function scope
1676 * @return {Boolean} True if no false value is returned by the callback function.
1678 every: function(array, fn, scope) {
1680 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1682 if (supportsEvery) {
1683 return array.every(fn, scope);
1689 for (; i < ln; ++i) {
1690 if (!fn.call(scope, array[i], i, array)) {
1699 * Executes the specified function for each array element until the function returns a truthy value.
1700 * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1702 * @param {Array} array
1703 * @param {Function} fn Callback function for each item
1704 * @param {Object} scope Callback function scope
1705 * @return {Boolean} True if the callback function returns a truthy value.
1707 some: function(array, fn, scope) {
1709 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1712 return array.some(fn, scope);
1718 for (; i < ln; ++i) {
1719 if (fn.call(scope, array[i], i, array)) {
1728 * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1730 * See {@link Ext.Array#filter}
1732 * @param {Array} array
1733 * @return {Array} results
1735 clean: function(array) {
1741 for (; i < ln; i++) {
1744 if (!Ext.isEmpty(item)) {
1753 * Returns a new array with unique items
1755 * @param {Array} array
1756 * @return {Array} results
1758 unique: function(array) {
1764 for (; i < ln; i++) {
1767 if (ExtArray.indexOf(clone, item) === -1) {
1776 * Creates a new array with all of the elements of this array for which
1777 * the provided filtering function returns true.
1779 * @param {Array} array
1780 * @param {Function} fn Callback function for each item
1781 * @param {Object} scope Callback function scope
1782 * @return {Array} results
1784 filter: function(array, fn, scope) {
1785 if (supportsFilter) {
1786 return array.filter(fn, scope);
1793 for (; i < ln; i++) {
1794 if (fn.call(scope, array[i], i, array)) {
1795 results.push(array[i]);
1803 * Converts a value to an array if it's not already an array; returns:
1805 * - An empty array if given value is `undefined` or `null`
1806 * - Itself if given value is already an array
1807 * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1808 * - An array with one item which is the given value, otherwise
1810 * @param {Object} value The value to convert to an array if it's not already is an array
1811 * @param {Boolean} newReference (Optional) True to clone the given array and return a new reference if necessary,
1813 * @return {Array} array
1815 from: function(value, newReference) {
1816 if (value === undefined || value === null) {
1820 if (Ext.isArray(value)) {
1821 return (newReference) ? slice.call(value) : value;
1824 if (value && value.length !== undefined && typeof value !== 'string') {
1825 return Ext.toArray(value);
1832 * Removes the specified item from the array if it exists
1834 * @param {Array} array The array
1835 * @param {Object} item The item to remove
1836 * @return {Array} The passed array itself
1838 remove: function(array, item) {
1839 var index = ExtArray.indexOf(array, item);
1842 erase(array, index, 1);
1849 * Push an item into the array only if the array doesn't contain it yet
1851 * @param {Array} array The array
1852 * @param {Object} item The item to include
1854 include: function(array, item) {
1855 if (!ExtArray.contains(array, item)) {
1861 * Clone a flat array without referencing the previous one. Note that this is different
1862 * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1863 * for Array.prototype.slice.call(array)
1865 * @param {Array} array The array
1866 * @return {Array} The clone array
1868 clone: function(array) {
1869 return slice.call(array);
1873 * Merge multiple arrays into one with unique items.
1875 * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
1877 * @param {Array} array1
1878 * @param {Array} array2
1879 * @param {Array} etc
1880 * @return {Array} merged
1883 var args = slice.call(arguments),
1887 for (i = 0, ln = args.length; i < ln; i++) {
1888 array = array.concat(args[i]);
1891 return ExtArray.unique(array);
1895 * Merge multiple arrays into one with unique items that exist in all of the arrays.
1897 * @param {Array} array1
1898 * @param {Array} array2
1899 * @param {Array} etc
1900 * @return {Array} intersect
1902 intersect: function() {
1904 arrays = slice.call(arguments),
1905 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1907 if (!arrays.length) {
1911 // Find the smallest array
1912 for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1913 if (!minArray || array.length < minArray.length) {
1919 minArray = ExtArray.unique(minArray);
1920 erase(arrays, x, 1);
1922 // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1923 // an item in the small array, we're likely to find it before reaching the end
1924 // of the inner loop and can terminate the search early.
1925 for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1928 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1929 for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1937 if (count === arraysLn) {
1946 * Perform a set difference A-B by subtracting all items in array B from array A.
1948 * @param {Array} arrayA
1949 * @param {Array} arrayB
1950 * @return {Array} difference
1952 difference: function(arrayA, arrayB) {
1953 var clone = slice.call(arrayA),
1957 for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1958 for (j = 0; j < ln; j++) {
1959 if (clone[j] === arrayB[i]) {
1971 * Returns a shallow copy of a part of an array. This is equivalent to the native
1972 * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
1973 * is "arguments" since the arguments object does not supply a slice method but can
1974 * be the context object to Array.prototype.slice.
1976 * @param {Array} array The array (or arguments object).
1977 * @param {Number} begin The index at which to begin. Negative values are offsets from
1978 * the end of the array.
1979 * @param {Number} end The index at which to end. The copied items do not include
1980 * end. Negative values are offsets from the end of the array. If end is omitted,
1981 * all items up to the end of the array are copied.
1982 * @return {Array} The copied piece of the array.
1984 // Note: IE6 will return [] on slice.call(x, undefined).
1985 slice: ([1,2].slice(1, undefined).length ?
1986 function (array, begin, end) {
1987 return slice.call(array, begin, end);
1989 // at least IE6 uses arguments.length for variadic signature
1990 function (array, begin, end) {
1991 // After tested for IE 6, the one below is of the best performance
1992 // see http://jsperf.com/slice-fix
1993 if (typeof begin === 'undefined') {
1994 return slice.call(array);
1996 if (typeof end === 'undefined') {
1997 return slice.call(array, begin);
1999 return slice.call(array, begin, end);
2004 * Sorts the elements of an Array.
2005 * By default, this method sorts the elements alphabetically and ascending.
2007 * @param {Array} array The array to sort.
2008 * @param {Function} sortFn (optional) The comparison function.
2009 * @return {Array} The sorted array.
2011 sort: function(array, sortFn) {
2014 return array.sort(sortFn);
2016 return array.sort();
2020 var length = array.length,
2025 for (; i < length; i++) {
2027 for (j = i + 1; j < length; j++) {
2029 comparison = sortFn(array[j], array[min]);
2030 if (comparison < 0) {
2033 } else if (array[j] < array[min]) {
2039 array[i] = array[min];
2048 * Recursively flattens into 1-d Array. Injects Arrays inline.
2050 * @param {Array} array The array to flatten
2051 * @return {Array} The 1-d array.
2053 flatten: function(array) {
2056 function rFlatten(a) {
2059 for (i = 0, ln = a.length; i < ln; i++) {
2062 if (Ext.isArray(v)) {
2072 return rFlatten(array);
2076 * Returns the minimum value in the Array.
2078 * @param {Array/NodeList} array The Array from which to select the minimum value.
2079 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
2080 * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
2081 * @return {Object} minValue The minimum value
2083 min: function(array, comparisonFn) {
2087 for (i = 0, ln = array.length; i < ln; i++) {
2091 if (comparisonFn(min, item) === 1) {
2106 * Returns the maximum value in the Array.
2108 * @param {Array/NodeList} array The Array from which to select the maximum value.
2109 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
2110 * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
2111 * @return {Object} maxValue The maximum value
2113 max: function(array, comparisonFn) {
2117 for (i = 0, ln = array.length; i < ln; i++) {
2121 if (comparisonFn(max, item) === -1) {
2136 * Calculates the mean of all items in the array.
2138 * @param {Array} array The Array to calculate the mean value of.
2139 * @return {Number} The mean.
2141 mean: function(array) {
2142 return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
2146 * Calculates the sum of all items in the given array.
2148 * @param {Array} array The Array to calculate the sum value of.
2149 * @return {Number} The sum.
2151 sum: function(array) {
2155 for (i = 0,ln = array.length; i < ln; i++) {
2164 _replaceSim: replaceSim, // for unit testing
2165 _spliceSim: spliceSim,
2168 * Removes items from an array. This is functionally equivalent to the splice method
2169 * of Array, but works around bugs in IE8's splice method and does not copy the
2170 * removed elements in order to return them (because very often they are ignored).
2172 * @param {Array} array The Array on which to replace.
2173 * @param {Number} index The index in the array at which to operate.
2174 * @param {Number} removeCount The number of items to remove at index.
2175 * @return {Array} The array passed.
2181 * Inserts items in to an array.
2183 * @param {Array} array The Array on which to replace.
2184 * @param {Number} index The index in the array at which to operate.
2185 * @param {Array} items The array of items to insert at index.
2186 * @return {Array} The array passed.
2188 insert: function (array, index, items) {
2189 return replace(array, index, 0, items);
2193 * Replaces items in an array. This is functionally equivalent to the splice method
2194 * of Array, but works around bugs in IE8's splice method and is often more convenient
2195 * to call because it accepts an array of items to insert rather than use a variadic
2198 * @param {Array} array The Array on which to replace.
2199 * @param {Number} index The index in the array at which to operate.
2200 * @param {Number} removeCount The number of items to remove at index (can be 0).
2201 * @param {Array} insert (optional) An array of items to insert at index.
2202 * @return {Array} The array passed.
2208 * Replaces items in an array. This is equivalent to the splice method of Array, but
2209 * works around bugs in IE8's splice method. The signature is exactly the same as the
2210 * splice method except that the array is the first argument. All arguments following
2211 * removeCount are inserted in the array at index.
2213 * @param {Array} array The Array on which to replace.
2214 * @param {Number} index The index in the array at which to operate.
2215 * @param {Number} removeCount The number of items to remove at index (can be 0).
2216 * @return {Array} An array containing the removed items.
2225 * @alias Ext.Array#each
2227 Ext.each = ExtArray.each;
2232 * @alias Ext.Array#merge
2234 ExtArray.union = ExtArray.merge;
2237 * Old alias to {@link Ext.Array#min}
2238 * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
2241 * @alias Ext.Array#min
2243 Ext.min = ExtArray.min;
2246 * Old alias to {@link Ext.Array#max}
2247 * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
2250 * @alias Ext.Array#max
2252 Ext.max = ExtArray.max;
2255 * Old alias to {@link Ext.Array#sum}
2256 * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
2259 * @alias Ext.Array#sum
2261 Ext.sum = ExtArray.sum;
2264 * Old alias to {@link Ext.Array#mean}
2265 * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
2268 * @alias Ext.Array#mean
2270 Ext.mean = ExtArray.mean;
2273 * Old alias to {@link Ext.Array#flatten}
2274 * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
2277 * @alias Ext.Array#flatten
2279 Ext.flatten = ExtArray.flatten;
2282 * Old alias to {@link Ext.Array#clean}
2283 * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
2286 * @alias Ext.Array#clean
2288 Ext.clean = ExtArray.clean;
2291 * Old alias to {@link Ext.Array#unique}
2292 * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
2295 * @alias Ext.Array#unique
2297 Ext.unique = ExtArray.unique;
2300 * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2301 * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2304 * @alias Ext.Array#pluck
2306 Ext.pluck = ExtArray.pluck;
2311 * @alias Ext.Array#toArray
2313 Ext.toArray = function() {
2314 return ExtArray.toArray.apply(ExtArray, arguments);
2319 * @class Ext.Function
2321 * A collection of useful static methods to deal with function callbacks
2327 * A very commonly used method throughout the framework. It acts as a wrapper around another method
2328 * which originally accepts 2 arguments for `name` and `value`.
2329 * The wrapped function then allows "flexible" value setting of either:
2331 * - `name` and `value` as 2 arguments
2332 * - one single object argument with multiple key - value pairs
2336 * var setValue = Ext.Function.flexSetter(function(name, value) {
2337 * this[name] = value;
2341 * // Setting a single name - value
2342 * setValue('name1', 'value1');
2344 * // Settings multiple name - value pairs
2351 * @param {Function} setter
2352 * @returns {Function} flexSetter
2354 flexSetter: function(fn) {
2355 return function(a, b) {
2362 if (typeof a !== 'string') {
2364 if (a.hasOwnProperty(k)) {
2365 fn.call(this, k, a[k]);
2369 if (Ext.enumerables) {
2370 for (i = Ext.enumerables.length; i--;) {
2371 k = Ext.enumerables[i];
2372 if (a.hasOwnProperty(k)) {
2373 fn.call(this, k, a[k]);
2378 fn.call(this, a, b);
2386 * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
2387 * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2389 * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
2391 * @param {Function} fn The function to delegate.
2392 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2393 * **If omitted, defaults to the browser window.**
2394 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2395 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2396 * if a number the args are inserted at the specified position
2397 * @return {Function} The new function
2399 bind: function(fn, scope, args, appendArgs) {
2400 if (arguments.length === 2) {
2402 return fn.apply(scope, arguments);
2407 slice = Array.prototype.slice;
2410 var callArgs = args || arguments;
2412 if (appendArgs === true) {
2413 callArgs = slice.call(arguments, 0);
2414 callArgs = callArgs.concat(args);
2416 else if (typeof appendArgs == 'number') {
2417 callArgs = slice.call(arguments, 0); // copy arguments first
2418 Ext.Array.insert(callArgs, appendArgs, args);
2421 return method.apply(scope || window, callArgs);
2426 * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
2427 * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2428 * This is especially useful when creating callbacks.
2432 * var originalFunction = function(){
2433 * alert(Ext.Array.from(arguments).join(' '));
2436 * var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2438 * callback(); // alerts 'Hello World'
2439 * callback('by Me'); // alerts 'Hello World by Me'
2441 * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
2443 * @param {Function} fn The original function
2444 * @param {Array} args The arguments to pass to new callback
2445 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2446 * @return {Function} The new callback function
2448 pass: function(fn, args, scope) {
2450 args = Ext.Array.from(args);
2454 return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2459 * Create an alias to the provided method property with name `methodName` of `object`.
2460 * Note that the execution scope will still be bound to the provided `object` itself.
2462 * @param {Object/Function} object
2463 * @param {String} methodName
2464 * @return {Function} aliasFn
2466 alias: function(object, methodName) {
2468 return object[methodName].apply(object, arguments);
2473 * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2474 * the original one is not called. The resulting function returns the results of the original function.
2475 * The passed function is called with the parameters of the original function. Example usage:
2477 * var sayHi = function(name){
2478 * alert('Hi, ' + name);
2481 * sayHi('Fred'); // alerts "Hi, Fred"
2483 * // create a new function that validates input without
2484 * // directly modifying the original function:
2485 * var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2486 * return name == 'Brian';
2489 * sayHiToFriend('Fred'); // no alert
2490 * sayHiToFriend('Brian'); // alerts "Hi, Brian"
2492 * @param {Function} origFn The original function.
2493 * @param {Function} newFn The function to call before the original
2494 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2495 * **If omitted, defaults to the scope in which the original function is called or the browser window.**
2496 * @param {Object} returnValue (optional) The value to return if the passed function return false (defaults to null).
2497 * @return {Function} The new function
2499 createInterceptor: function(origFn, newFn, scope, returnValue) {
2500 var method = origFn;
2501 if (!Ext.isFunction(newFn)) {
2509 newFn.method = origFn;
2510 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2516 * Creates a delegate (callback) which, when called, executes after a specific delay.
2518 * @param {Function} fn The function which will be called on a delay when the returned function is called.
2519 * Optionally, a replacement (or additional) argument list may be specified.
2520 * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2521 * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
2522 * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2523 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2524 * if a number the args are inserted at the specified position.
2525 * @return {Function} A function which, when called, executes the original function after the specified delay.
2527 createDelayed: function(fn, delay, scope, args, appendArgs) {
2528 if (scope || args) {
2529 fn = Ext.Function.bind(fn, scope, args, appendArgs);
2533 setTimeout(function() {
2534 fn.apply(me, arguments);
2540 * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2542 * var sayHi = function(name){
2543 * alert('Hi, ' + name);
2546 * // executes immediately:
2549 * // executes after 2 seconds:
2550 * Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2552 * // this syntax is sometimes useful for deferring
2553 * // execution of an anonymous function:
2554 * Ext.Function.defer(function(){
2555 * alert('Anonymous');
2558 * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
2560 * @param {Function} fn The function to defer.
2561 * @param {Number} millis The number of milliseconds for the setTimeout call
2562 * (if less than or equal to 0 the function is executed immediately)
2563 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2564 * **If omitted, defaults to the browser window.**
2565 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2566 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2567 * if a number the args are inserted at the specified position
2568 * @return {Number} The timeout id that can be used with clearTimeout
2570 defer: function(fn, millis, obj, args, appendArgs) {
2571 fn = Ext.Function.bind(fn, obj, args, appendArgs);
2573 return setTimeout(fn, millis);
2580 * Create a combined function call sequence of the original function + the passed function.
2581 * The resulting function returns the results of the original function.
2582 * The passed function is called with the parameters of the original function. Example usage:
2584 * var sayHi = function(name){
2585 * alert('Hi, ' + name);
2588 * sayHi('Fred'); // alerts "Hi, Fred"
2590 * var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2591 * alert('Bye, ' + name);
2594 * sayGoodbye('Fred'); // both alerts show
2596 * @param {Function} origFn The original function.
2597 * @param {Function} newFn The function to sequence
2598 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2599 * If omitted, defaults to the scope in which the original function is called or the browser window.
2600 * @return {Function} The new function
2602 createSequence: function(origFn, newFn, scope) {
2603 if (!Ext.isFunction(newFn)) {
2608 var retval = origFn.apply(this || window, arguments);
2609 newFn.apply(scope || this || window, arguments);
2616 * Creates a delegate function, optionally with a bound scope which, when called, buffers
2617 * the execution of the passed function for the configured number of milliseconds.
2618 * If called again within that period, the impending invocation will be canceled, and the
2619 * timeout period will begin again.
2621 * @param {Function} fn The function to invoke on a buffered timer.
2622 * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2624 * @param {Object} scope (optional) The scope (`this` reference) in which
2625 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2626 * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2627 * passed by the caller.
2628 * @return {Function} A function which invokes the passed function after buffering for the specified time.
2630 createBuffered: function(fn, buffer, scope, args) {
2636 clearTimeout(timerId);
2639 timerId = setTimeout(function(){
2640 fn.apply(scope || me, args || arguments);
2647 * Creates a throttled version of the passed function which, when called repeatedly and
2648 * rapidly, invokes the passed function only after a certain interval has elapsed since the
2649 * previous invocation.
2651 * This is useful for wrapping functions which may be called repeatedly, such as
2652 * a handler of a mouse move event when the processing is expensive.
2654 * @param {Function} fn The function to execute at a regular time interval.
2655 * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
2656 * @param {Object} scope (optional) The scope (`this` reference) in which
2657 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2658 * @returns {Function} A function which invokes the passed function at the specified interval.
2660 createThrottled: function(fn, interval, scope) {
2661 var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2662 fn.apply(scope || this, lastArgs);
2663 lastCallTime = new Date().getTime();
2667 elapsed = new Date().getTime() - lastCallTime;
2668 lastArgs = arguments;
2670 clearTimeout(timer);
2671 if (!lastCallTime || (elapsed >= interval)) {
2674 timer = setTimeout(execute, interval - elapsed);
2680 * Adds behavior to an existing method that is executed before the
2681 * original behavior of the function. For example:
2685 * add: function(ingredient) {
2686 * this.contents.push(ingredient);
2689 * Ext.Function.interceptBefore(soup, "add", function(ingredient){
2690 * if (!this.contents.length && ingredient !== "water") {
2691 * // Always add water to start with
2692 * this.contents.push("water");
2695 * soup.add("onions");
2697 * soup.contents; // will contain: water, onions, salt
2699 * @param {Object} object The target object
2700 * @param {String} methodName Name of the method to override
2701 * @param {Function} fn Function with the new behavior. It will
2702 * be called with the same arguments as the original method. The
2703 * return value of this function will be the return value of the
2705 * @return {Function} The new function just created.
2707 interceptBefore: function(object, methodName, fn) {
2708 var method = object[methodName] || Ext.emptyFn;
2710 return object[methodName] = function() {
2711 var ret = fn.apply(this, arguments);
2712 method.apply(this, arguments);
2719 * Adds behavior to an existing method that is executed after the
2720 * original behavior of the function. For example:
2724 * add: function(ingredient) {
2725 * this.contents.push(ingredient);
2728 * Ext.Function.interceptAfter(soup, "add", function(ingredient){
2729 * // Always add a bit of extra salt
2730 * this.contents.push("salt");
2732 * soup.add("water");
2733 * soup.add("onions");
2734 * soup.contents; // will contain: water, salt, onions, salt
2736 * @param {Object} object The target object
2737 * @param {String} methodName Name of the method to override
2738 * @param {Function} fn Function with the new behavior. It will
2739 * be called with the same arguments as the original method. The
2740 * return value of this function will be the return value of the
2742 * @return {Function} The new function just created.
2744 interceptAfter: function(object, methodName, fn) {
2745 var method = object[methodName] || Ext.emptyFn;
2747 return object[methodName] = function() {
2748 method.apply(this, arguments);
2749 return fn.apply(this, arguments);
2757 * @alias Ext.Function#defer
2759 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2764 * @alias Ext.Function#pass
2766 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2771 * @alias Ext.Function#bind
2773 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2776 * @author Jacky Nguyen <jacky@sencha.com>
2777 * @docauthor Jacky Nguyen <jacky@sencha.com>
2780 * A collection of useful static methods to deal with objects.
2787 var ExtObject = Ext.Object = {
2790 * Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
2791 * query strings. For example:
2793 * var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2795 * // objects then equals:
2797 * { name: 'hobbies', value: 'reading' },
2798 * { name: 'hobbies', value: 'cooking' },
2799 * { name: 'hobbies', value: 'swimming' },
2802 * var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2810 * }, true); // Recursive
2812 * // objects then equals:
2814 * { name: 'dateOfBirth[day]', value: 3 },
2815 * { name: 'dateOfBirth[month]', value: 8 },
2816 * { name: 'dateOfBirth[year]', value: 1987 },
2817 * { name: 'dateOfBirth[extra][hour]', value: 4 },
2818 * { name: 'dateOfBirth[extra][minute]', value: 30 },
2821 * @param {String} name
2822 * @param {Object/Array} value
2823 * @param {Boolean} [recursive=false] True to traverse object recursively
2826 toQueryObjects: function(name, value, recursive) {
2827 var self = ExtObject.toQueryObjects,
2831 if (Ext.isArray(value)) {
2832 for (i = 0, ln = value.length; i < ln; i++) {
2834 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2844 else if (Ext.isObject(value)) {
2846 if (value.hasOwnProperty(i)) {
2848 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2870 * Takes an object and converts it to an encoded query string.
2874 * Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2875 * Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2876 * Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2877 * Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2878 * Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2882 * Ext.Object.toQueryString({
2883 * username: 'Jacky',
2889 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2890 * }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2892 * // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2893 * // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2895 * @param {Object} object The object to encode
2896 * @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
2897 * (PHP / Ruby on Rails servers and similar).
2898 * @return {String} queryString
2900 toQueryString: function(object, recursive) {
2901 var paramObjects = [],
2903 i, j, ln, paramObject, value;
2906 if (object.hasOwnProperty(i)) {
2907 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2911 for (j = 0, ln = paramObjects.length; j < ln; j++) {
2912 paramObject = paramObjects[j];
2913 value = paramObject.value;
2915 if (Ext.isEmpty(value)) {
2918 else if (Ext.isDate(value)) {
2919 value = Ext.Date.toString(value);
2922 params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2925 return params.join('&');
2929 * Converts a query string back into an object.
2933 * Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2934 * Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2935 * Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2936 * Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2940 * Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true);
2943 * username: 'Jacky',
2949 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2952 * @param {String} queryString The query string to decode
2953 * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
2954 * PHP / Ruby on Rails servers and similar.
2957 fromQueryString: function(queryString, recursive) {
2958 var parts = queryString.replace(/^\?/, '').split('&'),
2960 temp, components, name, value, i, ln,
2961 part, j, subLn, matchedKeys, matchedName,
2964 for (i = 0, ln = parts.length; i < ln; i++) {
2967 if (part.length > 0) {
2968 components = part.split('=');
2969 name = decodeURIComponent(components[0]);
2970 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2973 if (object.hasOwnProperty(name)) {
2974 if (!Ext.isArray(object[name])) {
2975 object[name] = [object[name]];
2978 object[name].push(value);
2981 object[name] = value;
2985 matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2986 matchedName = name.match(/^([^\[]+)/);
2990 sourceClass: "Ext.Object",
2991 sourceMethod: "fromQueryString",
2992 queryString: queryString,
2993 recursive: recursive,
2994 msg: 'Malformed query string given, failed parsing name from "' + part + '"'
2998 name = matchedName[0];
3001 if (matchedKeys === null) {
3002 object[name] = value;
3006 for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
3007 key = matchedKeys[j];
3008 key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
3016 for (j = 0, subLn = keys.length; j < subLn; j++) {
3019 if (j === subLn - 1) {
3020 if (Ext.isArray(temp) && key === '') {
3028 if (temp[key] === undefined || typeof temp[key] === 'string') {
3029 nextKey = keys[j+1];
3031 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
3045 * Iterates through an object and invokes the given callback function for each iteration.
3046 * The iteration can be stopped by returning `false` in the callback function. For example:
3050 * hairColor: 'black'
3051 * loves: ['food', 'sleeping', 'wife']
3054 * Ext.Object.each(person, function(key, value, myself) {
3055 * console.log(key + ":" + value);
3057 * if (key === 'hairColor') {
3058 * return false; // stop the iteration
3062 * @param {Object} object The object to iterate
3063 * @param {Function} fn The callback function.
3064 * @param {String} fn.key
3065 * @param {Object} fn.value
3066 * @param {Object} fn.object The object itself
3067 * @param {Object} [scope] The execution scope (`this`) of the callback function
3069 each: function(object, fn, scope) {
3070 for (var property in object) {
3071 if (object.hasOwnProperty(property)) {
3072 if (fn.call(scope || object, property, object[property], object) === false) {
3080 * Merges any number of objects recursively without referencing them or their children.
3083 * companyName: 'Ext JS',
3084 * products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
3088 * location: 'Palo Alto',
3094 * companyName: 'Sencha Inc.',
3095 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3098 * location: 'Redwood City'
3102 * var sencha = Ext.Object.merge(extjs, newStuff);
3104 * // extjs and sencha then equals to
3106 * companyName: 'Sencha Inc.',
3107 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3111 * location: 'Redwood City'
3116 * @param {Object...} object Any number of objects to merge.
3117 * @return {Object} merged The object that is created as a result of merging all the objects passed in.
3119 merge: function(source, key, value) {
3120 if (typeof key === 'string') {
3121 if (value && value.constructor === Object) {
3122 if (source[key] && source[key].constructor === Object) {
3123 ExtObject.merge(source[key], value);
3126 source[key] = Ext.clone(value);
3130 source[key] = value;
3137 ln = arguments.length,
3140 for (; i < ln; i++) {
3141 object = arguments[i];
3143 for (property in object) {
3144 if (object.hasOwnProperty(property)) {
3145 ExtObject.merge(source, property, object[property]);
3154 * Returns the first matching key corresponding to the given value.
3155 * If no matching value is found, null is returned.
3162 * alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
3164 * @param {Object} object
3165 * @param {Object} value The value to find
3167 getKey: function(object, value) {
3168 for (var property in object) {
3169 if (object.hasOwnProperty(property) && object[property] === value) {
3178 * Gets all values of the given object as an array.
3180 * var values = Ext.Object.getValues({
3183 * }); // ['Jacky', 'food']
3185 * @param {Object} object
3186 * @return {Array} An array of values from the object
3188 getValues: function(object) {
3192 for (property in object) {
3193 if (object.hasOwnProperty(property)) {
3194 values.push(object[property]);
3202 * Gets all keys of the given object as an array.
3204 * var values = Ext.Object.getKeys({
3207 * }); // ['name', 'loves']
3209 * @param {Object} object
3210 * @return {String[]} An array of keys from the object
3213 getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
3217 for (property in object) {
3218 if (object.hasOwnProperty(property)) {
3219 keys.push(property);
3227 * Gets the total number of this object's own properties
3229 * var size = Ext.Object.getSize({
3232 * }); // size equals 2
3234 * @param {Object} object
3235 * @return {Number} size
3237 getSize: function(object) {
3241 for (property in object) {
3242 if (object.hasOwnProperty(property)) {
3253 * A convenient alias method for {@link Ext.Object#merge}.
3257 * @alias Ext.Object#merge
3259 Ext.merge = Ext.Object.merge;
3262 * Alias for {@link Ext.Object#toQueryString}.
3266 * @alias Ext.Object#toQueryString
3267 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
3269 Ext.urlEncode = function() {
3270 var args = Ext.Array.from(arguments),
3273 // Support for the old `pre` argument
3274 if ((typeof args[1] === 'string')) {
3275 prefix = args[1] + '&';
3279 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
3283 * Alias for {@link Ext.Object#fromQueryString}.
3287 * @alias Ext.Object#fromQueryString
3288 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
3290 Ext.urlDecode = function() {
3291 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
3298 * A set of useful static methods to deal with date
3299 * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
3300 * this object for convenience
3302 * The date parsing and formatting syntax contains a subset of
3303 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
3304 * supported will provide results equivalent to their PHP versions.
3306 * The following is a list of all currently supported formats:
3308 Format Description Example returned values
3309 ------ ----------------------------------------------------------------------- -----------------------
3310 d Day of the month, 2 digits with leading zeros 01 to 31
3311 D A short textual representation of the day of the week Mon to Sun
3312 j Day of the month without leading zeros 1 to 31
3313 l A full textual representation of the day of the week Sunday to Saturday
3314 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
3315 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
3316 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
3317 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
3318 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
3319 F A full textual representation of a month, such as January or March January to December
3320 m Numeric representation of a month, with leading zeros 01 to 12
3321 M A short textual representation of a month Jan to Dec
3322 n Numeric representation of a month, without leading zeros 1 to 12
3323 t Number of days in the given month 28 to 31
3324 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
3325 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
3326 belongs to the previous or next year, that year is used instead)
3327 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
3328 y A two digit representation of a year Examples: 99 or 03
3329 a Lowercase Ante meridiem and Post meridiem am or pm
3330 A Uppercase Ante meridiem and Post meridiem AM or PM
3331 g 12-hour format of an hour without leading zeros 1 to 12
3332 G 24-hour format of an hour without leading zeros 0 to 23
3333 h 12-hour format of an hour with leading zeros 01 to 12
3334 H 24-hour format of an hour with leading zeros 00 to 23
3335 i Minutes, with leading zeros 00 to 59
3336 s Seconds, with leading zeros 00 to 59
3337 u Decimal fraction of a second Examples:
3338 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
3339 100 (i.e. 0.100s) or
3340 999 (i.e. 0.999s) or
3341 999876543210 (i.e. 0.999876543210s)
3342 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
3343 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
3344 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
3345 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
3348 1) If unspecified, the month / day defaults to the current month / day, 1991 or
3349 the time defaults to midnight, while the timezone defaults to the 1992-10 or
3350 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
3351 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
3352 are optional. 1995-07-18T17:21:28-02:00 or
3353 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
3354 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
3355 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
3356 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
3357 date-time granularity which are supported, or see 2000-02-13T21:25:33
3358 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
3359 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
3360 MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
3361 \/Date(1238606590509+0800)\/
3364 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
3367 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
3369 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
3370 console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
3371 console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
3372 console.log(Ext.Date.format(dt, 'l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
3375 * Here are some standard date/time patterns that you might find helpful. They
3376 * are not part of the source of Ext.Date, but to use them you can simply copy this
3377 * block of code into any script that is included after Ext.Date and they will also become
3378 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
3380 Ext.Date.patterns = {
3381 ISO8601Long:"Y-m-d H:i:s",
3382 ISO8601Short:"Y-m-d",
3384 LongDate: "l, F d, Y",
3385 FullDateTime: "l, F d, Y g:i:s A",
3388 LongTime: "g:i:s A",
3389 SortableDateTime: "Y-m-d\\TH:i:s",
3390 UniversalSortableDateTime: "Y-m-d H:i:sO",
3397 var dt = new Date();
3398 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3400 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3401 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3406 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3407 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3408 * They generate precompiled functions from format patterns instead of parsing and
3409 * processing each pattern every time a date is formatted. These functions are available
3410 * on every Date object.
3415 // create private copy of Ext's Ext.util.Format.format() method
3416 // - to remove unnecessary dependency
3417 // - to resolve namespace conflict with MS-Ajax's implementation
3418 function xf(format) {
3419 var args = Array.prototype.slice.call(arguments, 1);
3420 return format.replace(/\{(\d+)\}/g, function(m, i) {
3427 * Returns the current timestamp
3428 * @return {Date} The current timestamp
3431 now: Date.now || function() {
3439 toString: function(date) {
3440 var pad = Ext.String.leftPad;
3442 return date.getFullYear() + "-"
3443 + pad(date.getMonth() + 1, 2, '0') + "-"
3444 + pad(date.getDate(), 2, '0') + "T"
3445 + pad(date.getHours(), 2, '0') + ":"
3446 + pad(date.getMinutes(), 2, '0') + ":"
3447 + pad(date.getSeconds(), 2, '0');
3451 * Returns the number of milliseconds between two dates
3452 * @param {Date} dateA The first date
3453 * @param {Date} dateB (optional) The second date, defaults to now
3454 * @return {Number} The difference in milliseconds
3456 getElapsed: function(dateA, dateB) {
3457 return Math.abs(dateA - (dateB || new Date()));
3461 * Global flag which determines if strict date parsing should be used.
3462 * Strict date parsing will not roll-over invalid dates, which is the
3463 * default behaviour of javascript Date objects.
3464 * (see {@link #parse} for more information)
3465 * Defaults to <tt>false</tt>.
3471 formatCodeToRegex: function(character, currentGroup) {
3472 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3473 var p = utilDate.parseCodes[character];
3476 p = typeof p == 'function'? p() : p;
3477 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3480 return p ? Ext.applyIf({
3481 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3485 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3490 * <p>An object hash in which each property is a date parsing function. The property name is the
3491 * format string which that function parses.</p>
3492 * <p>This object is automatically populated with date parsing functions as
3493 * date formats are requested for Ext standard formatting strings.</p>
3494 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3495 * may be used as a format string to {@link #parse}.<p>
3496 * <p>Example:</p><pre><code>
3497 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3499 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3500 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3501 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3502 * (i.e. prevent javascript Date "rollover") (The default must be false).
3503 * Invalid date strings should return null when parsed.</div></li>
3505 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3506 * formatting function must be placed into the {@link #formatFunctions} property.
3507 * @property parseFunctions
3511 "MS": function(input, strict) {
3512 // note: the timezone offset is ignored since the MS Ajax server sends
3513 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3514 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3515 var r = (input || '').match(re);
3516 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3522 * <p>An object hash in which each property is a date formatting function. The property name is the
3523 * format string which corresponds to the produced formatted date string.</p>
3524 * <p>This object is automatically populated with date formatting functions as
3525 * date formats are requested for Ext standard formatting strings.</p>
3526 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3527 * may be used as a format string to {@link #format}. Example:</p><pre><code>
3528 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3530 * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3531 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3533 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3534 * parsing function must be placed into the {@link #parseFunctions} property.
3535 * @property formatFunctions
3540 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3541 return '\\/Date(' + this.getTime() + ')\\/';
3548 * Date interval constant
3554 * Date interval constant
3560 * Date interval constant
3565 /** Date interval constant
3571 * Date interval constant
3577 * Date interval constant
3583 * Date interval constant
3589 * <p>An object hash containing default date values used during date parsing.</p>
3590 * <p>The following properties are available:<div class="mdetail-params"><ul>
3591 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3592 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3593 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3594 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3595 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3596 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3597 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3599 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3600 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3601 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3602 * It is the responsiblity of the developer to account for this.</b></p>
3605 // set default day value to the first day of the month
3606 Ext.Date.defaults.d = 1;
3608 // parse a February date string containing only year and month values.
3609 // setting the default day value to 1 prevents weird date rollover issues
3610 // when attempting to parse the following date string on, for example, March 31st 2009.
3611 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3613 * @property defaults
3619 * @property {String[]} dayNames
3620 * An array of textual day names.
3621 * Override these values for international dates.
3624 Ext.Date.dayNames = [
3642 * @property {String[]} monthNames
3643 * An array of textual month names.
3644 * Override these values for international dates.
3647 Ext.Date.monthNames = [
3670 * @property {Object} monthNumbers
3671 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3672 * Override these values for international dates.
3675 Ext.Date.monthNumbers = {
3676 'ShortJanNameInYourLang':0,
3677 'ShortFebNameInYourLang':1,
3697 * @property {String} defaultFormat
3698 * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
3699 * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.</p>
3700 * <p>This may be overridden in a locale file.</p>
3702 defaultFormat : "m/d/Y",
3704 * Get the short month name for the given month number.
3705 * Override this function for international dates.
3706 * @param {Number} month A zero-based javascript month number.
3707 * @return {String} The short month name.
3709 getShortMonthName : function(month) {
3710 return utilDate.monthNames[month].substring(0, 3);
3714 * Get the short day name for the given day number.
3715 * Override this function for international dates.
3716 * @param {Number} day A zero-based javascript day number.
3717 * @return {String} The short day name.
3719 getShortDayName : function(day) {
3720 return utilDate.dayNames[day].substring(0, 3);
3724 * Get the zero-based javascript month number for the given short/full month name.
3725 * Override this function for international dates.
3726 * @param {String} name The short/full month name.
3727 * @return {Number} The zero-based javascript month number.
3729 getMonthNumber : function(name) {
3730 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3731 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3735 * Checks if the specified format contains hour information
3736 * @param {String} format The format to check
3737 * @return {Boolean} True if the format contains hour information
3740 formatContainsHourInfo : (function(){
3741 var stripEscapeRe = /(\\.)/g,
3742 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3743 return function(format){
3744 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3749 * Checks if the specified format contains information about
3750 * anything other than the time.
3751 * @param {String} format The format to check
3752 * @return {Boolean} True if the format contains information about
3753 * date/day information.
3756 formatContainsDateInfo : (function(){
3757 var stripEscapeRe = /(\\.)/g,
3758 dateInfoRe = /([djzmnYycU]|MS)/;
3760 return function(format){
3761 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3766 * The base format-code to formatting-function hashmap used by the {@link #format} method.
3767 * Formatting functions are strings (or functions which return strings) which
3768 * will return the appropriate value when evaluated in the context of the Date object
3769 * from which the {@link #format} method is called.
3770 * Add to / override these mappings for custom date formatting.
3771 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3774 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3775 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3780 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3781 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3782 j: "this.getDate()",
3783 l: "Ext.Date.dayNames[this.getDay()]",
3784 N: "(this.getDay() ? this.getDay() : 7)",
3785 S: "Ext.Date.getSuffix(this)",
3787 z: "Ext.Date.getDayOfYear(this)",
3788 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3789 F: "Ext.Date.monthNames[this.getMonth()]",
3790 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3791 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3792 n: "(this.getMonth() + 1)",
3793 t: "Ext.Date.getDaysInMonth(this)",
3794 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3795 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3796 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3797 y: "('' + this.getFullYear()).substring(2, 4)",
3798 a: "(this.getHours() < 12 ? 'am' : 'pm')",
3799 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3800 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3801 G: "this.getHours()",
3802 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3803 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3804 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3805 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3806 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3807 O: "Ext.Date.getGMTOffset(this)",
3808 P: "Ext.Date.getGMTOffset(this, true)",
3809 T: "Ext.Date.getTimezone(this)",
3810 Z: "(this.getTimezoneOffset() * -60)",
3812 c: function() { // ISO-8601 -- GMT format
3813 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3814 var e = c.charAt(i);
3815 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3817 return code.join(" + ");
3820 c: function() { // ISO-8601 -- UTC format
3822 "this.getUTCFullYear()", "'-'",
3823 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3824 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3826 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3827 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3828 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3834 U: "Math.round(this.getTime() / 1000)"
3838 * Checks if the passed Date parameters will cause a javascript Date "rollover".
3839 * @param {Number} year 4-digit year
3840 * @param {Number} month 1-based month-of-year
3841 * @param {Number} day Day of month
3842 * @param {Number} hour (optional) Hour
3843 * @param {Number} minute (optional) Minute
3844 * @param {Number} second (optional) Second
3845 * @param {Number} millisecond (optional) Millisecond
3846 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3848 isValid : function(y, m, d, h, i, s, ms) {
3855 // Special handling for year < 100
3856 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3858 return y == dt.getFullYear() &&
3859 m == dt.getMonth() + 1 &&
3860 d == dt.getDate() &&
3861 h == dt.getHours() &&
3862 i == dt.getMinutes() &&
3863 s == dt.getSeconds() &&
3864 ms == dt.getMilliseconds();
3868 * Parses the passed string using the specified date format.
3869 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3870 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3871 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3872 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3873 * Keep in mind that the input date string must precisely match the specified format string
3874 * in order for the parse operation to be successful (failed parse operations return a null value).
3875 * <p>Example:</p><pre><code>
3876 //dt = Fri May 25 2007 (current date)
3877 var dt = new Date();
3879 //dt = Thu May 25 2006 (today's month/day in 2006)
3880 dt = Ext.Date.parse("2006", "Y");
3882 //dt = Sun Jan 15 2006 (all date parts specified)
3883 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3885 //dt = Sun Jan 15 2006 15:20:01
3886 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3888 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3889 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3891 * @param {String} input The raw date string.
3892 * @param {String} format The expected date string format.
3893 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3894 (defaults to false). Invalid date strings will return null when parsed.
3895 * @return {Date} The parsed Date.
3897 parse : function(input, format, strict) {
3898 var p = utilDate.parseFunctions;
3899 if (p[format] == null) {
3900 utilDate.createParser(format);
3902 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3906 parseDate: function(input, format, strict){
3907 return utilDate.parse(input, format, strict);
3912 getFormatCode : function(character) {
3913 var f = utilDate.formatCodes[character];
3916 f = typeof f == 'function'? f() : f;
3917 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3920 // note: unknown characters are treated as literals
3921 return f || ("'" + Ext.String.escape(character) + "'");
3925 createFormat : function(format) {
3930 for (var i = 0; i < format.length; ++i) {
3931 ch = format.charAt(i);
3932 if (!special && ch == "\\") {
3934 } else if (special) {
3936 code.push("'" + Ext.String.escape(ch) + "'");
3938 code.push(utilDate.getFormatCode(ch));
3941 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3945 createParser : (function() {
3947 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3948 "def = Ext.Date.defaults,",
3949 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3954 "if(u != null){", // i.e. unix time is defined
3955 "v = new Date(u * 1000);", // give top priority to UNIX time
3957 // create Date object representing midnight of the current day;
3958 // this will provide us with our date defaults
3959 // (note: clearTime() handles Daylight Saving Time automatically)
3960 "dt = Ext.Date.clearTime(new Date);",
3962 // date calculations (note: these calculations create a dependency on Ext.Number.from())
3963 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3964 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3965 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3967 // time calculations (note: these calculations create a dependency on Ext.Number.from())
3968 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3969 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3970 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3971 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3973 "if(z >= 0 && y >= 0){",
3974 // both the year and zero-based day of year are defined and >= 0.
3975 // these 2 values alone provide sufficient info to create a full date object
3977 // create Date object representing January 1st for the given year
3978 // handle years < 100 appropriately
3979 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3981 // then add day of year, checking for Date "rollover" if necessary
3982 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3983 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3984 "v = null;", // invalid date, so return null
3986 // plain old Date object
3987 // handle years < 100 properly
3988 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3994 // favour UTC offset over GMT offset
3996 // reset to UTC, then add offset
3997 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3999 // reset to GMT, then add offset
4000 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
4007 return function(format) {
4008 var regexNum = utilDate.parseRegexes.length,
4015 for (var i = 0; i < format.length; ++i) {
4016 ch = format.charAt(i);
4017 if (!special && ch == "\\") {
4019 } else if (special) {
4021 regex.push(Ext.String.escape(ch));
4023 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
4024 currentGroup += obj.g;
4026 if (obj.g && obj.c) {
4032 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4033 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
4041 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4042 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4043 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4047 c:"d = parseInt(results[{0}], 10);\n",
4048 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4052 c:"d = parseInt(results[{0}], 10);\n",
4053 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4056 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
4060 s:"(?:" + a.join("|") +")"
4067 s:"(?:" + utilDate.dayNames.join("|") + ")"
4073 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4083 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4087 c:"z = parseInt(results[{0}], 10);\n",
4088 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4093 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4098 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4099 s:"(" + utilDate.monthNames.join("|") + ")"
4103 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
4104 return Ext.applyIf({
4105 s:"(" + a.join("|") + ")"
4106 }, utilDate.formatCodeToRegex("F"));
4110 c:"m = parseInt(results[{0}], 10) - 1;\n",
4111 s:"(\\d{2})" // month number with leading zeros (01 - 12)
4115 c:"m = parseInt(results[{0}], 10) - 1;\n",
4116 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4121 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4129 return utilDate.formatCodeToRegex("Y");
4133 c:"y = parseInt(results[{0}], 10);\n",
4134 s:"(\\d{4})" // 4-digit year
4138 c:"var ty = parseInt(results[{0}], 10);\n"
4139 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4143 * In the am/pm parsing routines, we allow both upper and lower case
4144 * even though it doesn't exactly match the spec. It gives much more flexibility
4145 * in being able to specify case insensitive regexes.
4149 c:"if (/(am)/i.test(results[{0}])) {\n"
4150 + "if (!h || h == 12) { h = 0; }\n"
4151 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4156 c:"if (/(am)/i.test(results[{0}])) {\n"
4157 + "if (!h || h == 12) { h = 0; }\n"
4158 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4162 return utilDate.formatCodeToRegex("G");
4166 c:"h = parseInt(results[{0}], 10);\n",
4167 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4170 return utilDate.formatCodeToRegex("H");
4174 c:"h = parseInt(results[{0}], 10);\n",
4175 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
4179 c:"i = parseInt(results[{0}], 10);\n",
4180 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4184 c:"s = parseInt(results[{0}], 10);\n",
4185 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4189 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4190 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4195 "o = results[{0}];",
4196 "var sn = o.substring(0,1),", // get + / - sign
4197 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4198 "mn = o.substring(3,5) % 60;", // get minutes
4199 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
4201 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4206 "o = results[{0}];",
4207 "var sn = o.substring(0,1),", // get + / - sign
4208 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4209 "mn = o.substring(4,6) % 60;", // get minutes
4210 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
4212 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4217 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4221 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4222 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4223 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4228 utilDate.formatCodeToRegex("Y", 1), // year
4229 utilDate.formatCodeToRegex("m", 2), // month
4230 utilDate.formatCodeToRegex("d", 3), // day
4231 utilDate.formatCodeToRegex("h", 4), // hour
4232 utilDate.formatCodeToRegex("i", 5), // minute
4233 utilDate.formatCodeToRegex("s", 6), // second
4234 {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4235 {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
4236 "if(results[8]) {", // timezone specified
4237 "if(results[8] == 'Z'){",
4239 "}else if (results[8].indexOf(':') > -1){",
4240 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
4242 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
4248 for (var i = 0, l = arr.length; i < l; ++i) {
4249 calc.push(arr[i].c);
4256 arr[0].s, // year (required)
4257 "(?:", "-", arr[1].s, // month (optional)
4258 "(?:", "-", arr[2].s, // day (optional)
4260 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4261 arr[3].s, ":", arr[4].s, // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
4262 "(?::", arr[5].s, ")?", // seconds (optional)
4263 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4264 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4273 c:"u = parseInt(results[{0}], 10);\n",
4274 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4278 //Old Ext.Date prototype methods.
4280 dateFormat: function(date, format) {
4281 return utilDate.format(date, format);
4285 * Formats a date given the supplied format string.
4286 * @param {Date} date The date to format
4287 * @param {String} format The format string
4288 * @return {String} The formatted date
4290 format: function(date, format) {
4291 if (utilDate.formatFunctions[format] == null) {
4292 utilDate.createFormat(format);
4294 var result = utilDate.formatFunctions[format].call(date);
4299 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4301 * Note: The date string returned by the javascript Date object's toString() method varies
4302 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4303 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4304 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4305 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4306 * from the GMT offset portion of the date string.
4307 * @param {Date} date The date
4308 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4310 getTimezone : function(date) {
4311 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4313 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4314 // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
4315 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4316 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4317 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4319 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
4320 // step 1: (?:\((.*)\) -- find timezone in parentheses
4321 // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
4322 // step 3: remove all non uppercase characters found in step 1 and 2
4323 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
4327 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
4328 * @param {Date} date The date
4329 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
4330 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
4332 getGMTOffset : function(date, colon) {
4333 var offset = date.getTimezoneOffset();
4334 return (offset > 0 ? "-" : "+")
4335 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
4336 + (colon ? ":" : "")
4337 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
4341 * Get the numeric day number of the year, adjusted for leap year.
4342 * @param {Date} date The date
4343 * @return {Number} 0 to 364 (365 in leap years).
4345 getDayOfYear: function(date) {
4347 d = Ext.Date.clone(date),
4348 m = date.getMonth(),
4351 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
4352 num += utilDate.getDaysInMonth(d);
4354 return num + date.getDate() - 1;
4358 * Get the numeric ISO-8601 week number of the year.
4359 * (equivalent to the format specifier 'W', but without a leading zero).
4360 * @param {Date} date The date
4361 * @return {Number} 1 to 53
4364 getWeekOfYear : (function() {
4365 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4366 var ms1d = 864e5, // milliseconds in a day
4367 ms7d = 7 * ms1d; // milliseconds in a week
4369 return function(date) { // return a closure so constants get calculated only once
4370 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4371 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4372 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4374 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4379 * Checks if the current date falls within a leap year.
4380 * @param {Date} date The date
4381 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4383 isLeapYear : function(date) {
4384 var year = date.getFullYear();
4385 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4389 * Get the first day of the current month, adjusted for leap year. The returned value
4390 * is the numeric day index within the week (0-6) which can be used in conjunction with
4391 * the {@link #monthNames} array to retrieve the textual day name.
4394 var dt = new Date('1/10/2007'),
4395 firstDay = Ext.Date.getFirstDayOfMonth(dt);
4396 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4398 * @param {Date} date The date
4399 * @return {Number} The day number (0-6).
4401 getFirstDayOfMonth : function(date) {
4402 var day = (date.getDay() - (date.getDate() - 1)) % 7;
4403 return (day < 0) ? (day + 7) : day;
4407 * Get the last day of the current month, adjusted for leap year. The returned value
4408 * is the numeric day index within the week (0-6) which can be used in conjunction with
4409 * the {@link #monthNames} array to retrieve the textual day name.
4412 var dt = new Date('1/10/2007'),
4413 lastDay = Ext.Date.getLastDayOfMonth(dt);
4414 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4416 * @param {Date} date The date
4417 * @return {Number} The day number (0-6).
4419 getLastDayOfMonth : function(date) {
4420 return utilDate.getLastDateOfMonth(date).getDay();
4425 * Get the date of the first day of the month in which this date resides.
4426 * @param {Date} date The date
4429 getFirstDateOfMonth : function(date) {
4430 return new Date(date.getFullYear(), date.getMonth(), 1);
4434 * Get the date of the last day of the month in which this date resides.
4435 * @param {Date} date The date
4438 getLastDateOfMonth : function(date) {
4439 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4443 * Get the number of days in the current month, adjusted for leap year.
4444 * @param {Date} date The date
4445 * @return {Number} The number of days in the month.
4448 getDaysInMonth: (function() {
4449 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4451 return function(date) { // return a closure for efficiency
4452 var m = date.getMonth();
4454 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4459 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4460 * @param {Date} date The date
4461 * @return {String} 'st, 'nd', 'rd' or 'th'.
4463 getSuffix : function(date) {
4464 switch (date.getDate()) {
4481 * Creates and returns a new Date instance with the exact same date value as the called instance.
4482 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4483 * variable will also be changed. When the intention is to create a new variable that will not
4484 * modify the original instance, you should create a clone.
4486 * Example of correctly cloning a date:
4489 var orig = new Date('10/1/2006');
4492 console.log(orig); //returns 'Thu Oct 05 2006'!
4495 var orig = new Date('10/1/2006'),
4496 copy = Ext.Date.clone(orig);
4498 console.log(orig); //returns 'Thu Oct 01 2006'
4500 * @param {Date} date The date
4501 * @return {Date} The new Date instance.
4503 clone : function(date) {
4504 return new Date(date.getTime());
4508 * Checks if the current date is affected by Daylight Saving Time (DST).
4509 * @param {Date} date The date
4510 * @return {Boolean} True if the current date is affected by DST.
4512 isDST : function(date) {
4513 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4514 // courtesy of @geoffrey.mcgill
4515 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4519 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4520 * automatically adjusting for Daylight Saving Time (DST) where applicable.
4521 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4522 * @param {Date} date The date
4523 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4524 * @return {Date} this or the clone.
4526 clearTime : function(date, clone) {
4528 return Ext.Date.clearTime(Ext.Date.clone(date));
4531 // get current date before clearing time
4532 var d = date.getDate();
4538 date.setMilliseconds(0);
4540 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4541 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4542 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4544 // increment hour until cloned date == current date
4545 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4548 date.setHours(c.getHours());
4555 * Provides a convenient method for performing basic date arithmetic. This method
4556 * does not modify the Date instance being called - it creates and returns
4557 * a new Date instance containing the resulting date value.
4562 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4563 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4565 // Negative values will be subtracted:
4566 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4567 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4571 * @param {Date} date The date to modify
4572 * @param {String} interval A valid date interval enum value.
4573 * @param {Number} value The amount to add to the current date.
4574 * @return {Date} The new Date instance.
4576 add : function(date, interval, value) {
4577 var d = Ext.Date.clone(date),
4579 if (!interval || value === 0) return d;
4581 switch(interval.toLowerCase()) {
4582 case Ext.Date.MILLI:
4583 d.setMilliseconds(d.getMilliseconds() + value);
4585 case Ext.Date.SECOND:
4586 d.setSeconds(d.getSeconds() + value);
4588 case Ext.Date.MINUTE:
4589 d.setMinutes(d.getMinutes() + value);
4592 d.setHours(d.getHours() + value);
4595 d.setDate(d.getDate() + value);
4597 case Ext.Date.MONTH:
4598 var day = date.getDate();
4600 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4603 d.setMonth(date.getMonth() + value);
4606 d.setFullYear(date.getFullYear() + value);
4613 * Checks if a date falls on or between the given start and end dates.
4614 * @param {Date} date The date to check
4615 * @param {Date} start Start date
4616 * @param {Date} end End date
4617 * @return {Boolean} true if this date falls on or between the given start and end dates.
4619 between : function(date, start, end) {
4620 var t = date.getTime();
4621 return start.getTime() <= t && t <= end.getTime();
4624 //Maintains compatibility with old static and prototype window.Date methods.
4625 compat: function() {
4626 var nativeDate = window.Date,
4628 statics = ['useStrict', 'formatCodeToRegex', 'parseFunctions', 'parseRegexes', 'formatFunctions', 'y2kYear', 'MILLI', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH', 'YEAR', 'defaults', 'dayNames', 'monthNames', 'monthNumbers', 'getShortMonthName', 'getShortDayName', 'getMonthNumber', 'formatCodes', 'isValid', 'parseDate', 'getFormatCode', 'createFormat', 'createParser', 'parseCodes'],
4629 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4632 Ext.Array.forEach(statics, function(s) {
4633 nativeDate[s] = utilDate[s];
4636 //Append to prototype
4637 Ext.Array.forEach(proto, function(s) {
4638 nativeDate.prototype[s] = function() {
4639 var args = Array.prototype.slice.call(arguments);
4641 return utilDate[s].apply(utilDate, args);
4647 var utilDate = Ext.Date;
4652 * @author Jacky Nguyen <jacky@sencha.com>
4653 * @docauthor Jacky Nguyen <jacky@sencha.com>
4656 * The root of all classes created with {@link Ext#define}.
4658 * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
4659 * All prototype and static members of this class are inherited by all other classes.
4661 (function(flexSetter) {
4663 var Base = Ext.Base = function() {};
4665 $className: 'Ext.Base',
4670 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4671 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4672 * for a detailed comparison
4674 * Ext.define('My.Cat', {
4676 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4679 * constructor: function() {
4680 * alert(this.self.speciesName); / dependent on 'this'
4685 * clone: function() {
4686 * return new this.self();
4691 * Ext.define('My.SnowLeopard', {
4694 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4698 * var cat = new My.Cat(); // alerts 'Cat'
4699 * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
4701 * var clone = snowLeopard.clone();
4702 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4709 // Default constructor, simply returns `this`
4710 constructor: function() {
4714 //<feature classSystem.config>
4716 * Initialize configuration for this class. a typical example:
4718 * Ext.define('My.awesome.Class', {
4719 * // The default config
4725 * constructor: function(config) {
4726 * this.initConfig(config);
4732 * var awesome = new My.awesome.Class({
4733 * name: 'Super Awesome'
4736 * alert(awesome.getName()); // 'Super Awesome'
4739 * @param {Object} config
4740 * @return {Object} mixins The mixin prototypes as key - value pairs
4742 initConfig: function(config) {
4743 if (!this.$configInited) {
4744 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4746 this.applyConfig(this.config);
4748 this.$configInited = true;
4757 setConfig: function(config) {
4758 this.applyConfig(config || {});
4766 applyConfig: flexSetter(function(name, value) {
4767 var setter = 'set' + Ext.String.capitalize(name);
4769 if (typeof this[setter] === 'function') {
4770 this[setter].call(this, value);
4778 * Call the parent's overridden method. For example:
4780 * Ext.define('My.own.A', {
4781 * constructor: function(test) {
4786 * Ext.define('My.own.B', {
4787 * extend: 'My.own.A',
4789 * constructor: function(test) {
4792 * this.callParent([test + 1]);
4796 * Ext.define('My.own.C', {
4797 * extend: 'My.own.B',
4799 * constructor: function() {
4800 * alert("Going to call parent's overriden constructor...");
4802 * this.callParent(arguments);
4806 * var a = new My.own.A(1); // alerts '1'
4807 * var b = new My.own.B(1); // alerts '1', then alerts '2'
4808 * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4809 * // alerts '2', then alerts '3'
4812 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4813 * from the current method, for example: `this.callParent(arguments)`
4814 * @return {Object} Returns the result from the superclass' method
4816 callParent: function(args) {
4817 var method = this.callParent.caller,
4818 parentClass, methodName;
4820 if (!method.$owner) {
4821 if (!method.caller) {
4823 sourceClass: Ext.getClassName(this),
4824 sourceMethod: "callParent",
4825 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4829 method = method.caller;
4832 parentClass = method.$owner.superclass;
4833 methodName = method.$name;
4835 if (!(methodName in parentClass)) {
4837 sourceClass: Ext.getClassName(this),
4838 sourceMethod: methodName,
4839 msg: "this.callParent() was called but there's no such method (" + methodName +
4840 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4844 return parentClass[methodName].apply(this, args || []);
4849 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4850 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4851 * `this` points to during run-time
4853 * Ext.define('My.Cat', {
4856 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4859 * constructor: function() {
4860 * var statics = this.statics();
4862 * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
4863 * // equivalent to: My.Cat.speciesName
4865 * alert(this.self.speciesName); // dependent on 'this'
4867 * statics.totalCreated++;
4872 * clone: function() {
4873 * var cloned = new this.self; // dependent on 'this'
4875 * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
4882 * Ext.define('My.SnowLeopard', {
4886 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4889 * constructor: function() {
4890 * this.callParent();
4894 * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
4896 * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4898 * var clone = snowLeopard.clone();
4899 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4900 * alert(clone.groupName); // alerts 'Cat'
4902 * alert(My.Cat.totalCreated); // alerts 3
4905 * @return {Ext.Class}
4907 statics: function() {
4908 var method = this.statics.caller,
4915 return method.$owner;
4919 * Call the original method that was previously overridden with {@link Ext.Base#override}
4921 * Ext.define('My.Cat', {
4922 * constructor: function() {
4923 * alert("I'm a cat!");
4930 * constructor: function() {
4931 * alert("I'm going to be a cat!");
4933 * var instance = this.callOverridden();
4935 * alert("Meeeeoooowwww");
4941 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4942 * // alerts "I'm a cat!"
4943 * // alerts "Meeeeoooowwww"
4945 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4946 * @return {Object} Returns the result after calling the overridden method
4949 callOverridden: function(args) {
4950 var method = this.callOverridden.caller;
4952 if (!method.$owner) {
4954 sourceClass: Ext.getClassName(this),
4955 sourceMethod: "callOverridden",
4956 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4960 if (!method.$previous) {
4962 sourceClass: Ext.getClassName(this),
4963 sourceMethod: "callOverridden",
4964 msg: "this.callOverridden was called in '" + method.$name +
4965 "' but this method has never been overridden"
4969 return method.$previous.apply(this, args || []);
4972 destroy: function() {}
4975 // These static properties will be copied to every newly created class with {@link Ext#define}
4976 Ext.apply(Ext.Base, {
4978 * Create a new instance of this Class.
4980 * Ext.define('My.cool.Class', {
4984 * My.cool.Class.create({
4988 * All parameters are passed to the constructor of the class.
4990 * @return {Object} the created instance.
4994 create: function() {
4995 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
5002 own: function(name, value) {
5003 if (typeof value == 'function') {
5004 this.ownMethod(name, value);
5007 this.prototype[name] = value;
5015 ownMethod: function(name, fn) {
5018 if (typeof fn.$owner !== 'undefined' && fn !== Ext.emptyFn) {
5022 return originalFn.apply(this, arguments);
5027 className = Ext.getClassName(this);
5029 fn.displayName = className + '#' + name;
5034 this.prototype[name] = fn;
5038 * Add / override static properties of this class.
5040 * Ext.define('My.cool.Class', {
5044 * My.cool.Class.addStatics({
5045 * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
5046 * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
5047 * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
5050 * @param {Object} members
5051 * @return {Ext.Base} this
5055 addStatics: function(members) {
5056 for (var name in members) {
5057 if (members.hasOwnProperty(name)) {
5058 this[name] = members[name];
5067 * @param {Object} members
5069 addInheritableStatics: function(members) {
5070 var inheritableStatics,
5071 hasInheritableStatics,
5072 prototype = this.prototype,
5075 inheritableStatics = prototype.$inheritableStatics;
5076 hasInheritableStatics = prototype.$hasInheritableStatics;
5078 if (!inheritableStatics) {
5079 inheritableStatics = prototype.$inheritableStatics = [];
5080 hasInheritableStatics = prototype.$hasInheritableStatics = {};
5083 var className = Ext.getClassName(this);
5085 for (name in members) {
5086 if (members.hasOwnProperty(name)) {
5087 member = members[name];
5088 if (typeof member == 'function') {
5089 member.displayName = className + '.' + name;
5091 this[name] = member;
5093 if (!hasInheritableStatics[name]) {
5094 hasInheritableStatics[name] = true;
5095 inheritableStatics.push(name);
5104 * Add methods / properties to the prototype of this class.
5106 * Ext.define('My.awesome.Cat', {
5107 * constructor: function() {
5112 * My.awesome.Cat.implement({
5113 * meow: function() {
5114 * alert('Meowww...');
5118 * var kitty = new My.awesome.Cat;
5121 * @param {Object} members
5125 implement: function(members) {
5126 var prototype = this.prototype,
5127 enumerables = Ext.enumerables,
5129 var className = Ext.getClassName(this);
5130 for (name in members) {
5131 if (members.hasOwnProperty(name)) {
5132 member = members[name];
5134 if (typeof member === 'function') {
5135 member.$owner = this;
5136 member.$name = name;
5138 member.displayName = className + '#' + name;
5142 prototype[name] = member;
5147 for (i = enumerables.length; i--;) {
5148 name = enumerables[i];
5150 if (members.hasOwnProperty(name)) {
5151 member = members[name];
5152 member.$owner = this;
5153 member.$name = name;
5154 prototype[name] = member;
5161 * Borrow another class' members to the prototype of this class.
5163 * Ext.define('Bank', {
5165 * printMoney: function() {
5170 * Ext.define('Thief', {
5174 * Thief.borrow(Bank, ['money', 'printMoney']);
5176 * var steve = new Thief();
5178 * alert(steve.money); // alerts '$$$'
5179 * steve.printMoney(); // alerts '$$$$$$$'
5181 * @param {Ext.Base} fromClass The class to borrow members from
5182 * @param {String/String[]} members The names of the members to borrow
5183 * @return {Ext.Base} this
5187 borrow: function(fromClass, members) {
5188 var fromPrototype = fromClass.prototype,
5191 members = Ext.Array.from(members);
5193 for (i = 0, ln = members.length; i < ln; i++) {
5194 member = members[i];
5196 this.own(member, fromPrototype[member]);
5203 * Override prototype members of this class. Overridden methods can be invoked via
5204 * {@link Ext.Base#callOverridden}
5206 * Ext.define('My.Cat', {
5207 * constructor: function() {
5208 * alert("I'm a cat!");
5215 * constructor: function() {
5216 * alert("I'm going to be a cat!");
5218 * var instance = this.callOverridden();
5220 * alert("Meeeeoooowwww");
5226 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
5227 * // alerts "I'm a cat!"
5228 * // alerts "Meeeeoooowwww"
5230 * @param {Object} members
5231 * @return {Ext.Base} this
5235 override: function(members) {
5236 var prototype = this.prototype,
5237 enumerables = Ext.enumerables,
5238 name, i, member, previous;
5240 if (arguments.length === 2) {
5242 member = arguments[1];
5244 if (typeof member == 'function') {
5245 if (typeof prototype[name] == 'function') {
5246 previous = prototype[name];
5247 member.$previous = previous;
5250 this.ownMethod(name, member);
5253 prototype[name] = member;
5259 for (name in members) {
5260 if (members.hasOwnProperty(name)) {
5261 member = members[name];
5263 if (typeof member === 'function') {
5264 if (typeof prototype[name] === 'function') {
5265 previous = prototype[name];
5266 member.$previous = previous;
5269 this.ownMethod(name, member);
5272 prototype[name] = member;
5278 for (i = enumerables.length; i--;) {
5279 name = enumerables[i];
5281 if (members.hasOwnProperty(name)) {
5282 if (typeof prototype[name] !== 'undefined') {
5283 previous = prototype[name];
5284 members[name].$previous = previous;
5287 this.ownMethod(name, members[name]);
5295 //<feature classSystem.mixins>
5297 * Used internally by the mixins pre-processor
5301 mixin: function(name, cls) {
5302 var mixin = cls.prototype,
5303 my = this.prototype,
5306 for (key in mixin) {
5307 if (mixin.hasOwnProperty(key)) {
5308 if (typeof my[key] === 'undefined' && key !== 'mixins' && key !== 'mixinId') {
5309 if (typeof mixin[key] === 'function') {
5312 if (typeof fn.$owner === 'undefined') {
5313 this.ownMethod(key, fn);
5320 my[key] = mixin[key];
5323 //<feature classSystem.config>
5324 else if (key === 'config' && my.config && mixin.config) {
5325 Ext.Object.merge(my.config, mixin.config);
5331 if (typeof mixin.onClassMixedIn !== 'undefined') {
5332 mixin.onClassMixedIn.call(cls, this);
5335 if (!my.hasOwnProperty('mixins')) {
5336 if ('mixins' in my) {
5337 my.mixins = Ext.Object.merge({}, my.mixins);
5344 my.mixins[name] = mixin;
5349 * Get the current class' name in string format.
5351 * Ext.define('My.cool.Class', {
5352 * constructor: function() {
5353 * alert(this.self.getName()); // alerts 'My.cool.Class'
5357 * My.cool.Class.getName(); // 'My.cool.Class'
5359 * @return {String} className
5363 getName: function() {
5364 return Ext.getClassName(this);
5368 * Create aliases for existing prototype methods. Example:
5370 * Ext.define('My.cool.Class', {
5371 * method1: function() { ... },
5372 * method2: function() { ... }
5375 * var test = new My.cool.Class();
5377 * My.cool.Class.createAlias({
5378 * method3: 'method1',
5379 * method4: 'method2'
5382 * test.method3(); // test.method1()
5384 * My.cool.Class.createAlias('method5', 'method3');
5386 * test.method5(); // test.method3() -> test.method1()
5388 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
5389 * {@link Ext.Function#flexSetter flexSetter}
5390 * @param {String/Object} origin The original method name
5395 createAlias: flexSetter(function(alias, origin) {
5396 this.prototype[alias] = function() {
5397 return this[origin].apply(this, arguments);
5402 })(Ext.Function.flexSetter);
5405 * @author Jacky Nguyen <jacky@sencha.com>
5406 * @docauthor Jacky Nguyen <jacky@sencha.com>
5409 * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
5410 * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
5411 * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
5413 * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
5414 * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
5416 * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
5417 * from, see {@link Ext.Base}.
5423 baseStaticProperties = [],
5426 for (baseStaticProperty in Base) {
5427 if (Base.hasOwnProperty(baseStaticProperty)) {
5428 baseStaticProperties.push(baseStaticProperty);
5433 * @method constructor
5434 * Creates new class.
5435 * @param {Object} classData An object represent the properties of this class
5436 * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
5437 * Note that the creation process can be asynchronous depending on the pre-processors used.
5438 * @return {Ext.Base} The newly created class
5440 Ext.Class = Class = function(newClass, classData, onClassCreated) {
5441 if (typeof newClass != 'function') {
5442 onClassCreated = classData;
5443 classData = newClass;
5444 newClass = function() {
5445 return this.constructor.apply(this, arguments);
5453 var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5454 registeredPreprocessors = Class.getPreprocessors(),
5457 preprocessor, staticPropertyName, process, i, j, ln;
5459 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5460 staticPropertyName = baseStaticProperties[i];
5461 newClass[staticPropertyName] = Base[staticPropertyName];
5464 delete classData.preprocessors;
5466 for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5467 preprocessor = preprocessorStack[j];
5469 if (typeof preprocessor == 'string') {
5470 preprocessor = registeredPreprocessors[preprocessor];
5472 if (!preprocessor.always) {
5473 if (classData.hasOwnProperty(preprocessor.name)) {
5474 preprocessors.push(preprocessor.fn);
5478 preprocessors.push(preprocessor.fn);
5482 preprocessors.push(preprocessor);
5486 classData.onClassCreated = onClassCreated || Ext.emptyFn;
5488 classData.onBeforeClassCreated = function(cls, data) {
5489 onClassCreated = data.onClassCreated;
5491 delete data.onBeforeClassCreated;
5492 delete data.onClassCreated;
5494 cls.implement(data);
5496 onClassCreated.call(cls, cls);
5499 process = function(cls, data) {
5500 preprocessor = preprocessors[index++];
5502 if (!preprocessor) {
5503 data.onBeforeClassCreated.apply(this, arguments);
5507 if (preprocessor.call(this, cls, data, process) !== false) {
5508 process.apply(this, arguments);
5512 process.call(Class, newClass, classData);
5523 * Register a new pre-processor to be used during the class creation process
5526 * @param {String} name The pre-processor's name
5527 * @param {Function} fn The callback function to be executed. Typical format:
5529 * function(cls, data, fn) {
5532 * // Execute this when the processing is finished.
5533 * // Asynchronous processing is perfectly ok
5535 * fn.call(this, cls, data);
5539 * @param {Function} fn.cls The created class
5540 * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
5541 * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
5542 * regardless of whether the processing is synchronous or aynchronous
5544 * @return {Ext.Class} this
5547 registerPreprocessor: function(name, fn, always) {
5548 this.preprocessors[name] = {
5550 always: always || false,
5558 * Retrieve a pre-processor callback function by its name, which has been registered before
5560 * @param {String} name
5561 * @return {Function} preprocessor
5564 getPreprocessor: function(name) {
5565 return this.preprocessors[name];
5568 getPreprocessors: function() {
5569 return this.preprocessors;
5573 * Retrieve the array stack of default pre-processors
5575 * @return {Function[]} defaultPreprocessors
5578 getDefaultPreprocessors: function() {
5579 return this.defaultPreprocessors || [];
5583 * Set the default array stack of default pre-processors
5585 * @param {Function/Function[]} preprocessors
5586 * @return {Ext.Class} this
5589 setDefaultPreprocessors: function(preprocessors) {
5590 this.defaultPreprocessors = Ext.Array.from(preprocessors);
5596 * Inserts this pre-processor at a specific position in the stack, optionally relative to
5597 * any existing pre-processor. For example:
5599 * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5603 * fn.call(this, cls, data);
5605 * }).setDefaultPreprocessorPosition('debug', 'last');
5607 * @param {String} name The pre-processor name. Note that it needs to be registered with
5608 * {@link #registerPreprocessor registerPreprocessor} before this
5609 * @param {String} offset The insertion position. Four possible values are:
5610 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5611 * @param {String} relativeName
5612 * @return {Ext.Class} this
5615 setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5616 var defaultPreprocessors = this.defaultPreprocessors,
5619 if (typeof offset == 'string') {
5620 if (offset === 'first') {
5621 defaultPreprocessors.unshift(name);
5625 else if (offset === 'last') {
5626 defaultPreprocessors.push(name);
5631 offset = (offset === 'after') ? 1 : -1;
5634 index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5637 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
5645 * @cfg {String} extend
5646 * The parent class that this class extends. For example:
5648 * Ext.define('Person', {
5649 * say: function(text) { alert(text); }
5652 * Ext.define('Developer', {
5654 * say: function(text) { this.callParent(["print "+text]); }
5657 Class.registerPreprocessor('extend', function(cls, data) {
5658 var extend = data.extend,
5660 basePrototype = base.prototype,
5661 prototype = function() {},
5662 parent, i, k, ln, staticName, parentStatics,
5663 parentPrototype, clsPrototype;
5665 if (extend && extend !== Object) {
5672 parentPrototype = parent.prototype;
5674 prototype.prototype = parentPrototype;
5675 clsPrototype = cls.prototype = new prototype();
5677 if (!('$class' in parent)) {
5678 for (i in basePrototype) {
5679 if (!parentPrototype[i]) {
5680 parentPrototype[i] = basePrototype[i];
5685 clsPrototype.self = cls;
5687 cls.superclass = clsPrototype.superclass = parentPrototype;
5691 //<feature classSystem.inheritableStatics>
5692 // Statics inheritance
5693 parentStatics = parentPrototype.$inheritableStatics;
5695 if (parentStatics) {
5696 for (k = 0, ln = parentStatics.length; k < ln; k++) {
5697 staticName = parentStatics[k];
5699 if (!cls.hasOwnProperty(staticName)) {
5700 cls[staticName] = parent[staticName];
5706 //<feature classSystem.config>
5707 // Merge the parent class' config object without referencing it
5708 if (parentPrototype.config) {
5709 clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5712 clsPrototype.config = {};
5716 //<feature classSystem.onClassExtended>
5717 if (clsPrototype.$onExtended) {
5718 clsPrototype.$onExtended.call(cls, cls, data);
5721 if (data.onClassExtended) {
5722 clsPrototype.$onExtended = data.onClassExtended;
5723 delete data.onClassExtended;
5729 //<feature classSystem.statics>
5731 * @cfg {Object} statics
5732 * List of static methods for this class. For example:
5734 * Ext.define('Computer', {
5736 * factory: function(brand) {
5737 * // 'this' in static methods refer to the class itself
5738 * return new this(brand);
5742 * constructor: function() { ... }
5745 * var dellComputer = Computer.factory('Dell');
5747 Class.registerPreprocessor('statics', function(cls, data) {
5748 cls.addStatics(data.statics);
5750 delete data.statics;
5754 //<feature classSystem.inheritableStatics>
5756 * @cfg {Object} inheritableStatics
5757 * List of inheritable static methods for this class.
5758 * Otherwise just like {@link #statics} but subclasses inherit these methods.
5760 Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5761 cls.addInheritableStatics(data.inheritableStatics);
5763 delete data.inheritableStatics;
5767 //<feature classSystem.config>
5769 * @cfg {Object} config
5770 * List of configuration options with their default values, for which automatically
5771 * accessor methods are generated. For example:
5773 * Ext.define('SmartPhone', {
5775 * hasTouchScreen: false,
5776 * operatingSystem: 'Other',
5779 * constructor: function(cfg) {
5780 * this.initConfig(cfg);
5784 * var iPhone = new SmartPhone({
5785 * hasTouchScreen: true,
5786 * operatingSystem: 'iOS'
5789 * iPhone.getPrice(); // 500;
5790 * iPhone.getOperatingSystem(); // 'iOS'
5791 * iPhone.getHasTouchScreen(); // true;
5792 * iPhone.hasTouchScreen(); // true
5794 Class.registerPreprocessor('config', function(cls, data) {
5795 var prototype = cls.prototype;
5797 Ext.Object.each(data.config, function(name) {
5798 var cName = name.charAt(0).toUpperCase() + name.substr(1),
5800 apply = 'apply' + cName,
5801 setter = 'set' + cName,
5802 getter = 'get' + cName;
5804 if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5805 data[apply] = function(val) {
5810 if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5811 data[setter] = function(val) {
5812 var ret = this[apply].call(this, val, this[pName]);
5814 if (typeof ret != 'undefined') {
5822 if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5823 data[getter] = function() {
5829 Ext.Object.merge(prototype.config, data.config);
5834 //<feature classSystem.mixins>
5836 * @cfg {Object} mixins
5837 * List of classes to mix into this class. For example:
5839 * Ext.define('CanSing', {
5840 * sing: function() {
5841 * alert("I'm on the highway to hell...")
5845 * Ext.define('Musician', {
5849 * canSing: 'CanSing'
5853 Class.registerPreprocessor('mixins', function(cls, data) {
5854 var mixins = data.mixins,
5859 Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
5860 if (mixins instanceof Array) {
5861 for (i = 0,ln = mixins.length; i < ln; i++) {
5863 name = mixin.prototype.mixinId || mixin.$className;
5865 cls.mixin(name, mixin);
5869 for (name in mixins) {
5870 if (mixins.hasOwnProperty(name)) {
5871 cls.mixin(name, mixins[name]);
5880 Class.setDefaultPreprocessors([
5882 //<feature classSystem.statics>
5885 //<feature classSystem.inheritableStatics>
5886 ,'inheritableStatics'
5888 //<feature classSystem.config>
5891 //<feature classSystem.mixins>
5896 //<feature classSystem.backwardsCompatible>
5897 // Backwards compatible
5898 Ext.extend = function(subclass, superclass, members) {
5899 if (arguments.length === 2 && Ext.isObject(superclass)) {
5900 members = superclass;
5901 superclass = subclass;
5908 Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5911 members.extend = superclass;
5912 members.preprocessors = [
5914 //<feature classSystem.statics>
5917 //<feature classSystem.inheritableStatics>
5918 ,'inheritableStatics'
5920 //<feature classSystem.mixins>
5923 //<feature classSystem.config>
5929 cls = new Class(subclass, members);
5932 cls = new Class(members);
5935 cls.prototype.override = function(o) {
5937 if (o.hasOwnProperty(m)) {
5950 * @author Jacky Nguyen <jacky@sencha.com>
5951 * @docauthor Jacky Nguyen <jacky@sencha.com>
5952 * @class Ext.ClassManager
5954 * Ext.ClassManager manages all classes and handles mapping from string class name to
5955 * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5956 * these convenient shorthands:
5958 * - {@link Ext#define Ext.define}
5959 * - {@link Ext#create Ext.create}
5960 * - {@link Ext#widget Ext.widget}
5961 * - {@link Ext#getClass Ext.getClass}
5962 * - {@link Ext#getClassName Ext.getClassName}
5966 * Ext.define(className, properties);
5968 * in which `properties` is an object represent a collection of properties that apply to the class. See
5969 * {@link Ext.ClassManager#create} for more detailed instructions.
5971 * Ext.define('Person', {
5974 * constructor: function(name) {
5982 * eat: function(foodType) {
5983 * alert("I'm eating: " + foodType);
5989 * var aaron = new Person("Aaron");
5990 * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5992 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5993 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5997 * Ext.define('Developer', {
6000 * constructor: function(name, isGeek) {
6001 * this.isGeek = isGeek;
6003 * // Apply a method from the parent class' prototype
6004 * this.callParent([name]);
6010 * code: function(language) {
6011 * alert("I'm coding in: " + language);
6019 * var jacky = new Developer("Jacky", true);
6020 * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
6021 * // alert("I'm eating: Bugs");
6023 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
6027 * Ext.define('CanPlayGuitar', {
6028 * playGuitar: function() {
6029 * alert("F#...G...D...A");
6033 * Ext.define('CanComposeSongs', {
6034 * composeSongs: function() { ... }
6037 * Ext.define('CanSing', {
6038 * sing: function() {
6039 * alert("I'm on the highway to hell...")
6043 * Ext.define('Musician', {
6047 * canPlayGuitar: 'CanPlayGuitar',
6048 * canComposeSongs: 'CanComposeSongs',
6049 * canSing: 'CanSing'
6053 * Ext.define('CoolPerson', {
6057 * canPlayGuitar: 'CanPlayGuitar',
6058 * canSing: 'CanSing'
6061 * sing: function() {
6062 * alert("Ahem....");
6064 * this.mixins.canSing.sing.call(this);
6066 * alert("[Playing guitar at the same time...]");
6068 * this.playGuitar();
6072 * var me = new CoolPerson("Jacky");
6074 * me.sing(); // alert("Ahem...");
6075 * // alert("I'm on the highway to hell...");
6076 * // alert("[Playing guitar at the same time...]");
6077 * // alert("F#...G...D...A");
6081 * Ext.define('SmartPhone', {
6083 * hasTouchScreen: false,
6084 * operatingSystem: 'Other',
6088 * isExpensive: false,
6090 * constructor: function(config) {
6091 * this.initConfig(config);
6096 * applyPrice: function(price) {
6097 * this.isExpensive = (price > 500);
6102 * applyOperatingSystem: function(operatingSystem) {
6103 * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
6107 * return operatingSystem;
6111 * var iPhone = new SmartPhone({
6112 * hasTouchScreen: true,
6113 * operatingSystem: 'iOS'
6116 * iPhone.getPrice(); // 500;
6117 * iPhone.getOperatingSystem(); // 'iOS'
6118 * iPhone.getHasTouchScreen(); // true;
6119 * iPhone.hasTouchScreen(); // true
6121 * iPhone.isExpensive; // false;
6122 * iPhone.setPrice(600);
6123 * iPhone.getPrice(); // 600
6124 * iPhone.isExpensive; // true;
6126 * iPhone.setOperatingSystem('AlienOS');
6127 * iPhone.getOperatingSystem(); // 'Other'
6131 * Ext.define('Computer', {
6133 * factory: function(brand) {
6134 * // 'this' in static methods refer to the class itself
6135 * return new this(brand);
6139 * constructor: function() { ... }
6142 * var dellComputer = Computer.factory('Dell');
6144 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
6145 * static properties within class methods
6149 (function(Class, alias) {
6151 var slice = Array.prototype.slice;
6153 var Manager = Ext.ClassManager = {
6156 * @property {Object} classes
6157 * All classes which were defined through the ClassManager. Keys are the
6158 * name of the classes and the values are references to the classes.
6171 namespaceRewrites: [{
6180 alternateToName: {},
6186 enableNamespaceParseCache: true,
6189 namespaceParseCache: {},
6195 instantiationCounts: {},
6198 * Checks if a class has already been created.
6200 * @param {String} className
6201 * @return {Boolean} exist
6203 isCreated: function(className) {
6204 var i, ln, part, root, parts;
6206 if (typeof className !== 'string' || className.length < 1) {
6208 sourceClass: "Ext.ClassManager",
6209 sourceMethod: "exist",
6210 msg: "Invalid classname, must be a string and must not be empty"
6214 if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
6219 parts = this.parseNamespace(className);
6221 for (i = 0, ln = parts.length; i < ln; i++) {
6224 if (typeof part !== 'string') {
6227 if (!root || !root[part]) {
6235 Ext.Loader.historyPush(className);
6237 this.existCache[className] = true;
6243 * Supports namespace rewriting
6246 parseNamespace: function(namespace) {
6247 if (typeof namespace !== 'string') {
6249 sourceClass: "Ext.ClassManager",
6250 sourceMethod: "parseNamespace",
6251 msg: "Invalid namespace, must be a string"
6255 var cache = this.namespaceParseCache;
6257 if (this.enableNamespaceParseCache) {
6258 if (cache.hasOwnProperty(namespace)) {
6259 return cache[namespace];
6264 rewrites = this.namespaceRewrites,
6265 rewrite, from, to, i, ln, root = Ext.global;
6267 for (i = 0, ln = rewrites.length; i < ln; i++) {
6268 rewrite = rewrites[i];
6269 from = rewrite.from;
6272 if (namespace === from || namespace.substring(0, from.length) === from) {
6273 namespace = namespace.substring(from.length);
6275 if (typeof to !== 'string') {
6278 parts = parts.concat(to.split('.'));
6287 parts = parts.concat(namespace.split('.'));
6289 if (this.enableNamespaceParseCache) {
6290 cache[namespace] = parts;
6297 * Creates a namespace and assign the `value` to the created object
6299 * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
6301 * alert(MyCompany.pkg.Example === someObject); // alerts true
6303 * @param {String} name
6304 * @param {Object} value
6306 setNamespace: function(name, value) {
6307 var root = Ext.global,
6308 parts = this.parseNamespace(name),
6309 ln = parts.length - 1,
6313 for (i = 0; i < ln; i++) {
6316 if (typeof part !== 'string') {
6333 * The new Ext.ns, supports namespace rewriting
6336 createNamespaces: function() {
6337 var root = Ext.global,
6338 parts, part, i, j, ln, subLn;
6340 for (i = 0, ln = arguments.length; i < ln; i++) {
6341 parts = this.parseNamespace(arguments[i]);
6343 for (j = 0, subLn = parts.length; j < subLn; j++) {
6346 if (typeof part !== 'string') {
6362 * Sets a name reference to a class.
6364 * @param {String} name
6365 * @param {Object} value
6366 * @return {Ext.ClassManager} this
6368 set: function(name, value) {
6369 var targetName = this.getName(value);
6371 this.classes[name] = this.setNamespace(name, value);
6373 if (targetName && targetName !== name) {
6374 this.maps.alternateToName[name] = targetName;
6381 * Retrieve a class by its name.
6383 * @param {String} name
6384 * @return {Ext.Class} class
6386 get: function(name) {
6387 if (this.classes.hasOwnProperty(name)) {
6388 return this.classes[name];
6391 var root = Ext.global,
6392 parts = this.parseNamespace(name),
6395 for (i = 0, ln = parts.length; i < ln; i++) {
6398 if (typeof part !== 'string') {
6401 if (!root || !root[part]) {
6413 * Register the alias for a class.
6415 * @param {Ext.Class/String} cls a reference to a class or a className
6416 * @param {String} alias Alias to use when referring to this class
6418 setAlias: function(cls, alias) {
6419 var aliasToNameMap = this.maps.aliasToName,
6420 nameToAliasesMap = this.maps.nameToAliases,
6423 if (typeof cls === 'string') {
6426 className = this.getName(cls);
6429 if (alias && aliasToNameMap[alias] !== className) {
6430 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
6431 Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
6432 "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
6435 aliasToNameMap[alias] = className;
6438 if (!nameToAliasesMap[className]) {
6439 nameToAliasesMap[className] = [];
6443 Ext.Array.include(nameToAliasesMap[className], alias);
6450 * Get a reference to the class by its alias.
6452 * @param {String} alias
6453 * @return {Ext.Class} class
6455 getByAlias: function(alias) {
6456 return this.get(this.getNameByAlias(alias));
6460 * Get the name of a class by its alias.
6462 * @param {String} alias
6463 * @return {String} className
6465 getNameByAlias: function(alias) {
6466 return this.maps.aliasToName[alias] || '';
6470 * Get the name of a class by its alternate name.
6472 * @param {String} alternate
6473 * @return {String} className
6475 getNameByAlternate: function(alternate) {
6476 return this.maps.alternateToName[alternate] || '';
6480 * Get the aliases of a class by the class name
6482 * @param {String} name
6483 * @return {String[]} aliases
6485 getAliasesByName: function(name) {
6486 return this.maps.nameToAliases[name] || [];
6490 * Get the name of the class by its reference or its instance.
6492 * Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
6494 * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
6496 * @param {Ext.Class/Object} object
6497 * @return {String} className
6499 getName: function(object) {
6500 return object && object.$className || '';
6504 * Get the class of the provided object; returns null if it's not an instance
6505 * of any class created with Ext.define.
6507 * var component = new Ext.Component();
6509 * Ext.ClassManager.getClass(component); // returns Ext.Component
6511 * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
6513 * @param {Object} object
6514 * @return {Ext.Class} class
6516 getClass: function(object) {
6517 return object && object.self || null;
6523 * {@link Ext#define Ext.define} and {@link Ext.ClassManager#create Ext.ClassManager.create} are almost aliases
6524 * of each other, with the only exception that Ext.define allows definition of {@link Ext.Class#override overrides}.
6525 * To avoid trouble, always use Ext.define.
6527 * Ext.define('My.awesome.Class', {
6528 * someProperty: 'something',
6529 * someMethod: function() { ... }
6533 * alert('Created!');
6534 * alert(this === My.awesome.Class); // alerts true
6536 * var myInstance = new this();
6539 * @param {String} className The class name to create in string dot-namespaced format, for example:
6540 * `My.very.awesome.Class`, `FeedViewer.plugin.CoolPager`. It is highly recommended to follow this simple convention:
6542 * - The root and the class name are 'CamelCased'
6543 * - Everything else is lower-cased
6545 * @param {Object} data The key-value pairs of properties to apply to this class. Property names can be of any valid
6546 * strings, except those in the reserved list below:
6548 * - {@link Ext.Base#self self}
6549 * - {@link Ext.Class#alias alias}
6550 * - {@link Ext.Class#alternateClassName alternateClassName}
6551 * - {@link Ext.Class#config config}
6552 * - {@link Ext.Class#extend extend}
6553 * - {@link Ext.Class#inheritableStatics inheritableStatics}
6554 * - {@link Ext.Class#mixins mixins}
6555 * - {@link Ext.Class#override override} (only when using {@link Ext#define Ext.define})
6556 * - {@link Ext.Class#requires requires}
6557 * - {@link Ext.Class#singleton singleton}
6558 * - {@link Ext.Class#statics statics}
6559 * - {@link Ext.Class#uses uses}
6561 * @param {Function} [createdFn] callback to execute after the class is created, the execution scope of which
6562 * (`this`) will be the newly created class itself.
6564 * @return {Ext.Base}
6566 create: function(className, data, createdFn) {
6569 if (typeof className !== 'string') {
6572 sourceMethod: "define",
6573 msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6577 data.$className = className;
6579 return new Class(data, function() {
6580 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6581 registeredPostprocessors = manager.postprocessors,
6583 postprocessors = [],
6584 postprocessor, process, i, ln;
6586 delete data.postprocessors;
6588 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6589 postprocessor = postprocessorStack[i];
6591 if (typeof postprocessor === 'string') {
6592 postprocessor = registeredPostprocessors[postprocessor];
6594 if (!postprocessor.always) {
6595 if (data[postprocessor.name] !== undefined) {
6596 postprocessors.push(postprocessor.fn);
6600 postprocessors.push(postprocessor.fn);
6604 postprocessors.push(postprocessor);
6608 process = function(clsName, cls, clsData) {
6609 postprocessor = postprocessors[index++];
6611 if (!postprocessor) {
6612 manager.set(className, cls);
6614 Ext.Loader.historyPush(className);
6617 createdFn.call(cls, cls);
6623 if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6624 process.apply(this, arguments);
6628 process.call(manager, className, this, data);
6633 * Instantiate a class by its alias.
6635 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6636 * attempt to load the class via synchronous loading.
6638 * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6640 * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
6642 * @param {String} alias
6643 * @param {Object...} args Additional arguments after the alias will be passed to the
6644 * class constructor.
6645 * @return {Object} instance
6647 instantiateByAlias: function() {
6648 var alias = arguments[0],
6649 args = slice.call(arguments),
6650 className = this.getNameByAlias(alias);
6653 className = this.maps.aliasToName[alias];
6658 sourceMethod: "createByAlias",
6659 msg: "Cannot create an instance of unrecognized alias: " + alias
6663 if (Ext.global.console) {
6664 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6665 "Ext.require('" + alias + "') above Ext.onReady");
6668 Ext.syncRequire(className);
6671 args[0] = className;
6673 return this.instantiate.apply(this, args);
6677 * Instantiate a class by either full name, alias or alternate name.
6679 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6680 * attempt to load the class via synchronous loading.
6682 * For example, all these three lines return the same result:
6685 * var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6688 * var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6690 * // full class name
6691 * var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6693 * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
6695 * @param {String} name
6696 * @param {Object...} args Additional arguments after the name will be passed to the class' constructor.
6697 * @return {Object} instance
6699 instantiate: function() {
6700 var name = arguments[0],
6701 args = slice.call(arguments, 1),
6705 if (typeof name !== 'function') {
6706 if ((typeof name !== 'string' || name.length < 1)) {
6709 sourceMethod: "create",
6710 msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6714 cls = this.get(name);
6720 // No record of this class name, it's possibly an alias, so look it up
6722 possibleName = this.getNameByAlias(name);
6725 name = possibleName;
6727 cls = this.get(name);
6731 // Still no record of this class name, it's possibly an alternate name, so look it up
6733 possibleName = this.getNameByAlternate(name);
6736 name = possibleName;
6738 cls = this.get(name);
6742 // Still not existing at this point, try to load it via synchronous mode as the last resort
6744 if (Ext.global.console) {
6745 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6746 "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6749 Ext.syncRequire(name);
6751 cls = this.get(name);
6757 sourceMethod: "create",
6758 msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6762 if (typeof cls !== 'function') {
6765 sourceMethod: "create",
6766 msg: "'" + name + "' is a singleton and cannot be instantiated"
6770 if (!this.instantiationCounts[name]) {
6771 this.instantiationCounts[name] = 0;
6774 this.instantiationCounts[name]++;
6776 return this.getInstantiator(args.length)(cls, args);
6784 dynInstantiate: function(name, args) {
6785 args = Ext.Array.from(args, true);
6788 return this.instantiate.apply(this, args);
6795 getInstantiator: function(length) {
6796 if (!this.instantiators[length]) {
6800 for (i = 0; i < length; i++) {
6801 args.push('a['+i+']');
6804 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6807 return this.instantiators[length];
6818 defaultPostprocessors: [],
6821 * Register a post-processor function.
6823 * @param {String} name
6824 * @param {Function} postprocessor
6826 registerPostprocessor: function(name, fn, always) {
6827 this.postprocessors[name] = {
6829 always: always || false,
6837 * Set the default post processors array stack which are applied to every class.
6839 * @param {String/String[]} The name of a registered post processor or an array of registered names.
6840 * @return {Ext.ClassManager} this
6842 setDefaultPostprocessors: function(postprocessors) {
6843 this.defaultPostprocessors = Ext.Array.from(postprocessors);
6849 * Insert this post-processor at a specific position in the stack, optionally relative to
6850 * any existing post-processor
6852 * @param {String} name The post-processor name. Note that it needs to be registered with
6853 * {@link Ext.ClassManager#registerPostprocessor} before this
6854 * @param {String} offset The insertion position. Four possible values are:
6855 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6856 * @param {String} relativeName
6857 * @return {Ext.ClassManager} this
6859 setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6860 var defaultPostprocessors = this.defaultPostprocessors,
6863 if (typeof offset === 'string') {
6864 if (offset === 'first') {
6865 defaultPostprocessors.unshift(name);
6869 else if (offset === 'last') {
6870 defaultPostprocessors.push(name);
6875 offset = (offset === 'after') ? 1 : -1;
6878 index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6881 Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
6888 * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6889 * or class names. Expressions support wildcards:
6891 * // returns ['Ext.window.Window']
6892 * var window = Ext.ClassManager.getNamesByExpression('widget.window');
6894 * // returns ['widget.panel', 'widget.window', ...]
6895 * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6897 * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6898 * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6900 * @param {String} expression
6901 * @return {String[]} classNames
6903 getNamesByExpression: function(expression) {
6904 var nameToAliasesMap = this.maps.nameToAliases,
6906 name, alias, aliases, possibleName, regex, i, ln;
6908 if (typeof expression !== 'string' || expression.length < 1) {
6910 sourceClass: "Ext.ClassManager",
6911 sourceMethod: "getNamesByExpression",
6912 msg: "Expression " + expression + " is invalid, must be a non-empty string"
6916 if (expression.indexOf('*') !== -1) {
6917 expression = expression.replace(/\*/g, '(.*?)');
6918 regex = new RegExp('^' + expression + '$');
6920 for (name in nameToAliasesMap) {
6921 if (nameToAliasesMap.hasOwnProperty(name)) {
6922 aliases = nameToAliasesMap[name];
6924 if (name.search(regex) !== -1) {
6928 for (i = 0, ln = aliases.length; i < ln; i++) {
6931 if (alias.search(regex) !== -1) {
6941 possibleName = this.getNameByAlias(expression);
6944 names.push(possibleName);
6946 possibleName = this.getNameByAlternate(expression);
6949 names.push(possibleName);
6951 names.push(expression);
6960 var defaultPostprocessors = Manager.defaultPostprocessors;
6961 //<feature classSystem.alias>
6964 * @cfg {String[]} alias
6966 * List of short aliases for class names. Most useful for defining xtypes for widgets:
6968 * Ext.define('MyApp.CoolPanel', {
6969 * extend: 'Ext.panel.Panel',
6970 * alias: ['widget.coolpanel'],
6974 * // Using Ext.create
6975 * Ext.widget('widget.coolpanel');
6976 * // Using the shorthand for widgets and in xtypes
6977 * Ext.widget('panel', {
6979 * {xtype: 'coolpanel', html: 'Foo'},
6980 * {xtype: 'coolpanel', html: 'Bar'}
6984 Manager.registerPostprocessor('alias', function(name, cls, data) {
6985 var aliases = data.alias,
6990 for (i = 0, ln = aliases.length; i < ln; i++) {
6993 this.setAlias(cls, alias);
6998 * @cfg {Boolean} singleton
7000 * When set to true, the class will be instantiated as singleton. For example:
7002 * Ext.define('Logger', {
7004 * log: function(msg) {
7009 * Logger.log('Hello');
7011 Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
7012 fn.call(this, name, new cls(), data);
7017 * @cfg {String/String[]} alternateClassName
7019 * Defines alternate names for this class. For example:
7021 * Ext.define('Developer', {
7022 * alternateClassName: ['Coder', 'Hacker'],
7023 * code: function(msg) {
7024 * alert('Typing... ' + msg);
7028 * var joe = Ext.create('Developer');
7029 * joe.code('stackoverflow');
7031 * var rms = Ext.create('Hacker');
7032 * rms.code('hack hack');
7034 Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
7035 var alternates = data.alternateClassName,
7038 if (!(alternates instanceof Array)) {
7039 alternates = [alternates];
7042 for (i = 0, ln = alternates.length; i < ln; i++) {
7043 alternate = alternates[i];
7045 if (typeof alternate !== 'string') {
7048 sourceMethod: "define",
7049 msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
7053 this.set(alternate, cls);
7057 Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
7063 * @alias Ext.ClassManager#instantiate
7065 create: alias(Manager, 'instantiate'),
7069 * API to be stablized
7071 * @param {Object} item
7072 * @param {String} namespace
7074 factory: function(item, namespace) {
7075 if (item instanceof Array) {
7078 for (i = 0, ln = item.length; i < ln; i++) {
7079 item[i] = Ext.factory(item[i], namespace);
7085 var isString = (typeof item === 'string');
7087 if (isString || (item instanceof Object && item.constructor === Object)) {
7088 var name, config = {};
7094 name = item.className;
7096 delete config.className;
7099 if (namespace !== undefined && name.indexOf(namespace) === -1) {
7100 name = namespace + '.' + Ext.String.capitalize(name);
7103 return Ext.create(name, config);
7106 if (typeof item === 'function') {
7107 return Ext.create(item);
7114 * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
7116 * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
7117 * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
7121 * @param {String} name xtype of the widget to create.
7122 * @param {Object...} args arguments for the widget constructor.
7123 * @return {Object} widget instance
7125 widget: function(name) {
7126 var args = slice.call(arguments);
7127 args[0] = 'widget.' + name;
7129 return Manager.instantiateByAlias.apply(Manager, args);
7135 * @alias Ext.ClassManager#instantiateByAlias
7137 createByAlias: alias(Manager, 'instantiateByAlias'),
7140 * @cfg {String} override
7143 * Defines an override applied to a class. Note that **overrides can only be created using
7144 * {@link Ext#define}.** {@link Ext.ClassManager#create} only creates classes.
7146 * To define an override, include the override property. The content of an override is
7147 * aggregated with the specified class in order to extend or modify that class. This can be
7148 * as simple as setting default property values or it can extend and/or replace methods.
7149 * This can also extend the statics of the class.
7151 * One use for an override is to break a large class into manageable pieces.
7153 * // File: /src/app/Panel.js
7155 * Ext.define('My.app.Panel', {
7156 * extend: 'Ext.panel.Panel',
7158 * 'My.app.PanelPart2',
7159 * 'My.app.PanelPart3'
7162 * constructor: function (config) {
7163 * this.callSuper(arguments); // calls Ext.panel.Panel's constructor
7168 * method: function () {
7174 * // File: /src/app/PanelPart2.js
7175 * Ext.define('My.app.PanelPart2', {
7176 * override: 'My.app.Panel',
7178 * constructor: function (config) {
7179 * this.callSuper(arguments); // calls My.app.Panel's constructor
7184 * Another use of overrides is to provide optional parts of classes that can be
7185 * independently required. In this case, the class may even be unaware of the
7186 * override altogether.
7188 * Ext.define('My.ux.CoolTip', {
7189 * override: 'Ext.tip.ToolTip',
7191 * constructor: function (config) {
7192 * this.callSuper(arguments); // calls Ext.tip.ToolTip's constructor
7197 * The above override can now be required as normal.
7199 * Ext.define('My.app.App', {
7205 * Overrides can also contain statics:
7207 * Ext.define('My.app.BarMod', {
7208 * override: 'Ext.foo.Bar',
7211 * method: function (x) {
7212 * return this.callSuper([x * 2]); // call Ext.foo.Bar.method
7217 * IMPORTANT: An override is only included in a build if the class it overrides is
7218 * required. Otherwise, the override, like the target class, is not included.
7225 * @alias Ext.ClassManager#create
7227 define: function (className, data, createdFn) {
7228 if (!data.override) {
7229 return Manager.create.apply(Manager, arguments);
7232 var requires = data.requires,
7234 overrideName = className;
7236 className = data.override;
7238 // hoist any 'requires' or 'uses' from the body onto the faux class:
7239 data = Ext.apply({}, data);
7240 delete data.requires;
7242 delete data.override;
7244 // make sure className is in the requires list:
7245 if (typeof requires == 'string') {
7246 requires = [ className, requires ];
7247 } else if (requires) {
7248 requires = requires.slice(0);
7249 requires.unshift(className);
7251 requires = [ className ];
7254 // TODO - we need to rework this to allow the override to not require the target class
7255 // and rather 'wait' for it in such a way that if the target class is not in the build,
7256 // neither are any of its overrides.
7258 // Also, this should process the overrides for a class ASAP (ideally before any derived
7259 // classes) if the target class 'requires' the overrides. Without some special handling, the
7260 // overrides so required will be processed before the class and have to be bufferred even
7263 // TODO - we should probably support the "config" processor on an override (to config new
7264 // functionaliy like Aria) and maybe inheritableStatics (although static is now supported
7265 // by callSuper). If inheritableStatics causes those statics to be included on derived class
7266 // constructors, that probably means "no" to this since an override can come after other
7267 // classes extend the target.
7268 return Manager.create(overrideName, {
7272 constructor: function () {
7273 throw new Error("Cannot create override '" + overrideName + "'");
7276 var cls = Manager.get(className);
7277 if (cls.override) { // if (normal class)
7279 } else { // else (singleton)
7280 cls.self.override(data);
7284 // called once the override is applied and with the context of the
7285 // overridden class (the override itself is a meaningless, name-only
7287 createdFn.call(cls);
7295 * @alias Ext.ClassManager#getName
7297 getClassName: alias(Manager, 'getName'),
7300 * Returns the displayName property or className or object.
7301 * When all else fails, returns "Anonymous".
7302 * @param {Object} object
7305 getDisplayName: function(object) {
7306 if (object.displayName) {
7307 return object.displayName;
7310 if (object.$name && object.$class) {
7311 return Ext.getClassName(object.$class) + '#' + object.$name;
7314 if (object.$className) {
7315 return object.$className;
7324 * @alias Ext.ClassManager#getClass
7326 getClass: alias(Manager, 'getClass'),
7329 * Creates namespaces to be used for scoping variables and classes so that they are not global.
7330 * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
7332 * Ext.namespace('Company', 'Company.data');
7334 * // equivalent and preferable to the above syntax
7335 * Ext.namespace('Company.data');
7337 * Company.Widget = function() { ... };
7339 * Company.data.CustomStore = function(config) { ... };
7343 * @param {String} namespace1
7344 * @param {String} namespace2
7345 * @param {String} etc
7346 * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
7348 namespace: alias(Manager, 'createNamespaces')
7352 * Old name for {@link Ext#widget}.
7353 * @deprecated 4.0.0 Use {@link Ext#widget} instead.
7358 Ext.createWidget = Ext.widget;
7361 * Convenient alias for {@link Ext#namespace Ext.namespace}
7364 * @alias Ext#namespace
7366 Ext.ns = Ext.namespace;
7368 Class.registerPreprocessor('className', function(cls, data) {
7369 if (data.$className) {
7370 cls.$className = data.$className;
7371 cls.displayName = cls.$className;
7375 Class.setDefaultPreprocessorPosition('className', 'first');
7377 Class.registerPreprocessor('xtype', function(cls, data) {
7378 var xtypes = Ext.Array.from(data.xtype),
7379 widgetPrefix = 'widget.',
7380 aliases = Ext.Array.from(data.alias),
7383 data.xtype = xtypes[0];
7384 data.xtypes = xtypes;
7386 aliases = data.alias = Ext.Array.from(data.alias);
7388 for (i = 0,ln = xtypes.length; i < ln; i++) {
7391 if (typeof xtype != 'string' || xtype.length < 1) {
7392 throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
7395 aliases.push(widgetPrefix + xtype);
7398 data.alias = aliases;
7401 Class.setDefaultPreprocessorPosition('xtype', 'last');
7403 Class.registerPreprocessor('alias', function(cls, data) {
7404 var aliases = Ext.Array.from(data.alias),
7405 xtypes = Ext.Array.from(data.xtypes),
7406 widgetPrefix = 'widget.',
7407 widgetPrefixLength = widgetPrefix.length,
7408 i, ln, alias, xtype;
7410 for (i = 0, ln = aliases.length; i < ln; i++) {
7413 if (typeof alias != 'string') {
7414 throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
7417 if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
7418 xtype = alias.substring(widgetPrefixLength);
7419 Ext.Array.include(xtypes, xtype);
7422 cls.xtype = data.xtype = xtype;
7427 data.alias = aliases;
7428 data.xtypes = xtypes;
7431 Class.setDefaultPreprocessorPosition('alias', 'last');
7433 })(Ext.Class, Ext.Function.alias);
7438 * @author Jacky Nguyen <jacky@sencha.com>
7439 * @docauthor Jacky Nguyen <jacky@sencha.com>
7441 * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
7442 * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
7443 * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
7446 * # Asynchronous Loading
7450 * + No web server needed: you can run the application via the file system protocol
7451 * (i.e: `file://path/to/your/index.html`)
7452 * + Best possible debugging experience: error messages come with the exact file name and line number
7455 * + Dependencies need to be specified before-hand
7457 * ### Method 1: Explicitly include what you need:
7460 * Ext.require({String/Array} expressions);
7462 * // Example: Single alias
7463 * Ext.require('widget.window');
7465 * // Example: Single class name
7466 * Ext.require('Ext.window.Window');
7468 * // Example: Multiple aliases / class names mix
7469 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
7472 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
7474 * ### Method 2: Explicitly exclude what you don't need:
7476 * // Syntax: Note that it must be in this chaining format.
7477 * Ext.exclude({String/Array} expressions)
7478 * .require({String/Array} expressions);
7480 * // Include everything except Ext.data.*
7481 * Ext.exclude('Ext.data.*').require('*');Â
7483 * // Include all widgets except widget.checkbox*,
7484 * // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
7485 * Ext.exclude('widget.checkbox*').require('widget.*');
7487 * # Synchronous Loading on Demand
7490 * + There's no need to specify dependencies before-hand, which is always the convenience of including
7494 * + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
7495 * + Must be from the same domain due to XHR restriction
7496 * + Need a web server, same reason as above
7498 * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
7500 * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
7502 * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
7504 * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
7506 * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
7507 * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
7508 * the given class and all its dependencies.
7510 * # Hybrid Loading - The Best of Both Worlds
7512 * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
7514 * ### Step 1: Start writing your application using synchronous approach.
7516 * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
7518 * Ext.onReady(function(){
7519 * var window = Ext.createWidget('window', {
7526 * title: 'Hello Dialog',
7528 * title: 'Navigation',
7529 * collapsible: true,
7535 * title: 'TabPanel',
7543 * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
7545 * [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
7546 * [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
7548 * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
7550 * Ext.require('Ext.window.Window');
7551 * Ext.require('Ext.layout.container.Border');
7555 * Everything should now load via asynchronous mode.
7559 * It's important to note that dynamic loading should only be used during development on your local machines.
7560 * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
7561 * the whole process of transitioning from / to between development / maintenance and production as easy as
7562 * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
7563 * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
7564 * array into one, then include it on top of your application.
7566 * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
7568 (function(Manager, Class, flexSetter, alias) {
7571 dependencyProperties = ['extend', 'mixins', 'requires'],
7574 Loader = Ext.Loader = {
7578 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
7581 * Flag indicating whether there are still files being loaded
7587 * Maintain the queue for all dependencies. Each item in the array is an object of the format:
7589 * requires: [...], // The required classes for this queue item
7590 * callback: function() { ... } // The function to execute when all classes specified in requires exist
7597 * Maintain the list of files that have already been handled so that they never get double-loaded
7603 * Maintain the list of listeners to execute when all required scripts are fully loaded
7609 * Contains optional dependencies to be loaded last
7612 optionalRequires: [],
7615 * Map of fully qualified class names to an array of dependent classes.
7631 hasFileLoadError: false,
7636 classNameToFilePathMap: {},
7639 * @property {String[]} history
7640 * An array of class names to keep track of the dependency loading order.
7641 * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
7651 * @cfg {Boolean} enabled
7652 * Whether or not to enable the dynamic dependency loading feature.
7657 * @cfg {Boolean} disableCaching
7658 * Appends current timestamp to script files to prevent caching.
7660 disableCaching: true,
7663 * @cfg {String} disableCachingParam
7664 * The get parameter name for the cache buster's timestamp.
7666 disableCachingParam: '_dc',
7669 * @cfg {Object} paths
7670 * The mapping from namespaces to file paths
7673 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
7674 * // loaded from ./layout/Container.js
7676 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
7677 * // ./src/my_own_folder/layout/Container.js
7680 * Note that all relative paths are relative to the current HTML document.
7681 * If not being specified, for example, `Other.awesome.Class`
7682 * will simply be loaded from `./Other/awesome/Class.js`
7690 * Set the configuration for the loader. This should be called right after ext-core.js
7691 * (or ext-core-debug.js) is included in the page, e.g.:
7693 * <script type="text/javascript" src="ext-core-debug.js"></script>
7694 * <script type="text/javascript">
7695 * Ext.Loader.setConfig({
7698 * 'My': 'my_own_path'
7702 * <script type="text/javascript">
7705 * Ext.onReady(function() {
7706 * // application code here
7710 * Refer to config options of {@link Ext.Loader} for the list of possible properties.
7712 * @param {String/Object} name Name of the value to override, or a config object to override multiple values.
7713 * @param {Object} value (optional) The new value to set, needed if first parameter is String.
7714 * @return {Ext.Loader} this
7716 setConfig: function(name, value) {
7717 if (Ext.isObject(name) && arguments.length === 1) {
7718 Ext.Object.merge(this.config, name);
7721 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
7728 * Get the config value corresponding to the specified name.
7729 * If no name is given, will return the config object.
7730 * @param {String} name The config property name
7733 getConfig: function(name) {
7735 return this.config[name];
7742 * Sets the path of a namespace. For Example:
7744 * Ext.Loader.setPath('Ext', '.');
7746 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7747 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7748 * @return {Ext.Loader} this
7751 setPath: flexSetter(function(name, path) {
7752 this.config.paths[name] = path;
7758 * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
7761 * Ext.Loader.setPath('My', '/path/to/My');
7763 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7765 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7767 * Ext.Loader.setPath({
7768 * 'My': '/path/to/lib',
7769 * 'My.awesome': '/other/path/for/awesome/stuff',
7770 * 'My.awesome.more': '/more/awesome/path'
7773 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7775 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7777 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7779 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7781 * @param {String} className
7782 * @return {String} path
7784 getPath: function(className) {
7786 paths = this.config.paths,
7787 prefix = this.getPrefix(className);
7789 if (prefix.length > 0) {
7790 if (prefix === className) {
7791 return paths[prefix];
7794 path = paths[prefix];
7795 className = className.substring(prefix.length + 1);
7798 if (path.length > 0) {
7802 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7807 * @param {String} className
7809 getPrefix: function(className) {
7810 var paths = this.config.paths,
7811 prefix, deepestPrefix = '';
7813 if (paths.hasOwnProperty(className)) {
7817 for (prefix in paths) {
7818 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7819 if (prefix.length > deepestPrefix.length) {
7820 deepestPrefix = prefix;
7825 return deepestPrefix;
7829 * Refresh all items in the queue. If all dependencies for an item exist during looping,
7830 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7834 refreshQueue: function() {
7835 var ln = this.queue.length,
7836 i, item, j, requires;
7839 this.triggerReady();
7843 for (i = 0; i < ln; i++) {
7844 item = this.queue[i];
7847 requires = item.requires;
7849 // Don't bother checking when the number of files loaded
7850 // is still less than the array length
7851 if (requires.length > this.numLoadedFiles) {
7858 if (Manager.isCreated(requires[j])) {
7859 // Take out from the queue
7860 Ext.Array.erase(requires, j, 1);
7865 } while (j < requires.length);
7867 if (item.requires.length === 0) {
7868 Ext.Array.erase(this.queue, i, 1);
7869 item.callback.call(item.scope);
7870 this.refreshQueue();
7880 * Inject a script element to document's head, call onLoad and onError accordingly
7883 injectScriptElement: function(url, onLoad, onError, scope) {
7884 var script = document.createElement('script'),
7886 onLoadFn = function() {
7887 me.cleanupScriptElement(script);
7890 onErrorFn = function() {
7891 me.cleanupScriptElement(script);
7892 onError.call(scope);
7895 script.type = 'text/javascript';
7897 script.onload = onLoadFn;
7898 script.onerror = onErrorFn;
7899 script.onreadystatechange = function() {
7900 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7905 this.documentHead.appendChild(script);
7913 cleanupScriptElement: function(script) {
7914 script.onload = null;
7915 script.onreadystatechange = null;
7916 script.onerror = null;
7922 * Load a script file, supports both asynchronous and synchronous approaches
7924 * @param {String} url
7925 * @param {Function} onLoad
7926 * @param {Object} scope
7927 * @param {Boolean} synchronous
7930 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7932 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7933 fileName = url.split('/').pop(),
7934 isCrossOriginRestricted = false,
7935 xhr, status, onScriptError;
7937 scope = scope || this;
7939 this.isLoading = true;
7942 onScriptError = function() {
7943 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7946 if (!Ext.isReady && Ext.onDocumentReady) {
7947 Ext.onDocumentReady(function() {
7948 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7952 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7956 if (typeof XMLHttpRequest !== 'undefined') {
7957 xhr = new XMLHttpRequest();
7959 xhr = new ActiveXObject('Microsoft.XMLHTTP');
7963 xhr.open('GET', noCacheUrl, false);
7966 isCrossOriginRestricted = true;
7969 status = (xhr.status === 1223) ? 204 : xhr.status;
7971 if (!isCrossOriginRestricted) {
7972 isCrossOriginRestricted = (status === 0);
7975 if (isCrossOriginRestricted
7977 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7978 "being loaded from a different domain or from the local file system whereby cross origin " +
7979 "requests are not allowed due to security reasons. Use asynchronous loading with " +
7980 "Ext.require instead.", synchronous);
7982 else if (status >= 200 && status < 300
7984 // Firebug friendly, file names are still shown even though they're eval'ed code
7985 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7990 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7991 "verify that the file exists. " +
7992 "XHR status code: " + status, synchronous);
7995 // Prevent potential IE memory leak
8001 * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
8002 * Can be chained with more `require` and `exclude` methods, e.g.:
8004 * Ext.exclude('Ext.data.*').require('*');
8006 * Ext.exclude('widget.button*').require('widget.*');
8008 * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
8010 * @param {String/String[]} excludes
8011 * @return {Object} object contains `require` method for chaining
8013 exclude: function(excludes) {
8017 require: function(expressions, fn, scope) {
8018 return me.require(expressions, fn, scope, excludes);
8021 syncRequire: function(expressions, fn, scope) {
8022 return me.syncRequire(expressions, fn, scope, excludes);
8028 * Synchronously loads all classes by the given names and all their direct dependencies;
8029 * optionally executes the given callback function when finishes, within the optional scope.
8031 * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
8033 * @param {String/String[]} expressions Can either be a string or an array of string
8034 * @param {Function} fn (Optional) The callback function
8035 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
8036 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
8038 syncRequire: function() {
8039 this.syncModeEnabled = true;
8040 this.require.apply(this, arguments);
8041 this.refreshQueue();
8042 this.syncModeEnabled = false;
8046 * Loads all classes by the given names and all their direct dependencies;
8047 * optionally executes the given callback function when finishes, within the optional scope.
8049 * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
8051 * @param {String/String[]} expressions Can either be a string or an array of string
8052 * @param {Function} fn (Optional) The callback function
8053 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
8054 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
8056 require: function(expressions, fn, scope, excludes) {
8057 var filePath, expression, exclude, className, excluded = {},
8058 excludedClassNames = [],
8059 possibleClassNames = [],
8060 possibleClassName, classNames = [],
8063 expressions = Ext.Array.from(expressions);
8064 excludes = Ext.Array.from(excludes);
8066 fn = fn || Ext.emptyFn;
8068 scope = scope || Ext.global;
8070 for (i = 0, ln = excludes.length; i < ln; i++) {
8071 exclude = excludes[i];
8073 if (typeof exclude === 'string' && exclude.length > 0) {
8074 excludedClassNames = Manager.getNamesByExpression(exclude);
8076 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
8077 excluded[excludedClassNames[j]] = true;
8082 for (i = 0, ln = expressions.length; i < ln; i++) {
8083 expression = expressions[i];
8085 if (typeof expression === 'string' && expression.length > 0) {
8086 possibleClassNames = Manager.getNamesByExpression(expression);
8088 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
8089 possibleClassName = possibleClassNames[j];
8091 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
8092 Ext.Array.include(classNames, possibleClassName);
8098 // If the dynamic dependency feature is not being used, throw an error
8099 // if the dependencies are not defined
8100 if (!this.config.enabled) {
8101 if (classNames.length > 0) {
8103 sourceClass: "Ext.Loader",
8104 sourceMethod: "require",
8105 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
8106 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
8111 if (classNames.length === 0) {
8117 requires: classNames,
8122 classNames = classNames.slice();
8124 for (i = 0, ln = classNames.length; i < ln; i++) {
8125 className = classNames[i];
8127 if (!this.isFileLoaded.hasOwnProperty(className)) {
8128 this.isFileLoaded[className] = false;
8130 filePath = this.getPath(className);
8132 this.classNameToFilePathMap[className] = filePath;
8134 this.numPendingFiles++;
8136 this.loadScriptFile(
8138 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
8139 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
8141 this.syncModeEnabled
8151 * @param {String} className
8152 * @param {String} filePath
8154 onFileLoaded: function(className, filePath) {
8155 this.numLoadedFiles++;
8157 this.isFileLoaded[className] = true;
8159 this.numPendingFiles--;
8161 if (this.numPendingFiles === 0) {
8162 this.refreshQueue();
8165 if (this.numPendingFiles <= 1) {
8166 window.status = "Finished loading all dependencies, onReady fired!";
8169 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
8172 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
8173 var queue = this.queue,
8175 i, ln, j, subLn, missingClasses = [], missingPaths = [];
8177 for (i = 0, ln = queue.length; i < ln; i++) {
8178 requires = queue[i].requires;
8180 for (j = 0, subLn = requires.length; j < ln; j++) {
8181 if (this.isFileLoaded[requires[j]]) {
8182 missingClasses.push(requires[j]);
8187 if (missingClasses.length < 1) {
8191 missingClasses = Ext.Array.filter(missingClasses, function(item) {
8192 return !this.requiresMap.hasOwnProperty(item);
8195 for (i = 0,ln = missingClasses.length; i < ln; i++) {
8196 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
8200 sourceClass: "Ext.Loader",
8201 sourceMethod: "onFileLoaded",
8202 msg: "The following classes are not declared even if their files have been " +
8203 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
8204 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
8212 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
8213 this.numPendingFiles--;
8214 this.hasFileLoadError = true;
8217 sourceClass: "Ext.Loader",
8218 classToLoad: className,
8220 loadingType: isSynchronous ? 'synchronous' : 'async',
8228 addOptionalRequires: function(requires) {
8229 var optionalRequires = this.optionalRequires,
8232 requires = Ext.Array.from(requires);
8234 for (i = 0, ln = requires.length; i < ln; i++) {
8235 require = requires[i];
8237 Ext.Array.include(optionalRequires, require);
8246 triggerReady: function(force) {
8247 var readyListeners = this.readyListeners,
8248 optionalRequires, listener;
8250 if (this.isLoading || force) {
8251 this.isLoading = false;
8253 if (this.optionalRequires.length) {
8254 // Clone then empty the array to eliminate potential recursive loop issue
8255 optionalRequires = Ext.Array.clone(this.optionalRequires);
8257 // Empty the original array
8258 this.optionalRequires.length = 0;
8260 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
8264 while (readyListeners.length) {
8265 listener = readyListeners.shift();
8266 listener.fn.call(listener.scope);
8268 if (this.isLoading) {
8278 * Adds new listener to be executed when all required scripts are fully loaded.
8280 * @param {Function} fn The function callback to be executed
8281 * @param {Object} scope The execution scope (`this`) of the callback function
8282 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
8284 onReady: function(fn, scope, withDomReady, options) {
8287 if (withDomReady !== false && Ext.onDocumentReady) {
8291 Ext.onDocumentReady(oldFn, scope, options);
8295 if (!this.isLoading) {
8299 this.readyListeners.push({
8308 * @param {String} className
8310 historyPush: function(className) {
8311 if (className && this.isFileLoaded.hasOwnProperty(className)) {
8312 Ext.Array.include(this.history, className);
8322 * @alias Ext.Loader#require
8324 Ext.require = alias(Loader, 'require');
8328 * @method syncRequire
8329 * @alias Ext.Loader#syncRequire
8331 Ext.syncRequire = alias(Loader, 'syncRequire');
8336 * @alias Ext.Loader#exclude
8338 Ext.exclude = alias(Loader, 'exclude');
8343 * @alias Ext.Loader#onReady
8345 Ext.onReady = function(fn, scope, options) {
8346 Loader.onReady(fn, scope, true, options);
8350 * @cfg {String[]} requires
8352 * List of classes that have to be loaded before instantiating this class.
8355 * Ext.define('Mother', {
8356 * requires: ['Child'],
8357 * giveBirth: function() {
8358 * // we can be sure that child class is available.
8359 * return new Child();
8363 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
8366 className = Manager.getName(cls),
8367 i, j, ln, subLn, value, propertyName, propertyValue;
8370 Basically loop through the dependencyProperties, look for string class names and push
8371 them into a stack, regardless of whether the property's value is a string, array or object. For example:
8373 extend: 'Ext.MyClass',
8374 requires: ['Ext.some.OtherClass'],
8376 observable: 'Ext.util.Observable';
8379 which will later be transformed into:
8381 extend: Ext.MyClass,
8382 requires: [Ext.some.OtherClass],
8384 observable: Ext.util.Observable;
8389 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8390 propertyName = dependencyProperties[i];
8392 if (data.hasOwnProperty(propertyName)) {
8393 propertyValue = data[propertyName];
8395 if (typeof propertyValue === 'string') {
8396 dependencies.push(propertyValue);
8398 else if (propertyValue instanceof Array) {
8399 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8400 value = propertyValue[j];
8402 if (typeof value === 'string') {
8403 dependencies.push(value);
8407 else if (typeof propertyValue != 'function') {
8408 for (j in propertyValue) {
8409 if (propertyValue.hasOwnProperty(j)) {
8410 value = propertyValue[j];
8412 if (typeof value === 'string') {
8413 dependencies.push(value);
8421 if (dependencies.length === 0) {
8422 // Loader.historyPush(className);
8426 var deadlockPath = [],
8427 requiresMap = Loader.requiresMap,
8431 Automatically detect deadlocks before-hand,
8432 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
8434 - A extends B, then B extends A
8435 - A requires B, B requires C, then C requires A
8437 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
8438 no matter how deep the path is.
8442 requiresMap[className] = dependencies;
8444 detectDeadlock = function(cls) {
8445 deadlockPath.push(cls);
8447 if (requiresMap[cls]) {
8448 if (Ext.Array.contains(requiresMap[cls], className)) {
8450 sourceClass: "Ext.Loader",
8451 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
8452 deadlockPath[1] + "' " + "mutually require each other. Path: " +
8453 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
8457 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
8458 detectDeadlock(requiresMap[cls][i]);
8463 detectDeadlock(className);
8467 Loader.require(dependencies, function() {
8468 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8469 propertyName = dependencyProperties[i];
8471 if (data.hasOwnProperty(propertyName)) {
8472 propertyValue = data[propertyName];
8474 if (typeof propertyValue === 'string') {
8475 data[propertyName] = Manager.get(propertyValue);
8477 else if (propertyValue instanceof Array) {
8478 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8479 value = propertyValue[j];
8481 if (typeof value === 'string') {
8482 data[propertyName][j] = Manager.get(value);
8486 else if (typeof propertyValue != 'function') {
8487 for (var k in propertyValue) {
8488 if (propertyValue.hasOwnProperty(k)) {
8489 value = propertyValue[k];
8491 if (typeof value === 'string') {
8492 data[propertyName][k] = Manager.get(value);
8500 continueFn.call(me, cls, data);
8506 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
8509 * @cfg {String[]} uses
8511 * List of classes to load together with this class. These aren't neccessarily loaded before
8512 * this class is instantiated. For example:
8514 * Ext.define('Mother', {
8516 * giveBirth: function() {
8517 * // This code might, or might not work:
8518 * // return new Child();
8520 * // Instead use Ext.create() to load the class at the spot if not loaded already:
8521 * return Ext.create('Child');
8525 Manager.registerPostprocessor('uses', function(name, cls, data) {
8526 var uses = Ext.Array.from(data.uses),
8530 for (i = 0, ln = uses.length; i < ln; i++) {
8533 if (typeof item === 'string') {
8538 Loader.addOptionalRequires(items);
8541 Manager.setDefaultPostprocessorPosition('uses', 'last');
8543 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
8546 * @author Brian Moeskau <brian@sencha.com>
8547 * @docauthor Brian Moeskau <brian@sencha.com>
8549 * A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
8550 * errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
8551 * uses the Ext 4 class system, the Error class can automatically add the source class and method from which
8552 * the error was raised. It also includes logic to automatically log the eroor to the console, if available,
8553 * with additional metadata about the error. In all cases, the error will always be thrown at the end so that
8554 * execution will halt.
8556 * Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
8557 * handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
8558 * although in a real application it's usually a better idea to override the handling function and perform
8559 * logging or some other method of reporting the errors in a way that is meaningful to the application.
8561 * At its simplest you can simply raise an error as a simple string from within any code:
8565 * Ext.Error.raise('Something bad happened!');
8567 * If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
8568 * displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
8569 * additional metadata about the error being raised. The {@link #raise} method can also take a config object.
8570 * In this form the `msg` attribute becomes the error description, and any other data added to the config gets
8571 * added to the error object and, if the console is available, logged to the console for inspection.
8575 * Ext.define('Ext.Foo', {
8576 * doSomething: function(option){
8577 * if (someCondition === false) {
8579 * msg: 'You cannot do that!',
8580 * option: option, // whatever was passed into the method
8581 * 'error code': 100 // other arbitrary info
8587 * If a console is available (that supports the `console.dir` function) you'll see console output like:
8589 * An error was raised with the following data:
8590 * option: Object { foo: "bar"}
8593 * msg: "You cannot do that!"
8594 * sourceClass: "Ext.Foo"
8595 * sourceMethod: "doSomething"
8597 * uncaught exception: You cannot do that!
8599 * As you can see, the error will report exactly where it was raised and will include as much information as the
8600 * raising code can usefully provide.
8602 * If you want to handle all application errors globally you can simply override the static {@link #handle} method
8603 * and provide whatever handling logic you need. If the method returns true then the error is considered handled
8604 * and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
8608 * Ext.Error.handle = function(err) {
8609 * if (err.someProperty == 'NotReallyAnError') {
8610 * // maybe log something to the application here if applicable
8613 * // any non-true return value (including none) will cause the error to be thrown
8617 Ext.Error = Ext.extend(Error, {
8620 * @property {Boolean} ignore
8621 * Static flag that can be used to globally disable error reporting to the browser if set to true
8622 * (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
8623 * and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
8624 * be preferable to supply a custom error {@link #handle handling} function instead.
8628 * Ext.Error.ignore = true;
8635 * @property {Boolean} notify
8636 * Static flag that can be used to globally control error notification to the user. Unlike
8637 * Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
8638 * set to false to disable the alert notification (default is true for IE6 and IE7).
8640 * Only the first error will generate an alert. Internally this flag is set to false when the
8641 * first error occurs prior to displaying the alert.
8643 * This flag is not used in a release build.
8647 * Ext.Error.notify = false;
8651 //notify: Ext.isIE6 || Ext.isIE7,
8654 * Raise an error that can include additional data and supports automatic console logging if available.
8655 * You can pass a string error message or an object with the `msg` attribute which will be used as the
8656 * error message. The object can contain any other name-value attributes (or objects) to be logged
8657 * along with the error.
8659 * Note that after displaying the error message a JavaScript error will ultimately be thrown so that
8660 * execution will halt.
8664 * Ext.Error.raise('A simple string error message');
8668 * Ext.define('Ext.Foo', {
8669 * doSomething: function(option){
8670 * if (someCondition === false) {
8672 * msg: 'You cannot do that!',
8673 * option: option, // whatever was passed into the method
8674 * 'error code': 100 // other arbitrary info
8680 * @param {String/Object} err The error message string, or an object containing the attribute "msg" that will be
8681 * used as the error message. Any other data included in the object will also be logged to the browser console,
8685 raise: function(err){
8687 if (Ext.isString(err)) {
8691 var method = this.raise.caller;
8695 err.sourceMethod = method.$name;
8697 if (method.$owner) {
8698 err.sourceClass = method.$owner.$className;
8702 if (Ext.Error.handle(err) !== true) {
8703 var msg = Ext.Error.prototype.toString.call(err);
8712 throw new Ext.Error(err);
8717 * Globally handle any Ext errors that may be raised, optionally providing custom logic to
8718 * handle different errors individually. Return true from the function to bypass throwing the
8719 * error to the browser, otherwise the error will be thrown and execution will halt.
8723 * Ext.Error.handle = function(err) {
8724 * if (err.someProperty == 'NotReallyAnError') {
8725 * // maybe log something to the application here if applicable
8728 * // any non-true return value (including none) will cause the error to be thrown
8731 * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes that were originally
8732 * raised with it, plus properties about the method and class from which the error originated (if raised from a
8733 * class that uses the Ext 4 class system).
8737 return Ext.Error.ignore;
8741 // This is the standard property that is the name of the constructor.
8745 * Creates new Error object.
8746 * @param {String/Object} config The error message string, or an object containing the
8747 * attribute "msg" that will be used as the error message. Any other data included in
8748 * the object will be applied to the error instance and logged to the browser console, if available.
8750 constructor: function(config){
8751 if (Ext.isString(config)) {
8752 config = { msg: config };
8757 Ext.apply(me, config);
8759 me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
8760 // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
8764 * Provides a custom string representation of the error object. This is an override of the base JavaScript
8765 * `Object.toString` method, which is useful so that when logged to the browser console, an error object will
8766 * be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8768 * The default implementation will include the error message along with the raising class and method, if available,
8769 * but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8770 * a particular error instance, if you want to provide a custom description that will show up in the console.
8771 * @return {String} The error message. If raised from within the Ext 4 class system, the error message will also
8772 * include the raising class and method names, if available.
8774 toString: function(){
8776 className = me.className ? me.className : '',
8777 methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8778 msg = me.msg || '(No description provided)';
8780 return className + methodName + msg;
8785 * This mechanism is used to notify the user of the first error encountered on the page. This
8786 * was previously internal to Ext.Error.raise and is a desirable feature since errors often
8787 * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
8788 * where exceptions are handled in a try/catch.
8791 var prevOnError, timer, errors = 0,
8792 extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
8795 if (typeof window === 'undefined') {
8796 return; // build system or some such environment...
8799 // This method is called to notify the user of the current error status.
8800 function notify () {
8801 var counters = Ext.log.counters,
8802 supports = Ext.supports,
8803 hasOnError = supports && supports.WindowOnError; // TODO - timing
8805 // Put log counters to the status bar (for most browsers):
8806 if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
8807 var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
8808 'Info:',counters.info, 'Log:',counters.log].join(' ');
8810 msg = '*** Errors: ' + errors + ' - ' + msg;
8811 } else if (counters.error) {
8817 // Display an alert on the first error:
8818 if (!Ext.isDefined(Ext.Error.notify)) {
8819 Ext.Error.notify = Ext.isIE6 || Ext.isIE7; // TODO - timing
8821 if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
8822 Ext.Error.notify = false;
8825 win.clearInterval(timer); // ticks can queue up so stop...
8829 alert('Unhandled error on page: See console or log');
8834 // Sets up polling loop. This is the only way to know about errors in some browsers
8835 // (Opera/Safari) and is the only way to update the status bar for warnings and other
8838 timer = win.setInterval(notify, 1000);
8841 // window.onerror sounds ideal but it prevents the built-in error dialog from doing
8842 // its (better) thing.
8850 This file is part of Ext JS 4
8852 Copyright (c) 2011 Sencha Inc
8854 Contact: http://www.sencha.com/contact
8856 GNU General Public License Usage
8857 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.
8859 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
8864 * Modified version of Douglas Crockford's JSON.js that doesn't
8865 * mess with the Object prototype
8866 * http://www.json.org/js.html
8869 Ext.JSON = new(function() {
8870 var useHasOwn = !! {}.hasOwnProperty,
8871 isNative = function() {
8872 var useNative = null;
8875 if (useNative === null) {
8876 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
8883 return n < 10 ? "0" + n : n;
8885 doDecode = function(json) {
8886 return eval("(" + json + ')');
8888 doEncode = function(o) {
8889 if (!Ext.isDefined(o) || o === null) {
8891 } else if (Ext.isArray(o)) {
8892 return encodeArray(o);
8893 } else if (Ext.isDate(o)) {
8894 return Ext.JSON.encodeDate(o);
8895 } else if (Ext.isString(o)) {
8896 return encodeString(o);
8897 } else if (typeof o == "number") {
8898 //don't use isNumber here, since finite checks happen inside isNumber
8899 return isFinite(o) ? String(o) : "null";
8900 } else if (Ext.isBoolean(o)) {
8902 } else if (Ext.isObject(o)) {
8903 return encodeObject(o);
8904 } else if (typeof o === "function") {
8917 '\x0b': '\\u000b' //ie doesn't handle \v
8919 charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
8920 encodeString = function(s) {
8921 return '"' + s.replace(charToReplace, function(a) {
8923 return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
8926 encodeArray = function(o) {
8928 // Note empty string in case there are no serializable members.
8931 for (i = 0; i < len; i += 1) {
8932 a.push(doEncode(o[i]), ',');
8934 // Overwrite trailing comma (or empty string)
8935 a[a.length - 1] = ']';
8938 encodeObject = function(o) {
8940 // Note empty string in case there are no serializable members.
8943 if (!useHasOwn || o.hasOwnProperty(i)) {
8944 a.push(doEncode(i), ":", doEncode(o[i]), ',');
8947 // Overwrite trailing comma (or empty string)
8948 a[a.length - 1] = '}';
8953 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
8954 * <b>The returned value includes enclosing double quotation marks.</b></p>
8955 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
8956 * <p>To override this:</p><pre><code>
8957 Ext.JSON.encodeDate = function(d) {
8958 return Ext.Date.format(d, '"Y-m-d"');
8961 * @param {Date} d The Date to encode
8962 * @return {String} The string literal to use in a JSON string.
8964 this.encodeDate = function(o) {
8965 return '"' + o.getFullYear() + "-"
8966 + pad(o.getMonth() + 1) + "-"
8967 + pad(o.getDate()) + "T"
8968 + pad(o.getHours()) + ":"
8969 + pad(o.getMinutes()) + ":"
8970 + pad(o.getSeconds()) + '"';
8974 * Encodes an Object, Array or other value
8975 * @param {Object} o The variable to encode
8976 * @return {String} The JSON string
8978 this.encode = function() {
8980 return function(o) {
8982 // setup encoding function on first access
8983 ec = isNative() ? JSON.stringify : doEncode;
8991 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
8992 * @param {String} json The JSON string
8993 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8994 * @return {Object} The resulting object
8996 this.decode = function() {
8998 return function(json, safe) {
9000 // setup decoding function on first access
9001 dc = isNative() ? JSON.parse : doDecode;
9006 if (safe === true) {
9010 sourceClass: "Ext.JSON",
9011 sourceMethod: "decode",
9012 msg: "You're trying to decode an invalid JSON String: " + json
9020 * Shorthand for {@link Ext.JSON#encode}
9023 * @alias Ext.JSON#encode
9025 Ext.encode = Ext.JSON.encode;
9027 * Shorthand for {@link Ext.JSON#decode}
9030 * @alias Ext.JSON#decode
9032 Ext.decode = Ext.JSON.decode;
9038 The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
9039 Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
9040 as direct properties of the Ext namespace.
9042 Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
9043 For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
9045 Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
9046 This ensures all scripts have been loaded, preventing dependency issues. For example
9048 Ext.onReady(function(){
9050 renderTo: document.body,
9055 For more information about how to use the Ext classes, see
9057 - <a href="http://www.sencha.com/learn/">The Learning Center</a>
9058 - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
9059 - <a href="http://www.sencha.com/forum/">The forums</a>
9065 userAgent: navigator.userAgent.toLowerCase(),
9068 windowId: 'ext-window',
9069 documentId: 'ext-document',
9072 * True when the document is fully initialized and ready for action
9078 * True to automatically uncache orphaned Ext.Elements periodically
9081 enableGarbageCollector: true,
9084 * True to automatically purge event listeners during garbageCollection.
9087 enableListenerCollection: true,
9090 * Generates unique ids. If the element already has an id, it is unchanged
9091 * @param {HTMLElement/Ext.Element} el (optional) The element to generate an id for
9092 * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
9093 * @return {String} The generated Id.
9095 id: function(el, prefix) {
9098 el = Ext.getDom(el, true) || {};
9099 if (el === document) {
9100 el.id = me.documentId;
9102 else if (el === window) {
9103 el.id = me.windowId;
9106 if (me.isSandboxed) {
9107 if (!me.uniqueGlobalNamespace) {
9108 me.getUniqueGlobalNamespace();
9110 sandboxPrefix = me.uniqueGlobalNamespace + '-';
9112 el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
9118 * Returns the current document body as an {@link Ext.Element}.
9119 * @return Ext.Element The document body
9121 getBody: function() {
9122 return Ext.get(document.body || false);
9126 * Returns the current document head as an {@link Ext.Element}.
9127 * @return Ext.Element The document head
9130 getHead: function() {
9134 if (head == undefined) {
9135 head = Ext.get(document.getElementsByTagName("head")[0]);
9143 * Returns the current HTML document object as an {@link Ext.Element}.
9144 * @return Ext.Element The document
9146 getDoc: function() {
9147 return Ext.get(document);
9151 * This is shorthand reference to {@link Ext.ComponentManager#get}.
9152 * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
9153 * @param {String} id The component {@link Ext.Component#id id}
9154 * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
9157 getCmp: function(id) {
9158 return Ext.ComponentManager.get(id);
9162 * Returns the current orientation of the mobile device
9163 * @return {String} Either 'portrait' or 'landscape'
9165 getOrientation: function() {
9166 return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
9170 * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
9171 * DOM (if applicable) and calling their destroy functions (if available). This method is primarily
9172 * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
9173 * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be
9174 * passed into this function in a single call as separate arguments.
9175 * @param {Ext.Element/Ext.Component/Ext.Element[]/Ext.Component[]...} arg1
9176 * An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
9178 destroy: function() {
9179 var ln = arguments.length,
9182 for (i = 0; i < ln; i++) {
9185 if (Ext.isArray(arg)) {
9186 this.destroy.apply(this, arg);
9188 else if (Ext.isFunction(arg.destroy)) {
9199 * Execute a callback function in a particular scope. If no function is passed the call is ignored.
9201 * For example, these lines are equivalent:
9203 * Ext.callback(myFunc, this, [arg1, arg2]);
9204 * Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
9206 * @param {Function} callback The callback to execute
9207 * @param {Object} scope (optional) The scope to execute in
9208 * @param {Array} args (optional) The arguments to pass to the function
9209 * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
9211 callback: function(callback, scope, args, delay){
9212 if(Ext.isFunction(callback)){
9214 scope = scope || window;
9216 Ext.defer(callback, delay, scope, args);
9218 callback.apply(scope, args);
9224 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
9225 * @param {String} value The string to encode
9226 * @return {String} The encoded text
9228 htmlEncode : function(value) {
9229 return Ext.String.htmlEncode(value);
9233 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
9234 * @param {String} value The string to decode
9235 * @return {String} The decoded text
9237 htmlDecode : function(value) {
9238 return Ext.String.htmlDecode(value);
9242 * Appends content to the query string of a URL, handling logic for whether to place
9243 * a question mark or ampersand.
9244 * @param {String} url The URL to append to.
9245 * @param {String} s The content to append to the URL.
9246 * @return (String) The resulting URL
9248 urlAppend : function(url, s) {
9249 if (!Ext.isEmpty(s)) {
9250 return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
9257 Ext.ns = Ext.namespace;
9260 window.undefined = window.undefined;
9264 * Ext core utilities and functions.
9269 FF 3.6 - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17
9270 FF 4.0.1 - Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
9271 FF 5.0 - Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0
9273 IE6 - Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)
9274 IE7 - Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1;)
9275 IE8 - Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)
9276 IE9 - Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
9278 Chrome 11 - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.60 Safari/534.24
9280 Safari 5 - Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1
9282 Opera 11.11 - Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11
9284 var check = function(regex){
9285 return regex.test(Ext.userAgent);
9287 isStrict = document.compatMode == "CSS1Compat",
9288 version = function (is, regex) {
9290 return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0;
9292 docMode = document.documentMode,
9293 isOpera = check(/opera/),
9294 isOpera10_5 = isOpera && check(/version\/10\.5/),
9295 isChrome = check(/\bchrome\b/),
9296 isWebKit = check(/webkit/),
9297 isSafari = !isChrome && check(/safari/),
9298 isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
9299 isSafari3 = isSafari && check(/version\/3/),
9300 isSafari4 = isSafari && check(/version\/4/),
9301 isSafari5 = isSafari && check(/version\/5/),
9302 isIE = !isOpera && check(/msie/),
9303 isIE7 = isIE && (check(/msie 7/) || docMode == 7),
9304 isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
9305 isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
9306 isIE6 = isIE && check(/msie 6/),
9307 isGecko = !isWebKit && check(/gecko/),
9308 isGecko3 = isGecko && check(/rv:1\.9/),
9309 isGecko4 = isGecko && check(/rv:2\.0/),
9310 isGecko5 = isGecko && check(/rv:5\./),
9311 isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
9312 isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
9313 isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
9314 isWindows = check(/windows|win32/),
9315 isMac = check(/macintosh|mac os x/),
9316 isLinux = check(/linux/),
9317 scrollbarSize = null,
9318 chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/),
9319 firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/),
9320 ieVersion = version(isIE, /msie (\d+\.\d+)/),
9321 operaVersion = version(isOpera, /version\/(\d+\.\d+)/),
9322 safariVersion = version(isSafari, /version\/(\d+\.\d+)/),
9323 webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/),
9324 isSecure = /^https/i.test(window.location.protocol);
9326 // remove css image flicker
9328 document.execCommand("BackgroundImageCache", false, true);
9331 function dumpObject (object) {
9332 var member, members = [];
9334 // Cannot use Ext.encode since it can recurse endlessly (if we're lucky)
9335 // ...and the data could be prettier!
9336 Ext.Object.each(object, function (name, value) {
9337 if (typeof(value) === "function") {
9341 if (!Ext.isDefined(value) || value === null ||
9342 Ext.isDate(value) ||
9343 Ext.isString(value) || (typeof(value) == "number") ||
9344 Ext.isBoolean(value)) {
9345 member = Ext.encode(value);
9346 } else if (Ext.isArray(value)) {
9348 } else if (Ext.isObject(value)) {
9351 member = 'undefined';
9353 members.push(Ext.encode(name) + ': ' + member);
9356 if (members.length) {
9357 return ' \nData: {\n ' + members.join(',\n ') + '\n}';
9362 function log (message) {
9364 con = Ext.global.console,
9366 indent = log.indent || 0,
9369 log.indent = indent;
9371 if (!Ext.isString(message)) {
9373 message = options.msg || '';
9374 level = options.level || level;
9375 dump = options.dump;
9376 stack = options.stack;
9378 if (options.indent) {
9380 } else if (options.outdent) {
9381 log.indent = indent = Math.max(indent - 1, 0);
9384 if (dump && !(con && con.dir)) {
9385 message += dumpObject(dump);
9390 if (arguments.length > 1) {
9391 message += Array.prototype.slice.call(arguments, 1).join('');
9394 message = indent ? Ext.String.repeat(' ', indent) + message : message;
9395 // w/o console, all messages are equal, so munge the level into the message:
9396 if (level != 'log') {
9397 message = '[' + level.charAt(0).toUpperCase() + '] ' + message;
9400 // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
9401 // an early test may fail either direction if Firebug is toggled.
9403 if (con) { // if (Firebug-like console)
9405 con[level](message);
9414 if (stack && con.trace) {
9415 // Firebug's console.error() includes a trace already...
9416 if (!con.firebug || level != 'error') {
9422 opera.postError(message);
9427 if (out.length >= max) {
9428 // this formula allows out.max to change (via debugger), where the
9429 // more obvious "max/4" would not quite be the same
9430 Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%
9437 // Mostly informational, but the Ext.Error notifier uses them:
9439 ++log.counters[level];
9443 log.counters = { error: 0, warn: 0, info: 0, log: 0 };
9446 log.show = function () {
9447 window.open('','extlog').document.write([
9448 '<html><head><script type="text/javascript">',
9449 'var lastCount = 0;',
9450 'function update () {',
9451 'var ext = window.opener.Ext,',
9452 'extlog = ext && ext.log;',
9453 'if (extlog && extlog.out && lastCount != extlog.count) {',
9454 'lastCount = extlog.count;',
9455 'var s = "<tt>" + extlog.out.join("<br>").replace(/[ ]/g, " ") + "</tt>";',
9456 'document.body.innerHTML = s;',
9458 'setTimeout(update, 1000);',
9460 'setTimeout(update, 1000);',
9461 '</script></head><body></body></html>'].join(''));
9464 Ext.setVersion('extjs', '4.0.7');
9467 * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
9468 * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
9471 SSL_SECURE_URL : isSecure && isIE ? 'javascript:""' : 'about:blank',
9474 * True if the {@link Ext.fx.Anim} Class is available
9476 * @property enableFx
9480 * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
9481 * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
9482 * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
9485 scopeResetCSS : Ext.buildSettings.scopeResetCSS,
9488 * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
9489 * Currently not optimized for performance.
9492 enableNestedListenerRemoval : false,
9495 * Indicates whether to use native browser parsing for JSON methods.
9496 * This option is ignored if the browser does not support native JSON methods.
9497 * <b>Note: Native JSON methods will not work with objects that have functions.
9498 * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
9501 USE_NATIVE_JSON : false,
9504 * Return the dom node for the passed String (id), dom node, or Ext.Element.
9505 * Optional 'strict' flag is needed for IE since it can return 'name' and
9506 * 'id' elements by using getElementById.
9507 * Here are some examples:
9509 // gets dom node based on id
9510 var elDom = Ext.getDom('elId');
9511 // gets dom node based on the dom node
9512 var elDom1 = Ext.getDom(elDom);
9514 // If we don't know if we are working with an
9515 // Ext.Element or a dom node use Ext.getDom
9517 var dom = Ext.getDom(el);
9518 // do something with the dom node
9521 * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
9522 * when this method is called to be successful.
9523 * @param {String/HTMLElement/Ext.Element} el
9524 * @return HTMLElement
9526 getDom : function(el, strict) {
9527 if (!el || !document) {
9533 if (typeof el == 'string') {
9534 var e = document.getElementById(el);
9535 // IE returns elements with the 'name' and 'id' attribute.
9536 // we do a strict check to return the element with only the id attribute
9537 if (e && isIE && strict) {
9538 if (el == e.getAttribute('id')) {
9552 * Removes a DOM node from the document.
9553 * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
9554 * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
9555 * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
9556 * will be ignored if passed in.</p>
9557 * @param {HTMLElement} node The node to remove
9560 removeNode : isIE6 || isIE7 ? function() {
9563 if(n && n.tagName != 'BODY'){
9564 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9565 d = d || document.createElement('div');
9568 delete Ext.cache[n.id];
9572 if (n && n.parentNode && n.tagName != 'BODY') {
9573 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9574 n.parentNode.removeChild(n);
9575 delete Ext.cache[n.id];
9581 isIEQuirks: isIE && !isStrict,
9584 * True if the detected browser is Opera.
9590 * True if the detected browser is Opera 10.5x.
9593 isOpera10_5 : isOpera10_5,
9596 * True if the detected browser uses WebKit.
9599 isWebKit : isWebKit,
9602 * True if the detected browser is Chrome.
9605 isChrome : isChrome,
9608 * True if the detected browser is Safari.
9611 isSafari : isSafari,
9614 * True if the detected browser is Safari 3.x.
9617 isSafari3 : isSafari3,
9620 * True if the detected browser is Safari 4.x.
9623 isSafari4 : isSafari4,
9626 * True if the detected browser is Safari 5.x.
9629 isSafari5 : isSafari5,
9632 * True if the detected browser is Safari 2.x.
9635 isSafari2 : isSafari2,
9638 * True if the detected browser is Internet Explorer.
9644 * True if the detected browser is Internet Explorer 6.x.
9650 * True if the detected browser is Internet Explorer 7.x.
9656 * True if the detected browser is Internet Explorer 8.x.
9662 * True if the detected browser is Internet Explorer 9.x.
9668 * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
9674 * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
9677 isGecko3 : isGecko3,
9680 * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
9683 isGecko4 : isGecko4,
9686 * True if the detected browser uses a Gecko 5.0+ layout engine (e.g. Firefox 5.x).
9689 isGecko5 : isGecko5,
9692 * True if the detected browser uses FireFox 3.0
9698 * True if the detected browser uses FireFox 3.5
9704 * True if the detected browser uses FireFox 3.6
9710 * True if the detected browser uses FireFox 4
9713 isFF4 : 4 <= firefoxVersion && firefoxVersion < 5,
9716 * True if the detected browser uses FireFox 5
9719 isFF5 : 5 <= firefoxVersion && firefoxVersion < 6,
9722 * True if the detected platform is Linux.
9728 * True if the detected platform is Windows.
9731 isWindows : isWindows,
9734 * True if the detected platform is Mac OS.
9740 * The current version of Chrome (0 if the browser is not Chrome).
9743 chromeVersion: chromeVersion,
9746 * The current version of Firefox (0 if the browser is not Firefox).
9749 firefoxVersion: firefoxVersion,
9752 * The current version of IE (0 if the browser is not IE). This does not account
9753 * for the documentMode of the current page, which is factored into {@link #isIE7},
9754 * {@link #isIE8} and {@link #isIE9}. Thus this is not always true:
9756 * Ext.isIE8 == (Ext.ieVersion == 8)
9761 ieVersion: ieVersion,
9764 * The current version of Opera (0 if the browser is not Opera).
9767 operaVersion: operaVersion,
9770 * The current version of Safari (0 if the browser is not Safari).
9773 safariVersion: safariVersion,
9776 * The current version of WebKit (0 if the browser does not use WebKit).
9779 webKitVersion: webKitVersion,
9782 * True if the page is running over SSL
9788 * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
9789 * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
9790 * For other browsers it uses an inline data URL.
9793 BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
9796 * <p>Utility method for returning a default value if the passed value is empty.</p>
9797 * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
9799 * <li>undefined</li>
9800 * <li>an empty array</li>
9801 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
9803 * @param {Object} value The value to test
9804 * @param {Object} defaultValue The value to return if the original value is empty
9805 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
9806 * @return {Object} value, if non-empty, else defaultValue
9807 * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
9809 value : function(v, defaultValue, allowBlank){
9810 return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
9814 * Escapes the passed string for use in a regular expression
9815 * @param {String} str
9817 * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
9819 escapeRe : function(s) {
9820 return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
9824 * Applies event listeners to elements by selectors when the document is ready.
9825 * The event name is specified with an <tt>@</tt> suffix.
9828 // add a listener for click on all anchors in element with id foo
9829 '#foo a@click' : function(e, t){
9833 // add the same listener to multiple selectors (separated by comma BEFORE the @)
9834 '#foo a, #bar span.some-class@mouseover' : function(){
9839 * @param {Object} obj The list of behaviors to apply
9841 addBehaviors : function(o){
9843 Ext.onReady(function(){
9844 Ext.addBehaviors(o);
9847 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
9852 if ((parts = b.split('@'))[1]) { // for Object prototype breakers
9855 cache[s] = Ext.select(s);
9857 cache[s].on(parts[1], o[b]);
9865 * Returns the size of the browser scrollbars. This can differ depending on
9866 * operating system settings, such as the theme or font size.
9867 * @param {Boolean} force (optional) true to force a recalculation of the value.
9868 * @return {Object} An object containing the width of a vertical scrollbar and the
9869 * height of a horizontal scrollbar.
9871 getScrollbarSize: function (force) {
9876 if(force === true || scrollbarSize === null){
9878 // When IE9 positions an element offscreen via offsets, the offsetWidth is
9879 // inaccurately reported. For IE9 only, we render on screen before removing.
9880 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets',
9881 // Append our div, do our calculation and then remove it
9882 div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
9883 child = div.child('div', true),
9884 w1 = child.offsetWidth;
9886 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
9888 var w2 = child.offsetWidth, width = w1 - w2;
9891 // We assume width == height for now. TODO: is this always true?
9892 scrollbarSize = { width: width, height: width };
9895 return scrollbarSize;
9899 * Utility method for getting the width of the browser's vertical scrollbar. This
9900 * can differ depending on operating system settings, such as the theme or font size.
9902 * This method is deprected in favor of {@link #getScrollbarSize}.
9904 * @param {Boolean} force (optional) true to force a recalculation of the value.
9905 * @return {Number} The width of a vertical scrollbar.
9908 getScrollBarWidth: function(force){
9909 var size = Ext.getScrollbarSize(force);
9910 return size.width + 2; // legacy fudge factor
9914 * Copies a set of named properties fom the source object to the destination object.
9918 * ImageComponent = Ext.extend(Ext.Component, {
9919 * initComponent: function() {
9920 * this.autoEl = { tag: 'img' };
9921 * MyComponent.superclass.initComponent.apply(this, arguments);
9922 * this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
9926 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
9928 * @param {Object} dest The destination object.
9929 * @param {Object} source The source object.
9930 * @param {String/String[]} names Either an Array of property names, or a comma-delimited list
9931 * of property names to copy.
9932 * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
9933 * @return {Object} The modified object.
9935 copyTo : function(dest, source, names, usePrototypeKeys){
9936 if(typeof names == 'string'){
9937 names = names.split(/[,;\s]/);
9939 Ext.each(names, function(name){
9940 if(usePrototypeKeys || source.hasOwnProperty(name)){
9941 dest[name] = source[name];
9948 * Attempts to destroy and then remove a set of named properties of the passed object.
9949 * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
9950 * @param {String...} args One or more names of the properties to destroy and remove from the object.
9952 destroyMembers : function(o){
9953 for (var i = 1, a = arguments, len = a.length; i < len; i++) {
9954 Ext.destroy(o[a[i]]);
9960 * Logs a message. If a console is present it will be used. On Opera, the method
9961 * "opera.postError" is called. In other cases, the message is logged to an array
9962 * "Ext.log.out". An attached debugger can watch this array and view the log. The
9963 * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250).
9964 * The `Ext.log.out` array can also be written to a popup window by entering the
9965 * following in the URL bar (a "bookmarklet"):
9967 * javascript:void(Ext.log.show());
9969 * If additional parameters are passed, they are joined and appended to the message.
9970 * A technique for tracing entry and exit of a function is this:
9973 * Ext.log({ indent: 1 }, '>> foo');
9975 * // log statements in here or methods called from here will be indented
9978 * Ext.log({ outdent: 1 }, '<< foo');
9981 * This method does nothing in a release build.
9983 * @param {String/Object} message The message to log or an options object with any
9984 * of the following properties:
9986 * - `msg`: The message to log (required).
9987 * - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
9988 * - `dump`: An object to dump to the log as part of the message.
9989 * - `stack`: True to include a stack trace in the log.
9990 * - `indent`: Cause subsequent log statements to be indented one step.
9991 * - `outdent`: Cause this and following statements to be one step less indented.
9999 * Partitions the set into two sets: a true set and a false set.
10004 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
10010 return val.className == "class1"
10013 // true are those paragraph elements with a className of "class1",
10014 // false set are those that do not have that className.
10016 * @param {Array/NodeList} arr The array to partition
10017 * @param {Function} truth (optional) a function to determine truth. If this is omitted the element
10018 * itself must be able to be evaluated for its truthfulness.
10019 * @return {Array} [array of truish values, array of falsy values]
10020 * @deprecated 4.0.0 Will be removed in the next major version
10022 partition : function(arr, truth){
10024 Ext.each(arr, function(v, i, a) {
10025 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
10031 * Invokes a method on each item in an Array.
10034 Ext.invoke(Ext.query("p"), "getAttribute", "id");
10035 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
10037 * @param {Array/NodeList} arr The Array of items to invoke the method on.
10038 * @param {String} methodName The method name to invoke.
10039 * @param {Object...} args Arguments to send into the method invocation.
10040 * @return {Array} The results of invoking the method on each item in the array.
10041 * @deprecated 4.0.0 Will be removed in the next major version
10043 invoke : function(arr, methodName){
10045 args = Array.prototype.slice.call(arguments, 2);
10046 Ext.each(arr, function(v,i) {
10047 if (v && typeof v[methodName] == 'function') {
10048 ret.push(v[methodName].apply(v, args));
10050 ret.push(undefined);
10057 * <p>Zips N sets together.</p>
10060 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
10067 return "$" + a + "" + b + "." + c
10069 ); // ["$+12.43", "$-10.15", "$+22.96"]
10071 * @param {Array/NodeList...} arr This argument may be repeated. Array(s) to contribute values.
10072 * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
10073 * @return {Array} The zipped set.
10074 * @deprecated 4.0.0 Will be removed in the next major version
10077 var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
10080 len = Ext.max(Ext.pluck(arrs, "length")),
10083 for (var i = 0; i < len; i++) {
10086 ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
10088 for (var j = 0, aLen = arrs.length; j < aLen; j++){
10089 ret[i].push( arrs[j][i] );
10097 * Turns an array into a sentence, joined by a specified connector - e.g.:
10098 * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
10099 * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
10100 * @param {String[]} items The array to create a sentence from
10101 * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
10102 * @return {String} The sentence string
10103 * @deprecated 4.0.0 Will be removed in the next major version
10105 toSentence: function(items, connector) {
10106 var length = items.length;
10111 var head = items.slice(0, length - 1),
10112 tail = items[length - 1];
10114 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
10119 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
10120 * you may want to set this to true.
10128 * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
10130 * See Ext.app.Application for details.
10132 * @param {Object} config
10134 Ext.application = function(config) {
10135 Ext.require('Ext.app.Application');
10137 Ext.onReady(function() {
10138 Ext.create('Ext.app.Application', config);
10143 * @class Ext.util.Format
10145 This class is a centralized place for formatting functions. It includes
10146 functions to format various different types of data, such as text, dates and numeric values.
10149 This class contains several options for localization. These can be set once the library has loaded,
10150 all calls to the functions from that point will use the locale settings that were specified.
10152 - thousandSeparator
10157 This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}.
10159 __Using with renderers__
10160 There are two helper functions that return a new function that can be used in conjunction with
10165 renderer: Ext.util.Format.dateRenderer('Y-m-d')
10168 renderer: Ext.util.Format.numberRenderer('0.000')
10171 Functions that only take a single argument can also be passed directly:
10174 renderer: Ext.util.Format.usMoney
10176 dataIndex: 'productCode',
10177 renderer: Ext.util.Format.uppercase
10180 __Using with XTemplates__
10181 XTemplates can also directly use Ext.util.Format functions:
10183 new Ext.XTemplate([
10184 'Date: {startDate:date("Y-m-d")}',
10185 'Cost: {cost:usMoney}'
10192 Ext.ns('Ext.util');
10194 Ext.util.Format = {};
10195 var UtilFormat = Ext.util.Format,
10196 stripTagsRE = /<\/?[^>]+>/gi,
10197 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
10198 nl2brRe = /\r?\n/g,
10200 // A RegExp to remove from a number format string, all characters except digits and '.'
10201 formatCleanRe = /[^\d\.]/g,
10203 // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
10204 // Created on first use. The local decimal separator character must be initialized for this to be created.
10207 Ext.apply(UtilFormat, {
10209 * @property {String} thousandSeparator
10210 * <p>The character that the {@link #number} function uses as a thousand separator.</p>
10211 * <p>This may be overridden in a locale file.</p>
10213 thousandSeparator: ',',
10216 * @property {String} decimalSeparator
10217 * <p>The character that the {@link #number} function uses as a decimal point.</p>
10218 * <p>This may be overridden in a locale file.</p>
10220 decimalSeparator: '.',
10223 * @property {Number} currencyPrecision
10224 * <p>The number of decimal places that the {@link #currency} function displays.</p>
10225 * <p>This may be overridden in a locale file.</p>
10227 currencyPrecision: 2,
10230 * @property {String} currencySign
10231 * <p>The currency sign that the {@link #currency} function displays.</p>
10232 * <p>This may be overridden in a locale file.</p>
10237 * @property {Boolean} currencyAtEnd
10238 * <p>This may be set to <code>true</code> to make the {@link #currency} function
10239 * append the currency sign to the formatted value.</p>
10240 * <p>This may be overridden in a locale file.</p>
10242 currencyAtEnd: false,
10245 * Checks a reference and converts it to empty string if it is undefined
10246 * @param {Object} value Reference to check
10247 * @return {Object} Empty string if converted, otherwise the original value
10249 undef : function(value) {
10250 return value !== undefined ? value : "";
10254 * Checks a reference and converts it to the default value if it's empty
10255 * @param {Object} value Reference to check
10256 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
10259 defaultValue : function(value, defaultValue) {
10260 return value !== undefined && value !== '' ? value : defaultValue;
10264 * Returns a substring from within an original string
10265 * @param {String} value The original text
10266 * @param {Number} start The start index of the substring
10267 * @param {Number} length The length of the substring
10268 * @return {String} The substring
10270 substr : function(value, start, length) {
10271 return String(value).substr(start, length);
10275 * Converts a string to all lower case letters
10276 * @param {String} value The text to convert
10277 * @return {String} The converted text
10279 lowercase : function(value) {
10280 return String(value).toLowerCase();
10284 * Converts a string to all upper case letters
10285 * @param {String} value The text to convert
10286 * @return {String} The converted text
10288 uppercase : function(value) {
10289 return String(value).toUpperCase();
10293 * Format a number as US currency
10294 * @param {Number/String} value The numeric value to format
10295 * @return {String} The formatted currency string
10297 usMoney : function(v) {
10298 return UtilFormat.currency(v, '$', 2);
10302 * Format a number as a currency
10303 * @param {Number/String} value The numeric value to format
10304 * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
10305 * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
10306 * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
10307 * @return {String} The formatted currency string
10309 currency: function(v, currencySign, decimals, end) {
10310 var negativeSign = '',
10316 negativeSign = '-';
10318 decimals = decimals || UtilFormat.currencyPrecision;
10319 format += format + (decimals > 0 ? '.' : '');
10320 for (; i < decimals; i++) {
10323 v = UtilFormat.number(v, format);
10324 if ((end || UtilFormat.currencyAtEnd) === true) {
10325 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
10327 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
10332 * Formats the passed date using the specified format pattern.
10333 * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
10334 * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
10335 * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
10336 * @return {String} The formatted date string.
10338 date: function(v, format) {
10342 if (!Ext.isDate(v)) {
10343 v = new Date(Date.parse(v));
10345 return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
10349 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
10350 * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
10351 * @return {Function} The date formatting function
10353 dateRenderer : function(format) {
10354 return function(v) {
10355 return UtilFormat.date(v, format);
10360 * Strips all HTML tags
10361 * @param {Object} value The text from which to strip tags
10362 * @return {String} The stripped text
10364 stripTags : function(v) {
10365 return !v ? v : String(v).replace(stripTagsRE, "");
10369 * Strips all script tags
10370 * @param {Object} value The text from which to strip script tags
10371 * @return {String} The stripped text
10373 stripScripts : function(v) {
10374 return !v ? v : String(v).replace(stripScriptsRe, "");
10378 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
10379 * @param {Number/String} size The numeric value to format
10380 * @return {String} The formatted file size
10382 fileSize : function(size) {
10384 return size + " bytes";
10385 } else if (size < 1048576) {
10386 return (Math.round(((size*10) / 1024))/10) + " KB";
10388 return (Math.round(((size*10) / 1048576))/10) + " MB";
10393 * It does simple math for use in a template, for example:<pre><code>
10394 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
10396 * @return {Function} A function that operates on the passed value.
10402 return function(v, a){
10404 fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
10411 * Rounds the passed number to the required decimal precision.
10412 * @param {Number/String} value The numeric value to round.
10413 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
10414 * @return {Number} The rounded value.
10416 round : function(value, precision) {
10417 var result = Number(value);
10418 if (typeof precision == 'number') {
10419 precision = Math.pow(10, precision);
10420 result = Math.round(value * precision) / precision;
10426 * <p>Formats the passed number according to the passed format string.</p>
10427 * <p>The number of digits after the decimal separator character specifies the number of
10428 * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
10429 * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
10430 * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
10431 * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
10432 * <p><b>New to Ext JS 4</b></p>
10433 * <p>Locale-specific characters are always used in the formatted output when inserting
10434 * thousand and decimal separators.</p>
10435 * <p>The format string must specify separator characters according to US/UK conventions ("," as the
10436 * thousand separator, and "." as the decimal separator)</p>
10437 * <p>To allow specification of format strings according to local conventions for separator characters, add
10438 * the string <code>/i</code> to the end of the format string.</p>
10439 * <div style="margin-left:40px">examples (123456.789):
10440 * <div style="margin-left:10px">
10441 * 0 - (123456) show only digits, no precision<br>
10442 * 0.00 - (123456.78) show only digits, 2 precision<br>
10443 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
10444 * 0,000 - (123,456) show comma and digits, no precision<br>
10445 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
10446 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
10447 * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
10448 * For example: 0.000,00/i
10450 * @param {Number} v The number to format.
10451 * @param {String} format The way you would like to format this text.
10452 * @return {String} The formatted number.
10454 number: function(v, formatString) {
10455 if (!formatString) {
10458 v = Ext.Number.from(v, NaN);
10462 var comma = UtilFormat.thousandSeparator,
10463 dec = UtilFormat.decimalSeparator,
10471 // The "/i" suffix allows caller to use a locale-specific formatting string.
10472 // Clean the format string by removing all but numerals and the decimal separator.
10473 // Then split the format string into pre and post decimal segments according to *what* the
10474 // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
10475 if (formatString.substr(formatString.length - 2) == '/i') {
10476 if (!I18NFormatCleanRe) {
10477 I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
10479 formatString = formatString.substr(0, formatString.length - 2);
10481 hasComma = formatString.indexOf(comma) != -1;
10482 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
10484 hasComma = formatString.indexOf(',') != -1;
10485 psplit = formatString.replace(formatCleanRe, '').split('.');
10488 if (1 < psplit.length) {
10489 v = v.toFixed(psplit[1].length);
10490 } else if(2 < psplit.length) {
10492 sourceClass: "Ext.util.Format",
10493 sourceMethod: "number",
10495 formatString: formatString,
10496 msg: "Invalid number format, should have no more than 1 decimal"
10502 var fnum = v.toString();
10504 psplit = fnum.split('.');
10507 var cnum = psplit[0],
10510 m = Math.floor(j / 3),
10511 n = cnum.length % 3 || 3,
10514 for (i = 0; i < j; i += n) {
10519 parr[parr.length] = cnum.substr(i, n);
10522 fnum = parr.join(comma);
10524 fnum += dec + psplit[1];
10528 fnum = psplit[0] + dec + psplit[1];
10534 * Edge case. If we have a very small negative number it will get rounded to 0,
10535 * however the initial check at the top will still report as negative. Replace
10536 * everything but 1-9 and check if the string is empty to determine a 0 value.
10538 neg = fnum.replace(/[^1-9]/g, '') !== '';
10541 return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
10545 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
10546 * @param {String} format Any valid number format string for {@link #number}
10547 * @return {Function} The number formatting function
10549 numberRenderer : function(format) {
10550 return function(v) {
10551 return UtilFormat.number(v, format);
10556 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
10557 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
10558 * if the value is 0 or greater than 1.
10559 * @param {Number} value The value to compare against
10560 * @param {String} singular The singular form of the word
10561 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
10563 plural : function(v, s, p) {
10564 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
10568 * Converts newline characters to the HTML tag <br/>
10569 * @param {String} The string value to format.
10570 * @return {String} The string with embedded <br/> tags in place of newlines.
10572 nl2br : function(v) {
10573 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
10577 * Alias for {@link Ext.String#capitalize}.
10579 * @alias Ext.String#capitalize
10581 capitalize: Ext.String.capitalize,
10584 * Alias for {@link Ext.String#ellipsis}.
10586 * @alias Ext.String#ellipsis
10588 ellipsis: Ext.String.ellipsis,
10591 * Alias for {@link Ext.String#format}.
10593 * @alias Ext.String#format
10595 format: Ext.String.format,
10598 * Alias for {@link Ext.String#htmlDecode}.
10600 * @alias Ext.String#htmlDecode
10602 htmlDecode: Ext.String.htmlDecode,
10605 * Alias for {@link Ext.String#htmlEncode}.
10607 * @alias Ext.String#htmlEncode
10609 htmlEncode: Ext.String.htmlEncode,
10612 * Alias for {@link Ext.String#leftPad}.
10614 * @alias Ext.String#leftPad
10616 leftPad: Ext.String.leftPad,
10619 * Alias for {@link Ext.String#trim}.
10621 * @alias Ext.String#trim
10623 trim : Ext.String.trim,
10626 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
10627 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
10628 * @param {Number/String} v The encoded margins
10629 * @return {Object} An object with margin sizes for top, right, bottom and left
10631 parseBox : function(box) {
10632 if (Ext.isNumber(box)) {
10633 box = box.toString();
10635 var parts = box.split(' '),
10639 parts[1] = parts[2] = parts[3] = parts[0];
10641 else if (ln == 2) {
10642 parts[2] = parts[0];
10643 parts[3] = parts[1];
10645 else if (ln == 3) {
10646 parts[3] = parts[1];
10650 top :parseInt(parts[0], 10) || 0,
10651 right :parseInt(parts[1], 10) || 0,
10652 bottom:parseInt(parts[2], 10) || 0,
10653 left :parseInt(parts[3], 10) || 0
10658 * Escapes the passed string for use in a regular expression
10659 * @param {String} str
10662 escapeRegex : function(s) {
10663 return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
10669 * @class Ext.util.TaskRunner
10670 * Provides the ability to execute one or more arbitrary tasks in a multithreaded
10671 * manner. Generally, you can use the singleton {@link Ext.TaskManager} instead, but
10672 * if needed, you can create separate instances of TaskRunner. Any number of
10673 * separate tasks can be started at any time and will run independently of each
10674 * other. Example usage:
10676 // Start a simple clock task that updates a div once per second
10677 var updateClock = function(){
10678 Ext.fly('clock').update(new Date().format('g:i:s A'));
10682 interval: 1000 //1 second
10684 var runner = new Ext.util.TaskRunner();
10685 runner.start(task);
10687 // equivalent using TaskManager
10688 Ext.TaskManager.start({
10694 * <p>See the {@link #start} method for details about how to configure a task object.</p>
10695 * Also see {@link Ext.util.DelayedTask}.
10698 * @param {Number} [interval=10] The minimum precision in milliseconds supported by this TaskRunner instance
10700 Ext.ns('Ext.util');
10702 Ext.util.TaskRunner = function(interval) {
10703 interval = interval || 10;
10710 stopThread = function() {
10717 startThread = function() {
10720 id = setInterval(runTasks, interval);
10725 removeTask = function(t) {
10726 removeQueue.push(t);
10728 t.onStop.apply(t.scope || t);
10733 runTasks = function() {
10734 var rqLen = removeQueue.length,
10735 now = new Date().getTime(),
10739 for (i = 0; i < rqLen; i++) {
10740 Ext.Array.remove(tasks, removeQueue[i]);
10743 if (tasks.length < 1) {
10752 len = tasks.length;
10753 for (; i < len; ++i) {
10755 itime = now - t.taskRunTime;
10756 if (t.interval <= itime) {
10757 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
10758 t.taskRunTime = now;
10759 if (rt === false || t.taskRunCount === t.repeat) {
10764 if (t.duration && t.duration <= (now - t.taskStartTime)) {
10771 * Starts a new task.
10773 * @param {Object} task <p>A config object that supports the following properties:<ul>
10774 * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
10775 * function will be called at each interval and passed the <code>args</code> argument if specified, and the
10776 * current invocation count if not.</p>
10777 * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
10778 * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
10779 * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
10780 * should be invoked.</div></li>
10781 * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
10782 * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
10783 * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
10784 * <code>run</code> function. Defaults to the task config object.</div></li>
10785 * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
10786 * the task before stopping automatically (defaults to indefinite).</div></li>
10787 * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
10788 * stopping automatically (defaults to indefinite).</div></li>
10790 * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
10791 * that calculations based on the repeat count can be performed.</p>
10792 * @return {Object} The task
10794 this.start = function(task) {
10796 task.taskStartTime = new Date().getTime();
10797 task.taskRunTime = 0;
10798 task.taskRunCount = 0;
10804 * Stops an existing running task.
10806 * @param {Object} task The task to stop
10807 * @return {Object} The task
10809 this.stop = function(task) {
10815 * Stops all tasks that are currently running.
10818 this.stopAll = function() {
10820 for (var i = 0, len = tasks.length; i < len; i++) {
10821 if (tasks[i].onStop) {
10831 * @class Ext.TaskManager
10832 * @extends Ext.util.TaskRunner
10833 * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks. See
10834 * {@link Ext.util.TaskRunner} for supported methods and task config properties.
10836 // Start a simple clock task that updates a div once per second
10839 Ext.fly('clock').update(new Date().format('g:i:s A'));
10841 interval: 1000 //1 second
10843 Ext.TaskManager.start(task);
10845 * <p>See the {@link #start} method for details about how to configure a task object.</p>
10848 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
10852 * Determines information about the current platform the application is running on.
10857 init : function(navigator) {
10858 var platforms = this.platforms,
10859 ln = platforms.length,
10862 navigator = navigator || window.navigator;
10864 for (i = 0; i < ln; i++) {
10865 platform = platforms[i];
10866 this[platform.identity] = platform.regex.test(navigator[platform.property]);
10870 * @property Desktop True if the browser is running on a desktop machine
10873 this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
10875 * @property Tablet True if the browser is running on a tablet (iPad)
10877 this.Tablet = this.iPad;
10879 * @property Phone True if the browser is running on a phone.
10882 this.Phone = !this.Desktop && !this.Tablet;
10884 * @property iOS True if the browser is running on iOS
10887 this.iOS = this.iPhone || this.iPad || this.iPod;
10890 * @property Standalone Detects when application has been saved to homescreen.
10893 this.Standalone = !!window.navigator.standalone;
10897 * @property iPhone True when the browser is running on a iPhone
10901 property: 'platform',
10907 * @property iPod True when the browser is running on a iPod
10911 property: 'platform',
10917 * @property iPad True when the browser is running on a iPad
10921 property: 'userAgent',
10927 * @property Blackberry True when the browser is running on a Blackberry
10931 property: 'userAgent',
10932 regex: /Blackberry/i,
10933 identity: 'Blackberry'
10937 * @property Android True when the browser is running on an Android device
10941 property: 'userAgent',
10943 identity: 'Android'
10947 * @property Mac True when the browser is running on a Mac
10951 property: 'platform',
10957 * @property Windows True when the browser is running on Windows
10961 property: 'platform',
10963 identity: 'Windows'
10967 * @property Linux True when the browser is running on Linux
10971 property: 'platform',
10980 * @class Ext.supports
10982 * Determines information about features are supported in the current environment
10987 init : function() {
10988 var doc = document,
10989 div = doc.createElement('div'),
10990 tests = this.tests,
10995 '<div style="height:30px;width:50px;">',
10996 '<div style="height:20px;width:20px;"></div>',
10998 '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
10999 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
11001 '<div style="float:left; background-color:transparent;"></div>'
11004 doc.body.appendChild(div);
11006 for (i = 0; i < ln; i++) {
11008 this[test.identity] = test.fn.call(this, doc, div);
11011 doc.body.removeChild(div);
11015 * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
11018 CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
11021 * @property ClassList True if document environment supports the HTML5 classList API.
11024 ClassList: !!document.documentElement.classList,
11027 * @property OrientationChange True if the device supports orientation change
11030 OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
11033 * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
11036 DeviceMotion: ('ondevicemotion' in window),
11039 * @property Touch True if the device supports touch
11042 // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
11043 // and Safari 4.0 (they all have 'ontouchstart' in the window object).
11044 Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
11048 * @property Transitions True if the device supports CSS3 Transitions
11052 identity: 'Transitions',
11053 fn: function(doc, div) {
11061 TE = 'TransitionEnd',
11062 transitionEndName = [
11064 'transitionend', //Moz bucks the prefixing convention
11069 ln = prefix.length,
11072 div = Ext.get(div);
11073 for (; i < ln; i++) {
11074 if (div.getStyle(prefix[i] + "TransitionProperty")) {
11075 Ext.supports.CSS3Prefix = prefix[i];
11076 Ext.supports.CSS3TransitionEnd = transitionEndName[i];
11086 * @property RightMargin True if the device supports right margin.
11087 * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
11091 identity: 'RightMargin',
11092 fn: function(doc, div) {
11093 var view = doc.defaultView;
11094 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
11099 * @property DisplayChangeInputSelectionBug True if INPUT elements lose their
11100 * selection when their display style is changed. Essentially, if a text input
11101 * has focus and its display style is changed, the I-beam disappears.
11103 * This bug is encountered due to the work around in place for the {@link #RightMargin}
11104 * bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
11105 * in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
11106 * version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
11109 identity: 'DisplayChangeInputSelectionBug',
11111 var webKitVersion = Ext.webKitVersion;
11112 // WebKit but older than Safari 5 or Chrome 6:
11113 return 0 < webKitVersion && webKitVersion < 533;
11118 * @property DisplayChangeTextAreaSelectionBug True if TEXTAREA elements lose their
11119 * selection when their display style is changed. Essentially, if a text area has
11120 * focus and its display style is changed, the I-beam disappears.
11122 * This bug is encountered due to the work around in place for the {@link #RightMargin}
11123 * bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
11124 * be fixed in Chrome 11.
11127 identity: 'DisplayChangeTextAreaSelectionBug',
11129 var webKitVersion = Ext.webKitVersion;
11132 Has bug w/textarea:
11134 (Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
11135 AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
11137 (Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
11138 AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
11143 (Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
11144 AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
11147 return 0 < webKitVersion && webKitVersion < 534.24;
11152 * @property TransparentColor True if the device supports transparent color
11156 identity: 'TransparentColor',
11157 fn: function(doc, div, view) {
11158 view = doc.defaultView;
11159 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
11164 * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
11168 identity: 'ComputedStyle',
11169 fn: function(doc, div, view) {
11170 view = doc.defaultView;
11171 return view && view.getComputedStyle;
11176 * @property SVG True if the device supports SVG
11181 fn: function(doc) {
11182 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
11187 * @property Canvas True if the device supports Canvas
11191 identity: 'Canvas',
11192 fn: function(doc) {
11193 return !!doc.createElement('canvas').getContext;
11198 * @property VML True if the device supports VML
11203 fn: function(doc) {
11204 var d = doc.createElement("div");
11205 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
11206 return (d.childNodes.length == 2);
11211 * @property Float True if the device supports CSS float
11216 fn: function(doc, div) {
11217 return !!div.lastChild.style.cssFloat;
11222 * @property AudioTag True if the device supports the HTML5 audio tag
11226 identity: 'AudioTag',
11227 fn: function(doc) {
11228 return !!doc.createElement('audio').canPlayType;
11233 * @property History True if the device supports HTML5 history
11237 identity: 'History',
11239 return !!(window.history && history.pushState);
11244 * @property CSS3DTransform True if the device supports CSS3DTransform
11248 identity: 'CSS3DTransform',
11250 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
11255 * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
11259 identity: 'CSS3LinearGradient',
11260 fn: function(doc, div) {
11261 var property = 'background-image:',
11262 webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
11263 w3c = 'linear-gradient(left top, black, white)',
11264 moz = '-moz-' + w3c,
11265 options = [property + webkit, property + w3c, property + moz];
11267 div.style.cssText = options.join(';');
11269 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
11274 * @property CSS3BorderRadius True if the device supports CSS3 border radius
11278 identity: 'CSS3BorderRadius',
11279 fn: function(doc, div) {
11280 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
11283 for (i = 0; i < domPrefixes.length; i++) {
11284 if (document.body.style[domPrefixes[i]] !== undefined) {
11293 * @property GeoLocation True if the device supports GeoLocation
11297 identity: 'GeoLocation',
11299 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
11303 * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
11307 identity: 'MouseEnterLeave',
11308 fn: function(doc, div){
11309 return ('onmouseenter' in div && 'onmouseleave' in div);
11313 * @property MouseWheel True if the browser supports the mousewheel event
11317 identity: 'MouseWheel',
11318 fn: function(doc, div) {
11319 return ('onmousewheel' in div);
11323 * @property Opacity True if the browser supports normal css opacity
11327 identity: 'Opacity',
11328 fn: function(doc, div){
11329 // Not a strict equal comparison in case opacity can be converted to a number.
11330 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
11333 div.firstChild.style.cssText = 'opacity:0.73';
11334 return div.firstChild.style.opacity == '0.73';
11338 * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
11342 identity: 'Placeholder',
11343 fn: function(doc) {
11344 return 'placeholder' in doc.createElement('input');
11349 * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight,
11350 * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
11354 identity: 'Direct2DBug',
11356 return Ext.isString(document.body.style.msTransformOrigin);
11360 * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
11364 identity: 'BoundingClientRect',
11365 fn: function(doc, div) {
11366 return Ext.isFunction(div.getBoundingClientRect);
11370 identity: 'IncludePaddingInWidthCalculation',
11371 fn: function(doc, div){
11372 var el = Ext.get(div.childNodes[1].firstChild);
11373 return el.getWidth() == 210;
11377 identity: 'IncludePaddingInHeightCalculation',
11378 fn: function(doc, div){
11379 var el = Ext.get(div.childNodes[1].firstChild);
11380 return el.getHeight() == 210;
11385 * @property ArraySort True if the Array sort native method isn't bugged.
11389 identity: 'ArraySort',
11391 var a = [1,2,3,4,5].sort(function(){ return 0; });
11392 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
11396 * @property Range True if browser support document.createRange native method.
11402 return !!document.createRange;
11406 * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
11410 identity: 'CreateContextualFragment',
11412 var range = Ext.supports.Range ? document.createRange() : false;
11414 return range && !!range.createContextualFragment;
11419 * @property WindowOnError True if browser supports window.onerror.
11423 identity: 'WindowOnError',
11425 // sadly, we cannot feature detect this...
11426 return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; // Chrome 10+
11436 This file is part of Ext JS 4
11438 Copyright (c) 2011 Sencha Inc
11440 Contact: http://www.sencha.com/contact
11442 GNU General Public License Usage
11443 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.
11445 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
11449 * @class Ext.DomHelper
11450 * @alternateClassName Ext.core.DomHelper
11452 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
11453 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11454 * from your DOM building code.</p>
11456 * <p><b><u>DomHelper element specification object</u></b></p>
11457 * <p>A specification object is used when creating elements. Attributes of this object
11458 * are assumed to be element attributes, except for 4 special attributes:
11459 * <div class="mdetail-params"><ul>
11460 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
11461 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
11462 * same kind of element definition objects to be created and appended. These can be nested
11463 * as deep as you want.</div></li>
11464 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
11465 * This will end up being either the "class" attribute on a HTML fragment or className
11466 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
11467 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
11469 * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
11470 * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
11471 * contains special characters that would not normally be allowed in a double-quoted attribute value,
11472 * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
11473 * malformed HTML being created. This behavior may change in a future release.</p>
11475 * <p><b><u>Insertion methods</u></b></p>
11476 * <p>Commonly used insertion methods:
11477 * <div class="mdetail-params"><ul>
11478 * <li><tt>{@link #append}</tt> : <div class="sub-desc"></div></li>
11479 * <li><tt>{@link #insertBefore}</tt> : <div class="sub-desc"></div></li>
11480 * <li><tt>{@link #insertAfter}</tt> : <div class="sub-desc"></div></li>
11481 * <li><tt>{@link #overwrite}</tt> : <div class="sub-desc"></div></li>
11482 * <li><tt>{@link #createTemplate}</tt> : <div class="sub-desc"></div></li>
11483 * <li><tt>{@link #insertHtml}</tt> : <div class="sub-desc"></div></li>
11486 * <p><b><u>Example</u></b></p>
11487 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
11488 * element with id <tt>'my-div'</tt>:<br>
11490 var dh = Ext.DomHelper; // create shorthand alias
11491 // specification object
11496 // append children after creating
11497 children: [ // may also specify 'cn' instead of 'children'
11498 {tag: 'li', id: 'item0', html: 'List Item 0'},
11499 {tag: 'li', id: 'item1', html: 'List Item 1'},
11500 {tag: 'li', id: 'item2', html: 'List Item 2'}
11503 var list = dh.append(
11504 'my-div', // the context element 'my-div' can either be the id or the actual node
11505 spec // the specification object
11508 * <p>Element creation specification parameters in this class may also be passed as an Array of
11509 * specification objects. This can be used to insert multiple sibling nodes into an existing
11510 * container very efficiently. For example, to add more list items to the example above:<pre><code>
11511 dh.append('my-ul', [
11512 {tag: 'li', id: 'item3', html: 'List Item 3'},
11513 {tag: 'li', id: 'item4', html: 'List Item 4'}
11515 * </code></pre></p>
11517 * <p><b><u>Templating</u></b></p>
11518 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
11519 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
11520 * insert new elements. Revisiting the example above, we could utilize templating this time:
11523 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
11525 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
11527 for(var i = 0; i < 5, i++){
11528 tpl.append(list, [i]); // use template to append to the actual node
11530 * </code></pre></p>
11531 * <p>An example using a template:<pre><code>
11532 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
11534 var tpl = new Ext.DomHelper.createTemplate(html);
11535 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
11536 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
11537 * </code></pre></p>
11539 * <p>The same example using named parameters:<pre><code>
11540 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11542 var tpl = new Ext.DomHelper.createTemplate(html);
11543 tpl.append('blog-roll', {
11545 url: 'http://www.edspencer.net/',
11546 text: "Ed's Site"
11548 tpl.append('blog-roll', {
11550 url: 'http://www.dustindiaz.com/',
11551 text: "Dustin's Site"
11553 * </code></pre></p>
11555 * <p><b><u>Compiling Templates</u></b></p>
11556 * <p>Templates are applied using regular expressions. The performance is great, but if
11557 * you are adding a bunch of DOM elements using the same template, you can increase
11558 * performance even further by {@link Ext.Template#compile "compiling"} the template.
11559 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
11560 * broken up at the different variable points and a dynamic function is created and eval'ed.
11561 * The generated function performs string concatenation of these parts and the passed
11562 * variables instead of using regular expressions.
11564 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11566 var tpl = new Ext.DomHelper.createTemplate(html);
11569 //... use template like normal
11570 * </code></pre></p>
11572 * <p><b><u>Performance Boost</u></b></p>
11573 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
11574 * of DOM can significantly boost performance.</p>
11575 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
11576 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
11577 * results in the creation of a text node. Usage:</p>
11579 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
11583 Ext.ns('Ext.core');
11584 Ext.core.DomHelper = Ext.DomHelper = function(){
11585 var tempTableEl = null,
11586 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
11587 tableRe = /^table|tbody|tr|td$/i,
11588 confRe = /tag|children|cn|html$/i,
11589 tableElRe = /td|tr|tbody/i,
11592 // kill repeat to save bytes
11593 afterbegin = 'afterbegin',
11594 afterend = 'afterend',
11595 beforebegin = 'beforebegin',
11596 beforeend = 'beforeend',
11599 tbs = ts+'<tbody>',
11600 tbe = '</tbody>'+te,
11601 trs = tbs + '<tr>',
11605 function doInsert(el, o, returnElement, pos, sibling, append){
11606 el = Ext.getDom(el);
11609 newNode = createDom(o, null);
11611 el.appendChild(newNode);
11613 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
11616 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
11618 return returnElement ? Ext.get(newNode, true) : newNode;
11621 function createDom(o, parentNode){
11629 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
11630 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
11631 for (var i = 0, l = o.length; i < l; i++) {
11632 createDom(o[i], el);
11634 } else if (typeof o == 'string') { // Allow a string as a child spec.
11635 el = doc.createTextNode(o);
11637 el = doc.createElement( o.tag || 'div' );
11638 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
11640 if(!confRe.test(attr)){
11643 el.className = val;
11646 el.setAttribute(attr, val);
11653 Ext.DomHelper.applyStyles(el, o.style);
11655 if ((cn = o.children || o.cn)) {
11657 } else if (o.html) {
11658 el.innerHTML = o.html;
11662 parentNode.appendChild(el);
11667 // build as innerHTML where available
11668 function createHtml(o){
11676 if(typeof o == "string"){
11678 } else if (Ext.isArray(o)) {
11679 for (i=0; i < o.length; i++) {
11681 b += createHtml(o[i]);
11685 b += '<' + (o.tag = o.tag || 'div');
11688 if(!confRe.test(attr)){
11689 if (typeof val == "object") {
11690 b += ' ' + attr + '="';
11692 b += key + ':' + val[key] + ';';
11696 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
11700 // Now either just close the tag or try to add children and close the tag.
11701 if (emptyTags.test(o.tag)) {
11705 if ((cn = o.children || o.cn)) {
11706 b += createHtml(cn);
11710 b += '</' + o.tag + '>';
11716 function ieTable(depth, s, h, e){
11717 tempTableEl.innerHTML = [s, h, e].join('');
11721 while(++i < depth){
11722 el = el.firstChild;
11724 // If the result is multiple siblings, then encapsulate them into one fragment.
11725 ns = el.nextSibling;
11727 var df = document.createDocumentFragment();
11729 ns = el.nextSibling;
11730 df.appendChild(el);
11740 * Nasty code for IE's broken table implementation
11742 function insertIntoTable(tag, where, el, html) {
11746 tempTableEl = tempTableEl || document.createElement('div');
11748 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
11749 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
11752 before = where == beforebegin ? el :
11753 where == afterend ? el.nextSibling :
11754 where == afterbegin ? el.firstChild : null;
11756 if (where == beforebegin || where == afterend) {
11757 el = el.parentNode;
11760 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
11761 node = ieTable(4, trs, html, tre);
11762 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
11763 (tag == 'tr' && (where == beforebegin || where == afterend))) {
11764 node = ieTable(3, tbs, html, tbe);
11766 node = ieTable(2, ts, html, te);
11768 el.insertBefore(node, before);
11774 * Fix for IE9 createContextualFragment missing method
11776 function createContextualFragment(html){
11777 var div = document.createElement("div"),
11778 fragment = document.createDocumentFragment(),
11780 length, childNodes;
11782 div.innerHTML = html;
11783 childNodes = div.childNodes;
11784 length = childNodes.length;
11786 for (; i < length; i++) {
11787 fragment.appendChild(childNodes[i].cloneNode(true));
11795 * Returns the markup for the passed Element(s) config.
11796 * @param {Object} o The DOM object spec (and children)
11799 markup : function(o){
11800 return createHtml(o);
11804 * Applies a style specification to an element.
11805 * @param {String/HTMLElement} el The element to apply styles to
11806 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
11807 * a function which returns such a specification.
11809 applyStyles : function(el, styles){
11812 if (typeof styles == "function") {
11813 styles = styles.call();
11815 if (typeof styles == "string") {
11816 styles = Ext.Element.parseStyles(styles);
11818 if (typeof styles == "object") {
11819 el.setStyle(styles);
11825 * Inserts an HTML fragment into the DOM.
11826 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
11828 * For example take the following HTML: `<div>Contents</div>`
11830 * Using different `where` values inserts element to the following places:
11832 * - beforeBegin: `<HERE><div>Contents</div>`
11833 * - afterBegin: `<div><HERE>Contents</div>`
11834 * - beforeEnd: `<div>Contents<HERE></div>`
11835 * - afterEnd: `<div>Contents</div><HERE>`
11837 * @param {HTMLElement/TextNode} el The context element
11838 * @param {String} html The HTML fragment
11839 * @return {HTMLElement} The new node
11841 insertHtml : function(where, el, html){
11850 where = where.toLowerCase();
11851 // add these here because they are used in both branches of the condition.
11852 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
11853 hash[afterend] = ['AfterEnd', 'nextSibling'];
11855 // if IE and context element is an HTMLElement
11856 if (el.insertAdjacentHTML) {
11857 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
11861 // add these two to the hash.
11862 hash[afterbegin] = ['AfterBegin', 'firstChild'];
11863 hash[beforeend] = ['BeforeEnd', 'lastChild'];
11864 if ((hashVal = hash[where])) {
11865 el.insertAdjacentHTML(hashVal[0], html);
11866 return el[hashVal[1]];
11868 // if (not IE and context element is an HTMLElement) or TextNode
11870 // we cannot insert anything inside a textnode so...
11871 if (Ext.isTextNode(el)) {
11872 where = where === 'afterbegin' ? 'beforebegin' : where;
11873 where = where === 'beforeend' ? 'afterend' : where;
11875 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
11876 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
11879 range[setStart](el);
11880 frag = range.createContextualFragment(html);
11882 frag = createContextualFragment(html);
11884 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
11885 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
11887 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
11888 if (el.firstChild) {
11890 range[setStart](el[rangeEl]);
11891 frag = range.createContextualFragment(html);
11893 frag = createContextualFragment(html);
11896 if(where == afterbegin){
11897 el.insertBefore(frag, el.firstChild);
11899 el.appendChild(frag);
11902 el.innerHTML = html;
11904 return el[rangeEl];
11908 sourceClass: 'Ext.DomHelper',
11909 sourceMethod: 'insertHtml',
11910 htmlToInsert: html,
11912 msg: 'Illegal insertion point reached: "' + where + '"'
11917 * Creates new DOM element(s) and inserts them before el.
11918 * @param {String/HTMLElement/Ext.Element} el The context element
11919 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11920 * @param {Boolean} returnElement (optional) true to return a Ext.Element
11921 * @return {HTMLElement/Ext.Element} The new node
11923 insertBefore : function(el, o, returnElement){
11924 return doInsert(el, o, returnElement, beforebegin);
11928 * Creates new DOM element(s) and inserts them after el.
11929 * @param {String/HTMLElement/Ext.Element} el The context element
11930 * @param {Object} o The DOM object spec (and children)
11931 * @param {Boolean} returnElement (optional) true to return a Ext.Element
11932 * @return {HTMLElement/Ext.Element} The new node
11934 insertAfter : function(el, o, returnElement){
11935 return doInsert(el, o, returnElement, afterend, 'nextSibling');
11939 * Creates new DOM element(s) and inserts them as the first child of el.
11940 * @param {String/HTMLElement/Ext.Element} el The context element
11941 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11942 * @param {Boolean} returnElement (optional) true to return a Ext.Element
11943 * @return {HTMLElement/Ext.Element} The new node
11945 insertFirst : function(el, o, returnElement){
11946 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
11950 * Creates new DOM element(s) and appends them to el.
11951 * @param {String/HTMLElement/Ext.Element} el The context element
11952 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11953 * @param {Boolean} returnElement (optional) true to return a Ext.Element
11954 * @return {HTMLElement/Ext.Element} The new node
11956 append : function(el, o, returnElement){
11957 return doInsert(el, o, returnElement, beforeend, '', true);
11961 * Creates new DOM element(s) and overwrites the contents of el with them.
11962 * @param {String/HTMLElement/Ext.Element} el The context element
11963 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11964 * @param {Boolean} returnElement (optional) true to return a Ext.Element
11965 * @return {HTMLElement/Ext.Element} The new node
11967 overwrite : function(el, o, returnElement){
11968 el = Ext.getDom(el);
11969 el.innerHTML = createHtml(o);
11970 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
11973 createHtml : createHtml,
11976 * Creates new DOM element(s) without inserting them to the document.
11977 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11978 * @return {HTMLElement} The new uninserted node
11981 createDom: createDom,
11983 /** True to force the use of DOM instead of html fragments @type Boolean */
11987 * Creates a new Ext.Template from the DOM object spec.
11988 * @param {Object} o The DOM object spec (and children)
11989 * @return {Ext.Template} The new template
11991 createTemplate : function(o){
11992 var html = Ext.DomHelper.createHtml(o);
11993 return Ext.create('Ext.Template', html);
12000 * This is code is also distributed under MIT license for use
12001 * with jQuery and prototype JavaScript libraries.
12004 * @class Ext.DomQuery
12005 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).
12007 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>
12010 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.
12012 <h4>Element Selectors:</h4>
12014 <li> <b>*</b> any element</li>
12015 <li> <b>E</b> an element with the tag E</li>
12016 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
12017 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
12018 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
12019 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
12021 <h4>Attribute Selectors:</h4>
12022 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
12024 <li> <b>E[foo]</b> has an attribute "foo"</li>
12025 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
12026 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
12027 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
12028 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
12029 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
12030 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
12032 <h4>Pseudo Classes:</h4>
12034 <li> <b>E:first-child</b> E is the first child of its parent</li>
12035 <li> <b>E:last-child</b> E is the last child of its parent</li>
12036 <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>
12037 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
12038 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
12039 <li> <b>E:only-child</b> E is the only child of its parent</li>
12040 <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>
12041 <li> <b>E:first</b> the first E in the resultset</li>
12042 <li> <b>E:last</b> the last E in the resultset</li>
12043 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
12044 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
12045 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
12046 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
12047 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
12048 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
12049 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
12050 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
12051 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
12052 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
12054 <h4>CSS Value Selectors:</h4>
12056 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
12057 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
12058 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
12059 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
12060 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
12061 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
12065 Ext.ns('Ext.core');
12067 Ext.core.DomQuery = Ext.DomQuery = function(){
12072 trimRe = /^\s+|\s+$/g,
12073 tplRe = /\{(\d+)\}/g,
12074 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
12075 tagTokenRe = /^(#)?([\w-\*]+)/,
12076 nthRe = /(\d*)n\+?(\d*)/,
12078 startIdRe = /^\s*\#/,
12079 // This is for IE MSXML which does not support expandos.
12080 // IE runs the same speed using setAttribute, however FF slows way down
12081 // and Safari completely fails so they need to continue to use expandos.
12082 isIE = window.ActiveXObject ? true : false,
12085 // this eval is stop the compressor from
12086 // renaming the variable to something shorter
12087 eval("var batch = 30803;");
12089 // Retrieve the child node from a particular
12090 // parent at the specified index.
12091 function child(parent, index){
12093 n = parent.firstChild;
12095 if(n.nodeType == 1){
12105 // retrieve the next element node
12107 while((n = n.nextSibling) && n.nodeType != 1);
12111 // retrieve the previous element node
12113 while((n = n.previousSibling) && n.nodeType != 1);
12117 // Mark each child node with a nodeIndex skipping and
12118 // removing empty text nodes.
12119 function children(parent){
12120 var n = parent.firstChild,
12124 nextNode = n.nextSibling;
12125 // clean worthless empty nodes.
12126 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
12127 parent.removeChild(n);
12129 // add an expando nodeIndex
12130 n.nodeIndex = ++nodeIndex;
12138 // nodeSet - array of nodes
12140 function byClassName(nodeSet, cls){
12144 var result = [], ri = -1;
12145 for(var i = 0, ci; ci = nodeSet[i]; i++){
12146 if((' '+ci.className+' ').indexOf(cls) != -1){
12153 function attrValue(n, attr){
12154 // if its an array, use the first node.
12155 if(!n.tagName && typeof n.length != "undefined"){
12165 if(attr == "class" || attr == "className"){
12166 return n.className;
12168 return n.getAttribute(attr) || n[attr];
12174 // mode - false, /, >, +, ~
12175 // tagName - defaults to "*"
12176 function getNodes(ns, mode, tagName){
12177 var result = [], ri = -1, cs;
12181 tagName = tagName || "*";
12182 // convert to array
12183 if(typeof ns.getElementsByTagName != "undefined"){
12187 // no mode specified, grab all elements by tagName
12190 for(var i = 0, ni; ni = ns[i]; i++){
12191 cs = ni.getElementsByTagName(tagName);
12192 for(var j = 0, ci; ci = cs[j]; j++){
12196 // Direct Child mode (/ or >)
12197 // E > F or E/F all direct children elements of E that have the tag
12198 } else if(mode == "/" || mode == ">"){
12199 var utag = tagName.toUpperCase();
12200 for(var i = 0, ni, cn; ni = ns[i]; i++){
12201 cn = ni.childNodes;
12202 for(var j = 0, cj; cj = cn[j]; j++){
12203 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
12208 // Immediately Preceding mode (+)
12209 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
12210 }else if(mode == "+"){
12211 var utag = tagName.toUpperCase();
12212 for(var i = 0, n; n = ns[i]; i++){
12213 while((n = n.nextSibling) && n.nodeType != 1);
12214 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
12218 // Sibling mode (~)
12219 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
12220 }else if(mode == "~"){
12221 var utag = tagName.toUpperCase();
12222 for(var i = 0, n; n = ns[i]; i++){
12223 while((n = n.nextSibling)){
12224 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
12233 function concat(a, b){
12235 return a.concat(b);
12237 for(var i = 0, l = b.length; i < l; i++){
12238 a[a.length] = b[i];
12243 function byTag(cs, tagName){
12244 if(cs.tagName || cs == document){
12250 var result = [], ri = -1;
12251 tagName = tagName.toLowerCase();
12252 for(var i = 0, ci; ci = cs[i]; i++){
12253 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
12260 function byId(cs, id){
12261 if(cs.tagName || cs == document){
12267 var result = [], ri = -1;
12268 for(var i = 0, ci; ci = cs[i]; i++){
12269 if(ci && ci.id == id){
12277 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
12278 // custom can be "{"
12279 function byAttribute(cs, attr, value, op, custom){
12282 useGetStyle = custom == "{",
12283 fn = Ext.DomQuery.operators[op],
12288 for(var i = 0, ci; ci = cs[i]; i++){
12289 // skip non-element nodes.
12290 if(ci.nodeType != 1){
12293 // only need to do this for the first node
12295 xml = Ext.DomQuery.isXml(ci);
12299 // we only need to change the property names if we're dealing with html nodes, not XML
12302 a = Ext.DomQuery.getStyle(ci, attr);
12303 } else if (attr == "class" || attr == "className"){
12305 } else if (attr == "for"){
12307 } else if (attr == "href"){
12308 // getAttribute href bug
12309 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
12310 a = ci.getAttribute("href", 2);
12312 a = ci.getAttribute(attr);
12315 a = ci.getAttribute(attr);
12317 if((fn && fn(a, value)) || (!fn && a)){
12324 function byPseudo(cs, name, value){
12325 return Ext.DomQuery.pseudos[name](cs, value);
12328 function nodupIEXml(cs){
12331 cs[0].setAttribute("_nodup", d);
12333 for(var i = 1, len = cs.length; i < len; i++){
12335 if(!c.getAttribute("_nodup") != d){
12336 c.setAttribute("_nodup", d);
12340 for(var i = 0, len = cs.length; i < len; i++){
12341 cs[i].removeAttribute("_nodup");
12346 function nodup(cs){
12350 var len = cs.length, c, i, r = cs, cj, ri = -1;
12351 if(!len || typeof cs.nodeType != "undefined" || len == 1){
12354 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
12355 return nodupIEXml(cs);
12359 for(i = 1; c = cs[i]; i++){
12364 for(var j = 0; j < i; j++){
12367 for(j = i+1; cj = cs[j]; j++){
12368 if(cj._nodup != d){
12379 function quickDiffIEXml(c1, c2){
12382 for(var i = 0, len = c1.length; i < len; i++){
12383 c1[i].setAttribute("_qdiff", d);
12385 for(var i = 0, len = c2.length; i < len; i++){
12386 if(c2[i].getAttribute("_qdiff") != d){
12387 r[r.length] = c2[i];
12390 for(var i = 0, len = c1.length; i < len; i++){
12391 c1[i].removeAttribute("_qdiff");
12396 function quickDiff(c1, c2){
12397 var len1 = c1.length,
12403 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
12404 return quickDiffIEXml(c1, c2);
12406 for(var i = 0; i < len1; i++){
12409 for(var i = 0, len = c2.length; i < len; i++){
12410 if(c2[i]._qdiff != d){
12411 r[r.length] = c2[i];
12417 function quickId(ns, mode, root, id){
12419 var d = root.ownerDocument || root;
12420 return d.getElementById(id);
12422 ns = getNodes(ns, mode, "*");
12423 return byId(ns, id);
12427 getStyle : function(el, name){
12428 return Ext.fly(el).getStyle(name);
12431 * Compiles a selector/xpath query into a reusable function. The returned function
12432 * takes one parameter "root" (optional), which is the context node from where the query should start.
12433 * @param {String} selector The selector/xpath query
12434 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
12435 * @return {Function}
12437 compile : function(path, type){
12438 type = type || "select";
12440 // setup fn preamble
12441 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
12444 matchers = Ext.DomQuery.matchers,
12445 matchersLn = matchers.length,
12447 // accept leading mode switch
12448 lmode = path.match(modeRe);
12450 if(lmode && lmode[1]){
12451 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
12452 path = path.replace(lmode[1], "");
12455 // strip leading slashes
12456 while(path.substr(0, 1)=="/"){
12457 path = path.substr(1);
12460 while(path && lastPath != path){
12462 var tokenMatch = path.match(tagTokenRe);
12463 if(type == "select"){
12466 if(tokenMatch[1] == "#"){
12467 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
12469 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
12471 path = path.replace(tokenMatch[0], "");
12472 }else if(path.substr(0, 1) != '@'){
12473 fn[fn.length] = 'n = getNodes(n, mode, "*");';
12475 // type of "simple"
12478 if(tokenMatch[1] == "#"){
12479 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
12481 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
12483 path = path.replace(tokenMatch[0], "");
12486 while(!(modeMatch = path.match(modeRe))){
12487 var matched = false;
12488 for(var j = 0; j < matchersLn; j++){
12489 var t = matchers[j];
12490 var m = path.match(t.re);
12492 fn[fn.length] = t.select.replace(tplRe, function(x, i){
12495 path = path.replace(m[0], "");
12500 // prevent infinite loop on bad selector
12503 sourceClass: 'Ext.DomQuery',
12504 sourceMethod: 'compile',
12505 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
12510 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
12511 path = path.replace(modeMatch[1], "");
12515 fn[fn.length] = "return nodup(n);\n}";
12517 // eval fn and return it
12523 * Selects an array of DOM nodes using JavaScript-only implementation.
12525 * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
12527 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
12528 * @param {HTMLElement/String} root (optional) The start of the query (defaults to document).
12529 * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
12530 * no matches, and empty Array is returned.
12532 jsSelect: function(path, root, type){
12533 // set root to doc if not specified.
12534 root = root || document;
12536 if(typeof root == "string"){
12537 root = document.getElementById(root);
12539 var paths = path.split(","),
12542 // loop over each selector
12543 for(var i = 0, len = paths.length; i < len; i++){
12544 var subPath = paths[i].replace(trimRe, "");
12545 // compile and place in cache
12546 if(!cache[subPath]){
12547 cache[subPath] = Ext.DomQuery.compile(subPath);
12548 if(!cache[subPath]){
12550 sourceClass: 'Ext.DomQuery',
12551 sourceMethod: 'jsSelect',
12552 msg: subPath + ' is not a valid selector'
12556 var result = cache[subPath](root);
12557 if(result && result != document){
12558 results = results.concat(result);
12562 // if there were multiple selectors, make sure dups
12564 if(paths.length > 1){
12565 return nodup(results);
12570 isXml: function(el) {
12571 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
12572 return docEl ? docEl.nodeName !== "HTML" : false;
12576 * Selects an array of DOM nodes by CSS/XPath selector.
12578 * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
12579 * {@link Ext.DomQuery#jsSelect} to do the work.
12581 * Aliased as {@link Ext#query}.
12583 * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
12585 * @param {String} path The selector/xpath query
12586 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12587 * @return {HTMLElement[]} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
12588 * Empty array when no matches.
12591 select : document.querySelectorAll ? function(path, root, type) {
12592 root = root || document;
12594 * Safari 3.x can't handle uppercase or unicode characters when in quirks mode.
12596 if (!Ext.DomQuery.isXml(root) && !(Ext.isSafari3 && !Ext.isStrict)) {
12599 * This checking here is to "fix" the behaviour of querySelectorAll
12600 * for non root document queries. The way qsa works is intentional,
12601 * however it's definitely not the expected way it should work.
12602 * More info: http://ejohn.org/blog/thoughts-on-queryselectorall/
12604 * We only modify the path for single selectors (ie, no multiples),
12605 * without a full parser it makes it difficult to do this correctly.
12607 var isDocumentRoot = root.nodeType === 9,
12611 if (!isDocumentRoot && path.indexOf(',') === -1 && !startIdRe.test(path)) {
12612 _path = '#' + Ext.id(root) + ' ' + path;
12613 _root = root.parentNode;
12615 return Ext.Array.toArray(_root.querySelectorAll(_path));
12620 return Ext.DomQuery.jsSelect.call(this, path, root, type);
12621 } : function(path, root, type) {
12622 return Ext.DomQuery.jsSelect.call(this, path, root, type);
12626 * Selects a single element.
12627 * @param {String} selector The selector/xpath query
12628 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12629 * @return {HTMLElement} The DOM element which matched the selector.
12631 selectNode : function(path, root){
12632 return Ext.DomQuery.select(path, root)[0];
12636 * Selects the value of a node, optionally replacing null with the defaultValue.
12637 * @param {String} selector The selector/xpath query
12638 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12639 * @param {String} defaultValue (optional) When specified, this is return as empty value.
12642 selectValue : function(path, root, defaultValue){
12643 path = path.replace(trimRe, "");
12644 if(!valueCache[path]){
12645 valueCache[path] = Ext.DomQuery.compile(path, "select");
12647 var n = valueCache[path](root), v;
12648 n = n[0] ? n[0] : n;
12650 // overcome a limitation of maximum textnode size
12651 // Rumored to potentially crash IE6 but has not been confirmed.
12652 // http://reference.sitepoint.com/javascript/Node/normalize
12653 // https://developer.mozilla.org/En/DOM/Node.normalize
12654 if (typeof n.normalize == 'function') n.normalize();
12656 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
12657 return ((v === null||v === undefined||v==='') ? defaultValue : v);
12661 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
12662 * @param {String} selector The selector/xpath query
12663 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
12664 * @param {Number} defaultValue (optional) When specified, this is return as empty value.
12667 selectNumber : function(path, root, defaultValue){
12668 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
12669 return parseFloat(v);
12673 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
12674 * @param {String/HTMLElement/HTMLElement[]} el An element id, element or array of elements
12675 * @param {String} selector The simple selector to test
12676 * @return {Boolean}
12678 is : function(el, ss){
12679 if(typeof el == "string"){
12680 el = document.getElementById(el);
12682 var isArray = Ext.isArray(el),
12683 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
12684 return isArray ? (result.length == el.length) : (result.length > 0);
12688 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
12689 * @param {HTMLElement[]} el An array of elements to filter
12690 * @param {String} selector The simple selector to test
12691 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
12692 * the selector instead of the ones that match
12693 * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
12694 * no matches, and empty Array is returned.
12696 filter : function(els, ss, nonMatches){
12697 ss = ss.replace(trimRe, "");
12698 if(!simpleCache[ss]){
12699 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
12701 var result = simpleCache[ss](els);
12702 return nonMatches ? quickDiff(result, els) : result;
12706 * Collection of matching regular expressions and code snippets.
12707 * Each capture group within () will be replace the {} in the select
12708 * statement as specified by their index.
12712 select: 'n = byClassName(n, " {1} ");'
12714 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
12715 select: 'n = byPseudo(n, "{1}", "{2}");'
12717 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
12718 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
12721 select: 'n = byId(n, "{1}");'
12724 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
12729 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
12730 * 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, > <.
12733 "=" : function(a, v){
12736 "!=" : function(a, v){
12739 "^=" : function(a, v){
12740 return a && a.substr(0, v.length) == v;
12742 "$=" : function(a, v){
12743 return a && a.substr(a.length-v.length) == v;
12745 "*=" : function(a, v){
12746 return a && a.indexOf(v) !== -1;
12748 "%=" : function(a, v){
12749 return (a % v) == 0;
12751 "|=" : function(a, v){
12752 return a && (a == v || a.substr(0, v.length+1) == v+'-');
12754 "~=" : function(a, v){
12755 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
12760 Object hash of "pseudo class" filter functions which are used when filtering selections.
12761 Each function is passed two parameters:
12764 An Array of DOM elements to filter.
12767 The argument (if any) supplied in the selector.
12769 A filter function returns an Array of DOM elements which conform to the pseudo class.
12770 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
12771 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
12773 For example, to filter `a` elements to only return links to __external__ resources:
12775 Ext.DomQuery.pseudos.external = function(c, v){
12776 var r = [], ri = -1;
12777 for(var i = 0, ci; ci = c[i]; i++){
12778 // Include in result set only if it's a link to an external resource
12779 if(ci.hostname != location.hostname){
12786 Then external links could be gathered with the following statement:
12788 var externalLinks = Ext.select("a:external");
12793 "first-child" : function(c){
12794 var r = [], ri = -1, n;
12795 for(var i = 0, ci; ci = n = c[i]; i++){
12796 while((n = n.previousSibling) && n.nodeType != 1);
12804 "last-child" : function(c){
12805 var r = [], ri = -1, n;
12806 for(var i = 0, ci; ci = n = c[i]; i++){
12807 while((n = n.nextSibling) && n.nodeType != 1);
12815 "nth-child" : function(c, a) {
12816 var r = [], ri = -1,
12817 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
12818 f = (m[1] || 1) - 0, l = m[2] - 0;
12819 for(var i = 0, n; n = c[i]; i++){
12820 var pn = n.parentNode;
12821 if (batch != pn._batch) {
12823 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
12824 if(cn.nodeType == 1){
12825 cn.nodeIndex = ++j;
12831 if (l == 0 || n.nodeIndex == l){
12834 } else if ((n.nodeIndex + l) % f == 0){
12842 "only-child" : function(c){
12843 var r = [], ri = -1;;
12844 for(var i = 0, ci; ci = c[i]; i++){
12845 if(!prev(ci) && !next(ci)){
12852 "empty" : function(c){
12853 var r = [], ri = -1;
12854 for(var i = 0, ci; ci = c[i]; i++){
12855 var cns = ci.childNodes, j = 0, cn, empty = true;
12856 while(cn = cns[j]){
12858 if(cn.nodeType == 1 || cn.nodeType == 3){
12870 "contains" : function(c, v){
12871 var r = [], ri = -1;
12872 for(var i = 0, ci; ci = c[i]; i++){
12873 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
12880 "nodeValue" : function(c, v){
12881 var r = [], ri = -1;
12882 for(var i = 0, ci; ci = c[i]; i++){
12883 if(ci.firstChild && ci.firstChild.nodeValue == v){
12890 "checked" : function(c){
12891 var r = [], ri = -1;
12892 for(var i = 0, ci; ci = c[i]; i++){
12893 if(ci.checked == true){
12900 "not" : function(c, ss){
12901 return Ext.DomQuery.filter(c, ss, true);
12904 "any" : function(c, selectors){
12905 var ss = selectors.split('|'),
12906 r = [], ri = -1, s;
12907 for(var i = 0, ci; ci = c[i]; i++){
12908 for(var j = 0; s = ss[j]; j++){
12909 if(Ext.DomQuery.is(ci, s)){
12918 "odd" : function(c){
12919 return this["nth-child"](c, "odd");
12922 "even" : function(c){
12923 return this["nth-child"](c, "even");
12926 "nth" : function(c, a){
12927 return c[a-1] || [];
12930 "first" : function(c){
12934 "last" : function(c){
12935 return c[c.length-1] || [];
12938 "has" : function(c, ss){
12939 var s = Ext.DomQuery.select,
12941 for(var i = 0, ci; ci = c[i]; i++){
12942 if(s(ss, ci).length > 0){
12949 "next" : function(c, ss){
12950 var is = Ext.DomQuery.is,
12952 for(var i = 0, ci; ci = c[i]; i++){
12954 if(n && is(n, ss)){
12961 "prev" : function(c, ss){
12962 var is = Ext.DomQuery.is,
12964 for(var i = 0, ci; ci = c[i]; i++){
12966 if(n && is(n, ss)){
12977 * Shorthand of {@link Ext.DomQuery#select}
12980 * @alias Ext.DomQuery#select
12982 Ext.query = Ext.DomQuery.select;
12985 * @class Ext.Element
12986 * @alternateClassName Ext.core.Element
12988 * Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.
12990 * All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all
12993 * Note that the events documented in this class are not Ext events, they encapsulate browser events. Some older browsers
12994 * may not support the full range of events. Which events are supported is beyond the control of Ext JS.
12999 * var el = Ext.get("my-div");
13001 * // by DOM element reference
13002 * var el = Ext.get(myDivElement);
13006 * When an element is manipulated, by default there is no animation.
13008 * var el = Ext.get("my-div");
13011 * el.setWidth(100);
13013 * Many of the functions for manipulating an element have an optional "animate" parameter. This parameter can be
13014 * specified as boolean (true) for default animation effects.
13016 * // default animation
13017 * el.setWidth(100, true);
13019 * To configure the effects, an object literal with animation options to use as the Element animation configuration
13020 * object can also be specified. Note that the supported Element animation configuration options are a subset of the
13021 * {@link Ext.fx.Anim} animation options specific to Fx effects. The supported Element animation configuration options
13024 * Option Default Description
13025 * --------- -------- ---------------------------------------------
13026 * {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
13027 * {@link Ext.fx.Anim#easing easing} easeOut The easing method
13028 * {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
13029 * {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
13033 * // Element animation options object
13035 * {@link Ext.fx.Anim#duration duration}: 1,
13036 * {@link Ext.fx.Anim#easing easing}: 'elasticIn',
13037 * {@link Ext.fx.Anim#callback callback}: this.foo,
13038 * {@link Ext.fx.Anim#scope scope}: this
13040 * // animation with some options set
13041 * el.setWidth(100, opt);
13043 * The Element animation object being used for the animation will be set on the options object as "anim", which allows
13044 * you to stop or manipulate the animation. Here is an example:
13046 * // using the "anim" property to get the Anim object
13047 * if(opt.anim.isAnimated()){
13051 * # Composite (Collections of) Elements
13053 * For working with collections of Elements, see {@link Ext.CompositeElement}
13056 * Creates new Element directly.
13057 * @param {String/HTMLElement} element
13058 * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this
13059 * element in the cache and if there is it returns the same instance. This will skip that check (useful for extending
13064 var DOC = document,
13067 Ext.Element = Ext.core.Element = function(element, forceNew) {
13068 var dom = typeof element == "string" ? DOC.getElementById(element) : element,
13077 if (!forceNew && id && EC[id]) {
13078 // element object already exists
13083 * @property {HTMLElement} dom
13089 * @property {String} id
13090 * The DOM element ID
13092 this.id = id || Ext.id(dom);
13095 var DH = Ext.DomHelper,
13101 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
13102 * @param {Object} o The object with the attributes
13103 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
13104 * @return {Ext.Element} this
13106 set: function(o, useSet) {
13110 useSet = (useSet !== false) && !!el.setAttribute;
13113 if (o.hasOwnProperty(attr)) {
13115 if (attr == 'style') {
13116 DH.applyStyles(el, val);
13117 } else if (attr == 'cls') {
13118 el.className = val;
13119 } else if (useSet) {
13120 el.setAttribute(attr, val);
13132 * Fires when a mouse click is detected within the element.
13133 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13134 * @param {HTMLElement} t The target of the event.
13137 * @event contextmenu
13138 * Fires when a right click is detected within the element.
13139 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13140 * @param {HTMLElement} t The target of the event.
13144 * Fires when a mouse double click is detected within the element.
13145 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13146 * @param {HTMLElement} t The target of the event.
13150 * Fires when a mousedown is detected within the element.
13151 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13152 * @param {HTMLElement} t The target of the event.
13156 * Fires when a mouseup is detected within the element.
13157 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13158 * @param {HTMLElement} t The target of the event.
13162 * Fires when a mouseover is detected within the element.
13163 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13164 * @param {HTMLElement} t The target of the event.
13168 * Fires when a mousemove is detected with the element.
13169 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13170 * @param {HTMLElement} t The target of the event.
13174 * Fires when a mouseout is detected with the element.
13175 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13176 * @param {HTMLElement} t The target of the event.
13179 * @event mouseenter
13180 * Fires when the mouse enters the element.
13181 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13182 * @param {HTMLElement} t The target of the event.
13185 * @event mouseleave
13186 * Fires when the mouse leaves the element.
13187 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13188 * @param {HTMLElement} t The target of the event.
13194 * Fires when a keypress is detected within the element.
13195 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13196 * @param {HTMLElement} t The target of the event.
13200 * Fires when a keydown is detected within the element.
13201 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13202 * @param {HTMLElement} t The target of the event.
13206 * Fires when a keyup is detected within the element.
13207 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13208 * @param {HTMLElement} t The target of the event.
13212 // HTML frame/object events
13215 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames,
13216 * objects and images.
13217 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13218 * @param {HTMLElement} t The target of the event.
13222 * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target
13223 * element or any of its content has been removed.
13224 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13225 * @param {HTMLElement} t The target of the event.
13229 * Fires when an object/image is stopped from loading before completely loaded.
13230 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13231 * @param {HTMLElement} t The target of the event.
13235 * Fires when an object/image/frame cannot be loaded properly.
13236 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13237 * @param {HTMLElement} t The target of the event.
13241 * Fires when a document view is resized.
13242 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13243 * @param {HTMLElement} t The target of the event.
13247 * Fires when a document view is scrolled.
13248 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13249 * @param {HTMLElement} t The target of the event.
13255 * Fires when a user selects some text in a text field, including input and textarea.
13256 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13257 * @param {HTMLElement} t The target of the event.
13261 * Fires when a control loses the input focus and its value has been modified since gaining focus.
13262 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13263 * @param {HTMLElement} t The target of the event.
13267 * Fires when a form is submitted.
13268 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13269 * @param {HTMLElement} t The target of the event.
13273 * Fires when a form is reset.
13274 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13275 * @param {HTMLElement} t The target of the event.
13279 * Fires when an element receives focus either via the pointing device or by tab navigation.
13280 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13281 * @param {HTMLElement} t The target of the event.
13285 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
13286 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13287 * @param {HTMLElement} t The target of the event.
13290 // User Interface events
13292 * @event DOMFocusIn
13293 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
13294 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13295 * @param {HTMLElement} t The target of the event.
13298 * @event DOMFocusOut
13299 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
13300 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13301 * @param {HTMLElement} t The target of the event.
13304 * @event DOMActivate
13305 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
13306 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13307 * @param {HTMLElement} t The target of the event.
13310 // DOM Mutation events
13312 * @event DOMSubtreeModified
13313 * Where supported. Fires when the subtree is modified.
13314 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13315 * @param {HTMLElement} t The target of the event.
13318 * @event DOMNodeInserted
13319 * Where supported. Fires when a node has been added as a child of another node.
13320 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13321 * @param {HTMLElement} t The target of the event.
13324 * @event DOMNodeRemoved
13325 * Where supported. Fires when a descendant node of the element is removed.
13326 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13327 * @param {HTMLElement} t The target of the event.
13330 * @event DOMNodeRemovedFromDocument
13331 * Where supported. Fires when a node is being removed from a document.
13332 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13333 * @param {HTMLElement} t The target of the event.
13336 * @event DOMNodeInsertedIntoDocument
13337 * Where supported. Fires when a node is being inserted into a document.
13338 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13339 * @param {HTMLElement} t The target of the event.
13342 * @event DOMAttrModified
13343 * Where supported. Fires when an attribute has been modified.
13344 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13345 * @param {HTMLElement} t The target of the event.
13348 * @event DOMCharacterDataModified
13349 * Where supported. Fires when the character data has been modified.
13350 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
13351 * @param {HTMLElement} t The target of the event.
13355 * @property {String} defaultUnit
13356 * The default unit to append to CSS values where a unit isn't provided.
13361 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
13362 * @param {String} selector The simple selector to test
13363 * @return {Boolean} True if this element matches the selector, else false
13365 is: function(simpleSelector) {
13366 return Ext.DomQuery.is(this.dom, simpleSelector);
13370 * Tries to focus the element. Any exceptions are caught and ignored.
13371 * @param {Number} defer (optional) Milliseconds to defer the focus
13372 * @return {Ext.Element} this
13374 focus: function(defer,
13378 dom = dom || me.dom;
13380 if (Number(defer)) {
13381 Ext.defer(me.focus, defer, null, [null, dom]);
13390 * Tries to blur the element. Any exceptions are caught and ignored.
13391 * @return {Ext.Element} this
13401 * Returns the value of the "value" attribute
13402 * @param {Boolean} asNumber true to parse the value as a number
13403 * @return {String/Number}
13405 getValue: function(asNumber) {
13406 var val = this.dom.value;
13407 return asNumber ? parseInt(val, 10) : val;
13411 * Appends an event handler to this element.
13413 * @param {String} eventName The name of event to handle.
13415 * @param {Function} fn The handler function the event invokes. This function is passed the following parameters:
13417 * - **evt** : EventObject
13419 * The {@link Ext.EventObject EventObject} describing the event.
13421 * - **el** : HtmlElement
13423 * The DOM element which was the target of the event. Note that this may be filtered by using the delegate option.
13427 * The options object from the addListener call.
13429 * @param {Object} scope (optional) The scope (**this** reference) in which the handler function is executed. **If
13430 * omitted, defaults to this Element.**
13432 * @param {Object} options (optional) An object containing handler configuration properties. This may contain any of
13433 * the following properties:
13435 * - **scope** Object :
13437 * The scope (**this** reference) in which the handler function is executed. **If omitted, defaults to this
13440 * - **delegate** String:
13442 * A simple selector to filter the target or look for a descendant of the target. See below for additional details.
13444 * - **stopEvent** Boolean:
13446 * True to stop the event. That is stop propagation, and prevent the default action.
13448 * - **preventDefault** Boolean:
13450 * True to prevent the default action
13452 * - **stopPropagation** Boolean:
13454 * True to prevent event propagation
13456 * - **normalized** Boolean:
13458 * False to pass a browser event to the handler function instead of an Ext.EventObject
13460 * - **target** Ext.Element:
13462 * Only call the handler if the event was fired on the target Element, _not_ if the event was bubbled up from a
13465 * - **delay** Number:
13467 * The number of milliseconds to delay the invocation of the handler after the event fires.
13469 * - **single** Boolean:
13471 * True to add a handler to handle just the next firing of the event, and then remove itself.
13473 * - **buffer** Number:
13475 * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
13476 * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
13477 * handler is scheduled in its place.
13479 * **Combining Options**
13481 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose addListener. The
13482 * two are equivalent. Using the options argument, it is possible to combine different types of listeners:
13484 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the options
13485 * object. The options object is available as the third parameter in the handler function.
13489 * el.on('click', this.onClick, this, {
13492 * stopEvent : true,
13496 * **Attaching multiple handlers in 1 call**
13498 * The method also allows for a single argument to be passed which is a config object containing properties which
13499 * specify multiple handlers.
13505 * fn: this.onClick,
13510 * fn: this.onMouseOver,
13514 * fn: this.onMouseOut,
13519 * Or a shorthand syntax:
13524 * 'click' : this.onClick,
13525 * 'mouseover' : this.onMouseOver,
13526 * 'mouseout' : this.onMouseOut,
13532 * This is a configuration option that you can pass along when registering a handler for an event to assist with
13533 * event delegation. Event delegation is a technique that is used to reduce memory consumption and prevent exposure
13534 * to memory-leaks. By registering an event for a container element as opposed to each element within a container.
13535 * By setting this configuration option to a simple selector, the target element will be filtered to look for a
13536 * descendant of the target. For example:
13538 * // using this markup:
13540 * <p id='p1'>paragraph one</p>
13541 * <p id='p2' class='clickable'>paragraph two</p>
13542 * <p id='p3'>paragraph three</p>
13545 * // utilize event delegation to registering just one handler on the container element:
13546 * el = Ext.get('elId');
13551 * console.info(t.id); // 'p2'
13555 * // filter the target element to be a descendant with the class 'clickable'
13556 * delegate: '.clickable'
13560 * @return {Ext.Element} this
13562 addListener: function(eventName, fn, scope, options) {
13563 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
13568 * Removes an event handler from this element.
13570 * **Note**: if a *scope* was explicitly specified when {@link #addListener adding} the listener,
13571 * the same scope must be specified here.
13575 * el.removeListener('click', this.handlerFn);
13577 * el.un('click', this.handlerFn);
13579 * @param {String} eventName The name of the event from which to remove the handler.
13580 * @param {Function} fn The handler function to remove. **This must be a reference to the function passed into the
13581 * {@link #addListener} call.**
13582 * @param {Object} scope If a scope (**this** reference) was specified when the listener was added, then this must
13583 * refer to the same object.
13584 * @return {Ext.Element} this
13586 removeListener: function(eventName, fn, scope) {
13587 Ext.EventManager.un(this.dom, eventName, fn, scope || this);
13592 * Removes all previous added listeners from this element
13593 * @return {Ext.Element} this
13595 removeAllListeners: function() {
13596 Ext.EventManager.removeAll(this.dom);
13601 * Recursively removes all previous added listeners from this element and its children
13602 * @return {Ext.Element} this
13604 purgeAllListeners: function() {
13605 Ext.EventManager.purgeElement(this);
13610 * Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
13611 * @param size {Mixed} The size to set
13612 * @param units {String} The units to append to a numeric size value
13615 addUnits: function(size, units) {
13617 // Most common case first: Size is set to a number
13618 if (Ext.isNumber(size)) {
13619 return size + (units || this.defaultUnit || 'px');
13622 // Size set to a value which means "auto"
13623 if (size === "" || size == "auto" || size == null) {
13627 // Otherwise, warn if it's not a valid CSS measurement
13628 if (!unitPattern.test(size)) {
13629 if (Ext.isDefined(Ext.global.console)) {
13630 Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
13638 * Tests various css rules/browsers to determine if this element uses a border box
13639 * @return {Boolean}
13641 isBorderBox: function() {
13642 return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
13646 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode
13649 remove: function() {
13655 Ext.removeNode(dom);
13660 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
13661 * @param {Function} overFn The function to call when the mouse enters the Element.
13662 * @param {Function} outFn The function to call when the mouse leaves the Element.
13663 * @param {Object} scope (optional) The scope (`this` reference) in which the functions are executed. Defaults
13664 * to the Element's DOM element.
13665 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the
13666 * options parameter}.
13667 * @return {Ext.Element} this
13669 hover: function(overFn, outFn, scope, options) {
13671 me.on('mouseenter', overFn, scope || me.dom, options);
13672 me.on('mouseleave', outFn, scope || me.dom, options);
13677 * Returns true if this element is an ancestor of the passed element
13678 * @param {HTMLElement/String} el The element to check
13679 * @return {Boolean} True if this element is an ancestor of el, else false
13681 contains: function(el) {
13682 return ! el ? false: Ext.Element.isAncestor(this.dom, el.dom ? el.dom: el);
13686 * Returns the value of a namespaced attribute from the element's underlying DOM node.
13687 * @param {String} namespace The namespace in which to look for the attribute
13688 * @param {String} name The attribute name
13689 * @return {String} The attribute value
13691 getAttributeNS: function(ns, name) {
13692 return this.getAttribute(name, ns);
13696 * Returns the value of an attribute from the element's underlying DOM node.
13697 * @param {String} name The attribute name
13698 * @param {String} namespace (optional) The namespace in which to look for the attribute
13699 * @return {String} The attribute value
13702 getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
13703 function(name, ns) {
13707 type = typeof d[ns + ":" + name];
13708 if (type != 'undefined' && type != 'unknown') {
13709 return d[ns + ":" + name] || null;
13713 if (name === "for") {
13716 return d[name] || null;
13717 }: function(name, ns) {
13720 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
13722 return d.getAttribute(name) || d[name] || null;
13726 * Update the innerHTML of this element
13727 * @param {String} html The new HTML
13728 * @return {Ext.Element} this
13730 update: function(html) {
13732 this.dom.innerHTML = html;
13738 var ep = El.prototype;
13740 El.addMethods = function(o) {
13746 * @alias Ext.Element#addListener
13747 * Shorthand for {@link #addListener}.
13749 ep.on = ep.addListener;
13753 * @alias Ext.Element#removeListener
13754 * Shorthand for {@link #removeListener}.
13756 ep.un = ep.removeListener;
13760 * @alias Ext.Element#removeAllListeners
13761 * Alias for {@link #removeAllListeners}.
13763 ep.clearListeners = ep.removeAllListeners;
13767 * @member Ext.Element
13768 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode
13769 * Ext.removeNode}. Alias to {@link #remove}.
13771 ep.destroy = ep.remove;
13774 * @property {Boolean} autoBoxAdjust
13775 * true to automatically adjust width and height settings for box-model issues (default to true)
13777 ep.autoBoxAdjust = true;
13780 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
13784 * Retrieves Ext.Element objects. {@link Ext#get} is an alias for {@link Ext.Element#get}.
13786 * **This method does not retrieve {@link Ext.Component Component}s.** This method retrieves Ext.Element
13787 * objects which encapsulate DOM elements. To retrieve a Component by its ID, use {@link Ext.ComponentManager#get}.
13789 * Uses simple caching to consistently return the same object. Automatically fixes if an object was recreated with
13790 * the same id via AJAX or DOM.
13792 * @param {String/HTMLElement/Ext.Element} el The id of the node, a DOM Node or an existing Element.
13793 * @return {Ext.Element} The Element object (or null if no matching element was found)
13796 El.get = function(el) {
13803 if (typeof el == "string") {
13805 if (! (elm = DOC.getElementById(el))) {
13808 if (EC[el] && EC[el].el) {
13812 ex = El.addToCache(new El(elm));
13815 } else if (el.tagName) {
13817 if (! (id = el.id)) {
13820 if (EC[id] && EC[id].el) {
13824 ex = El.addToCache(new El(el));
13827 } else if (el instanceof El) {
13829 // refresh dom element in case no longer valid,
13830 // catch case where it hasn't been appended
13831 // If an el instance is passed, don't pass to getElementById without some kind of id
13832 if (Ext.isIE && (el.id == undefined || el.id == '')) {
13835 el.dom = DOC.getElementById(el.id) || el.dom;
13839 } else if (el.isComposite) {
13841 } else if (Ext.isArray(el)) {
13842 return El.select(el);
13843 } else if (el == DOC) {
13844 // create a bogus element object representing the document object
13846 var f = function() {};
13847 f.prototype = El.prototype;
13857 * Retrieves Ext.Element objects like {@link Ext#get} but is optimized for sub-elements.
13858 * This is helpful for performance, because in IE (prior to IE 9), `getElementById` uses
13859 * an non-optimized search. In those browsers, starting the search for an element with a
13860 * matching ID at a parent of that element will greatly speed up the process.
13862 * Unlike {@link Ext#get}, this method only accepts ID's. If the ID is not a child of
13863 * this element, it will still be found if it exists in the document, but will be slower
13864 * than calling {@link Ext#get} directly.
13866 * @param {String} id The id of the element to get.
13867 * @return {Ext.Element} The Element object (or null if no matching element was found)
13868 * @member Ext.Element
13872 ep.getById = (!Ext.isIE6 && !Ext.isIE7 && !Ext.isIE8) ? El.get :
13874 var dom = this.dom,
13880 // calling El.get here is a real hit (2x slower) because it has to
13881 // redetermine that we are giving it a dom el.
13883 if (cached && cached.el) {
13887 ret = El.addToCache(new El(el));
13896 El.addToCache = function(el, id) {
13908 // private method for getting and setting element data
13909 El.data = function(el, key, value) {
13914 var c = EC[el.id].data;
13915 if (arguments.length == 2) {
13918 return (c[key] = value);
13923 // Garbage collection - uncache elements/purge listeners on orphaned elements
13924 // so we don't hold a reference and cause the browser to retain them
13925 function garbageCollect() {
13926 if (!Ext.enableGarbageCollector) {
13927 clearInterval(El.collectorThreadId);
13935 if (!EC.hasOwnProperty(eid)) {
13939 if (o.skipGarbageCollection) {
13944 // -------------------------------------------------------
13945 // Determining what is garbage:
13946 // -------------------------------------------------------
13948 // dom node is null, definitely garbage
13949 // -------------------------------------------------------
13951 // no parentNode == direct orphan, definitely garbage
13952 // -------------------------------------------------------
13953 // !d.offsetParent && !document.getElementById(eid)
13954 // display none elements have no offsetParent so we will
13955 // also try to look it up by it's id. However, check
13956 // offsetParent first so we don't do unneeded lookups.
13957 // This enables collection of elements that are not orphans
13958 // directly, but somewhere up the line they have an orphan
13960 // -------------------------------------------------------
13961 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
13962 if (d && Ext.enableListenerCollection) {
13963 Ext.EventManager.removeAll(d);
13968 // Cleanup IE Object leaks
13972 if (!EC.hasOwnProperty(eid)) {
13977 EC = Ext.cache = t;
13981 El.collectorThreadId = setInterval(garbageCollect, 30000);
13983 var flyFn = function() {};
13984 flyFn.prototype = El.prototype;
13987 El.Flyweight = function(dom) {
13991 El.Flyweight.prototype = new flyFn();
13992 El.Flyweight.prototype.isFlyweight = true;
13993 El._flyweights = {};
13996 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference
13997 * to this element - the dom node can be overwritten by other code. {@link Ext#fly} is alias for
13998 * {@link Ext.Element#fly}.
14000 * Use this to make one-time references to DOM elements which are not going to be accessed again either by
14001 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link
14002 * Ext#get Ext.get} will be more appropriate to take advantage of the caching provided by the Ext.Element
14005 * @param {String/HTMLElement} el The dom node or id
14006 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts (e.g.
14007 * internally Ext uses "_global")
14008 * @return {Ext.Element} The shared Element object (or null if no matching element was found)
14011 El.fly = function(el, named) {
14013 named = named || '_global';
14014 el = Ext.getDom(el);
14016 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
14017 ret = El._flyweights[named];
14025 * @alias Ext.Element#get
14032 * @alias Ext.Element#fly
14036 // speedy lookup for elements never to box adjust
14037 var noBoxAdjust = Ext.isStrict ? {
14044 if (Ext.isIE || Ext.isGecko) {
14045 noBoxAdjust['button'] = 1;
14050 * @class Ext.Element
14052 Ext.Element.addMethods({
14054 * 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)
14055 * @param {String} selector The simple selector to test
14056 * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
14057 * The max depth to search as a number or element (defaults to 50 || document.body)
14058 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
14059 * @return {HTMLElement} The matching DOM node (or null if no match was found)
14061 findParent : function(simpleSelector, maxDepth, returnEl) {
14067 maxDepth = maxDepth || 50;
14068 if (isNaN(maxDepth)) {
14069 stopEl = Ext.getDom(maxDepth);
14070 maxDepth = Number.MAX_VALUE;
14072 while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
14073 if (Ext.DomQuery.is(p, simpleSelector)) {
14074 return returnEl ? Ext.get(p) : p;
14083 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
14084 * @param {String} selector The simple selector to test
14085 * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
14086 * The max depth to search as a number or element (defaults to 10 || document.body)
14087 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
14088 * @return {HTMLElement} The matching DOM node (or null if no match was found)
14090 findParentNode : function(simpleSelector, maxDepth, returnEl) {
14091 var p = Ext.fly(this.dom.parentNode, '_internal');
14092 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
14096 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
14097 * This is a shortcut for findParentNode() that always returns an Ext.Element.
14098 * @param {String} selector The simple selector to test
14099 * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
14100 * The max depth to search as a number or element (defaults to 10 || document.body)
14101 * @return {Ext.Element} The matching DOM node (or null if no match was found)
14103 up : function(simpleSelector, maxDepth) {
14104 return this.findParentNode(simpleSelector, maxDepth, true);
14108 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
14109 * @param {String} selector The CSS selector
14110 * @return {Ext.CompositeElement/Ext.CompositeElement} The composite element
14112 select : function(selector) {
14113 return Ext.Element.select(selector, false, this.dom);
14117 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
14118 * @param {String} selector The CSS selector
14119 * @return {HTMLElement[]} An array of the matched nodes
14121 query : function(selector) {
14122 return Ext.DomQuery.select(selector, this.dom);
14126 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
14127 * @param {String} selector The CSS selector
14128 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
14129 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
14131 down : function(selector, returnDom) {
14132 var n = Ext.DomQuery.selectNode(selector, this.dom);
14133 return returnDom ? n : Ext.get(n);
14137 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
14138 * @param {String} selector The CSS selector
14139 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
14140 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
14142 child : function(selector, returnDom) {
14146 id = Ext.get(me).id;
14148 id = id.replace(/[\.:]/g, "\\$0");
14149 node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
14150 return returnDom ? node : Ext.get(node);
14154 * Gets the parent node for this element, optionally chaining up trying to match a selector
14155 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
14156 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
14157 * @return {Ext.Element/HTMLElement} The parent node or null
14159 parent : function(selector, returnDom) {
14160 return this.matchNode('parentNode', 'parentNode', selector, returnDom);
14164 * Gets the next sibling, skipping text nodes
14165 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
14166 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
14167 * @return {Ext.Element/HTMLElement} The next sibling or null
14169 next : function(selector, returnDom) {
14170 return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
14174 * Gets the previous sibling, skipping text nodes
14175 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
14176 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
14177 * @return {Ext.Element/HTMLElement} The previous sibling or null
14179 prev : function(selector, returnDom) {
14180 return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
14185 * Gets the first child, skipping text nodes
14186 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
14187 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
14188 * @return {Ext.Element/HTMLElement} The first child or null
14190 first : function(selector, returnDom) {
14191 return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
14195 * Gets the last child, skipping text nodes
14196 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
14197 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
14198 * @return {Ext.Element/HTMLElement} The last child or null
14200 last : function(selector, returnDom) {
14201 return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
14204 matchNode : function(dir, start, selector, returnDom) {
14209 var n = this.dom[start];
14211 if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
14212 return !returnDom ? Ext.get(n) : n;
14221 * @class Ext.Element
14223 Ext.Element.addMethods({
14225 * Appends the passed element(s) to this element
14226 * @param {String/HTMLElement/Ext.Element} el
14227 * The id of the node, a DOM Node or an existing Element.
14228 * @return {Ext.Element} this
14230 appendChild : function(el) {
14231 return Ext.get(el).appendTo(this);
14235 * Appends this element to the passed element
14236 * @param {String/HTMLElement/Ext.Element} el The new parent element.
14237 * The id of the node, a DOM Node or an existing Element.
14238 * @return {Ext.Element} this
14240 appendTo : function(el) {
14241 Ext.getDom(el).appendChild(this.dom);
14246 * Inserts this element before the passed element in the DOM
14247 * @param {String/HTMLElement/Ext.Element} el The element before which this element will be inserted.
14248 * The id of the node, a DOM Node or an existing Element.
14249 * @return {Ext.Element} this
14251 insertBefore : function(el) {
14252 el = Ext.getDom(el);
14253 el.parentNode.insertBefore(this.dom, el);
14258 * Inserts this element after the passed element in the DOM
14259 * @param {String/HTMLElement/Ext.Element} el The element to insert after.
14260 * The id of the node, a DOM Node or an existing Element.
14261 * @return {Ext.Element} this
14263 insertAfter : function(el) {
14264 el = Ext.getDom(el);
14265 el.parentNode.insertBefore(this.dom, el.nextSibling);
14270 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
14271 * @param {String/HTMLElement/Ext.Element/Object} el The id or element to insert or a DomHelper config
14272 * to create and insert
14273 * @return {Ext.Element} The new child
14275 insertFirst : function(el, returnDom) {
14277 if (el.nodeType || el.dom || typeof el == 'string') { // element
14278 el = Ext.getDom(el);
14279 this.dom.insertBefore(el, this.dom.firstChild);
14280 return !returnDom ? Ext.get(el) : el;
14282 else { // dh config
14283 return this.createChild(el, this.dom.firstChild, returnDom);
14288 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
14289 * @param {String/HTMLElement/Ext.Element/Object/Array} el The id, element to insert or a DomHelper config
14290 * to create and insert *or* an array of any of those.
14291 * @param {String} where (optional) 'before' or 'after' defaults to before
14292 * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.Element
14293 * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
14295 insertSibling: function(el, where, returnDom){
14297 isAfter = (where || 'before').toLowerCase() == 'after',
14300 if(Ext.isArray(el)){
14302 Ext.each(el, function(e) {
14303 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
14313 if(el.nodeType || el.dom){
14314 rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
14319 if (isAfter && !me.dom.nextSibling) {
14320 rt = Ext.DomHelper.append(me.dom.parentNode, el, !returnDom);
14322 rt = Ext.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
14329 * Replaces the passed element with this element
14330 * @param {String/HTMLElement/Ext.Element} el The element to replace.
14331 * The id of the node, a DOM Node or an existing Element.
14332 * @return {Ext.Element} this
14334 replace : function(el) {
14336 this.insertBefore(el);
14342 * Replaces this element with the passed element
14343 * @param {String/HTMLElement/Ext.Element/Object} el The new element (id of the node, a DOM Node
14344 * or an existing Element) or a DomHelper config of an element to create
14345 * @return {Ext.Element} this
14347 replaceWith: function(el){
14350 if(el.nodeType || el.dom || typeof el == 'string'){
14352 me.dom.parentNode.insertBefore(el, me.dom);
14354 el = Ext.DomHelper.insertBefore(me.dom, el);
14357 delete Ext.cache[me.id];
14358 Ext.removeNode(me.dom);
14359 me.id = Ext.id(me.dom = el);
14360 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);
14365 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
14366 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
14367 * automatically generated with the specified attributes.
14368 * @param {HTMLElement} insertBefore (optional) a child element of this element
14369 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
14370 * @return {Ext.Element} The new child element
14372 createChild : function(config, insertBefore, returnDom) {
14373 config = config || {tag:'div'};
14374 if (insertBefore) {
14375 return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
14378 return Ext.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
14383 * Creates and wraps this element with another element
14384 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
14385 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
14386 * @return {HTMLElement/Ext.Element} The newly created wrapper element
14388 wrap : function(config, returnDom) {
14389 var newEl = Ext.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
14390 d = newEl.dom || newEl;
14392 d.appendChild(this.dom);
14397 * Inserts an html fragment into this element
14398 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
14399 * See {@link Ext.DomHelper#insertHtml} for details.
14400 * @param {String} html The HTML fragment
14401 * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
14402 * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
14404 insertHtml : function(where, html, returnEl) {
14405 var el = Ext.DomHelper.insertHtml(where, this.dom, html);
14406 return returnEl ? Ext.get(el) : el;
14411 * @class Ext.Element
14414 // local style camelizing for speed
14415 var ELEMENT = Ext.Element,
14416 supports = Ext.supports,
14417 view = document.defaultView,
14418 opacityRe = /alpha\(opacity=(.*)\)/i,
14419 trimRe = /^\s+|\s+$/g,
14422 adjustDirect2DTableRe = /table-row|table-.*-group/,
14423 INTERNAL = '_internal',
14424 PADDING = 'padding',
14430 BOTTOM = '-bottom',
14434 ISCLIPPED = 'isClipped',
14435 OVERFLOW = 'overflow',
14436 OVERFLOWX = 'overflow-x',
14437 OVERFLOWY = 'overflow-y',
14438 ORIGINALCLIP = 'originalClip',
14439 // special markup used throughout Ext when box wrapping elements
14440 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
14441 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
14442 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
14443 data = ELEMENT.data;
14445 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>';
14447 // These property values are read from the parentNode if they cannot be read
14449 ELEMENT.inheritedProps = {
14455 Ext.override(ELEMENT, {
14458 * TODO: Look at this
14460 // private ==> used by Fx
14461 adjustWidth : function(width) {
14463 isNum = (typeof width == 'number');
14465 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
14466 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
14468 return (isNum && width < 0) ? 0 : width;
14471 // private ==> used by Fx
14472 adjustHeight : function(height) {
14474 isNum = (typeof height == "number");
14476 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
14477 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
14479 return (isNum && height < 0) ? 0 : height;
14484 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
14485 * @param {String/String[]} className The CSS classes to add separated by space, or an array of classes
14486 * @return {Ext.Element} this
14488 addCls : function(className){
14491 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
14493 if (className === undefined) {
14496 // Separate case is for speed
14497 if (Object.prototype.toString.call(className) !== '[object Array]') {
14498 if (typeof className === 'string') {
14499 className = className.replace(trimRe, '').split(spacesRe);
14500 if (className.length === 1) {
14501 className = className[0];
14502 if (!me.hasCls(className)) {
14503 me.dom.className += space + className;
14506 this.addCls(className);
14510 for (i = 0, len = className.length; i < len; i++) {
14512 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
14517 me.dom.className += space + cls.join(" ");
14524 * Removes one or more CSS classes from the element.
14525 * @param {String/String[]} className The CSS classes to remove separated by space, or an array of classes
14526 * @return {Ext.Element} this
14528 removeCls : function(className){
14530 i, idx, len, cls, elClasses;
14531 if (className === undefined) {
14534 if (Object.prototype.toString.call(className) !== '[object Array]') {
14535 className = className.replace(trimRe, '').split(spacesRe);
14537 if (me.dom && me.dom.className) {
14538 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
14539 for (i = 0, len = className.length; i < len; i++) {
14540 cls = className[i];
14541 if (typeof cls == 'string') {
14542 cls = cls.replace(trimRe, '');
14543 idx = Ext.Array.indexOf(elClasses, cls);
14545 Ext.Array.erase(elClasses, idx, 1);
14549 me.dom.className = elClasses.join(" ");
14555 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
14556 * @param {String/String[]} className The CSS class to add, or an array of classes
14557 * @return {Ext.Element} this
14559 radioCls : function(className){
14560 var cn = this.dom.parentNode.childNodes,
14562 className = Ext.isArray(className) ? className : [className];
14563 for (i = 0, len = cn.length; i < len; i++) {
14565 if (v && v.nodeType == 1) {
14566 Ext.fly(v, '_internal').removeCls(className);
14569 return this.addCls(className);
14573 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
14574 * @param {String} className The CSS class to toggle
14575 * @return {Ext.Element} this
14578 toggleCls : Ext.supports.ClassList ?
14579 function(className) {
14580 this.dom.classList.toggle(Ext.String.trim(className));
14583 function(className) {
14584 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
14588 * Checks if the specified CSS class exists on this element's DOM node.
14589 * @param {String} className The CSS class to check for
14590 * @return {Boolean} True if the class exists, else false
14593 hasCls : Ext.supports.ClassList ?
14594 function(className) {
14598 className = className.split(spacesRe);
14599 var ln = className.length,
14601 for (; i < ln; i++) {
14602 if (className[i] && this.dom.classList.contains(className[i])) {
14608 function(className){
14609 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
14613 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
14614 * @param {String} oldClassName The CSS class to replace
14615 * @param {String} newClassName The replacement CSS class
14616 * @return {Ext.Element} this
14618 replaceCls : function(oldClassName, newClassName){
14619 return this.removeCls(oldClassName).addCls(newClassName);
14622 isStyle : function(style, val) {
14623 return this.getStyle(style) == val;
14627 * Normalizes currentStyle and computedStyle.
14628 * @param {String} property The style property whose value is returned.
14629 * @return {String} The current value of the style property for this element.
14632 getStyle : function() {
14633 return view && view.getComputedStyle ?
14636 v, cs, out, display, cleaner;
14638 if(el == document){
14641 prop = ELEMENT.normalize(prop);
14642 out = (v = el.style[prop]) ? v :
14643 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
14645 // Ignore cases when the margin is correctly reported as 0, the bug only shows
14647 if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
14648 cleaner = ELEMENT.getRightMarginFixCleaner(el);
14649 display = this.getStyle('display');
14650 el.style.display = 'inline-block';
14651 out = view.getComputedStyle(el, '').marginRight;
14652 el.style.display = display;
14656 if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
14657 out = 'transparent';
14665 if (el == document) {
14668 prop = ELEMENT.normalize(prop);
14671 if (prop == 'opacity') {
14672 if (el.style.filter.match) {
14673 m = el.style.filter.match(opacityRe);
14675 var fv = parseFloat(m[1]);
14677 return fv ? fv / 100 : 0;
14684 // the try statement does have a cost, so we avoid it unless we are
14687 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
14691 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
14693 // in some cases, IE6 will throw Invalid Argument for properties
14694 // like fontSize (see in /examples/tabs/tabs.html).
14697 if (!ELEMENT.inheritedProps[prop]) {
14701 el = el.parentNode;
14702 // this is _not_ perfect, but we can only hope that the style we
14703 // need is inherited from a parentNode. If not and since IE won't
14704 // give us the info we need, we are never going to be 100% right.
14709 msg: 'Failed to get ' + this.dom.id + '.currentStyle.' + prop
14716 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
14717 * are convert to standard 6 digit hex color.
14718 * @param {String} attr The css attribute
14719 * @param {String} defaultValue The default value to use when a valid color isn't found
14720 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
14723 getColor : function(attr, defaultValue, prefix){
14724 var v = this.getStyle(attr),
14725 color = prefix || prefix === '' ? prefix : '#',
14728 if(!v || (/transparent|inherit/.test(v))) {
14729 return defaultValue;
14732 Ext.each(v.slice(4, v.length -1).split(','), function(s){
14733 h = parseInt(s, 10);
14734 color += (h < 16 ? '0' : '') + h.toString(16);
14737 v = v.replace('#', '');
14738 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
14740 return(color.length > 5 ? color.toLowerCase() : defaultValue);
14744 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
14745 * @param {String/Object} property The style property to be set, or an object of multiple styles.
14746 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
14747 * @return {Ext.Element} this
14749 setStyle : function(prop, value){
14756 if (typeof prop === 'string') {
14761 for (style in prop) {
14762 if (prop.hasOwnProperty(style)) {
14763 value = Ext.value(prop[style], '');
14764 if (style == 'opacity') {
14765 me.setOpacity(value);
14768 me.dom.style[ELEMENT.normalize(style)] = value;
14776 * Set the opacity of the element
14777 * @param {Number} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
14778 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
14779 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
14780 * @return {Ext.Element} this
14782 setOpacity: function(opacity, animate) {
14792 style = me.dom.style;
14794 if (!animate || !me.anim) {
14795 if (!Ext.supports.Opacity) {
14796 opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
14797 val = style.filter.replace(opacityRe, '').replace(trimRe, '');
14800 style.filter = val + (val.length > 0 ? ' ': '') + opacity;
14803 style.opacity = opacity;
14807 if (!Ext.isObject(animate)) {
14813 me.animate(Ext.applyIf({
14825 * Clears any opacity settings from this element. Required in some cases for IE.
14826 * @return {Ext.Element} this
14828 clearOpacity : function(){
14829 var style = this.dom.style;
14830 if(!Ext.supports.Opacity){
14831 if(!Ext.isEmpty(style.filter)){
14832 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
14835 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
14842 * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
14843 * @return {Number} 0 or 1
14845 adjustDirect2DDimension: function(dimension) {
14848 display = me.getStyle('display'),
14849 inlineDisplay = dom.style['display'],
14850 inlinePosition = dom.style['position'],
14851 originIndex = dimension === 'width' ? 0 : 1,
14854 if (display === 'inline') {
14855 dom.style['display'] = 'inline-block';
14858 dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
14860 // floating will contain digits that appears after the decimal point
14861 // if height or width are set to auto we fallback to msTransformOrigin calculation
14862 floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
14864 dom.style['position'] = inlinePosition;
14866 if (display === 'inline') {
14867 dom.style['display'] = inlineDisplay;
14874 * Returns the offset height of the element
14875 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
14876 * @return {Number} The element's height
14878 getHeight: function(contentHeight, preciseHeight) {
14881 hidden = Ext.isIE && me.isStyle('display', 'none'),
14882 height, overflow, style, floating;
14884 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14885 // We will put the overflow back to it's original value when we are done measuring.
14886 if (Ext.isIEQuirks) {
14888 overflow = style.overflow;
14889 me.setStyle({ overflow: 'hidden'});
14892 height = dom.offsetHeight;
14894 height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
14896 // IE9 Direct2D dimension rounding bug
14897 if (!hidden && Ext.supports.Direct2DBug) {
14898 floating = me.adjustDirect2DDimension('height');
14899 if (preciseHeight) {
14900 height += floating;
14902 else if (floating > 0 && floating < 0.5) {
14907 if (contentHeight) {
14908 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
14911 if (Ext.isIEQuirks) {
14912 me.setStyle({ overflow: overflow});
14922 * Returns the offset width of the element
14923 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
14924 * @return {Number} The element's width
14926 getWidth: function(contentWidth, preciseWidth) {
14929 hidden = Ext.isIE && me.isStyle('display', 'none'),
14930 rect, width, overflow, style, floating, parentPosition;
14932 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14933 // We will put the overflow back to it's original value when we are done measuring.
14934 if (Ext.isIEQuirks) {
14936 overflow = style.overflow;
14937 me.setStyle({overflow: 'hidden'});
14940 // Fix Opera 10.5x width calculation issues
14941 if (Ext.isOpera10_5) {
14942 if (dom.parentNode.currentStyle.position === 'relative') {
14943 parentPosition = dom.parentNode.style.position;
14944 dom.parentNode.style.position = 'static';
14945 width = dom.offsetWidth;
14946 dom.parentNode.style.position = parentPosition;
14948 width = Math.max(width || 0, dom.offsetWidth);
14950 // Gecko will in some cases report an offsetWidth that is actually less than the width of the
14951 // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
14952 // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
14953 // subpixel measurements so we can force them to always be rounded up. See
14954 // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
14955 } else if (Ext.supports.BoundingClientRect) {
14956 rect = dom.getBoundingClientRect();
14957 width = rect.right - rect.left;
14958 width = preciseWidth ? width : Math.ceil(width);
14960 width = dom.offsetWidth;
14963 width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
14965 // IE9 Direct2D dimension rounding bug
14966 if (!hidden && Ext.supports.Direct2DBug) {
14967 floating = me.adjustDirect2DDimension('width');
14968 if (preciseWidth) {
14971 else if (floating > 0 && floating < 0.5) {
14976 if (contentWidth) {
14977 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
14980 if (Ext.isIEQuirks) {
14981 me.setStyle({ overflow: overflow});
14991 * Set the width of this Element.
14992 * @param {Number/String} width The new width. This may be one of:<div class="mdetail-params"><ul>
14993 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14994 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14996 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14997 * @return {Ext.Element} this
14999 setWidth : function(width, animate){
15001 width = me.adjustWidth(width);
15002 if (!animate || !me.anim) {
15003 me.dom.style.width = me.addUnits(width);
15006 if (!Ext.isObject(animate)) {
15009 me.animate(Ext.applyIf({
15019 * Set the height of this Element.
15021 // change the height to 200px and animate with default configuration
15022 Ext.fly('elementId').setHeight(200, true);
15024 // change the height to 150px and animate with a custom configuration
15025 Ext.fly('elId').setHeight(150, {
15026 duration : .5, // animation will have a duration of .5 seconds
15027 // will change the content to "finished"
15028 callback: function(){ this.{@link #update}("finished"); }
15031 * @param {Number/String} height The new height. This may be one of:<div class="mdetail-params"><ul>
15032 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
15033 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
15035 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15036 * @return {Ext.Element} this
15038 setHeight : function(height, animate){
15040 height = me.adjustHeight(height);
15041 if (!animate || !me.anim) {
15042 me.dom.style.height = me.addUnits(height);
15045 if (!Ext.isObject(animate)) {
15048 me.animate(Ext.applyIf({
15058 * Gets the width of the border(s) for the specified side(s)
15059 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
15060 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
15061 * @return {Number} The width of the sides passed added together
15063 getBorderWidth : function(side){
15064 return this.addStyles(side, borders);
15068 * Gets the width of the padding(s) for the specified side(s)
15069 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
15070 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
15071 * @return {Number} The padding of the sides passed added together
15073 getPadding : function(side){
15074 return this.addStyles(side, paddings);
15078 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
15079 * @return {Ext.Element} this
15085 if(!data(dom, ISCLIPPED)){
15086 data(dom, ISCLIPPED, true);
15087 data(dom, ORIGINALCLIP, {
15088 o: me.getStyle(OVERFLOW),
15089 x: me.getStyle(OVERFLOWX),
15090 y: me.getStyle(OVERFLOWY)
15092 me.setStyle(OVERFLOW, HIDDEN);
15093 me.setStyle(OVERFLOWX, HIDDEN);
15094 me.setStyle(OVERFLOWY, HIDDEN);
15100 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
15101 * @return {Ext.Element} this
15103 unclip : function(){
15108 if(data(dom, ISCLIPPED)){
15109 data(dom, ISCLIPPED, false);
15110 clip = data(dom, ORIGINALCLIP);
15112 me.setStyle(OVERFLOW, clip.o);
15115 me.setStyle(OVERFLOWX, clip.x);
15118 me.setStyle(OVERFLOWY, clip.y);
15125 addStyles : function(sides, styles){
15127 sidesArr = sides.match(wordsRe),
15129 len = sidesArr.length,
15131 for (; i < len; i++) {
15132 side = sidesArr[i];
15133 size = side && parseInt(this.getStyle(styles[side]), 10);
15135 totalSize += MATH.abs(size);
15144 * More flexible version of {@link #setStyle} for setting style properties.
15145 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
15146 * a function which returns such a specification.
15147 * @return {Ext.Element} this
15149 applyStyles : function(style){
15150 Ext.DomHelper.applyStyles(this.dom, style);
15155 * Returns an object with properties matching the styles requested.
15156 * For example, el.getStyles('color', 'font-size', 'width') might return
15157 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
15158 * @param {String} style1 A style name
15159 * @param {String} style2 A style name
15160 * @param {String} etc.
15161 * @return {Object} The style object
15163 getStyles : function(){
15165 len = arguments.length,
15168 for(; i < len; ++i) {
15169 style = arguments[i];
15170 styles[style] = this.getStyle(style);
15176 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
15177 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
15178 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
15179 * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}). The markup
15180 * is of this form:</p>
15182 Ext.Element.boxMarkup =
15183 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
15184 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
15185 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
15187 * <p>Example usage:</p>
15190 Ext.get("foo").boxWrap();
15192 // You can also add a custom class and use CSS inheritance rules to customize the box look.
15193 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
15194 // for how to create a custom box wrap style.
15195 Ext.get("foo").boxWrap().addCls("x-box-blue");
15197 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
15198 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
15199 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
15200 * also supply all of the necessary rules.
15201 * @return {Ext.Element} The outermost wrapping element of the created box structure.
15203 boxWrap : function(cls){
15204 cls = cls || Ext.baseCSSPrefix + 'box';
15205 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(ELEMENT.boxMarkup, cls) + "</div>"));
15206 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
15211 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
15212 * @param {Number/String} width The new width. This may be one of:<div class="mdetail-params"><ul>
15213 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
15214 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
15215 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
15217 * @param {Number/String} height The new height. This may be one of:<div class="mdetail-params"><ul>
15218 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
15219 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
15221 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15222 * @return {Ext.Element} this
15224 setSize : function(width, height, animate){
15226 if (Ext.isObject(width)) { // in case of object from getSize()
15228 height = width.height;
15229 width = width.width;
15231 width = me.adjustWidth(width);
15232 height = me.adjustHeight(height);
15233 if(!animate || !me.anim){
15234 // Must touch some property before setting style.width/height on non-quirk IE6,7, or the
15235 // properties will not reflect the changes on the style immediately
15236 if (!Ext.isIEQuirks && (Ext.isIE6 || Ext.isIE7)) {
15239 me.dom.style.width = me.addUnits(width);
15240 me.dom.style.height = me.addUnits(height);
15243 if (animate === true) {
15246 me.animate(Ext.applyIf({
15257 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
15258 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
15259 * if a height has not been set using CSS.
15262 getComputedHeight : function(){
15264 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
15266 h = parseFloat(me.getStyle('height')) || 0;
15267 if(!me.isBorderBox()){
15268 h += me.getFrameWidth('tb');
15275 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
15276 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
15277 * if a width has not been set using CSS.
15280 getComputedWidth : function(){
15282 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
15285 w = parseFloat(me.getStyle('width')) || 0;
15286 if(!me.isBorderBox()){
15287 w += me.getFrameWidth('lr');
15294 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
15295 for more information about the sides.
15296 * @param {String} sides
15299 getFrameWidth : function(sides, onlyContentBox){
15300 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
15304 * Sets up event handlers to add and remove a css class when the mouse is over this element
15305 * @param {String} className
15306 * @return {Ext.Element} this
15308 addClsOnOver : function(className){
15309 var dom = this.dom;
15312 Ext.fly(dom, INTERNAL).addCls(className);
15315 Ext.fly(dom, INTERNAL).removeCls(className);
15322 * Sets up event handlers to add and remove a css class when this element has the focus
15323 * @param {String} className
15324 * @return {Ext.Element} this
15326 addClsOnFocus : function(className){
15329 me.on("focus", function(){
15330 Ext.fly(dom, INTERNAL).addCls(className);
15332 me.on("blur", function(){
15333 Ext.fly(dom, INTERNAL).removeCls(className);
15339 * 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)
15340 * @param {String} className
15341 * @return {Ext.Element} this
15343 addClsOnClick : function(className){
15344 var dom = this.dom;
15345 this.on("mousedown", function(){
15346 Ext.fly(dom, INTERNAL).addCls(className);
15347 var d = Ext.getDoc(),
15349 Ext.fly(dom, INTERNAL).removeCls(className);
15350 d.removeListener("mouseup", fn);
15352 d.on("mouseup", fn);
15358 * <p>Returns the dimensions of the element available to lay content out in.<p>
15359 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
15360 * example:<pre><code>
15361 var vpSize = Ext.getBody().getViewSize();
15363 // all Windows created afterwards will have a default value of 90% height and 95% width
15364 Ext.Window.override({
15365 width: vpSize.width * 0.9,
15366 height: vpSize.height * 0.95
15368 // To handle window resizing you would have to hook onto onWindowResize.
15371 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
15372 * To obtain the size including scrollbars, use getStyleSize
15374 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
15377 getViewSize : function(){
15380 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
15381 style, overflow, ret;
15383 // If the body, use static methods
15386 width : ELEMENT.getViewWidth(),
15387 height : ELEMENT.getViewHeight()
15390 // Else use clientHeight/clientWidth
15393 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
15394 // We will put the overflow back to it's original value when we are done measuring.
15395 if (Ext.isIE6 || Ext.isIEQuirks) {
15397 overflow = style.overflow;
15398 me.setStyle({ overflow: 'hidden'});
15401 width : dom.clientWidth,
15402 height : dom.clientHeight
15404 if (Ext.isIE6 || Ext.isIEQuirks) {
15405 me.setStyle({ overflow: overflow });
15412 * <p>Returns the dimensions of the element available to lay content out in.<p>
15414 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
15415 * To obtain the size excluding scrollbars, use getViewSize
15417 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
15420 getStyleSize : function(){
15424 isDoc = (d == doc || d == doc.body),
15428 // If the body, use static methods
15431 width : ELEMENT.getViewWidth(),
15432 height : ELEMENT.getViewHeight()
15435 // Use Styles if they are set
15436 if(s.width && s.width != 'auto'){
15437 w = parseFloat(s.width);
15438 if(me.isBorderBox()){
15439 w -= me.getFrameWidth('lr');
15442 // Use Styles if they are set
15443 if(s.height && s.height != 'auto'){
15444 h = parseFloat(s.height);
15445 if(me.isBorderBox()){
15446 h -= me.getFrameWidth('tb');
15449 // Use getWidth/getHeight if style not set.
15450 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
15454 * Returns the size of the element.
15455 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
15456 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15458 getSize : function(contentSize){
15459 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
15463 * Forces the browser to repaint this element
15464 * @return {Ext.Element} this
15466 repaint : function(){
15467 var dom = this.dom;
15468 this.addCls(Ext.baseCSSPrefix + 'repaint');
15469 setTimeout(function(){
15470 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
15476 * Enable text selection for this element (normalized across browsers)
15477 * @return {Ext.Element} this
15479 selectable : function() {
15481 me.dom.unselectable = "off";
15482 // Prevent it from bubles up and enables it to be selectable
15483 me.on('selectstart', function (e) {
15484 e.stopPropagation();
15487 me.applyStyles("-moz-user-select: text; -khtml-user-select: text;");
15488 me.removeCls(Ext.baseCSSPrefix + 'unselectable');
15493 * Disables text selection for this element (normalized across browsers)
15494 * @return {Ext.Element} this
15496 unselectable : function(){
15498 me.dom.unselectable = "on";
15500 me.swallowEvent("selectstart", true);
15501 me.applyStyles("-moz-user-select:-moz-none;-khtml-user-select:none;");
15502 me.addCls(Ext.baseCSSPrefix + 'unselectable');
15508 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
15509 * then it returns the calculated width of the sides (see getPadding)
15510 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
15511 * @return {Object/Number}
15513 getMargin : function(side){
15515 hash = {t:"top", l:"left", r:"right", b: "bottom"},
15520 for (key in me.margins){
15521 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
15525 return me.addStyles.call(me, side, me.margins);
15531 * @class Ext.Element
15534 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
15538 Ext.Element.VISIBILITY = 1;
15540 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
15544 Ext.Element.DISPLAY = 2;
15547 * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
15552 Ext.Element.OFFSETS = 3;
15555 Ext.Element.ASCLASS = 4;
15558 * Defaults to 'x-hide-nosize'
15562 Ext.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
15564 Ext.Element.addMethods(function(){
15565 var El = Ext.Element,
15566 OPACITY = "opacity",
15567 VISIBILITY = "visibility",
15568 DISPLAY = "display",
15570 OFFSETS = "offsets",
15571 ASCLASS = "asclass",
15574 ORIGINALDISPLAY = 'originalDisplay',
15575 VISMODE = 'visibilityMode',
15576 ISVISIBLE = 'isVisible',
15578 getDisplay = function(dom){
15579 var d = data(dom, ORIGINALDISPLAY);
15580 if(d === undefined){
15581 data(dom, ORIGINALDISPLAY, d = '');
15585 getVisMode = function(dom){
15586 var m = data(dom, VISMODE);
15587 if(m === undefined){
15588 data(dom, VISMODE, m = 1);
15595 * @property {String} originalDisplay
15596 * The element's default display mode
15598 originalDisplay : "",
15599 visibilityMode : 1,
15602 * Sets the element's visibility mode. When setVisible() is called it
15603 * will use this to determine whether to set the visibility or the display property.
15604 * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
15605 * @return {Ext.Element} this
15607 setVisibilityMode : function(visMode){
15608 data(this.dom, VISMODE, visMode);
15613 * Checks whether the element is currently visible using both visibility and display properties.
15614 * @return {Boolean} True if the element is currently visible, else false
15616 isVisible : function() {
15619 visible = data(dom, ISVISIBLE);
15621 if(typeof visible == 'boolean'){ //return the cached value if registered
15624 //Determine the current state based on display states
15625 visible = !me.isStyle(VISIBILITY, HIDDEN) &&
15626 !me.isStyle(DISPLAY, NONE) &&
15627 !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
15629 data(dom, ISVISIBLE, visible);
15634 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
15635 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
15636 * @param {Boolean} visible Whether the element is visible
15637 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
15638 * @return {Ext.Element} this
15640 setVisible : function(visible, animate){
15641 var me = this, isDisplay, isVisibility, isOffsets, isNosize,
15643 visMode = getVisMode(dom);
15646 // hideMode string override
15647 if (typeof animate == 'string'){
15650 visMode = El.DISPLAY;
15653 visMode = El.VISIBILITY;
15656 visMode = El.OFFSETS;
15660 visMode = El.ASCLASS;
15663 me.setVisibilityMode(visMode);
15667 if (!animate || !me.anim) {
15668 if(visMode == El.ASCLASS ){
15670 me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
15672 } else if (visMode == El.DISPLAY){
15674 return me.setDisplayed(visible);
15676 } else if (visMode == El.OFFSETS){
15679 // Remember position for restoring, if we are not already hidden by offsets.
15680 if (!me.hideModeStyles) {
15681 me.hideModeStyles = {
15682 position: me.getStyle('position'),
15683 top: me.getStyle('top'),
15684 left: me.getStyle('left')
15687 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
15690 // Only "restore" as position if we have actually been hidden using offsets.
15691 // Calling setVisible(true) on a positioned element should not reposition it.
15692 else if (me.hideModeStyles) {
15693 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
15694 delete me.hideModeStyles;
15699 // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
15700 dom.style.visibility = visible ? '' : HIDDEN;
15703 // closure for composites
15705 me.setOpacity(0.01);
15706 me.setVisible(true);
15708 if (!Ext.isObject(animate)) {
15714 me.animate(Ext.applyIf({
15715 callback: function() {
15716 visible || me.setVisible(false).setOpacity(1);
15719 opacity: (visible) ? 1 : 0
15723 data(dom, ISVISIBLE, visible); //set logical visibility state
15730 * Determine if the Element has a relevant height and width available based
15731 * upon current logical visibility state
15733 hasMetrics : function(){
15734 var dom = this.dom;
15735 return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
15739 * Toggles the element's visibility or display, depending on visibility mode.
15740 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
15741 * @return {Ext.Element} this
15743 toggle : function(animate){
15745 me.setVisible(!me.isVisible(), me.anim(animate));
15750 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
15751 * @param {Boolean/String} value Boolean value to display the element using its default display, or a string to set the display directly.
15752 * @return {Ext.Element} this
15754 setDisplayed : function(value) {
15755 if(typeof value == "boolean"){
15756 value = value ? getDisplay(this.dom) : NONE;
15758 this.setStyle(DISPLAY, value);
15763 fixDisplay : function(){
15765 if (me.isStyle(DISPLAY, NONE)) {
15766 me.setStyle(VISIBILITY, HIDDEN);
15767 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
15768 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
15769 me.setStyle(DISPLAY, "block");
15775 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15776 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15777 * @return {Ext.Element} this
15779 hide : function(animate){
15780 // hideMode override
15781 if (typeof animate == 'string'){
15782 this.setVisible(false, animate);
15785 this.setVisible(false, this.anim(animate));
15790 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15791 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15792 * @return {Ext.Element} this
15794 show : function(animate){
15795 // hideMode override
15796 if (typeof animate == 'string'){
15797 this.setVisible(true, animate);
15800 this.setVisible(true, this.anim(animate));
15806 * @class Ext.Element
15808 Ext.applyIf(Ext.Element.prototype, {
15809 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15810 animate: function(config) {
15813 me = Ext.get(me.dom);
15815 if (Ext.fx.Manager.hasFxBlock(me.id)) {
15818 Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
15822 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15823 anim: function(config) {
15824 if (!Ext.isObject(config)) {
15825 return (config) ? {} : false;
15829 duration = config.duration || Ext.fx.Anim.prototype.duration,
15830 easing = config.easing || 'ease',
15833 if (config.stopAnimation) {
15834 me.stopAnimation();
15837 Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
15839 // Clear any 'paused' defaults.
15840 Ext.fx.Manager.setFxDefaults(me.id, {
15846 remove: config.remove,
15847 alternate: config.alternate || false,
15848 duration: duration,
15850 callback: config.callback,
15851 listeners: config.listeners,
15852 iterations: config.iterations || 1,
15853 scope: config.scope,
15854 block: config.block,
15855 concurrent: config.concurrent,
15856 delay: config.delay || 0,
15858 keyframes: config.keyframes,
15859 from: config.from || {},
15860 to: Ext.apply({}, config)
15862 Ext.apply(animConfig.to, config.to);
15864 // Anim API properties - backward compat
15865 delete animConfig.to.to;
15866 delete animConfig.to.from;
15867 delete animConfig.to.remove;
15868 delete animConfig.to.alternate;
15869 delete animConfig.to.keyframes;
15870 delete animConfig.to.iterations;
15871 delete animConfig.to.listeners;
15872 delete animConfig.to.target;
15873 delete animConfig.to.paused;
15874 delete animConfig.to.callback;
15875 delete animConfig.to.scope;
15876 delete animConfig.to.duration;
15877 delete animConfig.to.easing;
15878 delete animConfig.to.concurrent;
15879 delete animConfig.to.block;
15880 delete animConfig.to.stopAnimation;
15881 delete animConfig.to.delay;
15886 * Slides the element into view. An anchor point can be optionally passed to set the point of origin for the slide
15887 * effect. This function automatically handles wrapping the element with a fixed-size container if needed. See the
15888 * Fx class overview for valid anchor point options. Usage:
15890 * // default: slide the element in from the top
15893 * // custom: slide the element in from the right with a 2-second duration
15894 * el.slideIn('r', { duration: 2000 });
15896 * // common config options shown with default values
15897 * el.slideIn('t', {
15898 * easing: 'easeOut',
15902 * @param {String} [anchor='t'] One of the valid Fx anchor positions
15903 * @param {Object} [options] Object literal with any of the Fx config options
15904 * @return {Ext.Element} The Element
15906 slideIn: function(anchor, obj, slideOut) {
15908 elStyle = me.dom.style,
15909 beforeAnim, wrapAnim;
15911 anchor = anchor || "t";
15914 beforeAnim = function() {
15915 var animScope = this,
15916 listeners = obj.listeners,
15917 box, position, restoreSize, wrap, anim;
15924 if ((anchor == 't' || anchor == 'b') && box.height === 0) {
15925 box.height = me.dom.scrollHeight;
15927 else if ((anchor == 'l' || anchor == 'r') && box.width === 0) {
15928 box.width = me.dom.scrollWidth;
15931 position = me.getPositioning();
15932 me.setSize(box.width, box.height);
15936 visibility: slideOut ? 'visible' : 'hidden'
15939 wrap.setPositioning(position);
15940 if (wrap.isStyle('position', 'static')) {
15941 wrap.position('relative');
15943 me.clearPositioning('auto');
15946 // This element is temporarily positioned absolute within its wrapper.
15947 // Restore to its default, CSS-inherited visibility setting.
15948 // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
15951 position: 'absolute'
15954 wrap.setSize(box.width, box.height);
15961 width: box.width + 'px',
15965 width: box.width + 'px',
15966 height: box.height + 'px'
15969 elStyle.bottom = '0px';
15975 height: box.height + 'px'
15978 width: box.width + 'px',
15979 height: box.height + 'px'
15982 elStyle.right = '0px';
15987 x: box.x + box.width,
15989 height: box.height + 'px'
15993 width: box.width + 'px',
15994 height: box.height + 'px'
16001 y: box.y + box.height,
16002 width: box.width + 'px',
16007 width: box.width + 'px',
16008 height: box.height + 'px'
16021 width: box.width + 'px',
16022 height: box.height + 'px'
16025 elStyle.bottom = '0px';
16026 elStyle.right = '0px';
16031 x: box.x + box.width,
16037 width: box.width + 'px',
16038 height: box.height + 'px'
16041 elStyle.right = '0px';
16046 x: box.x + box.width,
16047 y: box.y + box.height,
16054 width: box.width + 'px',
16055 height: box.height + 'px'
16062 y: box.y + box.height,
16068 width: box.width + 'px',
16069 height: box.height + 'px'
16072 elStyle.bottom = '0px';
16077 wrapAnim = Ext.apply({}, obj);
16078 delete wrapAnim.listeners;
16079 wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
16082 easing: 'ease-out',
16083 from: slideOut ? anim.to : anim.from,
16084 to: slideOut ? anim.from : anim.to
16087 // In the absence of a callback, this listener MUST be added first
16088 wrapAnim.on('afteranimate', function() {
16090 me.setPositioning(position);
16091 if (obj.useDisplay) {
16092 me.setDisplayed(false);
16098 me.clearPositioning();
16099 me.setPositioning(position);
16102 wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
16105 me.setSize(box.width, box.height);
16108 // Add configured listeners after
16110 wrapAnim.on(listeners);
16115 duration: obj.duration ? obj.duration * 2 : 1000,
16122 if (wrapAnim && wrapAnim.running) {
16134 * Slides the element out of view. An anchor point can be optionally passed to set the end point for the slide
16135 * effect. When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will
16136 * still take up space in the document. The element must be removed from the DOM using the 'remove' config option if
16137 * desired. This function automatically handles wrapping the element with a fixed-size container if needed. See the
16138 * Fx class overview for valid anchor point options. Usage:
16140 * // default: slide the element out to the top
16143 * // custom: slide the element out to the right with a 2-second duration
16144 * el.slideOut('r', { duration: 2000 });
16146 * // common config options shown with default values
16147 * el.slideOut('t', {
16148 * easing: 'easeOut',
16151 * useDisplay: false
16154 * @param {String} [anchor='t'] One of the valid Fx anchor positions
16155 * @param {Object} [options] Object literal with any of the Fx config options
16156 * @return {Ext.Element} The Element
16158 slideOut: function(anchor, o) {
16159 return this.slideIn(anchor, o, true);
16163 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the element will
16164 * be hidden (visibility = 'hidden') but block elements will still take up space in the document. Usage:
16169 * // common config options shown with default values
16171 * easing: 'easeOut',
16173 * useDisplay: false
16176 * @param {Object} options (optional) Object literal with any of the Fx config options
16177 * @return {Ext.Element} The Element
16179 puff: function(obj) {
16182 obj = Ext.applyIf(obj || {}, {
16183 easing: 'ease-out',
16188 beforeAnim = function() {
16192 var box = me.getBox(),
16193 fontSize = me.getStyle('fontSize'),
16194 position = me.getPositioning();
16196 width: box.width * 2,
16197 height: box.height * 2,
16198 x: box.x - (box.width / 2),
16199 y: box.y - (box.height /2),
16203 this.on('afteranimate',function() {
16205 if (obj.useDisplay) {
16206 me.setDisplayed(false);
16211 me.setPositioning(position);
16212 me.setStyle({fontSize: fontSize});
16218 duration: obj.duration,
16219 easing: obj.easing,
16230 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
16231 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
16232 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if
16238 * // all config options shown with default values
16240 * easing: 'easeIn',
16243 * useDisplay: false
16246 * @param {Object} options (optional) Object literal with any of the Fx config options
16247 * @return {Ext.Element} The Element
16249 switchOff: function(obj) {
16253 obj = Ext.applyIf(obj || {}, {
16260 beforeAnim = function() {
16261 var animScope = this,
16262 size = me.getSize(),
16264 keyframe, position;
16267 position = me.getPositioning();
16269 keyframe = Ext.create('Ext.fx.Animator', {
16271 duration: obj.duration,
16272 easing: obj.easing,
16279 y: xy[1] + size.height / 2
16283 x: xy[0] + size.width / 2
16287 keyframe.on('afteranimate', function() {
16288 if (obj.useDisplay) {
16289 me.setDisplayed(false);
16294 me.setPositioning(position);
16300 duration: (obj.duration * 2),
16311 * Shows a ripple of exploding, attenuating borders to draw attention to an Element. Usage:
16313 * // default: a single light blue ripple
16316 * // custom: 3 red ripples lasting 3 seconds total
16317 * el.frame("#ff0000", 3, { duration: 3 });
16319 * // common config options shown with default values
16320 * el.frame("#C3DAF9", 1, {
16321 * duration: 1 //duration of each individual ripple.
16322 * // Note: Easing is not configurable and will be ignored if included
16325 * @param {String} [color='C3DAF9'] The color of the border. Should be a 6 char hex color without the leading #
16326 * (defaults to light blue).
16327 * @param {Number} [count=1] The number of ripples to display
16328 * @param {Object} [options] Object literal with any of the Fx config options
16329 * @return {Ext.Element} The Element
16331 frame : function(color, count, obj){
16335 color = color || '#C3DAF9';
16336 count = count || 1;
16339 beforeAnim = function() {
16341 var animScope = this,
16343 proxy = Ext.getBody().createChild({
16345 position : 'absolute',
16346 'pointer-events': 'none',
16348 border : '0px solid ' + color
16352 proxyAnim = Ext.create('Ext.fx.Anim', {
16354 duration: obj.duration || 1000,
16361 height: box.height,
16369 height: box.height + 40,
16370 width: box.width + 40
16373 proxyAnim.on('afteranimate', function() {
16380 duration: (obj.duration * 2) || 2000,
16391 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the ending point
16392 * of the effect. Usage:
16394 * // default: slide the element downward while fading out
16397 * // custom: slide the element out to the right with a 2-second duration
16398 * el.ghost('r', { duration: 2000 });
16400 * // common config options shown with default values
16402 * easing: 'easeOut',
16406 * @param {String} [anchor='b'] One of the valid Fx anchor positions
16407 * @param {Object} [options] Object literal with any of the Fx config options
16408 * @return {Ext.Element} The Element
16410 ghost: function(anchor, obj) {
16414 anchor = anchor || "b";
16415 beforeAnim = function() {
16416 var width = me.getWidth(),
16417 height = me.getHeight(),
16419 position = me.getPositioning(),
16425 to.y = xy[1] - height;
16428 to.x = xy[0] - width;
16431 to.x = xy[0] + width;
16434 to.y = xy[1] + height;
16437 to.x = xy[0] - width;
16438 to.y = xy[1] - height;
16441 to.x = xy[0] - width;
16442 to.y = xy[1] + height;
16445 to.x = xy[0] + width;
16446 to.y = xy[1] + height;
16449 to.x = xy[0] + width;
16450 to.y = xy[1] - height;
16454 this.on('afteranimate', function () {
16458 me.setPositioning(position);
16463 me.animate(Ext.applyIf(obj || {}, {
16465 easing: 'ease-out',
16476 * Highlights the Element by setting a color (applies to the background-color by default, but can be changed using
16477 * the "attr" config option) and then fading back to the original color. If no original color is available, you
16478 * should provide the "endColor" config option which will be cleared after the animation. Usage:
16480 * // default: highlight background to yellow
16483 * // custom: highlight foreground text to blue for 2 seconds
16484 * el.highlight("0000ff", { attr: 'color', duration: 2000 });
16486 * // common config options shown with default values
16487 * el.highlight("ffff9c", {
16488 * attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
16489 * endColor: (current color) or "ffffff",
16490 * easing: 'easeIn',
16494 * @param {String} [color='ffff9c'] The highlight color. Should be a 6 char hex color without the leading #
16495 * @param {Object} [options] Object literal with any of the Fx config options
16496 * @return {Ext.Element} The Element
16498 highlight: function(color, o) {
16502 restore, to, attr, lns, event, fn;
16505 lns = o.listeners || {};
16506 attr = o.attr || 'backgroundColor';
16507 from[attr] = color || 'ffff9c';
16511 to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
16517 // Don't apply directly on lns, since we reference it in our own callbacks below
16518 o.listeners = Ext.apply(Ext.apply({}, lns), {
16519 beforeanimate: function() {
16520 restore = dom.style[attr];
16524 event = lns.beforeanimate;
16526 fn = event.fn || event;
16527 return fn.apply(event.scope || lns.scope || window, arguments);
16530 afteranimate: function() {
16532 dom.style[attr] = restore;
16535 event = lns.afteranimate;
16537 fn = event.fn || event;
16538 fn.apply(event.scope || lns.scope || window, arguments);
16543 me.animate(Ext.apply({}, o, {
16554 * Creates a pause before any subsequent queued effects begin. If there are no effects queued after the pause it will
16555 * have no effect. Usage:
16559 * @param {Number} seconds The length of time to pause (in seconds)
16560 * @return {Ext.Element} The Element
16562 pause: function(ms) {
16564 Ext.fx.Manager.setFxDefaults(me.id, {
16571 * Fade an element in (from transparent to opaque). The ending opacity can be specified using the `opacity`
16572 * config option. Usage:
16574 * // default: fade in from opacity 0 to 100%
16577 * // custom: fade in from opacity 0 to 75% over 2 seconds
16578 * el.fadeIn({ opacity: .75, duration: 2000});
16580 * // common config options shown with default values
16582 * opacity: 1, //can be any value between 0 and 1 (e.g. .5)
16583 * easing: 'easeOut',
16587 * @param {Object} options (optional) Object literal with any of the Fx config options
16588 * @return {Ext.Element} The Element
16590 fadeIn: function(o) {
16591 this.animate(Ext.apply({}, o, {
16598 * Fade an element out (from opaque to transparent). The ending opacity can be specified using the `opacity`
16599 * config option. Note that IE may require `useDisplay:true` in order to redisplay correctly.
16602 * // default: fade out from the element's current opacity to 0
16605 * // custom: fade out from the element's current opacity to 25% over 2 seconds
16606 * el.fadeOut({ opacity: .25, duration: 2000});
16608 * // common config options shown with default values
16610 * opacity: 0, //can be any value between 0 and 1 (e.g. .5)
16611 * easing: 'easeOut',
16614 * useDisplay: false
16617 * @param {Object} options (optional) Object literal with any of the Fx config options
16618 * @return {Ext.Element} The Element
16620 fadeOut: function(o) {
16621 this.animate(Ext.apply({}, o, {
16629 * Animates the transition of an element's dimensions from a starting height/width to an ending height/width. This
16630 * method is a convenience implementation of {@link #shift}. Usage:
16632 * // change height and width to 100x100 pixels
16633 * el.scale(100, 100);
16635 * // common config options shown with default values. The height and width will default to
16636 * // the element's existing values if passed as null.
16638 * [element's width],
16639 * [element's height], {
16640 * easing: 'easeOut',
16645 * @param {Number} width The new width (pass undefined to keep the original width)
16646 * @param {Number} height The new height (pass undefined to keep the original height)
16647 * @param {Object} options (optional) Object literal with any of the Fx config options
16648 * @return {Ext.Element} The Element
16650 scale: function(w, h, o) {
16651 this.animate(Ext.apply({}, o, {
16660 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity. Any of these
16661 * properties not specified in the config object will not be changed. This effect requires that at least one new
16662 * dimension, position or opacity setting must be passed in on the config object in order for the function to have
16663 * any effect. Usage:
16665 * // slide the element horizontally to x position 200 while changing the height and opacity
16666 * el.shift({ x: 200, height: 50, opacity: .8 });
16668 * // common config options shown with default values.
16670 * width: [element's width],
16671 * height: [element's height],
16672 * x: [element's x position],
16673 * y: [element's y position],
16674 * opacity: [element's opacity],
16675 * easing: 'easeOut',
16679 * @param {Object} options Object literal with any of the Fx config options
16680 * @return {Ext.Element} The Element
16682 shift: function(config) {
16683 this.animate(config);
16689 * @class Ext.Element
16691 Ext.applyIf(Ext.Element, {
16692 unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
16693 camelRe: /(-[a-z])/gi,
16694 opacityRe: /alpha\(opacity=(.*)\)/i,
16695 cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
16697 defaultUnit : "px",
16698 borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
16699 paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
16700 margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
16702 // Reference the prototype's version of the method. Signatures are identical.
16703 addUnits : Ext.Element.prototype.addUnits,
16706 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16707 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16709 * @param {Number/String} box The encoded margins
16710 * @return {Object} An object with margin sizes for top, right, bottom and left
16712 parseBox : function(box) {
16713 if (Ext.isObject(box)) {
16716 right: box.right || 0,
16717 bottom: box.bottom || 0,
16718 left: box.left || 0
16721 if (typeof box != 'string') {
16722 box = box.toString();
16724 var parts = box.split(' '),
16728 parts[1] = parts[2] = parts[3] = parts[0];
16730 else if (ln == 2) {
16731 parts[2] = parts[0];
16732 parts[3] = parts[1];
16734 else if (ln == 3) {
16735 parts[3] = parts[1];
16739 top :parseFloat(parts[0]) || 0,
16740 right :parseFloat(parts[1]) || 0,
16741 bottom:parseFloat(parts[2]) || 0,
16742 left :parseFloat(parts[3]) || 0
16749 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16750 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16752 * @param {Number/String} box The encoded margins
16753 * @param {String} units The type of units to add
16754 * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
16756 unitizeBox : function(box, units) {
16757 var A = this.addUnits,
16758 B = this.parseBox(box);
16760 return A(B.top, units) + ' ' +
16761 A(B.right, units) + ' ' +
16762 A(B.bottom, units) + ' ' +
16768 camelReplaceFn : function(m, a) {
16769 return a.charAt(1).toUpperCase();
16773 * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
16776 * <li>border-width -> borderWidth</li>
16777 * <li>padding-top -> paddingTop</li>
16780 * @param {String} prop The property to normalize
16781 * @return {String} The normalized string
16783 normalize : function(prop) {
16784 if (prop == 'float') {
16785 prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
16787 return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
16791 * Retrieves the document height
16793 * @return {Number} documentHeight
16795 getDocumentHeight: function() {
16796 return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
16800 * Retrieves the document width
16802 * @return {Number} documentWidth
16804 getDocumentWidth: function() {
16805 return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
16809 * Retrieves the viewport height of the window.
16811 * @return {Number} viewportHeight
16813 getViewportHeight: function(){
16814 return window.innerHeight;
16818 * Retrieves the viewport width of the window.
16820 * @return {Number} viewportWidth
16822 getViewportWidth : function() {
16823 return window.innerWidth;
16827 * Retrieves the viewport size of the window.
16829 * @return {Object} object containing width and height properties
16831 getViewSize : function() {
16833 width: window.innerWidth,
16834 height: window.innerHeight
16839 * Retrieves the current orientation of the window. This is calculated by
16840 * determing if the height is greater than the width.
16842 * @return {String} Orientation of window: 'portrait' or 'landscape'
16844 getOrientation : function() {
16845 if (Ext.supports.OrientationChange) {
16846 return (window.orientation == 0) ? 'portrait' : 'landscape';
16849 return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
16853 * Returns the top Element that is located at the passed coordinates
16855 * @param {Number} x The x coordinate
16856 * @param {Number} y The y coordinate
16857 * @return {String} The found Element
16859 fromPoint: function(x, y) {
16860 return Ext.get(document.elementFromPoint(x, y));
16864 * Converts a CSS string into an object with a property for each style.
16866 * The sample code below would return an object with 2 properties, one
16867 * for background-color and one for color.</p>
16869 var css = 'background-color: red;color: blue; ';
16870 console.log(Ext.Element.parseStyles(css));
16873 * @param {String} styles A CSS string
16874 * @return {Object} styles
16876 parseStyles: function(styles){
16878 cssRe = this.cssRe,
16882 // Since we're using the g flag on the regex, we need to set the lastIndex.
16883 // This automatically happens on some implementations, but not others, see:
16884 // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
16885 // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
16886 cssRe.lastIndex = 0;
16887 while ((matches = cssRe.exec(styles))) {
16888 out[matches[1]] = matches[2];
16896 * @class Ext.CompositeElementLite
16897 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
16898 * members, or to perform collective actions upon the whole set.</p>
16899 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
16900 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
16901 * Example:<pre><code>
16902 var els = Ext.select("#some-el div.some-class");
16903 // or select directly from an existing element
16904 var el = Ext.get('some-el');
16905 el.select('div.some-class');
16907 els.setWidth(100); // all elements become 100 width
16908 els.hide(true); // all elements fade out and hide
16910 els.setWidth(100).hide(true);
16913 Ext.CompositeElementLite = function(els, root){
16915 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
16916 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
16917 * to augment the capabilities of the CompositeElementLite class may use it when adding
16918 * methods to the class.</p>
16919 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
16920 * following siblings of selected elements, the code would be</p><code><pre>
16921 Ext.override(Ext.CompositeElementLite, {
16922 nextAll: function() {
16923 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
16925 // Loop through all elements in this Composite, accumulating
16926 // an Array of all siblings.
16927 for (i = 0; i < l; i++) {
16928 for (n = els[i].nextSibling; n; n = n.nextSibling) {
16933 // Add all found siblings to this Composite
16934 return this.add(r);
16937 * @property {HTMLElement} elements
16939 this.elements = [];
16940 this.add(els, root);
16941 this.el = new Ext.Element.Flyweight();
16944 Ext.CompositeElementLite.prototype = {
16948 getElement : function(el){
16949 // Set the shared flyweight dom property to the current element
16957 transformElement : function(el){
16958 return Ext.getDom(el);
16962 * Returns the number of elements in this Composite.
16965 getCount : function(){
16966 return this.elements.length;
16969 * Adds elements to this Composite object.
16970 * @param {HTMLElement[]/Ext.CompositeElement} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
16971 * @return {Ext.CompositeElement} This Composite object.
16973 add : function(els, root){
16975 elements = me.elements;
16979 if(typeof els == "string"){
16980 els = Ext.Element.selectorFunction(els, root);
16981 }else if(els.isComposite){
16982 els = els.elements;
16983 }else if(!Ext.isIterable(els)){
16987 for(var i = 0, len = els.length; i < len; ++i){
16988 elements.push(me.transformElement(els[i]));
16993 invoke : function(fn, args){
17000 for(i = 0; i < len; i++) {
17003 Ext.Element.prototype[fn].apply(me.getElement(e), args);
17009 * Returns a flyweight Element of the dom element object at the specified index
17010 * @param {Number} index
17011 * @return {Ext.Element}
17013 item : function(index){
17015 el = me.elements[index],
17019 out = me.getElement(el);
17024 // fixes scope with flyweight
17025 addListener : function(eventName, handler, scope, opt){
17026 var els = this.elements,
17030 for(i = 0; i<len; i++) {
17033 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
17039 * <p>Calls the passed function for each element in this composite.</p>
17040 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
17041 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
17042 * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
17043 * a reference to the dom node, use el.dom.</b></div></li>
17044 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
17045 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
17047 * @param {Object} [scope] The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
17048 * @return {Ext.CompositeElement} this
17050 each : function(fn, scope){
17056 for(i = 0; i<len; i++) {
17059 e = this.getElement(e);
17060 if(fn.call(scope || e, e, me, i) === false){
17069 * Clears this Composite and adds the elements passed.
17070 * @param {HTMLElement[]/Ext.CompositeElement} els Either an array of DOM elements, or another Composite from which to fill this Composite.
17071 * @return {Ext.CompositeElement} this
17073 fill : function(els){
17081 * Filters this composite to only elements that match the passed selector.
17082 * @param {String/Function} selector A string CSS selector or a comparison function.
17083 * The comparison function will be called with the following arguments:<ul>
17084 * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
17085 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
17087 * @return {Ext.CompositeElement} this
17089 filter : function(selector){
17092 fn = Ext.isFunction(selector) ? selector
17094 return el.is(selector);
17097 me.each(function(el, self, i) {
17098 if (fn(el, i) !== false) {
17099 els[els.length] = me.transformElement(el);
17108 * Find the index of the passed element within the composite collection.
17109 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
17110 * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
17112 indexOf : function(el){
17113 return Ext.Array.indexOf(this.elements, this.transformElement(el));
17117 * Replaces the specified element with the passed element.
17118 * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite
17120 * @param {String/Ext.Element} replacement The id of an element or the Element itself.
17121 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
17122 * @return {Ext.CompositeElement} this
17124 replaceElement : function(el, replacement, domReplace){
17125 var index = !isNaN(el) ? el : this.indexOf(el),
17128 replacement = Ext.getDom(replacement);
17130 d = this.elements[index];
17131 d.parentNode.insertBefore(replacement, d);
17134 Ext.Array.splice(this.elements, index, 1, replacement);
17140 * Removes all elements.
17142 clear : function(){
17143 this.elements = [];
17147 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
17151 * Copies all of the functions from Ext.Element's prototype onto CompositeElementLite's prototype.
17152 * This is called twice - once immediately below, and once again after additional Ext.Element
17153 * are added in Ext JS
17155 Ext.CompositeElementLite.importElementMethods = function() {
17157 ElProto = Ext.Element.prototype,
17158 CelProto = Ext.CompositeElementLite.prototype;
17160 for (fnName in ElProto) {
17161 if (typeof ElProto[fnName] == 'function'){
17162 (function(fnName) {
17163 CelProto[fnName] = CelProto[fnName] || function() {
17164 return this.invoke(fnName, arguments);
17166 }).call(CelProto, fnName);
17172 Ext.CompositeElementLite.importElementMethods();
17175 Ext.Element.selectorFunction = Ext.DomQuery.select;
17179 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
17180 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
17181 * {@link Ext.CompositeElementLite CompositeElementLite} object.
17182 * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
17183 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
17184 * @return {Ext.CompositeElementLite/Ext.CompositeElement}
17185 * @member Ext.Element
17188 Ext.Element.select = function(selector, root){
17190 if(typeof selector == "string"){
17191 els = Ext.Element.selectorFunction(selector, root);
17192 }else if(selector.length !== undefined){
17196 sourceClass: "Ext.Element",
17197 sourceMethod: "select",
17198 selector: selector,
17200 msg: "Invalid selector specified: " + selector
17203 return new Ext.CompositeElementLite(els);
17206 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
17207 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
17208 * {@link Ext.CompositeElementLite CompositeElementLite} object.
17209 * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
17210 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
17211 * @return {Ext.CompositeElementLite/Ext.CompositeElement}
17215 Ext.select = Ext.Element.select;
17218 * @class Ext.util.DelayedTask
17220 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
17221 * performing setTimeout where a new timeout cancels the old timeout. When called, the
17222 * task will wait the specified time period before executing. If durng that time period,
17223 * the task is called again, the original call will be cancelled. This continues so that
17224 * the function is only called a single time for each iteration.
17226 * This method is especially useful for things like detecting whether a user has finished
17227 * typing in a text field. An example would be performing validation on a keypress. You can
17228 * use this class to buffer the keypress events for a certain number of milliseconds, and
17229 * perform only if they stop for that amount of time.
17233 * var task = new Ext.util.DelayedTask(function(){
17234 * alert(Ext.getDom('myInputField').value.length);
17237 * // Wait 500ms before calling our function. If the user presses another key
17238 * // during that 500ms, it will be cancelled and we'll wait another 500ms.
17239 * Ext.get('myInputField').on('keypress', function(){
17240 * task.{@link #delay}(500);
17243 * Note that we are using a DelayedTask here to illustrate a point. The configuration
17244 * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
17245 * also setup a delayed task for you to buffer events.
17247 * @constructor The parameters to this constructor serve as defaults and are not required.
17248 * @param {Function} fn (optional) The default function to call. If not specified here, it must be specified during the {@link #delay} call.
17249 * @param {Object} scope (optional) The default scope (The <code><b>this</b></code> reference) in which the
17250 * function is called. If not specified, <code>this</code> will refer to the browser window.
17251 * @param {Array} args (optional) The default Array of arguments.
17253 Ext.util.DelayedTask = function(fn, scope, args) {
17256 call = function() {
17259 fn.apply(scope, args || []);
17263 * Cancels any pending timeout and queues a new one
17264 * @param {Number} delay The milliseconds to delay
17265 * @param {Function} newFn (optional) Overrides function passed to constructor
17266 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
17267 * is specified, <code>this</code> will refer to the browser window.
17268 * @param {Array} newArgs (optional) Overrides args passed to constructor
17270 this.delay = function(delay, newFn, newScope, newArgs) {
17273 scope = newScope || scope;
17274 args = newArgs || args;
17275 id = setInterval(call, delay);
17279 * Cancel the last queued timeout
17281 this.cancel = function(){
17288 Ext.require('Ext.util.DelayedTask', function() {
17290 Ext.util.Event = Ext.extend(Object, (function() {
17291 function createBuffered(handler, listener, o, scope) {
17292 listener.task = new Ext.util.DelayedTask();
17293 return function() {
17294 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
17298 function createDelayed(handler, listener, o, scope) {
17299 return function() {
17300 var task = new Ext.util.DelayedTask();
17301 if (!listener.tasks) {
17302 listener.tasks = [];
17304 listener.tasks.push(task);
17305 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
17309 function createSingle(handler, listener, o, scope) {
17310 return function() {
17311 listener.ev.removeListener(listener.fn, scope);
17312 return handler.apply(scope, arguments);
17319 constructor: function(observable, name) {
17321 this.observable = observable;
17322 this.listeners = [];
17325 addListener: function(fn, scope, options) {
17328 scope = scope || me.observable;
17332 sourceClass: Ext.getClassName(this.observable),
17333 sourceMethod: "addListener",
17334 msg: "The specified callback function is undefined"
17338 if (!me.isListening(fn, scope)) {
17339 listener = me.createListener(fn, scope, options);
17341 // if we are currently firing this event, don't disturb the listener loop
17342 me.listeners = me.listeners.slice(0);
17344 me.listeners.push(listener);
17348 createListener: function(fn, scope, o) {
17350 scope = scope || this.observable;
17360 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
17361 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
17363 handler = createSingle(handler, listener, o, scope);
17366 handler = createDelayed(handler, listener, o, scope);
17369 handler = createBuffered(handler, listener, o, scope);
17372 listener.fireFn = handler;
17376 findListener: function(fn, scope) {
17377 var listeners = this.listeners,
17378 i = listeners.length,
17383 listener = listeners[i];
17385 s = listener.scope;
17386 if (listener.fn == fn && (s == scope || s == this.observable)) {
17395 isListening: function(fn, scope) {
17396 return this.findListener(fn, scope) !== -1;
17399 removeListener: function(fn, scope) {
17404 index = me.findListener(fn, scope);
17406 listener = me.listeners[index];
17409 me.listeners = me.listeners.slice(0);
17412 // cancel and remove a buffered handler that hasn't fired yet
17413 if (listener.task) {
17414 listener.task.cancel();
17415 delete listener.task;
17418 // cancel and remove all delayed handlers that haven't fired yet
17419 k = listener.tasks && listener.tasks.length;
17422 listener.tasks[k].cancel();
17424 delete listener.tasks;
17427 // remove this listener from the listeners array
17428 Ext.Array.erase(me.listeners, index, 1);
17435 // Iterate to stop any buffered/delayed events
17436 clearListeners: function() {
17437 var listeners = this.listeners,
17438 i = listeners.length;
17441 this.removeListener(listeners[i].fn, listeners[i].scope);
17447 listeners = me.listeners,
17448 count = listeners.length,
17455 for (i = 0; i < count; i++) {
17456 listener = listeners[i];
17457 args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
17459 args.push(listener.o);
17461 if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
17462 return (me.firing = false);
17474 * @class Ext.EventManager
17475 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
17476 * several useful events directly.
17477 * See {@link Ext.EventObject} for more details on normalized event objects.
17480 Ext.EventManager = {
17482 // --------------------- onReady ---------------------
17485 * Check if we have bound our global onReady listener
17488 hasBoundOnReady: false,
17491 * Check if fireDocReady has been called
17494 hasFiredReady: false,
17497 * Timer for the document ready event in old IE versions
17500 readyTimeout: null,
17503 * Checks if we have bound an onreadystatechange event
17506 hasOnReadyStateChange: false,
17509 * Holds references to any onReady functions
17512 readyEvent: new Ext.util.Event(),
17515 * Check the ready state for old IE versions
17517 * @return {Boolean} True if the document is ready
17519 checkReadyState: function(){
17520 var me = Ext.EventManager;
17522 if(window.attachEvent){
17523 // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
17524 // licensed courtesy of http://developer.yahoo.com/yui/license.html
17525 if (window != top) {
17529 document.documentElement.doScroll('left');
17536 if (document.readyState == 'complete') {
17540 me.readyTimeout = setTimeout(arguments.callee, 2);
17545 * Binds the appropriate browser event for checking if the DOM has loaded.
17548 bindReadyEvent: function(){
17549 var me = Ext.EventManager;
17550 if (me.hasBoundOnReady) {
17554 if (document.addEventListener) {
17555 document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
17556 // fallback, load will ~always~ fire
17557 window.addEventListener('load', me.fireDocReady, false);
17559 // check if the document is ready, this will also kick off the scroll checking timer
17560 if (!me.checkReadyState()) {
17561 document.attachEvent('onreadystatechange', me.checkReadyState);
17562 me.hasOnReadyStateChange = true;
17564 // fallback, onload will ~always~ fire
17565 window.attachEvent('onload', me.fireDocReady, false);
17567 me.hasBoundOnReady = true;
17571 * We know the document is loaded, so trigger any onReady events.
17574 fireDocReady: function(){
17575 var me = Ext.EventManager;
17577 // only unbind these events once
17578 if (!me.hasFiredReady) {
17579 me.hasFiredReady = true;
17581 if (document.addEventListener) {
17582 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
17583 window.removeEventListener('load', me.fireDocReady, false);
17585 if (me.readyTimeout !== null) {
17586 clearTimeout(me.readyTimeout);
17588 if (me.hasOnReadyStateChange) {
17589 document.detachEvent('onreadystatechange', me.checkReadyState);
17591 window.detachEvent('onload', me.fireDocReady);
17593 Ext.supports.init();
17595 if (!Ext.isReady) {
17596 Ext.isReady = true;
17597 me.onWindowUnload();
17598 me.readyEvent.fire();
17603 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
17604 * accessed shorthanded as Ext.onReady().
17605 * @param {Function} fn The method the event invokes.
17606 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
17607 * @param {Boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}.
17609 onDocumentReady: function(fn, scope, options){
17610 options = options || {};
17611 var me = Ext.EventManager,
17612 readyEvent = me.readyEvent;
17614 // force single to be true so our event is only ever fired once.
17615 options.single = true;
17617 // Document already loaded, let's just fire it
17619 readyEvent.addListener(fn, scope, options);
17622 options.delay = options.delay || 1;
17623 readyEvent.addListener(fn, scope, options);
17624 me.bindReadyEvent();
17629 // --------------------- event binding ---------------------
17632 * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
17635 stoppedMouseDownEvent: new Ext.util.Event(),
17638 * Options to parse for the 4th argument to addListener.
17641 propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
17644 * Get the id of the element. If one has not been assigned, automatically assign it.
17645 * @param {HTMLElement/Ext.Element} element The element to get the id for.
17646 * @return {String} id
17648 getId : function(element) {
17649 var skipGarbageCollection = false,
17652 element = Ext.getDom(element);
17654 if (element === document || element === window) {
17655 id = element === document ? Ext.documentId : Ext.windowId;
17658 id = Ext.id(element);
17660 // skip garbage collection for special elements (window, document, iframes)
17661 if (element && (element.getElementById || element.navigator)) {
17662 skipGarbageCollection = true;
17665 if (!Ext.cache[id]){
17666 Ext.Element.addToCache(new Ext.Element(element), id);
17667 if (skipGarbageCollection) {
17668 Ext.cache[id].skipGarbageCollection = true;
17675 * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
17677 * @param {Object} element The element the event is for
17678 * @param {Object} event The event configuration
17679 * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
17681 prepareListenerConfig: function(element, config, isRemove){
17683 propRe = me.propRe,
17686 // loop over all the keys in the object
17687 for (key in config) {
17688 if (config.hasOwnProperty(key)) {
17689 // if the key is something else then an event option
17690 if (!propRe.test(key)) {
17691 value = config[key];
17692 // if the value is a function it must be something like click: function(){}, scope: this
17693 // which means that there might be multiple event listeners with shared options
17694 if (Ext.isFunction(value)) {
17696 args = [element, key, value, config.scope, config];
17698 // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
17699 args = [element, key, value.fn, value.scope, value];
17702 if (isRemove === true) {
17703 me.removeListener.apply(this, args);
17705 me.addListener.apply(me, args);
17713 * Normalize cross browser event differences
17715 * @param {Object} eventName The event name
17716 * @param {Object} fn The function to execute
17717 * @return {Object} The new event name/function
17719 normalizeEvent: function(eventName, fn){
17720 if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
17722 fn = Ext.Function.createInterceptor(fn, this.contains, this);
17724 eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
17725 } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
17726 eventName = 'DOMMouseScroll';
17729 eventName: eventName,
17735 * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
17737 * @param {Object} event
17739 contains: function(event){
17740 var parent = event.browserEvent.currentTarget,
17741 child = this.getRelatedTarget(event);
17743 if (parent && parent.firstChild) {
17745 if (child === parent) {
17748 child = child.parentNode;
17749 if (child && (child.nodeType != 1)) {
17758 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
17759 * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
17760 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
17761 * @param {String} eventName The name of the event to listen for.
17762 * @param {Function} handler The handler function the event invokes. This function is passed
17763 * the following parameters:<ul>
17764 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
17765 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
17766 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
17767 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
17769 * @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>.
17770 * @param {Object} options (optional) An object containing handler configuration properties.
17771 * This may contain any of the following properties:<ul>
17772 * <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>
17773 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
17774 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
17775 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
17776 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
17777 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
17778 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
17779 * <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>
17780 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
17781 * by the specified number of milliseconds. If the event fires again within that time, the original
17782 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
17783 * <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>
17785 * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
17787 addListener: function(element, eventName, fn, scope, options){
17788 // Check if we've been passed a "config style" event.
17789 if (typeof eventName !== 'string') {
17790 this.prepareListenerConfig(element, eventName);
17794 var dom = Ext.getDom(element),
17800 sourceClass: 'Ext.EventManager',
17801 sourceMethod: 'addListener',
17802 targetElement: element,
17803 eventName: eventName,
17804 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
17809 sourceClass: 'Ext.EventManager',
17810 sourceMethod: 'addListener',
17811 targetElement: element,
17812 eventName: eventName,
17813 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
17817 // create the wrapper function
17818 options = options || {};
17820 bind = this.normalizeEvent(eventName, fn);
17821 wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
17824 if (dom.attachEvent) {
17825 dom.attachEvent('on' + bind.eventName, wrap);
17827 dom.addEventListener(bind.eventName, wrap, options.capture || false);
17830 if (dom == document && eventName == 'mousedown') {
17831 this.stoppedMouseDownEvent.addListener(wrap);
17834 // add all required data into the event cache
17835 this.getEventListenerCache(dom, eventName).push({
17843 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
17844 * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
17845 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
17846 * @param {String} eventName The name of the event.
17847 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
17848 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
17849 * then this must refer to the same object.
17851 removeListener : function(element, eventName, fn, scope) {
17852 // handle our listener config object syntax
17853 if (typeof eventName !== 'string') {
17854 this.prepareListenerConfig(element, eventName, true);
17858 var dom = Ext.getDom(element),
17859 cache = this.getEventListenerCache(dom, eventName),
17860 bindName = this.normalizeEvent(eventName).eventName,
17861 i = cache.length, j,
17862 listener, wrap, tasks;
17866 listener = cache[i];
17868 if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
17869 wrap = listener.wrap;
17871 // clear buffered calls
17873 clearTimeout(wrap.task);
17877 // clear delayed calls
17878 j = wrap.tasks && wrap.tasks.length;
17881 clearTimeout(wrap.tasks[j]);
17886 if (dom.detachEvent) {
17887 dom.detachEvent('on' + bindName, wrap);
17889 dom.removeEventListener(bindName, wrap, false);
17892 if (wrap && dom == document && eventName == 'mousedown') {
17893 this.stoppedMouseDownEvent.removeListener(wrap);
17896 // remove listener from cache
17897 Ext.Array.erase(cache, i, 1);
17903 * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
17904 * directly on an Element in favor of calling this version.
17905 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17907 removeAll : function(element){
17908 var dom = Ext.getDom(element),
17913 cache = this.getElementEventCache(dom);
17915 for (ev in cache) {
17916 if (cache.hasOwnProperty(ev)) {
17917 this.removeListener(dom, ev);
17920 Ext.cache[dom.id].events = {};
17924 * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.Element#purgeAllListeners}
17925 * directly on an Element in favor of calling this version.
17926 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17927 * @param {String} eventName (optional) The name of the event.
17929 purgeElement : function(element, eventName) {
17930 var dom = Ext.getDom(element),
17934 this.removeListener(dom, eventName);
17937 this.removeAll(dom);
17940 if(dom && dom.childNodes) {
17941 for(len = element.childNodes.length; i < len; i++) {
17942 this.purgeElement(element.childNodes[i], eventName);
17948 * Create the wrapper function for the event
17950 * @param {HTMLElement} dom The dom element
17951 * @param {String} ename The event name
17952 * @param {Function} fn The function to execute
17953 * @param {Object} scope The scope to execute callback in
17954 * @param {Object} options The options
17955 * @return {Function} the wrapper function
17957 createListenerWrap : function(dom, ename, fn, scope, options) {
17958 options = options || {};
17962 return function wrap(e, args) {
17963 // Compile the implementation upon first firing
17965 f = ['if(!Ext) {return;}'];
17967 if(options.buffer || options.delay || options.freezeEvent) {
17968 f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
17970 f.push('e = Ext.EventObject.setEvent(e);');
17973 if (options.delegate) {
17974 f.push('var t = e.getTarget("' + options.delegate + '", this);');
17975 f.push('if(!t) {return;}');
17977 f.push('var t = e.target;');
17980 if (options.target) {
17981 f.push('if(e.target !== options.target) {return;}');
17984 if(options.stopEvent) {
17985 f.push('e.stopEvent();');
17987 if(options.preventDefault) {
17988 f.push('e.preventDefault();');
17990 if(options.stopPropagation) {
17991 f.push('e.stopPropagation();');
17995 if(options.normalized === false) {
17996 f.push('e = e.browserEvent;');
17999 if(options.buffer) {
18000 f.push('(wrap.task && clearTimeout(wrap.task));');
18001 f.push('wrap.task = setTimeout(function(){');
18004 if(options.delay) {
18005 f.push('wrap.tasks = wrap.tasks || [];');
18006 f.push('wrap.tasks.push(setTimeout(function(){');
18009 // finally call the actual handler fn
18010 f.push('fn.call(scope || dom, e, t, options);');
18012 if(options.single) {
18013 f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
18016 if(options.delay) {
18017 f.push('}, ' + options.delay + '));');
18020 if(options.buffer) {
18021 f.push('}, ' + options.buffer + ');');
18024 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
18027 gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
18032 * Get the event cache for a particular element for a particular event
18034 * @param {HTMLElement} element The element
18035 * @param {Object} eventName The event name
18036 * @return {Array} The events for the element
18038 getEventListenerCache : function(element, eventName) {
18043 var eventCache = this.getElementEventCache(element);
18044 return eventCache[eventName] || (eventCache[eventName] = []);
18048 * Gets the event cache for the object
18050 * @param {HTMLElement} element The element
18051 * @return {Object} The event cache for the object
18053 getElementEventCache : function(element) {
18057 var elementCache = Ext.cache[this.getId(element)];
18058 return elementCache.events || (elementCache.events = {});
18061 // --------------------- utility methods ---------------------
18062 mouseLeaveRe: /(mouseout|mouseleave)/,
18063 mouseEnterRe: /(mouseover|mouseenter)/,
18066 * Stop the event (preventDefault and stopPropagation)
18067 * @param {Event} The event to stop
18069 stopEvent: function(event) {
18070 this.stopPropagation(event);
18071 this.preventDefault(event);
18075 * Cancels bubbling of the event.
18076 * @param {Event} The event to stop bubbling.
18078 stopPropagation: function(event) {
18079 event = event.browserEvent || event;
18080 if (event.stopPropagation) {
18081 event.stopPropagation();
18083 event.cancelBubble = true;
18088 * Prevents the browsers default handling of the event.
18089 * @param {Event} The event to prevent the default
18091 preventDefault: function(event) {
18092 event = event.browserEvent || event;
18093 if (event.preventDefault) {
18094 event.preventDefault();
18096 event.returnValue = false;
18097 // Some keys events require setting the keyCode to -1 to be prevented
18099 // all ctrl + X and F1 -> F12
18100 if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
18101 event.keyCode = -1;
18104 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
18110 * Gets the related target from the event.
18111 * @param {Object} event The event
18112 * @return {HTMLElement} The related target.
18114 getRelatedTarget: function(event) {
18115 event = event.browserEvent || event;
18116 var target = event.relatedTarget;
18118 if (this.mouseLeaveRe.test(event.type)) {
18119 target = event.toElement;
18120 } else if (this.mouseEnterRe.test(event.type)) {
18121 target = event.fromElement;
18124 return this.resolveTextNode(target);
18128 * Gets the x coordinate from the event
18129 * @param {Object} event The event
18130 * @return {Number} The x coordinate
18132 getPageX: function(event) {
18133 return this.getXY(event)[0];
18137 * Gets the y coordinate from the event
18138 * @param {Object} event The event
18139 * @return {Number} The y coordinate
18141 getPageY: function(event) {
18142 return this.getXY(event)[1];
18146 * Gets the x & y coordinate from the event
18147 * @param {Object} event The event
18148 * @return {Number[]} The x/y coordinate
18150 getPageXY: function(event) {
18151 event = event.browserEvent || event;
18152 var x = event.pageX,
18154 doc = document.documentElement,
18155 body = document.body;
18157 // pageX/pageY not available (undefined, not null), use clientX/clientY instead
18158 if (!x && x !== 0) {
18159 x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
18160 y = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
18166 * Gets the target of the event.
18167 * @param {Object} event The event
18168 * @return {HTMLElement} target
18170 getTarget: function(event) {
18171 event = event.browserEvent || event;
18172 return this.resolveTextNode(event.target || event.srcElement);
18176 * Resolve any text nodes accounting for browser differences.
18178 * @param {HTMLElement} node The node
18179 * @return {HTMLElement} The resolved node
18181 // 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.
18182 resolveTextNode: Ext.isGecko ?
18187 // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
18188 var s = HTMLElement.prototype.toString.call(node);
18189 if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
18192 return node.nodeType == 3 ? node.parentNode: node;
18193 }: function(node) {
18194 return node && node.nodeType == 3 ? node.parentNode: node;
18197 // --------------------- custom event binding ---------------------
18199 // Keep track of the current width/height
18204 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
18205 * passes new viewport width and height to handlers.
18206 * @param {Function} fn The handler function the window resize event invokes.
18207 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
18208 * @param {Boolean} options Options object as passed to {@link Ext.Element#addListener}
18210 onWindowResize: function(fn, scope, options){
18211 var resize = this.resizeEvent;
18213 this.resizeEvent = resize = new Ext.util.Event();
18214 this.on(window, 'resize', this.fireResize, this, {buffer: 100});
18216 resize.addListener(fn, scope, options);
18220 * Fire the resize event.
18223 fireResize: function(){
18225 w = Ext.Element.getViewWidth(),
18226 h = Ext.Element.getViewHeight();
18228 //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
18229 if(me.curHeight != h || me.curWidth != w){
18232 me.resizeEvent.fire(w, h);
18237 * Removes the passed window resize listener.
18238 * @param {Function} fn The method the event invokes
18239 * @param {Object} scope The scope of handler
18241 removeResizeListener: function(fn, scope){
18242 if (this.resizeEvent) {
18243 this.resizeEvent.removeListener(fn, scope);
18247 onWindowUnload: function() {
18248 var unload = this.unloadEvent;
18250 this.unloadEvent = unload = new Ext.util.Event();
18251 this.addListener(window, 'unload', this.fireUnload, this);
18256 * Fires the unload event for items bound with onWindowUnload
18259 fireUnload: function() {
18260 // wrap in a try catch, could have some problems during unload
18262 this.removeUnloadListener();
18263 // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
18264 if (Ext.isGecko3) {
18265 var gridviews = Ext.ComponentQuery.query('gridview'),
18267 ln = gridviews.length;
18268 for (; i < ln; i++) {
18269 gridviews[i].scrollToTop();
18272 // Purge all elements in the cache
18275 for (el in cache) {
18276 if (cache.hasOwnProperty(el)) {
18277 Ext.EventManager.removeAll(el);
18285 * Removes the passed window unload listener.
18286 * @param {Function} fn The method the event invokes
18287 * @param {Object} scope The scope of handler
18289 removeUnloadListener: function(){
18290 if (this.unloadEvent) {
18291 this.removeListener(window, 'unload', this.fireUnload);
18296 * note 1: IE fires ONLY the keydown event on specialkey autorepeat
18297 * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
18298 * (research done by Jan Wolter at http://unixpapa.com/js/key.html)
18301 useKeyDown: Ext.isWebKit ?
18302 parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
18303 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
18306 * Indicates which event to use for getting key presses.
18307 * @return {String} The appropriate event name.
18309 getKeyEvent: function(){
18310 return this.useKeyDown ? 'keydown' : 'keypress';
18315 * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
18319 Ext.onReady = function(fn, scope, options) {
18320 Ext.Loader.onReady(fn, scope, true, options);
18324 * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
18326 * @method onDocumentReady
18328 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
18331 * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
18332 * @member Ext.EventManager
18335 Ext.EventManager.on = Ext.EventManager.addListener;
18338 * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
18339 * @member Ext.EventManager
18342 Ext.EventManager.un = Ext.EventManager.removeListener;
18345 var initExtCss = function() {
18346 // find the body element
18347 var bd = document.body || document.getElementsByTagName('body')[0],
18348 baseCSSPrefix = Ext.baseCSSPrefix,
18349 cls = [baseCSSPrefix + 'body'],
18357 html = bd.parentNode;
18360 cls.push(baseCSSPrefix + c);
18363 //Let's keep this human readable!
18367 // very often CSS needs to do checks like "IE7+" or "IE6 or 7". To help
18368 // reduce the clutter (since CSS/SCSS cannot do these tests), we add some
18369 // additional classes:
18371 // x-ie7p : IE7+ : 7 <= ieVer
18372 // x-ie7m : IE7- : ieVer <= 7
18373 // x-ie8p : IE8+ : 8 <= ieVer
18374 // x-ie8m : IE8- : ieVer <= 8
18375 // x-ie9p : IE9+ : 9 <= ieVer
18376 // x-ie78 : IE7 or 8 : 7 <= ieVer <= 8
18380 } else { // ignore pre-IE6 :)
18400 if (Ext.isIE6 || Ext.isIE7) {
18403 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
18406 if (Ext.isIE7 || Ext.isIE8) {
18412 if (Ext.isGecko3) {
18415 if (Ext.isGecko4) {
18418 if (Ext.isGecko5) {
18425 if (Ext.isWebKit) {
18428 if (Ext.isSafari) {
18430 if (Ext.isSafari2) {
18433 if (Ext.isSafari3) {
18436 if (Ext.isSafari4) {
18439 if (Ext.isSafari5) {
18443 if (Ext.isChrome) {
18452 if (!Ext.supports.CSS3BorderRadius) {
18455 if (!Ext.supports.CSS3LinearGradient) {
18458 if (!Ext.scopeResetCSS) {
18462 // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
18464 if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
18465 Ext.isBorderBox = false;
18468 Ext.isBorderBox = true;
18471 htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
18472 if (!Ext.isStrict) {
18473 htmlCls.push(baseCSSPrefix + 'quirks');
18475 Ext.fly(html, '_internal').addCls(htmlCls);
18478 Ext.fly(bd, '_internal').addCls(cls);
18482 Ext.onReady(initExtCss);
18486 * @class Ext.EventObject
18488 Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
18489 wraps the browser's native event-object normalizing cross-browser differences,
18490 such as which mouse button is clicked, keys pressed, mechanisms to stop
18491 event-propagation along with a method to prevent default actions from taking place.
18495 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
18496 e.preventDefault();
18497 var target = e.getTarget(); // same as t (the target HTMLElement)
18501 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
18502 myDiv.on( // 'on' is shorthand for addListener
18503 "click", // perform an action on click of myDiv
18504 handleClick // reference to the action handler
18507 // other methods to do the same:
18508 Ext.EventManager.on("myDiv", 'click', handleClick);
18509 Ext.EventManager.addListener("myDiv", 'click', handleClick);
18514 Ext.define('Ext.EventObjectImpl', {
18515 uses: ['Ext.util.Point'],
18517 /** Key constant @type Number */
18519 /** Key constant @type Number */
18521 /** Key constant @type Number */
18523 /** Key constant @type Number */
18525 /** Key constant @type Number */
18527 /** Key constant @type Number */
18529 /** Key constant @type Number */
18531 /** Key constant @type Number */
18533 /** Key constant @type Number */
18535 /** Key constant @type Number */
18537 /** Key constant @type Number */
18539 /** Key constant @type Number */
18541 /** Key constant @type Number */
18543 /** Key constant @type Number */
18545 /** Key constant @type Number */
18547 /** Key constant @type Number */
18549 /** Key constant @type Number */
18551 /** Key constant @type Number */
18553 /** Key constant @type Number */
18555 /** Key constant @type Number */
18557 /** Key constant @type Number */
18559 /** Key constant @type Number */
18561 /** Key constant @type Number */
18563 /** Key constant @type Number */
18565 /** Key constant @type Number */
18567 /** Key constant @type Number */
18569 /** Key constant @type Number */
18571 /** Key constant @type Number */
18573 /** Key constant @type Number */
18575 /** Key constant @type Number */
18577 /** Key constant @type Number */
18579 /** Key constant @type Number */
18581 /** Key constant @type Number */
18583 /** Key constant @type Number */
18585 /** Key constant @type Number */
18587 /** Key constant @type Number */
18589 /** Key constant @type Number */
18591 /** Key constant @type Number */
18593 /** Key constant @type Number */
18595 /** Key constant @type Number */
18597 /** Key constant @type Number */
18599 /** Key constant @type Number */
18601 /** Key constant @type Number */
18603 /** Key constant @type Number */
18605 /** Key constant @type Number */
18607 /** Key constant @type Number */
18609 /** Key constant @type Number */
18611 /** Key constant @type Number */
18613 /** Key constant @type Number */
18615 /** Key constant @type Number */
18617 /** Key constant @type Number */
18619 /** Key constant @type Number */
18621 /** Key constant @type Number */
18623 /** Key constant @type Number */
18625 /** Key constant @type Number */
18627 /** Key constant @type Number */
18629 /** Key constant @type Number */
18631 /** Key constant @type Number */
18633 /** Key constant @type Number */
18635 /** Key constant @type Number */
18637 /** Key constant @type Number */
18639 /** Key constant @type Number */
18641 /** Key constant @type Number */
18643 /** Key constant @type Number */
18645 /** Key constant @type Number */
18647 /** Key constant @type Number */
18649 /** Key constant @type Number */
18651 /** Key constant @type Number */
18653 /** Key constant @type Number */
18655 /** Key constant @type Number */
18657 /** Key constant @type Number */
18659 /** Key constant @type Number */
18661 /** Key constant @type Number */
18663 /** Key constant @type Number */
18665 /** Key constant @type Number */
18667 /** Key constant @type Number */
18669 /** Key constant @type Number */
18671 /** Key constant @type Number */
18673 /** Key constant @type Number */
18675 /** Key constant @type Number */
18677 /** Key constant @type Number */
18679 /** Key constant @type Number */
18681 /** Key constant @type Number */
18683 /** Key constant @type Number */
18685 /** Key constant @type Number */
18687 /** Key constant @type Number */
18689 /** Key constant @type Number */
18692 * The mouse wheel delta scaling factor. This value depends on browser version and OS and
18693 * attempts to produce a similar scrolling experience across all platforms and browsers.
18695 * To change this value:
18697 * Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
18702 WHEEL_SCALE: (function () {
18706 // Firefox uses 3 on all platforms
18708 } else if (Ext.isMac) {
18709 // Continuous scrolling devices have momentum and produce much more scroll than
18710 // discrete devices on the same OS and browser. To make things exciting, Safari
18711 // (and not Chrome) changed from small values to 120 (like IE).
18713 if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
18714 // Safari changed the scrolling factor to match IE (for details see
18715 // https://bugs.webkit.org/show_bug.cgi?id=24368). The WebKit version where this
18716 // change was introduced was 532.0
18717 // Detailed discussion:
18718 // https://bugs.webkit.org/show_bug.cgi?id=29601
18719 // http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
18722 // MS optical wheel mouse produces multiples of 12 which is close enough
18723 // to help tame the speed of the continuous mice...
18727 // Momentum scrolling produces very fast scrolling, so increase the scale factor
18728 // to help produce similar results cross platform. This could be even larger and
18729 // it would help those mice, but other mice would become almost unusable as a
18730 // result (since we cannot tell which device type is in use).
18733 // IE, Opera and other Windows browsers use 120.
18741 * Simple click regex
18744 clickRe: /(dbl)?click/,
18745 // safari keypress events for special keys return bad keycodes
18749 63235: 39, // right
18752 63276: 33, // page up
18753 63277: 34, // page down
18754 63272: 46, // delete
18758 // normalize button clicks, don't see any way to feature detect this.
18759 btnMap: Ext.isIE ? {
18769 constructor: function(event, freezeEvent){
18771 this.setEvent(event.browserEvent || event, freezeEvent);
18775 setEvent: function(event, freezeEvent){
18776 var me = this, button, options;
18778 if (event == me || (event && event.browserEvent)) { // already wrapped
18781 me.browserEvent = event;
18783 // normalize buttons
18784 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
18785 if (me.clickRe.test(event.type) && button == -1) {
18791 shiftKey: event.shiftKey,
18792 // mac metaKey behaves like ctrlKey
18793 ctrlKey: event.ctrlKey || event.metaKey || false,
18794 altKey: event.altKey,
18795 // in getKey these will be normalized for the mac
18796 keyCode: event.keyCode,
18797 charCode: event.charCode,
18798 // cache the targets for the delayed and or buffered events
18799 target: Ext.EventManager.getTarget(event),
18800 relatedTarget: Ext.EventManager.getRelatedTarget(event),
18801 currentTarget: event.currentTarget,
18802 xy: (freezeEvent ? me.getXY() : null)
18816 Ext.apply(me, options);
18821 * Stop the event (preventDefault and stopPropagation)
18823 stopEvent: function(){
18824 this.stopPropagation();
18825 this.preventDefault();
18829 * Prevents the browsers default handling of the event.
18831 preventDefault: function(){
18832 if (this.browserEvent) {
18833 Ext.EventManager.preventDefault(this.browserEvent);
18838 * Cancels bubbling of the event.
18840 stopPropagation: function(){
18841 var browserEvent = this.browserEvent;
18843 if (browserEvent) {
18844 if (browserEvent.type == 'mousedown') {
18845 Ext.EventManager.stoppedMouseDownEvent.fire(this);
18847 Ext.EventManager.stopPropagation(browserEvent);
18852 * Gets the character code for the event.
18855 getCharCode: function(){
18856 return this.charCode || this.keyCode;
18860 * Returns a normalized keyCode for the event.
18861 * @return {Number} The key code
18863 getKey: function(){
18864 return this.normalizeKey(this.keyCode || this.charCode);
18868 * Normalize key codes across browsers
18870 * @param {Number} key The key code
18871 * @return {Number} The normalized code
18873 normalizeKey: function(key){
18874 // can't feature detect this
18875 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
18879 * Gets the x coordinate of the event.
18881 * @deprecated 4.0 Replaced by {@link #getX}
18883 getPageX: function(){
18884 return this.getX();
18888 * Gets the y coordinate of the event.
18890 * @deprecated 4.0 Replaced by {@link #getY}
18892 getPageY: function(){
18893 return this.getY();
18897 * Gets the x coordinate of the event.
18901 return this.getXY()[0];
18905 * Gets the y coordinate of the event.
18909 return this.getXY()[1];
18913 * Gets the page coordinates of the event.
18914 * @return {Number[]} The xy values like [x, y]
18916 getXY: function() {
18919 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
18925 * Gets the target for the event.
18926 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18927 * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18928 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
18929 * @return {HTMLElement}
18931 getTarget : function(selector, maxDepth, returnEl){
18933 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
18935 return returnEl ? Ext.get(this.target) : this.target;
18939 * Gets the related target.
18940 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18941 * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18942 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
18943 * @return {HTMLElement}
18945 getRelatedTarget : function(selector, maxDepth, returnEl){
18947 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
18949 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
18953 * Correctly scales a given wheel delta.
18954 * @param {Number} delta The delta value.
18956 correctWheelDelta : function (delta) {
18957 var scale = this.WHEEL_SCALE,
18958 ret = Math.round(delta / scale);
18960 if (!ret && delta) {
18961 ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
18968 * Returns the mouse wheel deltas for this event.
18969 * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
18971 getWheelDeltas : function () {
18973 event = me.browserEvent,
18974 dx = 0, dy = 0; // the deltas
18976 if (Ext.isDefined(event.wheelDeltaX)) { // WebKit has both dimensions
18977 dx = event.wheelDeltaX;
18978 dy = event.wheelDeltaY;
18979 } else if (event.wheelDelta) { // old WebKit and IE
18980 dy = event.wheelDelta;
18981 } else if (event.detail) { // Gecko
18982 dy = -event.detail; // gecko is backwards
18984 // Gecko sometimes returns really big values if the user changes settings to
18985 // scroll a whole page per scroll
18988 } else if (dy < -100) {
18992 // Firefox 3.1 adds an axis field to the event to indicate direction of
18993 // scroll. See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
18994 if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
19001 x: me.correctWheelDelta(dx),
19002 y: me.correctWheelDelta(dy)
19007 * Normalizes mouse wheel y-delta across browsers. To get x-delta information, use
19008 * {@link #getWheelDeltas} instead.
19009 * @return {Number} The mouse wheel y-delta
19011 getWheelDelta : function(){
19012 var deltas = this.getWheelDeltas();
19018 * 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.
19019 * Example usage:<pre><code>
19020 // Handle click on any child of an element
19021 Ext.getBody().on('click', function(e){
19022 if(e.within('some-el')){
19023 alert('Clicked on a child of some-el!');
19027 // Handle click directly on an element, ignoring clicks on child nodes
19028 Ext.getBody().on('click', function(e,t){
19029 if((t.id == 'some-el') && !e.within(t, true)){
19030 alert('Clicked directly on some-el!');
19034 * @param {String/HTMLElement/Ext.Element} el The id, DOM element or Ext.Element to check
19035 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
19036 * @param {Boolean} allowEl (optional) true to also check if the passed element is the target or related target
19037 * @return {Boolean}
19039 within : function(el, related, allowEl){
19041 var t = related ? this.getRelatedTarget() : this.getTarget(),
19045 result = Ext.fly(el).contains(t);
19046 if (!result && allowEl) {
19047 result = t == Ext.getDom(el);
19056 * Checks if the key pressed was a "navigation" key
19057 * @return {Boolean} True if the press is a navigation keypress
19059 isNavKeyPress : function(){
19061 k = this.normalizeKey(me.keyCode);
19063 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
19070 * Checks if the key pressed was a "special" key
19071 * @return {Boolean} True if the press is a special keypress
19073 isSpecialKey : function(){
19074 var k = this.normalizeKey(this.keyCode);
19075 return (this.type == 'keypress' && this.ctrlKey) ||
19076 this.isNavKeyPress() ||
19077 (k == this.BACKSPACE) || // Backspace
19078 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
19079 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
19083 * Returns a point object that consists of the object coordinates.
19084 * @return {Ext.util.Point} point
19086 getPoint : function(){
19087 var xy = this.getXY();
19088 return Ext.create('Ext.util.Point', xy[0], xy[1]);
19092 * Returns true if the control, meta, shift or alt key was pressed during this event.
19093 * @return {Boolean}
19095 hasModifier : function(){
19096 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
19100 * Injects a DOM event using the data in this object and (optionally) a new target.
19101 * This is a low-level technique and not likely to be used by application code. The
19102 * currently supported event types are:
19103 * <p><b>HTMLEvents</b></p>
19114 * <p><b>MouseEvents</b></p>
19117 * <li>dblclick</li>
19118 * <li>mousedown</li>
19120 * <li>mouseover</li>
19121 * <li>mousemove</li>
19122 * <li>mouseout</li>
19124 * <p><b>UIEvents</b></p>
19127 * <li>focusout</li>
19128 * <li>activate</li>
19132 * @param {Ext.Element/HTMLElement} target (optional) If specified, the target for the event. This
19133 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
19134 * is used to determine the target.
19136 injectEvent: function () {
19138 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
19140 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
19142 // IE9 has createEvent, but this code causes major problems with htmleditor (it
19143 // blocks all mouse events and maybe more). TODO
19145 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
19147 createHtmlEvent: function (doc, type, bubbles, cancelable) {
19148 var event = doc.createEvent('HTMLEvents');
19150 event.initEvent(type, bubbles, cancelable);
19154 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
19155 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
19156 button, relatedTarget) {
19157 var event = doc.createEvent('MouseEvents'),
19158 view = doc.defaultView || window;
19160 if (event.initMouseEvent) {
19161 event.initMouseEvent(type, bubbles, cancelable, view, detail,
19162 clientX, clientY, clientX, clientY, ctrlKey, altKey,
19163 shiftKey, metaKey, button, relatedTarget);
19164 } else { // old Safari
19165 event = doc.createEvent('UIEvents');
19166 event.initEvent(type, bubbles, cancelable);
19168 event.detail = detail;
19169 event.screenX = clientX;
19170 event.screenY = clientY;
19171 event.clientX = clientX;
19172 event.clientY = clientY;
19173 event.ctrlKey = ctrlKey;
19174 event.altKey = altKey;
19175 event.metaKey = metaKey;
19176 event.shiftKey = shiftKey;
19177 event.button = button;
19178 event.relatedTarget = relatedTarget;
19184 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
19185 var event = doc.createEvent('UIEvents'),
19186 view = doc.defaultView || window;
19188 event.initUIEvent(type, bubbles, cancelable, view, detail);
19192 fireEvent: function (target, type, event) {
19193 target.dispatchEvent(event);
19196 fixTarget: function (target) {
19197 // Safari3 doesn't have window.dispatchEvent()
19198 if (target == window && !target.dispatchEvent) {
19205 } else if (document.createEventObject) { // else if (IE)
19206 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
19209 createHtmlEvent: function (doc, type, bubbles, cancelable) {
19210 var event = doc.createEventObject();
19211 event.bubbles = bubbles;
19212 event.cancelable = cancelable;
19216 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
19217 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
19218 button, relatedTarget) {
19219 var event = doc.createEventObject();
19220 event.bubbles = bubbles;
19221 event.cancelable = cancelable;
19222 event.detail = detail;
19223 event.screenX = clientX;
19224 event.screenY = clientY;
19225 event.clientX = clientX;
19226 event.clientY = clientY;
19227 event.ctrlKey = ctrlKey;
19228 event.altKey = altKey;
19229 event.shiftKey = shiftKey;
19230 event.metaKey = metaKey;
19231 event.button = crazyIEButtons[button] || button;
19232 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
19236 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
19237 var event = doc.createEventObject();
19238 event.bubbles = bubbles;
19239 event.cancelable = cancelable;
19243 fireEvent: function (target, type, event) {
19244 target.fireEvent('on' + type, event);
19247 fixTarget: function (target) {
19248 if (target == document) {
19249 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
19250 // IE6,IE7 cannot properly call document.fireEvent()
19251 return document.documentElement;
19263 load: [false, false],
19264 unload: [false, false],
19265 select: [true, false],
19266 change: [true, false],
19267 submit: [true, true],
19268 reset: [true, false],
19269 resize: [true, false],
19270 scroll: [true, false]
19272 function (name, value) {
19273 var bubbles = value[0], cancelable = value[1];
19274 dispatchers[name] = function (targetEl, srcEvent) {
19275 var e = API.createHtmlEvent(name, bubbles, cancelable);
19276 API.fireEvent(targetEl, name, e);
19283 function createMouseEventDispatcher (type, detail) {
19284 var cancelable = (type != 'mousemove');
19285 return function (targetEl, srcEvent) {
19286 var xy = srcEvent.getXY(),
19287 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
19288 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
19289 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
19290 srcEvent.relatedTarget);
19291 API.fireEvent(targetEl, type, e);
19295 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
19296 function (eventName) {
19297 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
19304 focusin: [true, false],
19305 focusout: [true, false],
19306 activate: [true, true],
19307 focus: [false, false],
19308 blur: [false, false]
19310 function (name, value) {
19311 var bubbles = value[0], cancelable = value[1];
19312 dispatchers[name] = function (targetEl, srcEvent) {
19313 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
19314 API.fireEvent(targetEl, name, e);
19320 // not even sure what ancient browsers fall into this category...
19322 dispatchers = {}; // never mind all those we just built :P
19325 fixTarget: function (t) {
19331 function cannotInject (target, srcEvent) {
19332 // TODO log something
19335 return function (target) {
19337 dispatcher = dispatchers[me.type] || cannotInject,
19338 t = target ? (target.dom || target) : me.getTarget();
19340 t = API.fixTarget(t);
19343 }() // call to produce method
19347 Ext.EventObject = new Ext.EventObjectImpl();
19353 * @class Ext.Element
19356 var doc = document,
19357 activeElement = null,
19358 isCSS1 = doc.compatMode == "CSS1Compat",
19359 ELEMENT = Ext.Element,
19360 fly = function(el){
19362 _fly = new Ext.Element.Flyweight();
19368 // If the browser does not support document.activeElement we need some assistance.
19369 // This covers old Safari 3.2 (4.0 added activeElement along with just about all
19370 // other browsers). We need this support to handle issues with old Safari.
19371 if (!('activeElement' in doc) && doc.addEventListener) {
19372 doc.addEventListener('focus',
19374 if (ev && ev.target) {
19375 activeElement = (ev.target == doc) ? null : ev.target;
19381 * Helper function to create the function that will restore the selection.
19383 function makeSelectionRestoreFn (activeEl, start, end) {
19384 return function () {
19385 activeEl.selectionStart = start;
19386 activeEl.selectionEnd = end;
19390 Ext.apply(ELEMENT, {
19391 isAncestor : function(p, c) {
19398 return p.contains(c);
19399 } else if (p.compareDocumentPosition) {
19400 return !!(p.compareDocumentPosition(c) & 16);
19402 while ((c = c.parentNode)) {
19403 ret = c == p || ret;
19411 * Returns the active element in the DOM. If the browser supports activeElement
19412 * on the document, this is returned. If not, the focus is tracked and the active
19413 * element is maintained internally.
19414 * @return {HTMLElement} The active (focused) element in the document.
19416 getActiveElement: function () {
19417 return doc.activeElement || activeElement;
19421 * Creates a function to call to clean up problems with the work-around for the
19422 * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
19423 * the element before calling getComputedStyle and then to restore its original
19424 * display value. The problem with this is that it corrupts the selection of an
19425 * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
19426 * To cleanup after this, we need to capture the selection of any such element and
19427 * then restore it after we have restored the display style.
19429 * @param target {Element} The top-most element being adjusted.
19432 getRightMarginFixCleaner: function (target) {
19433 var supports = Ext.supports,
19434 hasInputBug = supports.DisplayChangeInputSelectionBug,
19435 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
19437 if (hasInputBug || hasTextAreaBug) {
19438 var activeEl = doc.activeElement || activeElement, // save a call
19439 tag = activeEl && activeEl.tagName,
19443 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
19444 (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
19445 if (ELEMENT.isAncestor(target, activeEl)) {
19446 start = activeEl.selectionStart;
19447 end = activeEl.selectionEnd;
19449 if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
19450 // We don't create the raw closure here inline because that
19451 // will be costly even if we don't want to return it (nested
19452 // function decls and exprs are often instantiated on entry
19453 // regardless of whether execution ever reaches them):
19454 return makeSelectionRestoreFn(activeEl, start, end);
19460 return Ext.emptyFn; // avoid special cases, just return a nop
19463 getViewWidth : function(full) {
19464 return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
19467 getViewHeight : function(full) {
19468 return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
19471 getDocumentHeight: function() {
19472 return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
19475 getDocumentWidth: function() {
19476 return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
19479 getViewportHeight: function(){
19481 (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
19485 getViewportWidth : function() {
19486 return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
19487 Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
19490 getY : function(el) {
19491 return ELEMENT.getXY(el)[1];
19494 getX : function(el) {
19495 return ELEMENT.getXY(el)[0];
19498 getOffsetParent: function (el) {
19499 el = Ext.getDom(el);
19501 // accessing offsetParent can throw "Unspecified Error" in IE6-8 (not 9)
19502 return el.offsetParent;
19504 var body = document.body; // safe bet, unless...
19505 return (el == body) ? null : body;
19509 getXY : function(el) {
19520 bd = (doc.body || doc.documentElement),
19523 el = Ext.getDom(el);
19526 hasAbsolute = fly(el).isStyle("position", "absolute");
19528 if (el.getBoundingClientRect) {
19530 b = el.getBoundingClientRect();
19531 scroll = fly(document).getScroll();
19532 ret = [ Math.round(b.left + scroll.left), Math.round(b.top + scroll.top) ];
19534 // IE6-8 can also throw from getBoundingClientRect...
19539 for (p = el; p; p = ELEMENT.getOffsetParent(p)) {
19544 hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
19547 y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
19548 x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
19550 if (p != el && !pe.isStyle('overflow','visible')) {
19557 if (Ext.isSafari && hasAbsolute) {
19558 x -= bd.offsetLeft;
19562 if (Ext.isGecko && !hasAbsolute) {
19564 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
19565 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
19569 while (p && p != bd) {
19570 if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
19579 return ret || [0,0];
19582 setXY : function(el, xy) {
19583 (el = Ext.fly(el, '_setXY')).position();
19585 var pts = el.translatePoints(xy),
19586 style = el.dom.style,
19590 if (!isNaN(pts[pos])) {
19591 style[pos] = pts[pos] + "px";
19596 setX : function(el, x) {
19597 ELEMENT.setXY(el, [x, false]);
19600 setY : function(el, y) {
19601 ELEMENT.setXY(el, [false, y]);
19605 * Serializes a DOM form into a url encoded string
19606 * @param {Object} form The form
19607 * @return {String} The url encoded form
19609 serializeForm: function(form) {
19610 var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
19612 encoder = encodeURIComponent,
19618 Ext.each(fElements, function(element){
19619 name = element.name;
19620 type = element.type;
19622 if (!element.disabled && name) {
19623 if (/select-(one|multiple)/i.test(type)) {
19624 Ext.each(element.options, function(opt){
19625 if (opt.selected) {
19626 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
19627 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
19630 } else if (!(/file|undefined|reset|button/i.test(type))) {
19631 if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
19632 data += encoder(name) + '=' + encoder(element.value) + '&';
19633 hasSubmit = /submit/i.test(type);
19638 return data.substr(0, data.length - 1);
19644 * @class Ext.Element
19647 Ext.Element.addMethods((function(){
19648 var focusRe = /button|input|textarea|select|object/;
19651 * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
19652 * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
19653 * back in, the function is not called.
19654 * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
19655 * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
19656 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
19657 * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:<pre><code>
19658 // Hide the menu if the mouse moves out for 250ms or more
19659 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
19662 // Remove mouseleave monitor on menu destroy
19663 this.menuEl.un(this.mouseLeaveMonitor);
19666 monitorMouseLeave: function(delay, handler, scope) {
19670 mouseleave: function(e) {
19671 timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
19673 mouseenter: function() {
19674 clearTimeout(timer);
19684 * Stops the specified event(s) from bubbling and optionally prevents the default action
19685 * @param {String/String[]} eventName an event / array of events to stop from bubbling
19686 * @param {Boolean} preventDefault (optional) true to prevent the default action too
19687 * @return {Ext.Element} this
19689 swallowEvent : function(eventName, preventDefault) {
19692 e.stopPropagation();
19693 if (preventDefault) {
19694 e.preventDefault();
19698 if (Ext.isArray(eventName)) {
19699 Ext.each(eventName, function(e) {
19704 me.on(eventName, fn);
19709 * Create an event handler on this element such that when the event fires and is handled by this element,
19710 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
19711 * @param {String} eventName The type of event to relay
19712 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
19713 * for firing the relayed event
19715 relayEvent : function(eventName, observable) {
19716 this.on(eventName, function(e) {
19717 observable.fireEvent(eventName, e);
19722 * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
19723 * @param {Boolean} forceReclean (optional) By default the element
19724 * keeps track if it has been cleaned already so
19725 * you can call this over and over. However, if you update the element and
19726 * need to force a reclean, you can pass true.
19728 clean : function(forceReclean) {
19731 n = dom.firstChild,
19735 if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) {
19740 nx = n.nextSibling;
19741 if (n.nodeType == 3) {
19742 // Remove empty/whitespace text nodes
19743 if (!(/\S/.test(n.nodeValue))) {
19744 dom.removeChild(n);
19745 // Combine adjacent text nodes
19746 } else if (nx && nx.nodeType == 3) {
19747 n.appendData(Ext.String.trim(nx.data));
19748 dom.removeChild(nx);
19749 nx = n.nextSibling;
19750 n.nodeIndex = ++ni;
19753 // Recursively clean
19754 Ext.fly(n).clean();
19755 n.nodeIndex = ++ni;
19760 Ext.Element.data(dom, 'isCleaned', true);
19765 * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
19766 * parameter as {@link Ext.ElementLoader#load}
19767 * @return {Ext.Element} this
19769 load : function(options) {
19770 this.getLoader().load(options);
19775 * Gets this element's {@link Ext.ElementLoader ElementLoader}
19776 * @return {Ext.ElementLoader} The loader
19778 getLoader : function() {
19779 var dom = this.dom,
19780 data = Ext.Element.data,
19781 loader = data(dom, 'loader');
19784 loader = Ext.create('Ext.ElementLoader', {
19787 data(dom, 'loader', loader);
19793 * Update the innerHTML of this element, optionally searching for and processing scripts
19794 * @param {String} html The new HTML
19795 * @param {Boolean} [loadScripts=false] True to look for and process scripts
19796 * @param {Function} [callback] For async script loading you can be notified when the update completes
19797 * @return {Ext.Element} this
19799 update : function(html, loadScripts, callback) {
19811 if (loadScripts !== true) {
19812 dom.innerHTML = html;
19813 Ext.callback(callback, me);
19818 html += '<span id="' + id + '"></span>';
19820 interval = setInterval(function(){
19821 if (!document.getElementById(id)) {
19824 clearInterval(interval);
19825 var DOC = document,
19826 hd = DOC.getElementsByTagName("head")[0],
19827 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
19828 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
19829 typeRe = /\stype=([\'\"])(.*?)\1/i,
19837 while ((match = re.exec(html))) {
19839 srcMatch = attrs ? attrs.match(srcRe) : false;
19840 if (srcMatch && srcMatch[2]) {
19841 s = DOC.createElement("script");
19842 s.src = srcMatch[2];
19843 typeMatch = attrs.match(typeRe);
19844 if (typeMatch && typeMatch[2]) {
19845 s.type = typeMatch[2];
19848 } else if (match[2] && match[2].length > 0) {
19849 if (window.execScript) {
19850 window.execScript(match[2]);
19852 window.eval(match[2]);
19857 el = DOC.getElementById(id);
19859 Ext.removeNode(el);
19861 Ext.callback(callback, me);
19863 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
19867 // inherit docs, overridden so we can add removeAnchor
19868 removeAllListeners : function() {
19869 this.removeAnchor();
19870 Ext.EventManager.removeAll(this.dom);
19875 * Gets the parent node of the current element taking into account Ext.scopeResetCSS
19877 * @return {HTMLElement} The parent element
19879 getScopeParent: function(){
19880 var parent = this.dom.parentNode;
19881 return Ext.scopeResetCSS ? parent.parentNode : parent;
19885 * Creates a proxy element of this element
19886 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
19887 * @param {String/HTMLElement} [renderTo] The element or element id to render the proxy to (defaults to document.body)
19888 * @param {Boolean} [matchBox=false] True to align and size the proxy to this element now.
19889 * @return {Ext.Element} The new proxy element
19891 createProxy : function(config, renderTo, matchBox) {
19892 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
19895 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
19896 Ext.DomHelper.insertBefore(me.dom, config, true);
19898 proxy.setVisibilityMode(Ext.Element.DISPLAY);
19900 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
19901 proxy.setBox(me.getBox());
19907 * Checks whether this element can be focused.
19908 * @return {Boolean} True if the element is focusable
19910 focusable: function(){
19911 var dom = this.dom,
19912 nodeName = dom.nodeName.toLowerCase(),
19914 hasTabIndex = !isNaN(dom.tabIndex);
19916 if (!dom.disabled) {
19917 if (focusRe.test(nodeName)) {
19920 canFocus = nodeName == 'a' ? dom.href || hasTabIndex : hasTabIndex;
19923 return canFocus && this.isVisible(true);
19927 Ext.Element.prototype.clearListeners = Ext.Element.prototype.removeAllListeners;
19930 * @class Ext.Element
19932 Ext.Element.addMethods({
19934 * Gets the x,y coordinates specified by the anchor position on the element.
19935 * @param {String} [anchor='c'] The specified anchor position. See {@link #alignTo}
19936 * for details on supported anchor positions.
19937 * @param {Boolean} [local] True to get the local (element top/left-relative) anchor position instead
19938 * of page coordinates
19939 * @param {Object} [size] An object containing the size to use for calculating anchor position
19940 * {width: (target width), height: (target height)} (defaults to the element's current size)
19941 * @return {Number[]} [x, y] An array containing the element's x and y coordinates
19943 getAnchorXY : function(anchor, local, s){
19944 //Passing a different size is useful for pre-calculating anchors,
19945 //especially for anchored animations that change the el size.
19946 anchor = (anchor || "tl").toLowerCase();
19950 vp = me.dom == document.body || me.dom == document,
19951 w = s.width || vp ? Ext.Element.getViewWidth() : me.getWidth(),
19952 h = s.height || vp ? Ext.Element.getViewHeight() : me.getHeight(),
19956 scroll = me.getScroll(),
19957 extraX = vp ? scroll.left : !local ? o[0] : 0,
19958 extraY = vp ? scroll.top : !local ? o[1] : 0,
19960 c : [r(w * 0.5), r(h * 0.5)],
19961 t : [r(w * 0.5), 0],
19962 l : [0, r(h * 0.5)],
19963 r : [w, r(h * 0.5)],
19964 b : [r(w * 0.5), h],
19972 return [xy[0] + extraX, xy[1] + extraY];
19976 * Anchors an element to another element and realigns it when the window is resized.
19977 * @param {String/HTMLElement/Ext.Element} element The element to align to.
19978 * @param {String} position The position to align to.
19979 * @param {Number[]} [offsets] Offset the positioning by [x, y]
19980 * @param {Boolean/Object} [animate] True for the default animation or a standard Element animation config object
19981 * @param {Boolean/Number} [monitorScroll] True to monitor body scroll and reposition. If this parameter
19982 * is a number, it is used as the buffer delay (defaults to 50ms).
19983 * @param {Function} [callback] The function to call after the animation finishes
19984 * @return {Ext.Element} this
19986 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
19989 scroll = !Ext.isEmpty(monitorScroll),
19990 action = function(){
19991 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
19992 Ext.callback(callback, Ext.fly(dom));
19994 anchor = this.getAnchor();
19996 // previous listener anchor, remove it
19997 this.removeAnchor();
19998 Ext.apply(anchor, {
20003 Ext.EventManager.onWindowResize(action, null);
20006 Ext.EventManager.on(window, 'scroll', action, null,
20007 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
20009 action.call(me); // align immediately
20014 * Remove any anchor to this element. See {@link #anchorTo}.
20015 * @return {Ext.Element} this
20017 removeAnchor : function(){
20019 anchor = this.getAnchor();
20021 if(anchor && anchor.fn){
20022 Ext.EventManager.removeResizeListener(anchor.fn);
20024 Ext.EventManager.un(window, 'scroll', anchor.fn);
20032 getAnchor : function(){
20033 var data = Ext.Element.data,
20038 var anchor = data(dom, '_anchor');
20041 anchor = data(dom, '_anchor', {});
20046 getAlignVector: function(el, spec, offset) {
20048 side = {t:"top", l:"left", r:"right", b: "bottom"},
20049 thisRegion = me.getRegion(),
20053 if(!el || !el.dom){
20055 sourceClass: 'Ext.Element',
20056 sourceMethod: 'getAlignVector',
20057 msg: 'Attempted to align an element that doesn\'t exist'
20061 elRegion = el.getRegion();
20065 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
20066 * supported position values.
20067 * @param {String/HTMLElement/Ext.Element} element The element to align to.
20068 * @param {String} [position="tl-bl?"] The position to align to (defaults to )
20069 * @param {Number[]} [offsets] Offset the positioning by [x, y]
20070 * @return {Number[]} [x, y]
20072 getAlignToXY : function(el, p, o){
20075 if(!el || !el.dom){
20077 sourceClass: 'Ext.Element',
20078 sourceMethod: 'getAlignToXY',
20079 msg: 'Attempted to align an element that doesn\'t exist'
20084 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
20092 //constrain the aligned el to viewport if necessary
20096 dw = Ext.Element.getViewWidth() -10, // 10px of margin for ie
20097 dh = Ext.Element.getViewHeight()-10, // 10px of margin for ie
20105 docElement = doc.documentElement,
20106 docBody = doc.body,
20107 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
20108 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
20109 c = false, //constrain to viewport
20112 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
20116 sourceClass: 'Ext.Element',
20117 sourceMethod: 'getAlignToXY',
20121 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
20129 //Subtract the aligned el's internal xy from the target's offset xy
20130 //plus custom offset to get the aligned el's new offset xy
20131 a1 = me.getAnchorXY(p1, true);
20132 a2 = el.getAnchorXY(p2, false);
20134 x = a2[0] - a1[0] + o[0];
20135 y = a2[1] - a1[1] + o[1];
20139 h = me.getHeight();
20140 r = el.getRegion();
20141 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
20142 //perpendicular to the vp border, allow the aligned el to slide on that border,
20143 //otherwise swap the aligned el to the opposite border of the target.
20144 p1y = p1.charAt(0);
20145 p1x = p1.charAt(p1.length-1);
20146 p2y = p2.charAt(0);
20147 p2x = p2.charAt(p2.length-1);
20148 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
20149 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
20152 if (x + w > dw + scrollX) {
20153 x = swapX ? r.left-w : dw+scrollX-w;
20156 x = swapX ? r.right : scrollX;
20158 if (y + h > dh + scrollY) {
20159 y = swapY ? r.top-h : dh+scrollY-h;
20162 y = swapY ? r.bottom : scrollY;
20169 * Aligns this element with another element relative to the specified anchor points. If the other element is the
20170 * document it aligns it to the viewport.
20171 * The position parameter is optional, and can be specified in any one of the following formats:
20173 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
20174 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
20175 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
20176 * deprecated in favor of the newer two anchor syntax below</i>.</li>
20177 * <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
20178 * element's anchor point, and the second value is used as the target's anchor point.</li>
20180 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
20181 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
20182 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
20183 * that specified in order to enforce the viewport constraints.
20184 * Following are all of the supported anchor positions:
20187 ----- -----------------------------
20188 tl The top left corner (default)
20189 t The center of the top edge
20190 tr The top right corner
20191 l The center of the left edge
20192 c In the center of the element
20193 r The center of the right edge
20194 bl The bottom left corner
20195 b The center of the bottom edge
20196 br The bottom right corner
20200 // align el to other-el using the default positioning ("tl-bl", non-constrained)
20201 el.alignTo("other-el");
20203 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
20204 el.alignTo("other-el", "tr?");
20206 // align the bottom right corner of el with the center left edge of other-el
20207 el.alignTo("other-el", "br-l?");
20209 // align the center of el with the bottom left corner of other-el and
20210 // adjust the x position by -6 pixels (and the y position by 0)
20211 el.alignTo("other-el", "c-bl", [-6, 0]);
20213 * @param {String/HTMLElement/Ext.Element} element The element to align to.
20214 * @param {String} [position="tl-bl?"] The position to align to
20215 * @param {Number[]} [offsets] Offset the positioning by [x, y]
20216 * @param {Boolean/Object} [animate] true for the default animation or a standard Element animation config object
20217 * @return {Ext.Element} this
20219 alignTo : function(element, position, offsets, animate){
20221 return me.setXY(me.getAlignToXY(element, position, offsets),
20222 me.anim && !!animate ? me.anim(animate) : false);
20225 // private ==> used outside of core
20226 adjustForConstraints : function(xy, parent) {
20227 var vector = this.getConstrainVector(parent, xy);
20229 xy[0] += vector[0];
20230 xy[1] += vector[1];
20236 * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
20237 * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
20238 * <p>Priority is given to constraining the top and left within the constraint.</p>
20239 * <p>The constraint may either be an existing element into which this element is to be constrained, or
20240 * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
20241 * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
20242 * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
20243 * of using this Element's current position;
20244 * @returns {Number[]/Boolean} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
20245 * vector by which this element must be translated. Otherwise, <code>false</code>.
20247 getConstrainVector: function(constrainTo, proposedPosition) {
20248 if (!(constrainTo instanceof Ext.util.Region)) {
20249 constrainTo = Ext.get(constrainTo).getViewRegion();
20251 var thisRegion = this.getRegion(),
20253 shadowSize = this.shadow && this.shadow.offset,
20254 overflowed = false;
20256 // Shift this region to occupy the proposed position
20257 if (proposedPosition) {
20258 thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
20261 // Reduce the constrain region to allow for shadow
20262 // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
20264 constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
20267 // Constrain the X coordinate by however much this Element overflows
20268 if (thisRegion.right > constrainTo.right) {
20270 vector[0] = (constrainTo.right - thisRegion.right); // overflowed the right
20272 if (thisRegion.left + vector[0] < constrainTo.left) {
20274 vector[0] = (constrainTo.left - thisRegion.left); // overflowed the left
20277 // Constrain the Y coordinate by however much this Element overflows
20278 if (thisRegion.bottom > constrainTo.bottom) {
20280 vector[1] = (constrainTo.bottom - thisRegion.bottom); // overflowed the bottom
20282 if (thisRegion.top + vector[1] < constrainTo.top) {
20284 vector[1] = (constrainTo.top - thisRegion.top); // overflowed the top
20286 return overflowed ? vector : false;
20290 * Calculates the x, y to center this element on the screen
20291 * @return {Number[]} The x, y values [x, y]
20293 getCenterXY : function(){
20294 return this.getAlignToXY(document, 'c-c');
20298 * Centers the Element in either the viewport, or another Element.
20299 * @param {String/HTMLElement/Ext.Element} centerIn (optional) The element in which to center the element.
20301 center : function(centerIn){
20302 return this.alignTo(centerIn || document, 'c-c');
20307 * @class Ext.Element
20311 var ELEMENT = Ext.Element,
20316 POSITION = "position",
20318 RELATIVE = "relative",
20320 ZINDEX = "z-index";
20322 Ext.override(Ext.Element, {
20324 * 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).
20325 * @return {Number} The X position of the element
20328 return ELEMENT.getX(this.dom);
20332 * 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).
20333 * @return {Number} The Y position of the element
20336 return ELEMENT.getY(this.dom);
20340 * 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).
20341 * @return {Number[]} The XY position of the element
20343 getXY : function(){
20344 return ELEMENT.getXY(this.dom);
20348 * 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.
20349 * @param {String/HTMLElement/Ext.Element} element The element to get the offsets from.
20350 * @return {Number[]} The XY page offsets (e.g. [100, -200])
20352 getOffsetsTo : function(el){
20353 var o = this.getXY(),
20354 e = Ext.fly(el, '_internal').getXY();
20355 return [o[0]-e[0],o[1]-e[1]];
20359 * 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).
20360 * @param {Number} The X position of the element
20361 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
20362 * @return {Ext.Element} this
20364 setX : function(x, animate){
20365 return this.setXY([x, this.getY()], animate);
20369 * 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).
20370 * @param {Number} The Y position of the element
20371 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
20372 * @return {Ext.Element} this
20374 setY : function(y, animate){
20375 return this.setXY([this.getX(), y], animate);
20379 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
20380 * @param {String} left The left CSS property value
20381 * @return {Ext.Element} this
20383 setLeft : function(left){
20384 this.setStyle(LEFT, this.addUnits(left));
20389 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
20390 * @param {String} top The top CSS property value
20391 * @return {Ext.Element} this
20393 setTop : function(top){
20394 this.setStyle(TOP, this.addUnits(top));
20399 * Sets the element's CSS right style.
20400 * @param {String} right The right CSS property value
20401 * @return {Ext.Element} this
20403 setRight : function(right){
20404 this.setStyle(RIGHT, this.addUnits(right));
20409 * Sets the element's CSS bottom style.
20410 * @param {String} bottom The bottom CSS property value
20411 * @return {Ext.Element} this
20413 setBottom : function(bottom){
20414 this.setStyle(BOTTOM, this.addUnits(bottom));
20419 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
20420 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
20421 * @param {Number[]} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
20422 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
20423 * @return {Ext.Element} this
20425 setXY: function(pos, animate) {
20427 if (!animate || !me.anim) {
20428 ELEMENT.setXY(me.dom, pos);
20431 if (!Ext.isObject(animate)) {
20434 me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
20440 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
20441 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
20442 * @param {Number} x X value for new position (coordinates are page-based)
20443 * @param {Number} y Y value for new position (coordinates are page-based)
20444 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
20445 * @return {Ext.Element} this
20447 setLocation : function(x, y, animate){
20448 return this.setXY([x, y], animate);
20452 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
20453 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
20454 * @param {Number} x X value for new position (coordinates are page-based)
20455 * @param {Number} y Y value for new position (coordinates are page-based)
20456 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
20457 * @return {Ext.Element} this
20459 moveTo : function(x, y, animate){
20460 return this.setXY([x, y], animate);
20464 * Gets the left X coordinate
20465 * @param {Boolean} local True to get the local css position instead of page coordinate
20468 getLeft : function(local){
20469 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
20473 * Gets the right X coordinate of the element (element X position + element width)
20474 * @param {Boolean} local True to get the local css position instead of page coordinate
20477 getRight : function(local){
20479 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
20483 * Gets the top Y coordinate
20484 * @param {Boolean} local True to get the local css position instead of page coordinate
20487 getTop : function(local) {
20488 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
20492 * Gets the bottom Y coordinate of the element (element Y position + element height)
20493 * @param {Boolean} local True to get the local css position instead of page coordinate
20496 getBottom : function(local){
20498 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
20502 * Initializes positioning on this element. If a desired position is not passed, it will make the
20503 * the element positioned relative IF it is not already positioned.
20504 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
20505 * @param {Number} zIndex (optional) The zIndex to apply
20506 * @param {Number} x (optional) Set the page X position
20507 * @param {Number} y (optional) Set the page Y position
20509 position : function(pos, zIndex, x, y) {
20512 if (!pos && me.isStyle(POSITION, STATIC)){
20513 me.setStyle(POSITION, RELATIVE);
20515 me.setStyle(POSITION, pos);
20518 me.setStyle(ZINDEX, zIndex);
20521 me.setXY([x || false, y || false]);
20526 * Clear positioning back to the default when the document was loaded
20527 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
20528 * @return {Ext.Element} this
20530 clearPositioning : function(value){
20531 value = value || '';
20544 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
20545 * snapshot before performing an update and then restoring the element.
20548 getPositioning : function(){
20549 var l = this.getStyle(LEFT);
20550 var t = this.getStyle(TOP);
20552 "position" : this.getStyle(POSITION),
20554 "right" : l ? "" : this.getStyle(RIGHT),
20556 "bottom" : t ? "" : this.getStyle(BOTTOM),
20557 "z-index" : this.getStyle(ZINDEX)
20562 * Set positioning with an object returned by getPositioning().
20563 * @param {Object} posCfg
20564 * @return {Ext.Element} this
20566 setPositioning : function(pc){
20568 style = me.dom.style;
20572 if(pc.right == AUTO){
20575 if(pc.bottom == AUTO){
20583 * Translates the passed page coordinates into left/top css values for this element
20584 * @param {Number/Number[]} x The page x or an array containing [x, y]
20585 * @param {Number} y (optional) The page y, required if x is not an array
20586 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
20588 translatePoints: function(x, y) {
20589 if (Ext.isArray(x)) {
20594 relative = me.isStyle(POSITION, RELATIVE),
20596 left = parseInt(me.getStyle(LEFT), 10),
20597 top = parseInt(me.getStyle(TOP), 10);
20599 if (!Ext.isNumber(left)) {
20600 left = relative ? 0 : me.dom.offsetLeft;
20602 if (!Ext.isNumber(top)) {
20603 top = relative ? 0 : me.dom.offsetTop;
20605 left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
20606 top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
20614 * 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.
20615 * @param {Object} box The box to fill {x, y, width, height}
20616 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
20617 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20618 * @return {Ext.Element} this
20620 setBox: function(box, adjust, animate) {
20624 if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
20625 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
20626 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
20628 me.setBounds(box.x, box.y, w, h, animate);
20633 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
20634 * set another Element's size/location to match this element.
20635 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
20636 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
20637 * @return {Object} box An object in the format<pre><code>
20639 x: <Element's X position>,
20640 y: <Element's Y position>,
20641 width: <Element's width>,
20642 height: <Element's height>,
20643 bottom: <Element's lower bound>,
20644 right: <Element's rightmost bound>
20647 * The returned object may also be addressed as an Array where index 0 contains the X position
20648 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
20650 getBox: function(contentBox, local) {
20655 getBorderWidth = me.getBorderWidth,
20656 getPadding = me.getPadding,
20657 l, r, t, b, w, h, bx;
20661 left = parseInt(me.getStyle("left"), 10) || 0;
20662 top = parseInt(me.getStyle("top"), 10) || 0;
20666 h = me.getHeight();
20677 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
20678 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
20679 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
20680 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
20686 width: w - (l + r),
20687 height: h - (t + b)
20690 bx.right = bx.x + bx.width;
20691 bx.bottom = bx.y + bx.height;
20696 * Move this element relative to its current position.
20697 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
20698 * @param {Number} distance How far to move the element in pixels
20699 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20701 move: function(direction, distance, animate) {
20706 left = [x - distance, y],
20707 right = [x + distance, y],
20708 top = [x, y - distance],
20709 bottom = [x, y + distance],
20723 direction = direction.toLowerCase();
20724 me.moveTo(hash[direction][0], hash[direction][1], animate);
20728 * Quick set left and top adding default units
20729 * @param {String} left The left CSS property value
20730 * @param {String} top The top CSS property value
20731 * @return {Ext.Element} this
20733 setLeftTop: function(left, top) {
20735 style = me.dom.style;
20736 style.left = me.addUnits(left);
20737 style.top = me.addUnits(top);
20742 * Returns the region of this element.
20743 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
20744 * @return {Ext.util.Region} A Region containing "top, left, bottom, right" member data.
20746 getRegion: function() {
20747 return this.getPageBox(true);
20751 * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
20752 * @return {Ext.util.Region} A Region containing "top, left, bottom, right" member data.
20754 getViewRegion: function() {
20756 isBody = me.dom === document.body,
20757 scroll, pos, top, left, width, height;
20759 // For the body we want to do some special logic
20761 scroll = me.getScroll();
20762 left = scroll.left;
20764 width = Ext.Element.getViewportWidth();
20765 height = Ext.Element.getViewportHeight();
20769 left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
20770 top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
20771 width = me.getWidth(true);
20772 height = me.getHeight(true);
20775 return Ext.create('Ext.util.Region', top, left + width, top + height, left);
20779 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
20780 * set another Element's size/location to match this element.
20781 * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
20782 * @return {Object} box An object in the format<pre><code>
20784 x: <Element's X position>,
20785 y: <Element's Y position>,
20786 width: <Element's width>,
20787 height: <Element's height>,
20788 bottom: <Element's lower bound>,
20789 right: <Element's rightmost bound>
20792 * The returned object may also be addressed as an Array where index 0 contains the X position
20793 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
20795 getPageBox : function(getRegion) {
20798 isDoc = el === document.body,
20799 w = isDoc ? Ext.Element.getViewWidth() : el.offsetWidth,
20800 h = isDoc ? Ext.Element.getViewHeight() : el.offsetHeight,
20808 return Ext.create('Ext.util.Region', t, r, b, l);
20823 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
20824 * @param {Number} x X value for new position (coordinates are page-based)
20825 * @param {Number} y Y value for new position (coordinates are page-based)
20826 * @param {Number/String} width The new width. This may be one of:<div class="mdetail-params"><ul>
20827 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20828 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
20830 * @param {Number/String} height The new height. This may be one of:<div class="mdetail-params"><ul>
20831 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20832 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
20834 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20835 * @return {Ext.Element} this
20837 setBounds: function(x, y, width, height, animate) {
20839 if (!animate || !me.anim) {
20840 me.setSize(width, height);
20841 me.setLocation(x, y);
20843 if (!Ext.isObject(animate)) {
20846 me.animate(Ext.applyIf({
20850 width: me.adjustWidth(width),
20851 height: me.adjustHeight(height)
20859 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
20860 * @param {Ext.util.Region} region The region to fill
20861 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20862 * @return {Ext.Element} this
20864 setRegion: function(region, animate) {
20865 return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
20871 * @class Ext.Element
20873 Ext.override(Ext.Element, {
20875 * Returns true if this element is scrollable.
20876 * @return {Boolean}
20878 isScrollable : function(){
20879 var dom = this.dom;
20880 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
20884 * Returns the current scroll position of the element.
20885 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
20887 getScroll : function() {
20891 docElement = doc.documentElement,
20896 if (d == doc || d == body) {
20897 if (Ext.isIE && Ext.isStrict) {
20898 l = docElement.scrollLeft;
20899 t = docElement.scrollTop;
20901 l = window.pageXOffset;
20902 t = window.pageYOffset;
20905 left: l || (body ? body.scrollLeft : 0),
20906 top : t || (body ? body.scrollTop : 0)
20910 left: d.scrollLeft,
20919 * 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().
20920 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
20921 * @param {Number} value The new scroll value
20922 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20923 * @return {Ext.Element} this
20925 scrollTo : function(side, value, animate) {
20926 //check if we're scrolling top or left
20927 var top = /top/i.test(side),
20932 if (!animate || !me.anim) {
20933 // just setting the value, so grab the direction
20934 prop = 'scroll' + (top ? 'Top' : 'Left');
20938 if (!Ext.isObject(animate)) {
20941 obj['scroll' + (top ? 'Top' : 'Left')] = value;
20942 me.animate(Ext.applyIf({
20950 * Scrolls this element into view within the passed container.
20951 * @param {String/HTMLElement/Ext.Element} container (optional) The container element to scroll (defaults to document.body). Should be a
20952 * string (id), dom node, or Ext.Element.
20953 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
20954 * @return {Ext.Element} this
20956 scrollIntoView : function(container, hscroll) {
20957 container = Ext.getDom(container) || Ext.getBody().dom;
20959 offsets = this.getOffsetsTo(container),
20961 left = offsets[0] + container.scrollLeft,
20962 top = offsets[1] + container.scrollTop,
20963 bottom = top + el.offsetHeight,
20964 right = left + el.offsetWidth,
20966 ctClientHeight = container.clientHeight,
20967 ctScrollTop = parseInt(container.scrollTop, 10),
20968 ctScrollLeft = parseInt(container.scrollLeft, 10),
20969 ctBottom = ctScrollTop + ctClientHeight,
20970 ctRight = ctScrollLeft + container.clientWidth;
20972 if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
20973 container.scrollTop = top;
20974 } else if (bottom > ctBottom) {
20975 container.scrollTop = bottom - ctClientHeight;
20977 // corrects IE, other browsers will ignore
20978 container.scrollTop = container.scrollTop;
20980 if (hscroll !== false) {
20981 if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
20982 container.scrollLeft = left;
20984 else if (right > ctRight) {
20985 container.scrollLeft = right - container.clientWidth;
20987 container.scrollLeft = container.scrollLeft;
20993 scrollChildIntoView : function(child, hscroll) {
20994 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
20998 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
20999 * within this element's scrollable range.
21000 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
21001 * @param {Number} distance How far to scroll the element in pixels
21002 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
21003 * @return {Boolean} Returns true if a scroll was triggered or false if the element
21004 * was scrolled as far as it could go.
21006 scroll : function(direction, distance, animate) {
21007 if (!this.isScrollable()) {
21011 l = el.scrollLeft, t = el.scrollTop,
21012 w = el.scrollWidth, h = el.scrollHeight,
21013 cw = el.clientWidth, ch = el.clientHeight,
21014 scrolled = false, v,
21016 l: Math.min(l + distance, w-cw),
21017 r: v = Math.max(l - distance, 0),
21018 t: Math.max(t - distance, 0),
21019 b: Math.min(t + distance, h-ch)
21024 direction = direction.substr(0, 1);
21025 if ((v = hash[direction]) > -1) {
21027 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
21033 * @class Ext.Element
21035 Ext.Element.addMethods(
21037 var VISIBILITY = "visibility",
21038 DISPLAY = "display",
21041 XMASKED = Ext.baseCSSPrefix + "masked",
21042 XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
21043 data = Ext.Element.data;
21047 * Checks whether the element is currently visible using both visibility and display properties.
21048 * @param {Boolean} [deep=false] True to walk the dom and see if parent elements are hidden
21049 * @return {Boolean} True if the element is currently visible, else false
21051 isVisible : function(deep) {
21052 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
21053 p = this.dom.parentNode;
21055 if (deep !== true || !vis) {
21059 while (p && !(/^body/i.test(p.tagName))) {
21060 if (!Ext.fly(p, '_isVisible').isVisible()) {
21069 * Returns true if display is not "none"
21070 * @return {Boolean}
21072 isDisplayed : function() {
21073 return !this.isStyle(DISPLAY, NONE);
21077 * Convenience method for setVisibilityMode(Element.DISPLAY)
21078 * @param {String} display (optional) What to set display to when visible
21079 * @return {Ext.Element} this
21081 enableDisplayMode : function(display) {
21082 this.setVisibilityMode(Ext.Element.DISPLAY);
21084 if (!Ext.isEmpty(display)) {
21085 data(this.dom, 'originalDisplay', display);
21092 * Puts a mask over this element to disable user interaction. Requires core.css.
21093 * This method can only be applied to elements which accept child nodes.
21094 * @param {String} msg (optional) A message to display in the mask
21095 * @param {String} msgCls (optional) A css class to apply to the msg element
21096 * @return {Ext.Element} The mask element
21098 mask : function(msg, msgCls) {
21101 setExpression = dom.style.setExpression,
21102 dh = Ext.DomHelper,
21103 EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
21107 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
21108 me.addCls(XMASKEDRELATIVE);
21110 el = data(dom, 'maskMsg');
21114 el = data(dom, 'mask');
21119 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
21120 data(dom, 'mask', mask);
21122 me.addCls(XMASKED);
21123 mask.setDisplayed(true);
21125 if (typeof msg == 'string') {
21126 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
21127 data(dom, 'maskMsg', mm);
21128 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
21129 mm.dom.firstChild.innerHTML = msg;
21130 mm.setDisplayed(true);
21133 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
21134 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
21135 // In normal use cases an element will be masked for a limited period of time.
21136 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
21137 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
21138 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
21139 mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
21142 // Some versions and modes of IE subtract top+bottom padding when calculating height.
21143 // Different versions from those which make the same error for width!
21144 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
21145 mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
21147 // ie will not expand full height automatically
21148 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
21149 mask.setSize(undefined, me.getHeight());
21155 * Removes a previously applied mask.
21157 unmask : function() {
21160 mask = data(dom, 'mask'),
21161 maskMsg = data(dom, 'maskMsg');
21164 // Remove resource-intensive CSS expressions as soon as they are not required.
21165 if (mask.dom.style.clearExpression) {
21166 mask.dom.style.clearExpression('width');
21167 mask.dom.style.clearExpression('height');
21171 data(dom, 'maskMsg', undefined);
21175 data(dom, 'mask', undefined);
21176 me.removeCls([XMASKED, XMASKEDRELATIVE]);
21180 * Returns true if this element is masked. Also re-centers any displayed message within the mask.
21181 * @return {Boolean}
21183 isMasked : function() {
21185 mask = data(me.dom, 'mask'),
21186 maskMsg = data(me.dom, 'maskMsg');
21188 if (mask && mask.isVisible()) {
21190 maskMsg.center(me);
21198 * Creates an iframe shim for this element to keep selects and other windowed objects from
21200 * @return {Ext.Element} The new shim element
21202 createShim : function() {
21203 var el = document.createElement('iframe'),
21206 el.frameBorder = '0';
21207 el.className = Ext.baseCSSPrefix + 'shim';
21208 el.src = Ext.SSL_SECURE_URL;
21209 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
21210 shim.autoBoxAdjust = false;
21217 * @class Ext.Element
21219 Ext.Element.addMethods({
21221 * Convenience method for constructing a KeyMap
21222 * @param {String/Number/Number[]/Object} 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:
21223 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
21224 * @param {Function} fn The function to call
21225 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
21226 * @return {Ext.util.KeyMap} The KeyMap created
21228 addKeyListener : function(key, fn, scope){
21230 if(typeof key != 'object' || Ext.isArray(key)){
21246 return Ext.create('Ext.util.KeyMap', this, config);
21250 * Creates a KeyMap for this element
21251 * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
21252 * @return {Ext.util.KeyMap} The KeyMap created
21254 addKeyMap : function(config){
21255 return Ext.create('Ext.util.KeyMap', this, config);
21259 //Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because
21260 //Element.keys.js is the last extra Ext.Element include in the ext-all.js build
21261 Ext.CompositeElementLite.importElementMethods();
21264 * @class Ext.CompositeElementLite
21266 Ext.apply(Ext.CompositeElementLite.prototype, {
21267 addElements : function(els, root){
21271 if(typeof els == "string"){
21272 els = Ext.Element.selectorFunction(els, root);
21274 var yels = this.elements;
21275 Ext.each(els, function(e) {
21276 yels.push(Ext.get(e));
21282 * Returns the first Element
21283 * @return {Ext.Element}
21285 first : function(){
21286 return this.item(0);
21290 * Returns the last Element
21291 * @return {Ext.Element}
21294 return this.item(this.getCount()-1);
21298 * Returns true if this composite contains the passed element
21299 * @param el {String/HTMLElement/Ext.Element/Number} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
21302 contains : function(el){
21303 return this.indexOf(el) != -1;
21307 * Removes the specified element(s).
21308 * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite
21309 * or an array of any of those.
21310 * @param {Boolean} removeDom (optional) True to also remove the element from the document
21311 * @return {Ext.CompositeElement} this
21313 removeElement : function(keys, removeDom){
21315 els = this.elements,
21317 Ext.each(keys, function(val){
21318 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
21323 Ext.removeNode(el);
21326 Ext.Array.erase(els, val, 1);
21334 * @class Ext.CompositeElement
21335 * @extends Ext.CompositeElementLite
21336 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
21337 * members, or to perform collective actions upon the whole set.</p>
21338 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
21339 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
21340 * <p>All methods return <i>this</i> and can be chained.</p>
21343 var els = Ext.select("#some-el div.some-class", true);
21344 // or select directly from an existing element
21345 var el = Ext.get('some-el');
21346 el.select('div.some-class', true);
21348 els.setWidth(100); // all elements become 100 width
21349 els.hide(true); // all elements fade out and hide
21351 els.setWidth(100).hide(true);
21354 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
21356 constructor : function(els, root){
21357 this.elements = [];
21358 this.add(els, root);
21362 getElement : function(el){
21363 // In this case just return it, since we already have a reference to it
21368 transformElement : function(el){
21369 return Ext.get(el);
21374 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
21375 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
21376 * {@link Ext.CompositeElementLite CompositeElementLite} object.
21377 * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
21378 * @param {Boolean} [unique] true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
21379 * @param {HTMLElement/String} [root] The root element of the query or id of the root
21380 * @return {Ext.CompositeElementLite/Ext.CompositeElement}
21381 * @member Ext.Element
21384 Ext.Element.select = function(selector, unique, root){
21386 if(typeof selector == "string"){
21387 els = Ext.Element.selectorFunction(selector, root);
21388 }else if(selector.length !== undefined){
21392 sourceClass: "Ext.Element",
21393 sourceMethod: "select",
21394 selector: selector,
21397 msg: "Invalid selector specified: " + selector
21400 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
21404 * Shorthand of {@link Ext.Element#select}.
21407 * @alias Ext.Element#select
21409 Ext.select = Ext.Element.select;