2 Ext JS - JavaScript Library
3 Copyright (c) 2006-2011, Sencha Inc.
13 objectPrototype = Object.prototype,
14 toString = Object.prototype.toString,
16 enumerablesTest = { toString: 1 },
19 if (typeof Ext === 'undefined') {
25 for (i in enumerablesTest) {
30 enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
31 'toLocaleString', 'toString', 'constructor'];
35 * An array containing extra enumerables for old browsers
38 Ext.enumerables = enumerables;
41 * Copies all the properties of config to the specified object.
42 * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
43 * {@link Ext.Object#merge} instead.
44 * @param {Object} object The receiver of the properties
45 * @param {Object} config The source of the properties
46 * @param {Object} defaults A different object that will also be applied for default values
47 * @return {Object} returns obj
49 Ext.apply = function(object, config, defaults) {
51 Ext.apply(object, defaults);
54 if (object && config && typeof config === 'object') {
58 object[i] = config[i];
62 for (j = enumerables.length; j--;) {
64 if (config.hasOwnProperty(k)) {
65 object[k] = config[k];
74 Ext.buildSettings = Ext.apply({
77 }, Ext.buildSettings || {});
81 * A reusable empty function
83 emptyFn: function() {},
85 baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
88 * Copies all the properties of config to object if they don't already exist.
90 * @param {Object} object The receiver of the properties
91 * @param {Object} config The source of the properties
92 * @return {Object} returns obj
94 applyIf: function(object, config) {
98 for (property in config) {
99 if (object[property] === undefined) {
100 object[property] = config[property];
109 * Iterates either an array or an object. This method delegates to
110 * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
112 * @param {Object/Array} object The object or array to be iterated.
113 * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
114 * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
115 * type that is being iterated.
116 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
117 * Defaults to the object being iterated itself.
120 iterate: function(object, fn, scope) {
121 if (Ext.isEmpty(object)) {
125 if (scope === undefined) {
129 if (Ext.isIterable(object)) {
130 Ext.Array.each.call(Ext.Array, object, fn, scope);
133 Ext.Object.each.call(Ext.Object, object, fn, scope);
141 * This method deprecated. Use {@link Ext#define Ext.define} instead.
143 * @param {Function} superclass
144 * @param {Object} overrides
145 * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
146 * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
150 var objectConstructor = objectPrototype.constructor,
151 inlineOverrides = function(o) {
153 if (!o.hasOwnProperty(m)) {
160 return function(subclass, superclass, overrides) {
161 // First we check if the user passed in just the superClass with overrides
162 if (Ext.isObject(superclass)) {
163 overrides = superclass;
164 superclass = subclass;
165 subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
166 superclass.apply(this, arguments);
173 sourceMethod: 'extend',
174 msg: 'Attempting to extend from a class which has not been loaded on the page.'
178 // We create a new temporary class
179 var F = function() {},
180 subclassProto, superclassProto = superclass.prototype;
182 F.prototype = superclassProto;
183 subclassProto = subclass.prototype = new F();
184 subclassProto.constructor = subclass;
185 subclass.superclass = superclassProto;
187 if (superclassProto.constructor === objectConstructor) {
188 superclassProto.constructor = superclass;
191 subclass.override = function(overrides) {
192 Ext.override(subclass, overrides);
195 subclassProto.override = inlineOverrides;
196 subclassProto.proto = subclassProto;
198 subclass.override(overrides);
199 subclass.extend = function(o) {
200 return Ext.extend(subclass, o);
208 * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
210 Ext.define('My.cool.Class', {
216 Ext.override(My.cool.Class, {
218 alert('About to say...');
220 this.callOverridden();
224 var cool = new My.cool.Class();
225 cool.sayHi(); // alerts 'About to say...'
228 * Please note that `this.callOverridden()` only works if the class was previously
229 * created with {@link Ext#define)
231 * @param {Object} cls The class to override
232 * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
233 * containing one or more methods.
237 override: function(cls, overrides) {
238 if (cls.prototype.$className) {
239 return cls.override(overrides);
242 Ext.apply(cls.prototype, overrides);
247 // A full set of static methods to do type checking
251 * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
252 * value (second argument) otherwise.
254 * @param {Mixed} value The value to test
255 * @param {Mixed} defaultValue The value to return if the original value is empty
256 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
257 * @return {Mixed} value, if non-empty, else defaultValue
259 valueFrom: function(value, defaultValue, allowBlank){
260 return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
264 * Returns the type of the given variable in string format. List of possible values are:
266 * - `undefined`: If the given value is `undefined`
267 * - `null`: If the given value is `null`
268 * - `string`: If the given value is a string
269 * - `number`: If the given value is a number
270 * - `boolean`: If the given value is a boolean value
271 * - `date`: If the given value is a `Date` object
272 * - `function`: If the given value is a function reference
273 * - `object`: If the given value is an object
274 * - `array`: If the given value is an array
275 * - `regexp`: If the given value is a regular expression
276 * - `element`: If the given value is a DOM Element
277 * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
278 * - `whitespace`: If the given value is a DOM text node and contains only whitespace
280 * @param {Mixed} value
284 typeOf: function(value) {
285 if (value === null) {
289 var type = typeof value;
291 if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
295 var typeToString = toString.call(value);
297 switch(typeToString) {
298 case '[object Array]':
300 case '[object Date]':
302 case '[object Boolean]':
304 case '[object Number]':
306 case '[object RegExp]':
310 if (type === 'function') {
314 if (type === 'object') {
315 if (value.nodeType !== undefined) {
316 if (value.nodeType === 3) {
317 return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
329 sourceMethod: 'typeOf',
330 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
335 * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
339 * - a zero-length array
340 * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
342 * @param {Mixed} value The value to test
343 * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
347 isEmpty: function(value, allowEmptyString) {
348 return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
352 * Returns true if the passed value is a JavaScript Array, false otherwise.
354 * @param {Mixed} target The target to test
357 isArray: ('isArray' in Array) ? Array.isArray : function(value) {
358 return toString.call(value) === '[object Array]';
362 * Returns true if the passed value is a JavaScript Date object, false otherwise.
363 * @param {Object} object The object to test
366 isDate: function(value) {
367 return toString.call(value) === '[object Date]';
371 * Returns true if the passed value is a JavaScript Object, false otherwise.
372 * @param {Mixed} value The value to test
375 isObject: (toString.call(null) === '[object Object]') ?
377 return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.nodeType === undefined;
380 return toString.call(value) === '[object Object]';
384 * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
385 * @param {Mixed} value The value to test
388 isPrimitive: function(value) {
389 var type = typeof value;
391 return type === 'string' || type === 'number' || type === 'boolean';
395 * Returns true if the passed value is a JavaScript Function, false otherwise.
396 * @param {Mixed} value The value to test
400 // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
401 // Object.prorotype.toString (slower)
402 (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
403 return toString.call(value) === '[object Function]';
404 } : function(value) {
405 return typeof value === 'function';
409 * Returns true if the passed value is a number. Returns false for non-finite numbers.
410 * @param {Mixed} value The value to test
413 isNumber: function(value) {
414 return typeof value === 'number' && isFinite(value);
418 * Validates that a value is numeric.
419 * @param {Mixed} value Examples: 1, '1', '2.34'
420 * @return {Boolean} True if numeric, false otherwise
422 isNumeric: function(value) {
423 return !isNaN(parseFloat(value)) && isFinite(value);
427 * Returns true if the passed value is a string.
428 * @param {Mixed} value The value to test
431 isString: function(value) {
432 return typeof value === 'string';
436 * Returns true if the passed value is a boolean.
438 * @param {Mixed} value The value to test
441 isBoolean: function(value) {
442 return typeof value === 'boolean';
446 * Returns true if the passed value is an HTMLElement
447 * @param {Mixed} value The value to test
450 isElement: function(value) {
451 return value ? value.nodeType !== undefined : false;
455 * Returns true if the passed value is a TextNode
456 * @param {Mixed} value The value to test
459 isTextNode: function(value) {
460 return value ? value.nodeName === "#text" : false;
464 * Returns true if the passed value is defined.
465 * @param {Mixed} value The value to test
468 isDefined: function(value) {
469 return typeof value !== 'undefined';
473 * Returns true if the passed value is iterable, false otherwise
474 * @param {Mixed} value The value to test
477 isIterable: function(value) {
478 return (value && typeof value !== 'string') ? value.length !== undefined : false;
485 * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
486 * @param {Mixed} item The variable to clone
487 * @return {Mixed} clone
489 clone: function(item) {
490 if (item === null || item === undefined) {
495 // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
497 if (item.nodeType && item.cloneNode) {
498 return item.cloneNode(true);
501 var type = toString.call(item);
504 if (type === '[object Date]') {
505 return new Date(item.getTime());
508 var i, j, k, clone, key;
511 if (type === '[object Array]') {
517 clone[i] = Ext.clone(item[i]);
521 else if (type === '[object Object]' && item.constructor === Object) {
525 clone[key] = Ext.clone(item[key]);
529 for (j = enumerables.length; j--;) {
536 return clone || item;
541 * Generate a unique reference of Ext in the global scope, useful for sandboxing
543 getUniqueGlobalNamespace: function() {
544 var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
546 if (uniqueGlobalNamespace === undefined) {
550 uniqueGlobalNamespace = 'ExtSandbox' + (++i);
551 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
553 Ext.global[uniqueGlobalNamespace] = Ext;
554 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
557 return uniqueGlobalNamespace;
563 functionFactory: function() {
564 var args = Array.prototype.slice.call(arguments);
566 if (args.length > 0) {
567 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
568 args[args.length - 1];
571 return Function.prototype.constructor.apply(Function.prototype, args);
576 * Old alias to {@link Ext#typeOf}
577 * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
579 Ext.type = Ext.typeOf;
584 * @author Jacky Nguyen <jacky@sencha.com>
585 * @docauthor Jacky Nguyen <jacky@sencha.com>
588 * A utility class that wrap around a string version number and provide convenient
589 * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
591 var version = new Ext.Version('1.0.2beta');
592 console.log("Version is " + version); // Version is 1.0.2beta
594 console.log(version.getMajor()); // 1
595 console.log(version.getMinor()); // 0
596 console.log(version.getPatch()); // 2
597 console.log(version.getBuild()); // 0
598 console.log(version.getRelease()); // beta
600 console.log(version.isGreaterThan('1.0.1')); // True
601 console.log(version.isGreaterThan('1.0.2alpha')); // True
602 console.log(version.isGreaterThan('1.0.2RC')); // False
603 console.log(version.isGreaterThan('1.0.2')); // False
604 console.log(version.isLessThan('1.0.2')); // True
606 console.log(version.match(1.0)); // True
607 console.log(version.match('1.0.2')); // True
613 // Current core version
614 var version = '4.0.0', Version;
615 Ext.Version = Version = Ext.extend(Object, {
619 * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
620 * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
621 * @return {Ext.Version} this
624 constructor: function(version) {
625 var parts, releaseStartIndex;
627 if (version instanceof Version) {
631 this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
633 releaseStartIndex = this.version.search(/([^\d\.])/);
635 if (releaseStartIndex !== -1) {
636 this.release = this.version.substr(releaseStartIndex, version.length);
637 this.shortVersion = this.version.substr(0, releaseStartIndex);
640 this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
642 parts = this.version.split('.');
644 this.major = parseInt(parts.shift() || 0, 10);
645 this.minor = parseInt(parts.shift() || 0, 10);
646 this.patch = parseInt(parts.shift() || 0, 10);
647 this.build = parseInt(parts.shift() || 0, 10);
653 * Override the native toString method
655 * @return {String} version
657 toString: function() {
662 * Override the native valueOf method
664 * @return {String} version
666 valueOf: function() {
671 * Returns the major component value
672 * @return {Number} major
674 getMajor: function() {
675 return this.major || 0;
679 * Returns the minor component value
680 * @return {Number} minor
682 getMinor: function() {
683 return this.minor || 0;
687 * Returns the patch component value
688 * @return {Number} patch
690 getPatch: function() {
691 return this.patch || 0;
695 * Returns the build component value
696 * @return {Number} build
698 getBuild: function() {
699 return this.build || 0;
703 * Returns the release component value
704 * @return {Number} release
706 getRelease: function() {
707 return this.release || '';
711 * Returns whether this version if greater than the supplied argument
712 * @param {String/Number} target The version to compare with
713 * @return {Boolean} True if this version if greater than the target, false otherwise
715 isGreaterThan: function(target) {
716 return Version.compare(this.version, target) === 1;
720 * Returns whether this version if smaller than the supplied argument
721 * @param {String/Number} target The version to compare with
722 * @return {Boolean} True if this version if smaller than the target, false otherwise
724 isLessThan: function(target) {
725 return Version.compare(this.version, target) === -1;
729 * Returns whether this version equals to the supplied argument
730 * @param {String/Number} target The version to compare with
731 * @return {Boolean} True if this version equals to the target, false otherwise
733 equals: function(target) {
734 return Version.compare(this.version, target) === 0;
738 * Returns whether this version matches the supplied argument. Example:
740 * var version = new Ext.Version('1.0.2beta');
741 * console.log(version.match(1)); // True
742 * console.log(version.match(1.0)); // True
743 * console.log(version.match('1.0.2')); // True
744 * console.log(version.match('1.0.2RC')); // False
746 * @param {String/Number} target The version to compare with
747 * @return {Boolean} True if this version matches the target, false otherwise
749 match: function(target) {
750 target = String(target);
751 return this.version.substr(0, target.length) === target;
755 * Returns this format: [major, minor, patch, build, release]. Useful for comparison
758 toArray: function() {
759 return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
763 * Returns shortVersion version without dots and release
766 getShortVersion: function() {
767 return this.shortVersion;
786 * Converts a version component to a comparable value
789 * @param {Mixed} value The value to convert
792 getComponentValue: function(value) {
793 return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
797 * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
798 * they are handled in the following order:
799 * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
802 * @param {String} current The current version to compare to
803 * @param {String} target The target version to compare to
804 * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
806 compare: function(current, target) {
807 var currentValue, targetValue, i;
809 current = new Version(current).toArray();
810 target = new Version(target).toArray();
812 for (i = 0; i < Math.max(current.length, target.length); i++) {
813 currentValue = this.getComponentValue(current[i]);
814 targetValue = this.getComponentValue(target[i]);
816 if (currentValue < targetValue) {
818 } else if (currentValue > targetValue) {
836 lastRegisteredVersion: null,
839 * Set version number for the given package name.
841 * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
842 * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
845 setVersion: function(packageName, version) {
846 Ext.versions[packageName] = new Version(version);
847 Ext.lastRegisteredVersion = Ext.versions[packageName];
853 * Get the version number of the supplied package name; will return the last registered version
854 * (last Ext.setVersion call) if there's no package name given.
856 * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
857 * @return {Ext.Version} The version
859 getVersion: function(packageName) {
860 if (packageName === undefined) {
861 return Ext.lastRegisteredVersion;
864 return Ext.versions[packageName];
868 * Create a closure for deprecated code.
870 // This means Ext.oldMethod is only supported in 4.0.0beta and older.
871 // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
872 // the closure will not be invoked
873 Ext.deprecate('extjs', '4.0.0beta', function() {
874 Ext.oldMethod = Ext.newMethod;
879 * @param {String} packageName The package name
880 * @param {String} since The last version before it's deprecated
881 * @param {Function} closure The callback function to be executed with the specified version is less than the current version
882 * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
885 deprecate: function(packageName, since, closure, scope) {
886 if (Version.compare(Ext.getVersion(packageName), since) < 1) {
890 }); // End Versioning
892 Ext.setVersion('core', version);
899 * A collection of useful static methods to deal with strings
904 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,
906 formatRe: /\{(\d+)\}/g,
907 escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
910 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
911 * @param {String} value The string to encode
912 * @return {String} The encoded text
914 htmlEncode: (function() {
920 }, keys = [], p, regex;
922 for (p in entities) {
926 regex = new RegExp('(' + keys.join('|') + ')', 'g');
928 return function(value) {
929 return (!value) ? value : String(value).replace(regex, function(match, capture) {
930 return entities[capture];
936 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
937 * @param {String} value The string to decode
938 * @return {String} The decoded text
940 htmlDecode: (function() {
946 }, keys = [], p, regex;
948 for (p in entities) {
952 regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
954 return function(value) {
955 return (!value) ? value : String(value).replace(regex, function(match, capture) {
956 if (capture in entities) {
957 return entities[capture];
959 return String.fromCharCode(parseInt(capture.substr(2), 10));
966 * Appends content to the query string of a URL, handling logic for whether to place
967 * a question mark or ampersand.
968 * @param {String} url The URL to append to.
969 * @param {String} string The content to append to the URL.
970 * @return (String) The resulting URL
972 urlAppend : function(url, string) {
973 if (!Ext.isEmpty(string)) {
974 return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
981 * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
984 alert('-' + s + '-'); //alerts "- foo bar -"
985 alert('-' + Ext.String.trim(s) + '-'); //alerts "-foo bar-"
987 * @param {String} string The string to escape
988 * @return {String} The trimmed string
990 trim: function(string) {
991 return string.replace(Ext.String.trimRegex, "");
995 * Capitalize the given string
996 * @param {String} string
999 capitalize: function(string) {
1000 return string.charAt(0).toUpperCase() + string.substr(1);
1004 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1005 * @param {String} value The string to truncate
1006 * @param {Number} length The maximum length to allow before truncating
1007 * @param {Boolean} word True to try to find a common word break
1008 * @return {String} The converted text
1010 ellipsis: function(value, len, word) {
1011 if (value && value.length > len) {
1013 var vs = value.substr(0, len - 2),
1014 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1015 if (index !== -1 && index >= (len - 15)) {
1016 return vs.substr(0, index) + "...";
1019 return value.substr(0, len - 3) + "...";
1025 * Escapes the passed string for use in a regular expression
1026 * @param {String} string
1029 escapeRegex: function(string) {
1030 return string.replace(Ext.String.escapeRegexRe, "\\$1");
1034 * Escapes the passed string for ' and \
1035 * @param {String} string The string to escape
1036 * @return {String} The escaped string
1038 escape: function(string) {
1039 return string.replace(Ext.String.escapeRe, "\\$1");
1043 * Utility function that allows you to easily switch a string between two alternating values. The passed value
1044 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
1045 * they are already different, the first value passed in is returned. Note that this method returns the new value
1046 * but does not change the current string.
1048 // alternate sort directions
1049 sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1051 // instead of conditional logic:
1052 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1054 * @param {String} string The current string
1055 * @param {String} value The value to compare to the current string
1056 * @param {String} other The new value to use if the string already equals the first value passed in
1057 * @return {String} The new value
1059 toggle: function(string, value, other) {
1060 return string === value ? other : value;
1064 * Pads the left side of a string with a specified character. This is especially useful
1065 * for normalizing number and date strings. Example usage:
1068 var s = Ext.String.leftPad('123', 5, '0');
1069 // s now contains the string: '00123'
1071 * @param {String} string The original string
1072 * @param {Number} size The total length of the output string
1073 * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1074 * @return {String} The padded string
1076 leftPad: function(string, size, character) {
1077 var result = String(string);
1078 character = character || " ";
1079 while (result.length < size) {
1080 result = character + result;
1086 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
1087 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
1089 var cls = 'my-class', text = 'Some text';
1090 var s = Ext.String.format('<div class="{0}">{1}</div>', cls, text);
1091 // s now contains the string: '<div class="my-class">Some text</div>'
1093 * @param {String} string The tokenized string to be formatted
1094 * @param {String} value1 The value to replace token {0}
1095 * @param {String} value2 Etc...
1096 * @return {String} The formatted string
1098 format: function(format) {
1099 var args = Ext.Array.toArray(arguments, 1);
1100 return format.replace(Ext.String.formatRe, function(m, i) {
1109 * A collection of useful static methods to deal with numbers
1115 var isToFixedBroken = (0.9).toFixed() !== '1';
1119 * Checks whether or not the current number is within a desired range. If the number is already within the
1120 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1121 * exceeded. Note that this method returns the constrained value but does not change the current number.
1122 * @param {Number} number The number to check
1123 * @param {Number} min The minimum number in the range
1124 * @param {Number} max The maximum number in the range
1125 * @return {Number} The constrained value if outside the range, otherwise the current value
1127 constrain: function(number, min, max) {
1128 number = parseFloat(number);
1131 number = Math.max(number, min);
1134 number = Math.min(number, max);
1140 * Formats a number using fixed-point notation
1141 * @param {Number} value The number to format
1142 * @param {Number} precision The number of digits to show after the decimal point
1144 toFixed: function(value, precision) {
1145 if (isToFixedBroken) {
1146 precision = precision || 0;
1147 var pow = Math.pow(10, precision);
1148 return (Math.round(value * pow) / pow).toFixed(precision);
1151 return value.toFixed(precision);
1155 * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1158 Ext.Number.from('1.23', 1); // returns 1.23
1159 Ext.Number.from('abc', 1); // returns 1
1161 * @param {Mixed} value
1162 * @param {Number} defaultValue The value to return if the original value is non-numeric
1163 * @return {Number} value, if numeric, defaultValue otherwise
1165 from: function(value, defaultValue) {
1166 if (isFinite(value)) {
1167 value = parseFloat(value);
1170 return !isNaN(value) ? value : defaultValue;
1177 * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead
1179 * @deprecated 4.0.0 Replaced by Ext.Number.from
1183 Ext.num = function() {
1184 return Ext.Number.from.apply(this, arguments);
1187 * @author Jacky Nguyen <jacky@sencha.com>
1188 * @docauthor Jacky Nguyen <jacky@sencha.com>
1191 * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1198 var arrayPrototype = Array.prototype,
1199 slice = arrayPrototype.slice,
1200 supportsForEach = 'forEach' in arrayPrototype,
1201 supportsMap = 'map' in arrayPrototype,
1202 supportsIndexOf = 'indexOf' in arrayPrototype,
1203 supportsEvery = 'every' in arrayPrototype,
1204 supportsSome = 'some' in arrayPrototype,
1205 supportsFilter = 'filter' in arrayPrototype,
1206 supportsSort = function() {
1207 var a = [1,2,3,4,5].sort(function(){ return 0; });
1208 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1210 supportsSliceOnNodeList = true,
1213 // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1214 if (typeof document !== 'undefined') {
1215 slice.call(document.getElementsByTagName('body'));
1218 supportsSliceOnNodeList = false;
1221 ExtArray = Ext.Array = {
1223 * Iterates an array or an iterable value and invoke the given callback function for each item.
1225 var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1227 Ext.Array.each(countries, function(name, index, countriesItSelf) {
1231 var sum = function() {
1234 Ext.Array.each(arguments, function(value) {
1241 sum(1, 2, 3); // returns 6
1243 * The iteration can be stopped by returning false in the function callback.
1245 Ext.Array.each(countries, function(name, index, countriesItSelf) {
1246 if (name === 'Singapore') {
1247 return false; // break here
1251 * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this
1252 * argument is not iterable, the callback function is called once.
1253 * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1254 * the current `index`. Arguments passed to this callback function are:
1256 - `item`: {Mixed} The item at the current `index` in the passed `array`
1257 - `index`: {Number} The current `index` within the `array`
1258 - `allItems`: {Array/NodeList/Mixed} The `array` passed as the first argument to `Ext.Array.each`
1260 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1261 * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1263 * @return {Boolean} See description for the `fn` parameter.
1266 each: function(array, fn, scope, reverse) {
1267 array = ExtArray.from(array);
1272 if (reverse !== true) {
1273 for (i = 0; i < ln; i++) {
1274 if (fn.call(scope || array[i], array[i], i, array) === false) {
1280 for (i = ln - 1; i > -1; i--) {
1281 if (fn.call(scope || array[i], array[i], i, array) === false) {
1291 * Iterates an array and invoke the given callback function for each item. Note that this will simply
1292 * delegate to the native Array.prototype.forEach method if supported.
1293 * It doesn't support stopping the iteration by returning false in the callback function like
1294 * {@link Ext.Array#each}. However, performance could be much better in modern browsers comparing with
1295 * {@link Ext.Array#each}
1297 * @param {Array} array The array to iterate
1298 * @param {Function} fn The function callback, to be invoked these arguments:
1300 - `item`: {Mixed} The item at the current `index` in the passed `array`
1301 - `index`: {Number} The current `index` within the `array`
1302 - `allItems`: {Array} The `array` itself which was passed as the first argument
1304 * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1307 forEach: function(array, fn, scope) {
1308 if (supportsForEach) {
1309 return array.forEach(fn, scope);
1315 for (; i < ln; i++) {
1316 fn.call(scope, array[i], i, array);
1321 * Get the index of the provided `item` in the given `array`, a supplement for the
1322 * missing arrayPrototype.indexOf in Internet Explorer.
1324 * @param {Array} array The array to check
1325 * @param {Mixed} item The item to look for
1326 * @param {Number} from (Optional) The index at which to begin the search
1327 * @return {Number} The index of item in the array (or -1 if it is not found)
1330 indexOf: function(array, item, from) {
1331 if (supportsIndexOf) {
1332 return array.indexOf(item, from);
1335 var i, length = array.length;
1337 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1338 if (array[i] === item) {
1347 * Checks whether or not the given `array` contains the specified `item`
1349 * @param {Array} array The array to check
1350 * @param {Mixed} item The item to look for
1351 * @return {Boolean} True if the array contains the item, false otherwise
1354 contains: function(array, item) {
1355 if (supportsIndexOf) {
1356 return array.indexOf(item) !== -1;
1361 for (i = 0, ln = array.length; i < ln; i++) {
1362 if (array[i] === item) {
1371 * Converts any iterable (numeric indices and a length property) into a true array.
1374 var args = Ext.Array.toArray(arguments),
1375 fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1377 alert(args.join(' '));
1378 alert(fromSecondToLastArgs.join(' '));
1381 test('just', 'testing', 'here'); // alerts 'just testing here';
1382 // alerts 'testing here';
1384 Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1385 Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1386 Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1388 * @param {Mixed} iterable the iterable object to be turned into a true Array.
1389 * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1390 * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1391 * index of the iterable value
1392 * @return {Array} array
1395 toArray: function(iterable, start, end){
1396 if (!iterable || !iterable.length) {
1400 if (typeof iterable === 'string') {
1401 iterable = iterable.split('');
1404 if (supportsSliceOnNodeList) {
1405 return slice.call(iterable, start || 0, end || iterable.length);
1412 end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1414 for (i = start; i < end; i++) {
1415 array.push(iterable[i]);
1422 * Plucks the value of a property from each item in the Array. Example:
1424 Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1426 * @param {Array|NodeList} array The Array of items to pluck the value from.
1427 * @param {String} propertyName The property name to pluck from each element.
1428 * @return {Array} The value from each item in the Array.
1430 pluck: function(array, propertyName) {
1434 for (i = 0, ln = array.length; i < ln; i++) {
1437 ret.push(item[propertyName]);
1444 * Creates a new array with the results of calling a provided function on every element in this array.
1445 * @param {Array} array
1446 * @param {Function} fn Callback function for each item
1447 * @param {Object} scope Callback function scope
1448 * @return {Array} results
1450 map: function(array, fn, scope) {
1452 return array.map(fn, scope);
1459 for (; i < len; i++) {
1460 results[i] = fn.call(scope, array[i], i, array);
1467 * Executes the specified function for each array element until the function returns a falsy value.
1468 * If such an item is found, the function will return false immediately.
1469 * Otherwise, it will return true.
1471 * @param {Array} array
1472 * @param {Function} fn Callback function for each item
1473 * @param {Object} scope Callback function scope
1474 * @return {Boolean} True if no false value is returned by the callback function.
1476 every: function(array, fn, scope) {
1478 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1480 if (supportsEvery) {
1481 return array.every(fn, scope);
1487 for (; i < ln; ++i) {
1488 if (!fn.call(scope, array[i], i, array)) {
1497 * Executes the specified function for each array element until the function returns a truthy value.
1498 * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1500 * @param {Array} array
1501 * @param {Function} fn Callback function for each item
1502 * @param {Object} scope Callback function scope
1503 * @return {Boolean} True if the callback function returns a truthy value.
1505 some: function(array, fn, scope) {
1507 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1510 return array.some(fn, scope);
1516 for (; i < ln; ++i) {
1517 if (fn.call(scope, array[i], i, array)) {
1526 * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1528 * @see Ext.Array.filter
1529 * @param {Array} array
1530 * @return {Array} results
1532 clean: function(array) {
1538 for (; i < ln; i++) {
1541 if (!Ext.isEmpty(item)) {
1550 * Returns a new array with unique items
1552 * @param {Array} array
1553 * @return {Array} results
1555 unique: function(array) {
1561 for (; i < ln; i++) {
1564 if (ExtArray.indexOf(clone, item) === -1) {
1573 * Creates a new array with all of the elements of this array for which
1574 * the provided filtering function returns true.
1575 * @param {Array} array
1576 * @param {Function} fn Callback function for each item
1577 * @param {Object} scope Callback function scope
1578 * @return {Array} results
1580 filter: function(array, fn, scope) {
1581 if (supportsFilter) {
1582 return array.filter(fn, scope);
1589 for (; i < ln; i++) {
1590 if (fn.call(scope, array[i], i, array)) {
1591 results.push(array[i]);
1599 * Converts a value to an array if it's not already an array; returns:
1601 * - An empty array if given value is `undefined` or `null`
1602 * - Itself if given value is already an array
1603 * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1604 * - An array with one item which is the given value, otherwise
1606 * @param {Array/Mixed} value The value to convert to an array if it's not already is an array
1607 * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary,
1609 * @return {Array} array
1612 from: function(value, newReference) {
1613 if (value === undefined || value === null) {
1617 if (Ext.isArray(value)) {
1618 return (newReference) ? slice.call(value) : value;
1621 if (value && value.length !== undefined && typeof value !== 'string') {
1622 return Ext.toArray(value);
1629 * Removes the specified item from the array if it exists
1631 * @param {Array} array The array
1632 * @param {Mixed} item The item to remove
1633 * @return {Array} The passed array itself
1635 remove: function(array, item) {
1636 var index = ExtArray.indexOf(array, item);
1639 array.splice(index, 1);
1646 * Push an item into the array only if the array doesn't contain it yet
1648 * @param {Array} array The array
1649 * @param {Mixed} item The item to include
1650 * @return {Array} The passed array itself
1652 include: function(array, item) {
1653 if (!ExtArray.contains(array, item)) {
1659 * Clone a flat array without referencing the previous one. Note that this is different
1660 * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1661 * for Array.prototype.slice.call(array)
1663 * @param {Array} array The array
1664 * @return {Array} The clone array
1666 clone: function(array) {
1667 return slice.call(array);
1671 * Merge multiple arrays into one with unique items. Alias to {@link Ext.Array#union}.
1673 * @param {Array} array,...
1674 * @return {Array} merged
1677 var args = slice.call(arguments),
1681 for (i = 0, ln = args.length; i < ln; i++) {
1682 array = array.concat(args[i]);
1685 return ExtArray.unique(array);
1689 * Merge multiple arrays into one with unique items that exist in all of the arrays.
1691 * @param {Array} array,...
1692 * @return {Array} intersect
1694 intersect: function() {
1696 arrays = slice.call(arguments),
1697 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1699 if (!arrays.length) {
1703 // Find the smallest array
1704 for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1705 if (!minArray || array.length < minArray.length) {
1711 minArray = Ext.Array.unique(minArray);
1712 arrays.splice(x, 1);
1714 // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1715 // an item in the small array, we're likely to find it before reaching the end
1716 // of the inner loop and can terminate the search early.
1717 for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1720 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1721 for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1729 if (count === arraysLn) {
1738 * Perform a set difference A-B by subtracting all items in array B from array A.
1740 * @param {Array} array A
1741 * @param {Array} array B
1742 * @return {Array} difference
1744 difference: function(arrayA, arrayB) {
1745 var clone = slice.call(arrayA),
1749 for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1750 for (j = 0; j < ln; j++) {
1751 if (clone[j] === arrayB[i]) {
1763 * Sorts the elements of an Array.
1764 * By default, this method sorts the elements alphabetically and ascending.
1766 * @param {Array} array The array to sort.
1767 * @param {Function} sortFn (optional) The comparison function.
1768 * @return {Array} The sorted array.
1770 sort: function(array, sortFn) {
1773 return array.sort(sortFn);
1775 return array.sort();
1779 var length = array.length,
1784 for (; i < length; i++) {
1786 for (j = i + 1; j < length; j++) {
1788 comparison = sortFn(array[j], array[min]);
1789 if (comparison < 0) {
1792 } else if (array[j] < array[min]) {
1798 array[i] = array[min];
1807 * Recursively flattens into 1-d Array. Injects Arrays inline.
1808 * @param {Array} array The array to flatten
1809 * @return {Array} The new, flattened array.
1811 flatten: function(array) {
1814 function rFlatten(a) {
1817 for (i = 0, ln = a.length; i < ln; i++) {
1820 if (Ext.isArray(v)) {
1830 return rFlatten(array);
1834 * Returns the minimum value in the Array.
1835 * @param {Array|NodeList} array The Array from which to select the minimum value.
1836 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
1837 * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
1838 * @return {Mixed} minValue The minimum value
1840 min: function(array, comparisonFn) {
1844 for (i = 0, ln = array.length; i < ln; i++) {
1848 if (comparisonFn(min, item) === 1) {
1863 * Returns the maximum value in the Array
1864 * @param {Array|NodeList} array The Array from which to select the maximum value.
1865 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
1866 * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
1867 * @return {Mixed} maxValue The maximum value
1869 max: function(array, comparisonFn) {
1873 for (i = 0, ln = array.length; i < ln; i++) {
1877 if (comparisonFn(max, item) === -1) {
1892 * Calculates the mean of all items in the array
1893 * @param {Array} array The Array to calculate the mean value of.
1894 * @return {Number} The mean.
1896 mean: function(array) {
1897 return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
1901 * Calculates the sum of all items in the given array
1902 * @param {Array} array The Array to calculate the sum value of.
1903 * @return {Number} The sum.
1905 sum: function(array) {
1909 for (i = 0,ln = array.length; i < ln; i++) {
1921 * Convenient alias to {@link Ext.Array#each}
1925 Ext.each = Ext.Array.each;
1928 * Alias to {@link Ext.Array#merge}.
1932 Ext.Array.union = Ext.Array.merge;
1935 * Old alias to {@link Ext.Array#min}
1936 * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
1940 Ext.min = Ext.Array.min;
1943 * Old alias to {@link Ext.Array#max}
1944 * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
1948 Ext.max = Ext.Array.max;
1951 * Old alias to {@link Ext.Array#sum}
1952 * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
1956 Ext.sum = Ext.Array.sum;
1959 * Old alias to {@link Ext.Array#mean}
1960 * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
1964 Ext.mean = Ext.Array.mean;
1967 * Old alias to {@link Ext.Array#flatten}
1968 * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
1972 Ext.flatten = Ext.Array.flatten;
1975 * Old alias to {@link Ext.Array#clean Ext.Array.clean}
1976 * @deprecated 4.0.0 Use {@link Ext.Array.clean} instead
1980 Ext.clean = Ext.Array.clean;
1983 * Old alias to {@link Ext.Array#unique Ext.Array.unique}
1984 * @deprecated 4.0.0 Use {@link Ext.Array.unique} instead
1988 Ext.unique = Ext.Array.unique;
1991 * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
1992 * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
1996 Ext.pluck = Ext.Array.pluck;
1999 * Convenient alias to {@link Ext.Array#toArray Ext.Array.toArray}
2000 * @param {Iterable} the iterable object to be turned into a true Array.
2003 * @return {Array} array
2005 Ext.toArray = function() {
2006 return ExtArray.toArray.apply(ExtArray, arguments);
2011 * @class Ext.Function
2013 * A collection of useful static methods to deal with function callbacks
2020 * A very commonly used method throughout the framework. It acts as a wrapper around another method
2021 * which originally accepts 2 arguments for <code>name</code> and <code>value</code>.
2022 * The wrapped function then allows "flexible" value setting of either:
2025 * <li><code>name</code> and <code>value</code> as 2 arguments</li>
2026 * <li>one single object argument with multiple key - value pairs</li>
2031 var setValue = Ext.Function.flexSetter(function(name, value) {
2036 // Setting a single name - value
2037 setValue('name1', 'value1');
2039 // Settings multiple name - value pairs
2046 * @param {Function} setter
2047 * @returns {Function} flexSetter
2049 flexSetter: function(fn) {
2050 return function(a, b) {
2057 if (typeof a !== 'string') {
2059 if (a.hasOwnProperty(k)) {
2060 fn.call(this, k, a[k]);
2064 if (Ext.enumerables) {
2065 for (i = Ext.enumerables.length; i--;) {
2066 k = Ext.enumerables[i];
2067 if (a.hasOwnProperty(k)) {
2068 fn.call(this, k, a[k]);
2073 fn.call(this, a, b);
2081 * Create a new function from the provided <code>fn</code>, change <code>this</code> to the provided scope, optionally
2082 * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2084 * @param {Function} fn The function to delegate.
2085 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
2086 * <b>If omitted, defaults to the browser window.</b>
2087 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2088 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2089 * if a number the args are inserted at the specified position
2090 * @return {Function} The new function
2092 bind: function(fn, scope, args, appendArgs) {
2097 var callArgs = args || arguments;
2099 if (appendArgs === true) {
2100 callArgs = Array.prototype.slice.call(arguments, 0);
2101 callArgs = callArgs.concat(args);
2103 else if (Ext.isNumber(appendArgs)) {
2104 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
2105 applyArgs = [appendArgs, 0].concat(args); // create method call params
2106 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
2109 return method.apply(scope || window, callArgs);
2114 * Create a new function from the provided <code>fn</code>, the arguments of which are pre-set to `args`.
2115 * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2116 * This is especially useful when creating callbacks.
2119 var originalFunction = function(){
2120 alert(Ext.Array.from(arguments).join(' '));
2123 var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2125 callback(); // alerts 'Hello World'
2126 callback('by Me'); // alerts 'Hello World by Me'
2128 * @param {Function} fn The original function
2129 * @param {Array} args The arguments to pass to new callback
2130 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
2131 * @return {Function} The new callback function
2133 pass: function(fn, args, scope) {
2135 args = Ext.Array.from(args);
2139 return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2144 * Create an alias to the provided method property with name <code>methodName</code> of <code>object</code>.
2145 * Note that the execution scope will still be bound to the provided <code>object</code> itself.
2147 * @param {Object/Function} object
2148 * @param {String} methodName
2149 * @return {Function} aliasFn
2151 alias: function(object, methodName) {
2153 return object[methodName].apply(object, arguments);
2158 * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2159 * the original one is not called. The resulting function returns the results of the original function.
2160 * The passed function is called with the parameters of the original function. Example usage:
2162 var sayHi = function(name){
2163 alert('Hi, ' + name);
2166 sayHi('Fred'); // alerts "Hi, Fred"
2168 // create a new function that validates input without
2169 // directly modifying the original function:
2170 var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2171 return name == 'Brian';
2174 sayHiToFriend('Fred'); // no alert
2175 sayHiToFriend('Brian'); // alerts "Hi, Brian"
2177 * @param {Function} origFn The original function.
2178 * @param {Function} newFn The function to call before the original
2179 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
2180 * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
2181 * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
2182 * @return {Function} The new function
2184 createInterceptor: function(origFn, newFn, scope, returnValue) {
2185 var method = origFn;
2186 if (!Ext.isFunction(newFn)) {
2194 newFn.method = origFn;
2195 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2201 * Creates a delegate (callback) which, when called, executes after a specific delay.
2202 * @param {Function} fn The function which will be called on a delay when the returned function is called.
2203 * Optionally, a replacement (or additional) argument list may be specified.
2204 * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2205 * @param {Object} scope (optional) The scope (<code>this</code> reference) used by the function at execution time.
2206 * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2207 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2208 * if a number the args are inserted at the specified position.
2209 * @return {Function} A function which, when called, executes the original function after the specified delay.
2211 createDelayed: function(fn, delay, scope, args, appendArgs) {
2212 if (scope || args) {
2213 fn = Ext.Function.bind(fn, scope, args, appendArgs);
2217 setTimeout(function() {
2218 fn.apply(me, arguments);
2224 * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2226 var sayHi = function(name){
2227 alert('Hi, ' + name);
2230 // executes immediately:
2233 // executes after 2 seconds:
2234 Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2236 // this syntax is sometimes useful for deferring
2237 // execution of an anonymous function:
2238 Ext.Function.defer(function(){
2242 * @param {Function} fn The function to defer.
2243 * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
2244 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
2245 * <b>If omitted, defaults to the browser window.</b>
2246 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2247 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2248 * if a number the args are inserted at the specified position
2249 * @return {Number} The timeout id that can be used with clearTimeout
2251 defer: function(fn, millis, obj, args, appendArgs) {
2252 fn = Ext.Function.bind(fn, obj, args, appendArgs);
2254 return setTimeout(fn, millis);
2261 * Create a combined function call sequence of the original function + the passed function.
2262 * The resulting function returns the results of the original function.
2263 * The passed function is called with the parameters of the original function. Example usage:
2266 var sayHi = function(name){
2267 alert('Hi, ' + name);
2270 sayHi('Fred'); // alerts "Hi, Fred"
2272 var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2273 alert('Bye, ' + name);
2276 sayGoodbye('Fred'); // both alerts show
2279 * @param {Function} origFn The original function.
2280 * @param {Function} newFn The function to sequence
2281 * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
2282 * If omitted, defaults to the scope in which the original function is called or the browser window.
2283 * @return {Function} The new function
2285 createSequence: function(origFn, newFn, scope) {
2286 if (!Ext.isFunction(newFn)) {
2291 var retval = origFn.apply(this || window, arguments);
2292 newFn.apply(scope || this || window, arguments);
2299 * <p>Creates a delegate function, optionally with a bound scope which, when called, buffers
2300 * the execution of the passed function for the configured number of milliseconds.
2301 * If called again within that period, the impending invocation will be canceled, and the
2302 * timeout period will begin again.</p>
2304 * @param {Function} fn The function to invoke on a buffered timer.
2305 * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2307 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which
2308 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2309 * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2310 * passed by the caller.
2311 * @return {Function} A function which invokes the passed function after buffering for the specified time.
2313 createBuffered: function(fn, buffer, scope, args) {
2319 clearInterval(timerId);
2322 timerId = setTimeout(function(){
2323 fn.apply(scope || me, args || arguments);
2330 * <p>Creates a throttled version of the passed function which, when called repeatedly and
2331 * rapidly, invokes the passed function only after a certain interval has elapsed since the
2332 * previous invocation.</p>
2334 * <p>This is useful for wrapping functions which may be called repeatedly, such as
2335 * a handler of a mouse move event when the processing is expensive.</p>
2337 * @param fn {Function} The function to execute at a regular time interval.
2338 * @param interval {Number} The interval <b>in milliseconds</b> on which the passed function is executed.
2339 * @param scope (optional) The scope (<code><b>this</b></code> reference) in which
2340 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2341 * @returns {Function} A function which invokes the passed function at the specified interval.
2343 createThrottled: function(fn, interval, scope) {
2344 var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2345 fn.apply(scope || this, lastArgs);
2346 lastCallTime = new Date().getTime();
2350 elapsed = new Date().getTime() - lastCallTime;
2351 lastArgs = arguments;
2353 clearTimeout(timer);
2354 if (!lastCallTime || (elapsed >= interval)) {
2357 timer = setTimeout(execute, interval - elapsed);
2364 * Shorthand for {@link Ext.Function#defer}
2368 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2371 * Shorthand for {@link Ext.Function#pass}
2375 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2378 * Shorthand for {@link Ext.Function#bind}
2382 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2385 * @author Jacky Nguyen <jacky@sencha.com>
2386 * @docauthor Jacky Nguyen <jacky@sencha.com>
2389 * A collection of useful static methods to deal with objects
2396 var ExtObject = Ext.Object = {
2399 * Convert a `name` - `value` pair to an array of objects with support for nested structures; useful to construct
2400 * query strings. For example:
2402 var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2404 // objects then equals:
2406 { name: 'hobbies', value: 'reading' },
2407 { name: 'hobbies', value: 'cooking' },
2408 { name: 'hobbies', value: 'swimming' },
2411 var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2419 }, true); // Recursive
2421 // objects then equals:
2423 { name: 'dateOfBirth[day]', value: 3 },
2424 { name: 'dateOfBirth[month]', value: 8 },
2425 { name: 'dateOfBirth[year]', value: 1987 },
2426 { name: 'dateOfBirth[extra][hour]', value: 4 },
2427 { name: 'dateOfBirth[extra][minute]', value: 30 },
2430 * @param {String} name
2431 * @param {Mixed} value
2432 * @param {Boolean} recursive
2435 toQueryObjects: function(name, value, recursive) {
2436 var self = ExtObject.toQueryObjects,
2440 if (Ext.isArray(value)) {
2441 for (i = 0, ln = value.length; i < ln; i++) {
2443 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2453 else if (Ext.isObject(value)) {
2455 if (value.hasOwnProperty(i)) {
2457 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2479 * Takes an object and converts it to an encoded query string
2483 Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2484 Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2485 Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2486 Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2487 Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2491 Ext.Object.toQueryString({
2498 hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2499 }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2501 // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2502 // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2505 * @param {Object} object The object to encode
2506 * @param {Boolean} recursive (optional) Whether or not to interpret the object in recursive format.
2507 * (PHP / Ruby on Rails servers and similar). Defaults to false
2508 * @return {String} queryString
2511 toQueryString: function(object, recursive) {
2512 var paramObjects = [],
2514 i, j, ln, paramObject, value;
2517 if (object.hasOwnProperty(i)) {
2518 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2522 for (j = 0, ln = paramObjects.length; j < ln; j++) {
2523 paramObject = paramObjects[j];
2524 value = paramObject.value;
2526 if (Ext.isEmpty(value)) {
2529 else if (Ext.isDate(value)) {
2530 value = Ext.Date.toString(value);
2533 params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2536 return params.join('&');
2540 * Converts a query string back into an object.
2544 Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2545 Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2546 Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2547 Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2551 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);
2561 hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2564 * @param {String} queryString The query string to decode
2565 * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by
2566 * PHP / Ruby on Rails servers and similar. Defaults to false
2569 fromQueryString: function(queryString, recursive) {
2570 var parts = queryString.replace(/^\?/, '').split('&'),
2572 temp, components, name, value, i, ln,
2573 part, j, subLn, matchedKeys, matchedName,
2576 for (i = 0, ln = parts.length; i < ln; i++) {
2579 if (part.length > 0) {
2580 components = part.split('=');
2581 name = decodeURIComponent(components[0]);
2582 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2585 if (object.hasOwnProperty(name)) {
2586 if (!Ext.isArray(object[name])) {
2587 object[name] = [object[name]];
2590 object[name].push(value);
2593 object[name] = value;
2597 matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2598 matchedName = name.match(/^([^\[]+)/);
2602 sourceClass: "Ext.Object",
2603 sourceMethod: "fromQueryString",
2604 queryString: queryString,
2605 recursive: recursive,
2606 msg: 'Malformed query string given, failed parsing name from "' + part + '"'
2610 name = matchedName[0];
2613 if (matchedKeys === null) {
2614 object[name] = value;
2618 for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
2619 key = matchedKeys[j];
2620 key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
2628 for (j = 0, subLn = keys.length; j < subLn; j++) {
2631 if (j === subLn - 1) {
2632 if (Ext.isArray(temp) && key === '') {
2640 if (temp[key] === undefined || typeof temp[key] === 'string') {
2641 nextKey = keys[j+1];
2643 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
2657 * Iterate through an object and invoke the given callback function for each iteration. The iteration can be stop
2658 * by returning `false` in the callback function. For example:
2663 loves: ['food', 'sleeping', 'wife']
2666 Ext.Object.each(person, function(key, value, myself) {
2667 console.log(key + ":" + value);
2669 if (key === 'hairColor') {
2670 return false; // stop the iteration
2674 * @param {Object} object The object to iterate
2675 * @param {Function} fn The callback function. Passed arguments for each iteration are:
2679 - {Object} `object` The object itself
2681 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
2684 each: function(object, fn, scope) {
2685 for (var property in object) {
2686 if (object.hasOwnProperty(property)) {
2687 if (fn.call(scope || object, property, object[property], object) === false) {
2695 * Merges any number of objects recursively without referencing them or their children.
2698 companyName: 'Ext JS',
2699 products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
2703 location: 'Palo Alto',
2709 companyName: 'Sencha Inc.',
2710 products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
2713 location: 'Redwood City'
2717 var sencha = Ext.Object.merge(extjs, newStuff);
2719 // extjs and sencha then equals to
2721 companyName: 'Sencha Inc.',
2722 products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
2726 location: 'Redwood City'
2731 * @param {Object} object,...
2732 * @return {Object} merged The object that is created as a result of merging all the objects passed in.
2735 merge: function(source, key, value) {
2736 if (typeof key === 'string') {
2737 if (value && value.constructor === Object) {
2738 if (source[key] && source[key].constructor === Object) {
2739 ExtObject.merge(source[key], value);
2742 source[key] = Ext.clone(value);
2746 source[key] = value;
2753 ln = arguments.length,
2756 for (; i < ln; i++) {
2757 object = arguments[i];
2759 for (property in object) {
2760 if (object.hasOwnProperty(property)) {
2761 ExtObject.merge(source, property, object[property]);
2770 * Returns the first matching key corresponding to the given value.
2771 * If no matching value is found, null is returned.
2778 alert(Ext.Object.getKey(sencha, 'loves')); // alerts 'food'
2780 * @param {Object} object
2781 * @param {Object} value The value to find
2784 getKey: function(object, value) {
2785 for (var property in object) {
2786 if (object.hasOwnProperty(property) && object[property] === value) {
2795 * Gets all values of the given object as an array.
2797 var values = Ext.Object.getValues({
2800 }); // ['Jacky', 'food']
2802 * @param {Object} object
2803 * @return {Array} An array of values from the object
2806 getValues: function(object) {
2810 for (property in object) {
2811 if (object.hasOwnProperty(property)) {
2812 values.push(object[property]);
2820 * Gets all keys of the given object as an array.
2822 var values = Ext.Object.getKeys({
2825 }); // ['name', 'loves']
2827 * @param {Object} object
2828 * @return {Array} An array of keys from the object
2830 getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
2834 for (property in object) {
2835 if (object.hasOwnProperty(property)) {
2836 keys.push(property);
2844 * Gets the total number of this object's own properties
2846 var size = Ext.Object.getSize({
2849 }); // size equals 2
2851 * @param {Object} object
2852 * @return {Number} size
2855 getSize: function(object) {
2859 for (property in object) {
2860 if (object.hasOwnProperty(property)) {
2871 * A convenient alias method for {@link Ext.Object#merge}
2876 Ext.merge = Ext.Object.merge;
2879 * A convenient alias method for {@link Ext.Object#toQueryString}
2883 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead
2885 Ext.urlEncode = function() {
2886 var args = Ext.Array.from(arguments),
2889 // Support for the old `pre` argument
2890 if ((typeof args[1] === 'string')) {
2891 prefix = args[1] + '&';
2895 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
2899 * A convenient alias method for {@link Ext.Object#fromQueryString}
2903 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead
2905 Ext.urlDecode = function() {
2906 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
2913 * A set of useful static methods to deal with date
2914 * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
2915 * this object for convenience
2917 * The date parsing and formatting syntax contains a subset of
2918 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
2919 * supported will provide results equivalent to their PHP versions.
2921 * The following is a list of all currently supported formats:
2923 Format Description Example returned values
2924 ------ ----------------------------------------------------------------------- -----------------------
2925 d Day of the month, 2 digits with leading zeros 01 to 31
2926 D A short textual representation of the day of the week Mon to Sun
2927 j Day of the month without leading zeros 1 to 31
2928 l A full textual representation of the day of the week Sunday to Saturday
2929 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
2930 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
2931 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
2932 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
2933 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
2934 F A full textual representation of a month, such as January or March January to December
2935 m Numeric representation of a month, with leading zeros 01 to 12
2936 M A short textual representation of a month Jan to Dec
2937 n Numeric representation of a month, without leading zeros 1 to 12
2938 t Number of days in the given month 28 to 31
2939 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
2940 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
2941 belongs to the previous or next year, that year is used instead)
2942 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
2943 y A two digit representation of a year Examples: 99 or 03
2944 a Lowercase Ante meridiem and Post meridiem am or pm
2945 A Uppercase Ante meridiem and Post meridiem AM or PM
2946 g 12-hour format of an hour without leading zeros 1 to 12
2947 G 24-hour format of an hour without leading zeros 0 to 23
2948 h 12-hour format of an hour with leading zeros 01 to 12
2949 H 24-hour format of an hour with leading zeros 00 to 23
2950 i Minutes, with leading zeros 00 to 59
2951 s Seconds, with leading zeros 00 to 59
2952 u Decimal fraction of a second Examples:
2953 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
2954 100 (i.e. 0.100s) or
2955 999 (i.e. 0.999s) or
2956 999876543210 (i.e. 0.999876543210s)
2957 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
2958 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
2959 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
2960 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
2963 1) If unspecified, the month / day defaults to the current month / day, 1991 or
2964 the time defaults to midnight, while the timezone defaults to the 1992-10 or
2965 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
2966 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
2967 are optional. 1995-07-18T17:21:28-02:00 or
2968 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
2969 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
2970 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
2971 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
2972 date-time granularity which are supported, or see 2000-02-13T21:25:33
2973 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
2974 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
2975 MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
2976 \/Date(1238606590509+0800)\/
2979 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
2982 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
2984 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
2985 console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
2986 console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
2987 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
2990 * Here are some standard date/time patterns that you might find helpful. They
2991 * are not part of the source of Ext.Date, but to use them you can simply copy this
2992 * block of code into any script that is included after Ext.Date and they will also become
2993 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
2995 Ext.Date.patterns = {
2996 ISO8601Long:"Y-m-d H:i:s",
2997 ISO8601Short:"Y-m-d",
2999 LongDate: "l, F d, Y",
3000 FullDateTime: "l, F d, Y g:i:s A",
3003 LongTime: "g:i:s A",
3004 SortableDateTime: "Y-m-d\\TH:i:s",
3005 UniversalSortableDateTime: "Y-m-d H:i:sO",
3012 var dt = new Date();
3013 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3015 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3016 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3021 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3022 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3023 * They generate precompiled functions from format patterns instead of parsing and
3024 * processing each pattern every time a date is formatted. These functions are available
3025 * on every Date object.
3030 // create private copy of Ext's Ext.util.Format.format() method
3031 // - to remove unnecessary dependency
3032 // - to resolve namespace conflict with MS-Ajax's implementation
3033 function xf(format) {
3034 var args = Array.prototype.slice.call(arguments, 1);
3035 return format.replace(/\{(\d+)\}/g, function(m, i) {
3042 * Returns the current timestamp
3043 * @return {Date} The current timestamp
3045 now: Date.now || function() {
3053 toString: function(date) {
3054 var pad = Ext.String.leftPad;
3056 return date.getFullYear() + "-"
3057 + pad(date.getMonth() + 1, 2, '0') + "-"
3058 + pad(date.getDate(), 2, '0') + "T"
3059 + pad(date.getHours(), 2, '0') + ":"
3060 + pad(date.getMinutes(), 2, '0') + ":"
3061 + pad(date.getSeconds(), 2, '0');
3065 * Returns the number of milliseconds between two dates
3066 * @param {Date} dateA The first date
3067 * @param {Date} dateB (optional) The second date, defaults to now
3068 * @return {Number} The difference in milliseconds
3070 getElapsed: function(dateA, dateB) {
3071 return Math.abs(dateA - (dateB || new Date()));
3075 * Global flag which determines if strict date parsing should be used.
3076 * Strict date parsing will not roll-over invalid dates, which is the
3077 * default behaviour of javascript Date objects.
3078 * (see {@link #parse} for more information)
3079 * Defaults to <tt>false</tt>.
3086 formatCodeToRegex: function(character, currentGroup) {
3087 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3088 var p = utilDate.parseCodes[character];
3091 p = typeof p == 'function'? p() : p;
3092 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3095 return p ? Ext.applyIf({
3096 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3100 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3105 * <p>An object hash in which each property is a date parsing function. The property name is the
3106 * format string which that function parses.</p>
3107 * <p>This object is automatically populated with date parsing functions as
3108 * date formats are requested for Ext standard formatting strings.</p>
3109 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3110 * may be used as a format string to {@link #parse}.<p>
3111 * <p>Example:</p><pre><code>
3112 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3114 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3115 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3116 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3117 * (i.e. prevent javascript Date "rollover") (The default must be false).
3118 * Invalid date strings should return null when parsed.</div></li>
3120 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3121 * formatting function must be placed into the {@link #formatFunctions} property.
3122 * @property parseFunctions
3127 "MS": function(input, strict) {
3128 // note: the timezone offset is ignored since the MS Ajax server sends
3129 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3130 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3131 var r = (input || '').match(re);
3132 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3138 * <p>An object hash in which each property is a date formatting function. The property name is the
3139 * format string which corresponds to the produced formatted date string.</p>
3140 * <p>This object is automatically populated with date formatting functions as
3141 * date formats are requested for Ext standard formatting strings.</p>
3142 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3143 * may be used as a format string to {@link #format}. Example:</p><pre><code>
3144 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3146 * <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>
3147 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3149 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3150 * parsing function must be placed into the {@link #parseFunctions} property.
3151 * @property formatFunctions
3157 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3158 return '\\/Date(' + this.getTime() + ')\\/';
3165 * Date interval constant
3172 * Date interval constant
3179 * Date interval constant
3185 /** Date interval constant
3192 * Date interval constant
3199 * Date interval constant
3206 * Date interval constant
3213 * <p>An object hash containing default date values used during date parsing.</p>
3214 * <p>The following properties are available:<div class="mdetail-params"><ul>
3215 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3216 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3217 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3218 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3219 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3220 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3221 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3223 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3224 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3225 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3226 * It is the responsiblity of the developer to account for this.</b></p>
3229 // set default day value to the first day of the month
3230 Ext.Date.defaults.d = 1;
3232 // parse a February date string containing only year and month values.
3233 // setting the default day value to 1 prevents weird date rollover issues
3234 // when attempting to parse the following date string on, for example, March 31st 2009.
3235 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3237 * @property defaults
3244 * An array of textual day names.
3245 * Override these values for international dates.
3248 Ext.Date.dayNames = [
3268 * An array of textual month names.
3269 * Override these values for international dates.
3272 Ext.Date.monthNames = [
3297 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3298 * Override these values for international dates.
3301 Ext.Date.monthNumbers = {
3302 'ShortJanNameInYourLang':0,
3303 'ShortFebNameInYourLang':1,
3325 * <p>The date format string that the {@link #dateRenderer} and {@link #date} functions use.
3326 * see {@link #Date} for details.</p>
3327 * <p>This defaults to <code>m/d/Y</code>, but may be overridden in a locale file.</p>
3328 * @property defaultFormat
3332 defaultFormat : "m/d/Y",
3334 * Get the short month name for the given month number.
3335 * Override this function for international dates.
3336 * @param {Number} month A zero-based javascript month number.
3337 * @return {String} The short month name.
3340 getShortMonthName : function(month) {
3341 return utilDate.monthNames[month].substring(0, 3);
3345 * Get the short day name for the given day number.
3346 * Override this function for international dates.
3347 * @param {Number} day A zero-based javascript day number.
3348 * @return {String} The short day name.
3351 getShortDayName : function(day) {
3352 return utilDate.dayNames[day].substring(0, 3);
3356 * Get the zero-based javascript month number for the given short/full month name.
3357 * Override this function for international dates.
3358 * @param {String} name The short/full month name.
3359 * @return {Number} The zero-based javascript month number.
3362 getMonthNumber : function(name) {
3363 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3364 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3368 * Checks if the specified format contains hour information
3369 * @param {String} format The format to check
3370 * @return {Boolean} True if the format contains hour information
3373 formatContainsHourInfo : (function(){
3374 var stripEscapeRe = /(\\.)/g,
3375 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3376 return function(format){
3377 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3382 * Checks if the specified format contains information about
3383 * anything other than the time.
3384 * @param {String} format The format to check
3385 * @return {Boolean} True if the format contains information about
3386 * date/day information.
3389 formatContainsDateInfo : (function(){
3390 var stripEscapeRe = /(\\.)/g,
3391 dateInfoRe = /([djzmnYycU]|MS)/;
3393 return function(format){
3394 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3399 * The base format-code to formatting-function hashmap used by the {@link #format} method.
3400 * Formatting functions are strings (or functions which return strings) which
3401 * will return the appropriate value when evaluated in the context of the Date object
3402 * from which the {@link #format} method is called.
3403 * Add to / override these mappings for custom date formatting.
3404 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3407 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3408 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3414 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3415 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3416 j: "this.getDate()",
3417 l: "Ext.Date.dayNames[this.getDay()]",
3418 N: "(this.getDay() ? this.getDay() : 7)",
3419 S: "Ext.Date.getSuffix(this)",
3421 z: "Ext.Date.getDayOfYear(this)",
3422 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3423 F: "Ext.Date.monthNames[this.getMonth()]",
3424 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3425 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3426 n: "(this.getMonth() + 1)",
3427 t: "Ext.Date.getDaysInMonth(this)",
3428 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3429 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3430 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3431 y: "('' + this.getFullYear()).substring(2, 4)",
3432 a: "(this.getHours() < 12 ? 'am' : 'pm')",
3433 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3434 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3435 G: "this.getHours()",
3436 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3437 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3438 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3439 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3440 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3441 O: "Ext.Date.getGMTOffset(this)",
3442 P: "Ext.Date.getGMTOffset(this, true)",
3443 T: "Ext.Date.getTimezone(this)",
3444 Z: "(this.getTimezoneOffset() * -60)",
3446 c: function() { // ISO-8601 -- GMT format
3447 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3448 var e = c.charAt(i);
3449 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3451 return code.join(" + ");
3454 c: function() { // ISO-8601 -- UTC format
3456 "this.getUTCFullYear()", "'-'",
3457 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3458 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3460 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3461 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3462 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3468 U: "Math.round(this.getTime() / 1000)"
3472 * Checks if the passed Date parameters will cause a javascript Date "rollover".
3473 * @param {Number} year 4-digit year
3474 * @param {Number} month 1-based month-of-year
3475 * @param {Number} day Day of month
3476 * @param {Number} hour (optional) Hour
3477 * @param {Number} minute (optional) Minute
3478 * @param {Number} second (optional) Second
3479 * @param {Number} millisecond (optional) Millisecond
3480 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3483 isValid : function(y, m, d, h, i, s, ms) {
3490 // Special handling for year < 100
3491 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3493 return y == dt.getFullYear() &&
3494 m == dt.getMonth() + 1 &&
3495 d == dt.getDate() &&
3496 h == dt.getHours() &&
3497 i == dt.getMinutes() &&
3498 s == dt.getSeconds() &&
3499 ms == dt.getMilliseconds();
3503 * Parses the passed string using the specified date format.
3504 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3505 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3506 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3507 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3508 * Keep in mind that the input date string must precisely match the specified format string
3509 * in order for the parse operation to be successful (failed parse operations return a null value).
3510 * <p>Example:</p><pre><code>
3511 //dt = Fri May 25 2007 (current date)
3512 var dt = new Date();
3514 //dt = Thu May 25 2006 (today's month/day in 2006)
3515 dt = Ext.Date.parse("2006", "Y");
3517 //dt = Sun Jan 15 2006 (all date parts specified)
3518 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3520 //dt = Sun Jan 15 2006 15:20:01
3521 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3523 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3524 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3526 * @param {String} input The raw date string.
3527 * @param {String} format The expected date string format.
3528 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3529 (defaults to false). Invalid date strings will return null when parsed.
3530 * @return {Date} The parsed Date.
3533 parse : function(input, format, strict) {
3534 var p = utilDate.parseFunctions;
3535 if (p[format] == null) {
3536 utilDate.createParser(format);
3538 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3542 parseDate: function(input, format, strict){
3543 return utilDate.parse(input, format, strict);
3548 getFormatCode : function(character) {
3549 var f = utilDate.formatCodes[character];
3552 f = typeof f == 'function'? f() : f;
3553 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3556 // note: unknown characters are treated as literals
3557 return f || ("'" + Ext.String.escape(character) + "'");
3561 createFormat : function(format) {
3566 for (var i = 0; i < format.length; ++i) {
3567 ch = format.charAt(i);
3568 if (!special && ch == "\\") {
3570 } else if (special) {
3572 code.push("'" + Ext.String.escape(ch) + "'");
3574 code.push(utilDate.getFormatCode(ch));
3577 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3581 createParser : (function() {
3583 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3584 "def = Ext.Date.defaults,",
3585 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3590 "if(u != null){", // i.e. unix time is defined
3591 "v = new Date(u * 1000);", // give top priority to UNIX time
3593 // create Date object representing midnight of the current day;
3594 // this will provide us with our date defaults
3595 // (note: clearTime() handles Daylight Saving Time automatically)
3596 "dt = Ext.Date.clearTime(new Date);",
3598 // date calculations (note: these calculations create a dependency on Ext.Number.from())
3599 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3600 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3601 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3603 // time calculations (note: these calculations create a dependency on Ext.Number.from())
3604 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3605 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3606 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3607 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3609 "if(z >= 0 && y >= 0){",
3610 // both the year and zero-based day of year are defined and >= 0.
3611 // these 2 values alone provide sufficient info to create a full date object
3613 // create Date object representing January 1st for the given year
3614 // handle years < 100 appropriately
3615 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3617 // then add day of year, checking for Date "rollover" if necessary
3618 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3619 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3620 "v = null;", // invalid date, so return null
3622 // plain old Date object
3623 // handle years < 100 properly
3624 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3630 // favour UTC offset over GMT offset
3632 // reset to UTC, then add offset
3633 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3635 // reset to GMT, then add offset
3636 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
3643 return function(format) {
3644 var regexNum = utilDate.parseRegexes.length,
3651 for (var i = 0; i < format.length; ++i) {
3652 ch = format.charAt(i);
3653 if (!special && ch == "\\") {
3655 } else if (special) {
3657 regex.push(Ext.String.escape(ch));
3659 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
3660 currentGroup += obj.g;
3662 if (obj.g && obj.c) {
3668 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
3669 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
3677 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
3678 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
3679 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
3683 c:"d = parseInt(results[{0}], 10);\n",
3684 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
3688 c:"d = parseInt(results[{0}], 10);\n",
3689 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
3692 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
3696 s:"(?:" + a.join("|") +")"
3703 s:"(?:" + utilDate.dayNames.join("|") + ")"
3709 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
3719 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
3723 c:"z = parseInt(results[{0}], 10);\n",
3724 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
3729 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
3734 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
3735 s:"(" + utilDate.monthNames.join("|") + ")"
3739 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
3740 return Ext.applyIf({
3741 s:"(" + a.join("|") + ")"
3742 }, utilDate.formatCodeToRegex("F"));
3746 c:"m = parseInt(results[{0}], 10) - 1;\n",
3747 s:"(\\d{2})" // month number with leading zeros (01 - 12)
3751 c:"m = parseInt(results[{0}], 10) - 1;\n",
3752 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
3757 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
3765 return utilDate.formatCodeToRegex("Y");
3769 c:"y = parseInt(results[{0}], 10);\n",
3770 s:"(\\d{4})" // 4-digit year
3774 c:"var ty = parseInt(results[{0}], 10);\n"
3775 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
3779 * In the am/pm parsing routines, we allow both upper and lower case
3780 * even though it doesn't exactly match the spec. It gives much more flexibility
3781 * in being able to specify case insensitive regexes.
3785 c:"if (/(am)/i.test(results[{0}])) {\n"
3786 + "if (!h || h == 12) { h = 0; }\n"
3787 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
3792 c:"if (/(am)/i.test(results[{0}])) {\n"
3793 + "if (!h || h == 12) { h = 0; }\n"
3794 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
3798 return utilDate.formatCodeToRegex("G");
3802 c:"h = parseInt(results[{0}], 10);\n",
3803 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
3806 return utilDate.formatCodeToRegex("H");
3810 c:"h = parseInt(results[{0}], 10);\n",
3811 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
3815 c:"i = parseInt(results[{0}], 10);\n",
3816 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
3820 c:"s = parseInt(results[{0}], 10);\n",
3821 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
3825 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
3826 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
3831 "o = results[{0}];",
3832 "var sn = o.substring(0,1),", // get + / - sign
3833 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
3834 "mn = o.substring(3,5) % 60;", // get minutes
3835 "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
3837 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
3842 "o = results[{0}];",
3843 "var sn = o.substring(0,1),", // get + / - sign
3844 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
3845 "mn = o.substring(4,6) % 60;", // get minutes
3846 "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
3848 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
3853 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
3857 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
3858 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
3859 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
3864 utilDate.formatCodeToRegex("Y", 1), // year
3865 utilDate.formatCodeToRegex("m", 2), // month
3866 utilDate.formatCodeToRegex("d", 3), // day
3867 utilDate.formatCodeToRegex("h", 4), // hour
3868 utilDate.formatCodeToRegex("i", 5), // minute
3869 utilDate.formatCodeToRegex("s", 6), // second
3870 {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)
3871 {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
3872 "if(results[8]) {", // timezone specified
3873 "if(results[8] == 'Z'){",
3875 "}else if (results[8].indexOf(':') > -1){",
3876 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
3878 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
3884 for (var i = 0, l = arr.length; i < l; ++i) {
3885 calc.push(arr[i].c);
3892 arr[0].s, // year (required)
3893 "(?:", "-", arr[1].s, // month (optional)
3894 "(?:", "-", arr[2].s, // day (optional)
3896 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
3897 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
3898 "(?::", arr[5].s, ")?", // seconds (optional)
3899 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
3900 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
3909 c:"u = parseInt(results[{0}], 10);\n",
3910 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
3914 //Old Ext.Date prototype methods.
3916 dateFormat: function(date, format) {
3917 return utilDate.format(date, format);
3921 * Formats a date given the supplied format string.
3922 * @param {Date} date The date to format
3923 * @param {String} format The format string
3924 * @return {String} The formatted date
3926 format: function(date, format) {
3927 if (utilDate.formatFunctions[format] == null) {
3928 utilDate.createFormat(format);
3930 var result = utilDate.formatFunctions[format].call(date);
3935 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
3937 * Note: The date string returned by the javascript Date object's toString() method varies
3938 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
3939 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
3940 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
3941 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
3942 * from the GMT offset portion of the date string.
3943 * @param {Date} date The date
3944 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
3946 getTimezone : function(date) {
3947 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
3949 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
3950 // 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)
3951 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
3952 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
3953 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
3955 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
3956 // step 1: (?:\((.*)\) -- find timezone in parentheses
3957 // 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
3958 // step 3: remove all non uppercase characters found in step 1 and 2
3959 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
3963 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
3964 * @param {Date} date The date
3965 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
3966 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
3968 getGMTOffset : function(date, colon) {
3969 var offset = date.getTimezoneOffset();
3970 return (offset > 0 ? "-" : "+")
3971 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
3972 + (colon ? ":" : "")
3973 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
3977 * Get the numeric day number of the year, adjusted for leap year.
3978 * @param {Date} date The date
3979 * @return {Number} 0 to 364 (365 in leap years).
3981 getDayOfYear: function(date) {
3983 d = Ext.Date.clone(date),
3984 m = date.getMonth(),
3987 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
3988 num += utilDate.getDaysInMonth(d);
3990 return num + date.getDate() - 1;
3994 * Get the numeric ISO-8601 week number of the year.
3995 * (equivalent to the format specifier 'W', but without a leading zero).
3996 * @param {Date} date The date
3997 * @return {Number} 1 to 53
3999 getWeekOfYear : (function() {
4000 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4001 var ms1d = 864e5, // milliseconds in a day
4002 ms7d = 7 * ms1d; // milliseconds in a week
4004 return function(date) { // return a closure so constants get calculated only once
4005 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4006 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4007 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4009 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4014 * Checks if the current date falls within a leap year.
4015 * @param {Date} date The date
4016 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4018 isLeapYear : function(date) {
4019 var year = date.getFullYear();
4020 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4024 * Get the first day of the current month, adjusted for leap year. The returned value
4025 * is the numeric day index within the week (0-6) which can be used in conjunction with
4026 * the {@link #monthNames} array to retrieve the textual day name.
4029 var dt = new Date('1/10/2007'),
4030 firstDay = Ext.Date.getFirstDayOfMonth(dt);
4031 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4033 * @param {Date} date The date
4034 * @return {Number} The day number (0-6).
4036 getFirstDayOfMonth : function(date) {
4037 var day = (date.getDay() - (date.getDate() - 1)) % 7;
4038 return (day < 0) ? (day + 7) : day;
4042 * Get the last day of the current month, adjusted for leap year. The returned value
4043 * is the numeric day index within the week (0-6) which can be used in conjunction with
4044 * the {@link #monthNames} array to retrieve the textual day name.
4047 var dt = new Date('1/10/2007'),
4048 lastDay = Ext.Date.getLastDayOfMonth(dt);
4049 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4051 * @param {Date} date The date
4052 * @return {Number} The day number (0-6).
4054 getLastDayOfMonth : function(date) {
4055 return utilDate.getLastDateOfMonth(date).getDay();
4060 * Get the date of the first day of the month in which this date resides.
4061 * @param {Date} date The date
4064 getFirstDateOfMonth : function(date) {
4065 return new Date(date.getFullYear(), date.getMonth(), 1);
4069 * Get the date of the last day of the month in which this date resides.
4070 * @param {Date} date The date
4073 getLastDateOfMonth : function(date) {
4074 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4078 * Get the number of days in the current month, adjusted for leap year.
4079 * @param {Date} date The date
4080 * @return {Number} The number of days in the month.
4082 getDaysInMonth: (function() {
4083 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4085 return function(date) { // return a closure for efficiency
4086 var m = date.getMonth();
4088 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4093 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4094 * @param {Date} date The date
4095 * @return {String} 'st, 'nd', 'rd' or 'th'.
4097 getSuffix : function(date) {
4098 switch (date.getDate()) {
4115 * Creates and returns a new Date instance with the exact same date value as the called instance.
4116 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4117 * variable will also be changed. When the intention is to create a new variable that will not
4118 * modify the original instance, you should create a clone.
4120 * Example of correctly cloning a date:
4123 var orig = new Date('10/1/2006');
4126 console.log(orig); //returns 'Thu Oct 05 2006'!
4129 var orig = new Date('10/1/2006'),
4130 copy = Ext.Date.clone(orig);
4132 console.log(orig); //returns 'Thu Oct 01 2006'
4134 * @param {Date} date The date
4135 * @return {Date} The new Date instance.
4137 clone : function(date) {
4138 return new Date(date.getTime());
4142 * Checks if the current date is affected by Daylight Saving Time (DST).
4143 * @param {Date} date The date
4144 * @return {Boolean} True if the current date is affected by DST.
4146 isDST : function(date) {
4147 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4148 // courtesy of @geoffrey.mcgill
4149 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4153 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4154 * automatically adjusting for Daylight Saving Time (DST) where applicable.
4155 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4156 * @param {Date} date The date
4157 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4158 * @return {Date} this or the clone.
4160 clearTime : function(date, clone) {
4162 return Ext.Date.clearTime(Ext.Date.clone(date));
4165 // get current date before clearing time
4166 var d = date.getDate();
4172 date.setMilliseconds(0);
4174 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4175 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4176 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4178 // increment hour until cloned date == current date
4179 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4182 date.setHours(c.getHours());
4189 * Provides a convenient method for performing basic date arithmetic. This method
4190 * does not modify the Date instance being called - it creates and returns
4191 * a new Date instance containing the resulting date value.
4196 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4197 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4199 // Negative values will be subtracted:
4200 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4201 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4205 * @param {Date} date The date to modify
4206 * @param {String} interval A valid date interval enum value.
4207 * @param {Number} value The amount to add to the current date.
4208 * @return {Date} The new Date instance.
4210 add : function(date, interval, value) {
4211 var d = Ext.Date.clone(date),
4213 if (!interval || value === 0) return d;
4215 switch(interval.toLowerCase()) {
4216 case Ext.Date.MILLI:
4217 d.setMilliseconds(d.getMilliseconds() + value);
4219 case Ext.Date.SECOND:
4220 d.setSeconds(d.getSeconds() + value);
4222 case Ext.Date.MINUTE:
4223 d.setMinutes(d.getMinutes() + value);
4226 d.setHours(d.getHours() + value);
4229 d.setDate(d.getDate() + value);
4231 case Ext.Date.MONTH:
4232 var day = date.getDate();
4234 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4237 d.setMonth(date.getMonth() + value);
4240 d.setFullYear(date.getFullYear() + value);
4247 * Checks if a date falls on or between the given start and end dates.
4248 * @param {Date} date The date to check
4249 * @param {Date} start Start date
4250 * @param {Date} end End date
4251 * @return {Boolean} true if this date falls on or between the given start and end dates.
4253 between : function(date, start, end) {
4254 var t = date.getTime();
4255 return start.getTime() <= t && t <= end.getTime();
4258 //Maintains compatibility with old static and prototype window.Date methods.
4259 compat: function() {
4260 var nativeDate = window.Date,
4262 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'],
4263 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4266 Ext.Array.forEach(statics, function(s) {
4267 nativeDate[s] = utilDate[s];
4270 //Append to prototype
4271 Ext.Array.forEach(proto, function(s) {
4272 nativeDate.prototype[s] = function() {
4273 var args = Array.prototype.slice.call(arguments);
4275 return utilDate[s].apply(utilDate, args);
4281 var utilDate = Ext.Date;
4286 * @author Jacky Nguyen <jacky@sencha.com>
4287 * @docauthor Jacky Nguyen <jacky@sencha.com>
4290 * The root of all classes created with {@link Ext#define}
4291 * All prototype and static members of this class are inherited by any other class
4294 (function(flexSetter) {
4296 var Base = Ext.Base = function() {};
4298 $className: 'Ext.Base',
4303 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4304 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4305 * for a detailed comparison
4307 Ext.define('My.Cat', {
4309 speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4312 constructor: function() {
4313 alert(this.self.speciesName); / dependent on 'this'
4319 return new this.self();
4324 Ext.define('My.SnowLeopard', {
4327 speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4331 var cat = new My.Cat(); // alerts 'Cat'
4332 var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
4334 var clone = snowLeopard.clone();
4335 alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4344 * Default constructor, simply returns `this`
4348 * @return {Object} this
4350 constructor: function() {
4355 * Initialize configuration for this class. a typical example:
4357 Ext.define('My.awesome.Class', {
4358 // The default config
4364 constructor: function(config) {
4365 this.initConfig(config);
4371 var awesome = new My.awesome.Class({
4372 name: 'Super Awesome'
4375 alert(awesome.getName()); // 'Super Awesome'
4378 * @param {Object} config
4379 * @return {Object} mixins The mixin prototypes as key - value pairs
4382 initConfig: function(config) {
4383 if (!this.$configInited) {
4384 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4386 this.applyConfig(this.config);
4388 this.$configInited = true;
4397 setConfig: function(config) {
4398 this.applyConfig(config || {});
4406 applyConfig: flexSetter(function(name, value) {
4407 var setter = 'set' + Ext.String.capitalize(name);
4409 if (typeof this[setter] === 'function') {
4410 this[setter].call(this, value);
4417 * Call the parent's overridden method. For example:
4419 Ext.define('My.own.A', {
4420 constructor: function(test) {
4425 Ext.define('My.own.B', {
4428 constructor: function(test) {
4431 this.callParent([test + 1]);
4435 Ext.define('My.own.C', {
4438 constructor: function() {
4439 alert("Going to call parent's overriden constructor...");
4441 this.callParent(arguments);
4445 var a = new My.own.A(1); // alerts '1'
4446 var b = new My.own.B(1); // alerts '1', then alerts '2'
4447 var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4448 // alerts '2', then alerts '3'
4451 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4452 * from the current method, for example: `this.callParent(arguments)`
4453 * @return {Mixed} Returns the result from the superclass' method
4456 callParent: function(args) {
4457 var method = this.callParent.caller,
4458 parentClass, methodName;
4460 if (!method.$owner) {
4461 if (!method.caller) {
4463 sourceClass: Ext.getClassName(this),
4464 sourceMethod: "callParent",
4465 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4469 method = method.caller;
4472 parentClass = method.$owner.superclass;
4473 methodName = method.$name;
4475 if (!(methodName in parentClass)) {
4477 sourceClass: Ext.getClassName(this),
4478 sourceMethod: methodName,
4479 msg: "this.callParent() was called but there's no such method (" + methodName +
4480 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4484 return parentClass[methodName].apply(this, args || []);
4489 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4490 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4491 * `this` points to during run-time
4493 Ext.define('My.Cat', {
4496 speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4499 constructor: function() {
4500 var statics = this.statics();
4502 alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
4503 // equivalent to: My.Cat.speciesName
4505 alert(this.self.speciesName); // dependent on 'this'
4507 statics.totalCreated++;
4513 var cloned = new this.self; // dependent on 'this'
4515 cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
4522 Ext.define('My.SnowLeopard', {
4526 speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4529 constructor: function() {
4534 var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
4536 var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4538 var clone = snowLeopard.clone();
4539 alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4540 alert(clone.groupName); // alerts 'Cat'
4542 alert(My.Cat.totalCreated); // alerts 3
4548 statics: function() {
4549 var method = this.statics.caller,
4556 return method.$owner;
4560 * Call the original method that was previously overridden with {@link Ext.Base#override}
4562 Ext.define('My.Cat', {
4563 constructor: function() {
4564 alert("I'm a cat!");
4571 constructor: function() {
4572 alert("I'm going to be a cat!");
4574 var instance = this.callOverridden();
4576 alert("Meeeeoooowwww");
4582 var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4583 // alerts "I'm a cat!"
4584 // alerts "Meeeeoooowwww"
4586 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4587 * @return {Mixed} Returns the result after calling the overridden method
4590 callOverridden: function(args) {
4591 var method = this.callOverridden.caller;
4593 if (!method.$owner) {
4595 sourceClass: Ext.getClassName(this),
4596 sourceMethod: "callOverridden",
4597 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4601 if (!method.$previous) {
4603 sourceClass: Ext.getClassName(this),
4604 sourceMethod: "callOverridden",
4605 msg: "this.callOverridden was called in '" + method.$name +
4606 "' but this method has never been overridden"
4610 return method.$previous.apply(this, args || []);
4613 destroy: function() {}
4616 // These static properties will be copied to every newly created class with {@link Ext#define}
4617 Ext.apply(Ext.Base, {
4619 * Create a new instance of this Class.
4620 Ext.define('My.cool.Class', {
4624 My.cool.Class.create({
4632 create: function() {
4633 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
4639 own: flexSetter(function(name, value) {
4640 if (typeof value === 'function') {
4641 this.ownMethod(name, value);
4644 this.prototype[name] = value;
4651 ownMethod: function(name, fn) {
4654 if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
4658 return originalFn.apply(this, arguments);
4663 className = Ext.getClassName(this);
4665 fn.displayName = className + '#' + name;
4670 this.prototype[name] = fn;
4674 * Add / override static properties of this class.
4676 Ext.define('My.cool.Class', {
4680 My.cool.Class.addStatics({
4681 someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
4682 method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
4683 method2: function() { ... } // My.cool.Class.method2 = function() { ... };
4686 * @property addStatics
4689 * @param {Object} members
4692 addStatics: function(members) {
4693 for (var name in members) {
4694 if (members.hasOwnProperty(name)) {
4695 this[name] = members[name];
4703 * Add methods / properties to the prototype of this class.
4705 Ext.define('My.awesome.Cat', {
4706 constructor: function() {
4711 My.awesome.Cat.implement({
4717 var kitty = new My.awesome.Cat;
4720 * @property implement
4723 * @param {Object} members
4726 implement: function(members) {
4727 var prototype = this.prototype,
4728 name, i, member, previous;
4729 var className = Ext.getClassName(this);
4730 for (name in members) {
4731 if (members.hasOwnProperty(name)) {
4732 member = members[name];
4734 if (typeof member === 'function') {
4735 member.$owner = this;
4736 member.$name = name;
4738 member.displayName = className + '#' + name;
4742 prototype[name] = member;
4746 if (Ext.enumerables) {
4747 var enumerables = Ext.enumerables;
4749 for (i = enumerables.length; i--;) {
4750 name = enumerables[i];
4752 if (members.hasOwnProperty(name)) {
4753 member = members[name];
4754 member.$owner = this;
4755 member.$name = name;
4756 prototype[name] = member;
4763 * Borrow another class' members to the prototype of this class.
4765 Ext.define('Bank', {
4767 printMoney: function() {
4772 Ext.define('Thief', {
4776 Thief.borrow(Bank, ['money', 'printMoney']);
4778 var steve = new Thief();
4780 alert(steve.money); // alerts '$$$'
4781 steve.printMoney(); // alerts '$$$$$$$'
4786 * @param {Ext.Base} fromClass The class to borrow members from
4787 * @param {Array/String} members The names of the members to borrow
4788 * @return {Ext.Base} this
4791 borrow: function(fromClass, members) {
4792 var fromPrototype = fromClass.prototype,
4795 members = Ext.Array.from(members);
4797 for (i = 0, ln = members.length; i < ln; i++) {
4798 member = members[i];
4800 this.own(member, fromPrototype[member]);
4807 * Override prototype members of this class. Overridden methods can be invoked via
4808 * {@link Ext.Base#callOverridden}
4810 Ext.define('My.Cat', {
4811 constructor: function() {
4812 alert("I'm a cat!");
4819 constructor: function() {
4820 alert("I'm going to be a cat!");
4822 var instance = this.callOverridden();
4824 alert("Meeeeoooowwww");
4830 var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4831 // alerts "I'm a cat!"
4832 // alerts "Meeeeoooowwww"
4834 * @property override
4837 * @param {Object} members
4838 * @return {Ext.Base} this
4841 override: function(members) {
4842 var prototype = this.prototype,
4843 name, i, member, previous;
4845 for (name in members) {
4846 if (members.hasOwnProperty(name)) {
4847 member = members[name];
4849 if (typeof member === 'function') {
4850 if (typeof prototype[name] === 'function') {
4851 previous = prototype[name];
4852 member.$previous = previous;
4855 this.ownMethod(name, member);
4858 prototype[name] = member;
4863 if (Ext.enumerables) {
4864 var enumerables = Ext.enumerables;
4866 for (i = enumerables.length; i--;) {
4867 name = enumerables[i];
4869 if (members.hasOwnProperty(name)) {
4870 if (prototype[name] !== undefined) {
4871 previous = prototype[name];
4872 members[name].$previous = previous;
4875 this.ownMethod(name, members[name]);
4884 * Used internally by the mixins pre-processor
4887 mixin: flexSetter(function(name, cls) {
4888 var mixin = cls.prototype,
4889 my = this.prototype,
4893 if (mixin.hasOwnProperty(i)) {
4894 if (my[i] === undefined) {
4895 if (typeof mixin[i] === 'function') {
4898 if (fn.$owner === undefined) {
4899 this.ownMethod(i, fn);
4909 else if (i === 'config' && my.config && mixin.config) {
4910 Ext.Object.merge(my.config, mixin.config);
4915 if (my.mixins === undefined) {
4919 my.mixins[name] = mixin;
4923 * Get the current class' name in string format.
4925 Ext.define('My.cool.Class', {
4926 constructor: function() {
4927 alert(this.self.getName()); // alerts 'My.cool.Class'
4931 My.cool.Class.getName(); // 'My.cool.Class'
4933 * @return {String} className
4936 getName: function() {
4937 return Ext.getClassName(this);
4941 * Create aliases for existing prototype methods. Example:
4943 Ext.define('My.cool.Class', {
4944 method1: function() { ... },
4945 method2: function() { ... }
4948 var test = new My.cool.Class();
4950 My.cool.Class.createAlias({
4955 test.method3(); // test.method1()
4957 My.cool.Class.createAlias('method5', 'method3');
4959 test.method5(); // test.method3() -> test.method1()
4961 * @property createAlias
4964 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
4965 * {@link Ext.Function#flexSetter flexSetter}
4966 * @param {String/Object} origin The original method name
4969 createAlias: flexSetter(function(alias, origin) {
4970 this.prototype[alias] = this.prototype[origin];
4974 })(Ext.Function.flexSetter);
4977 * @author Jacky Nguyen <jacky@sencha.com>
4978 * @docauthor Jacky Nguyen <jacky@sencha.com>
4981 * Handles class creation throughout the whole framework. Note that most of the time {@link Ext#define Ext.define} should
4982 * be used instead, since it's a higher level wrapper that aliases to {@link Ext.ClassManager#create}
4983 * to enable namespacing and dynamic dependency resolution.
4987 * Ext.define(className, properties);
4989 * in which `properties` is an object represent a collection of properties that apply to the class. See
4990 * {@link Ext.ClassManager#create} for more detailed instructions.
4992 * Ext.define('Person', {
4995 * constructor: function(name) {
5003 * eat: function(foodType) {
5004 * alert("I'm eating: " + foodType);
5010 * var aaron = new Person("Aaron");
5011 * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5013 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5014 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5018 * Ext.define('Developer', {
5021 * constructor: function(name, isGeek) {
5022 * this.isGeek = isGeek;
5024 * // Apply a method from the parent class' prototype
5025 * this.callParent([name]);
5031 * code: function(language) {
5032 * alert("I'm coding in: " + language);
5040 * var jacky = new Developer("Jacky", true);
5041 * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
5042 * // alert("I'm eating: Bugs");
5044 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
5048 * Ext.define('CanPlayGuitar', {
5049 * playGuitar: function() {
5050 * alert("F#...G...D...A");
5054 * Ext.define('CanComposeSongs', {
5055 * composeSongs: function() { ... }
5058 * Ext.define('CanSing', {
5059 * sing: function() {
5060 * alert("I'm on the highway to hell...")
5064 * Ext.define('Musician', {
5068 * canPlayGuitar: 'CanPlayGuitar',
5069 * canComposeSongs: 'CanComposeSongs',
5070 * canSing: 'CanSing'
5074 * Ext.define('CoolPerson', {
5078 * canPlayGuitar: 'CanPlayGuitar',
5079 * canSing: 'CanSing'
5082 * sing: function() {
5083 * alert("Ahem....");
5085 * this.mixins.canSing.sing.call(this);
5087 * alert("[Playing guitar at the same time...]");
5089 * this.playGuitar();
5093 * var me = new CoolPerson("Jacky");
5095 * me.sing(); // alert("Ahem...");
5096 * // alert("I'm on the highway to hell...");
5097 * // alert("[Playing guitar at the same time...]");
5098 * // alert("F#...G...D...A");
5102 * Ext.define('SmartPhone', {
5104 * hasTouchScreen: false,
5105 * operatingSystem: 'Other',
5109 * isExpensive: false,
5111 * constructor: function(config) {
5112 * this.initConfig(config);
5117 * applyPrice: function(price) {
5118 * this.isExpensive = (price > 500);
5123 * applyOperatingSystem: function(operatingSystem) {
5124 * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
5128 * return operatingSystem;
5132 * var iPhone = new SmartPhone({
5133 * hasTouchScreen: true,
5134 * operatingSystem: 'iOS'
5137 * iPhone.getPrice(); // 500;
5138 * iPhone.getOperatingSystem(); // 'iOS'
5139 * iPhone.getHasTouchScreen(); // true;
5140 * iPhone.hasTouchScreen(); // true
5142 * iPhone.isExpensive; // false;
5143 * iPhone.setPrice(600);
5144 * iPhone.getPrice(); // 600
5145 * iPhone.isExpensive; // true;
5147 * iPhone.setOperatingSystem('AlienOS');
5148 * iPhone.getOperatingSystem(); // 'Other'
5152 * Ext.define('Computer', {
5154 * factory: function(brand) {
5155 * // 'this' in static methods refer to the class itself
5156 * return new this(brand);
5160 * constructor: function() { ... }
5163 * var dellComputer = Computer.factory('Dell');
5165 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
5166 * static properties within class methods
5173 baseStaticProperties = [],
5176 for (baseStaticProperty in Base) {
5177 if (Base.hasOwnProperty(baseStaticProperty)) {
5178 baseStaticProperties.push(baseStaticProperty);
5184 * @param {Object} classData An object represent the properties of this class
5185 * @param {Function} createdFn Optional, the callback function to be executed when this class is fully created.
5186 * Note that the creation process can be asynchronous depending on the pre-processors used.
5187 * @return {Ext.Base} The newly created class
5189 Ext.Class = Class = function(newClass, classData, onClassCreated) {
5190 if (typeof newClass !== 'function') {
5191 onClassCreated = classData;
5192 classData = newClass;
5193 newClass = function() {
5194 return this.constructor.apply(this, arguments);
5202 var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5203 registeredPreprocessors = Class.getPreprocessors(),
5206 preprocessor, preprocessors, staticPropertyName, process, i, j, ln;
5208 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5209 staticPropertyName = baseStaticProperties[i];
5210 newClass[staticPropertyName] = Base[staticPropertyName];
5213 delete classData.preprocessors;
5215 for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5216 preprocessor = preprocessorStack[j];
5218 if (typeof preprocessor === 'string') {
5219 preprocessor = registeredPreprocessors[preprocessor];
5221 if (!preprocessor.always) {
5222 if (classData.hasOwnProperty(preprocessor.name)) {
5223 preprocessors.push(preprocessor.fn);
5227 preprocessors.push(preprocessor.fn);
5231 preprocessors.push(preprocessor);
5235 classData.onClassCreated = onClassCreated;
5237 classData.onBeforeClassCreated = function(cls, data) {
5238 onClassCreated = data.onClassCreated;
5240 delete data.onBeforeClassCreated;
5241 delete data.onClassCreated;
5243 cls.implement(data);
5245 if (onClassCreated) {
5246 onClassCreated.call(cls, cls);
5250 process = function(cls, data) {
5251 preprocessor = preprocessors[index++];
5253 if (!preprocessor) {
5254 data.onBeforeClassCreated.apply(this, arguments);
5258 if (preprocessor.call(this, cls, data, process) !== false) {
5259 process.apply(this, arguments);
5263 process.call(Class, newClass, classData);
5274 * Register a new pre-processor to be used during the class creation process
5276 * @member Ext.Class registerPreprocessor
5277 * @param {String} name The pre-processor's name
5278 * @param {Function} fn The callback function to be executed. Typical format:
5280 function(cls, data, fn) {
5283 // Execute this when the processing is finished.
5284 // Asynchronous processing is perfectly ok
5286 fn.call(this, cls, data);
5290 * Passed arguments for this function are:
5292 * - `{Function} cls`: The created class
5293 * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
5294 * - `{Function} fn`: The callback function that <b>must</b> to be executed when this pre-processor finishes,
5295 * regardless of whether the processing is synchronous or aynchronous
5297 * @return {Ext.Class} this
5300 registerPreprocessor: function(name, fn, always) {
5301 this.preprocessors[name] = {
5303 always: always || false,
5311 * Retrieve a pre-processor callback function by its name, which has been registered before
5313 * @param {String} name
5314 * @return {Function} preprocessor
5316 getPreprocessor: function(name) {
5317 return this.preprocessors[name];
5320 getPreprocessors: function() {
5321 return this.preprocessors;
5325 * Retrieve the array stack of default pre-processors
5327 * @return {Function} defaultPreprocessors
5329 getDefaultPreprocessors: function() {
5330 return this.defaultPreprocessors || [];
5334 * Set the default array stack of default pre-processors
5336 * @param {Array} preprocessors
5337 * @return {Ext.Class} this
5339 setDefaultPreprocessors: function(preprocessors) {
5340 this.defaultPreprocessors = Ext.Array.from(preprocessors);
5346 * Insert this pre-processor at a specific position in the stack, optionally relative to
5347 * any existing pre-processor. For example:
5349 Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5353 fn.call(this, cls, data);
5355 }).insertDefaultPreprocessor('debug', 'last');
5357 * @param {String} name The pre-processor name. Note that it needs to be registered with
5358 * {@link Ext#registerPreprocessor registerPreprocessor} before this
5359 * @param {String} offset The insertion position. Four possible values are:
5360 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5361 * @param {String} relativeName
5362 * @return {Ext.Class} this
5365 setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5366 var defaultPreprocessors = this.defaultPreprocessors,
5369 if (typeof offset === 'string') {
5370 if (offset === 'first') {
5371 defaultPreprocessors.unshift(name);
5375 else if (offset === 'last') {
5376 defaultPreprocessors.push(name);
5381 offset = (offset === 'after') ? 1 : -1;
5384 index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5387 defaultPreprocessors.splice(Math.max(0, index + offset), 0, name);
5394 Class.registerPreprocessor('extend', function(cls, data) {
5395 var extend = data.extend,
5397 basePrototype = base.prototype,
5398 prototype = function() {},
5399 parent, i, k, ln, staticName, parentStatics,
5400 parentPrototype, clsPrototype;
5402 if (extend && extend !== Object) {
5409 parentPrototype = parent.prototype;
5411 prototype.prototype = parentPrototype;
5412 clsPrototype = cls.prototype = new prototype();
5414 if (!('$class' in parent)) {
5415 for (i in basePrototype) {
5416 if (!parentPrototype[i]) {
5417 parentPrototype[i] = basePrototype[i];
5422 clsPrototype.self = cls;
5424 cls.superclass = clsPrototype.superclass = parentPrototype;
5428 // Statics inheritance
5429 parentStatics = parentPrototype.$inheritableStatics;
5431 if (parentStatics) {
5432 for (k = 0, ln = parentStatics.length; k < ln; k++) {
5433 staticName = parentStatics[k];
5435 if (!cls.hasOwnProperty(staticName)) {
5436 cls[staticName] = parent[staticName];
5441 // Merge the parent class' config object without referencing it
5442 if (parentPrototype.config) {
5443 clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5446 clsPrototype.config = {};
5449 if (clsPrototype.$onExtended) {
5450 clsPrototype.$onExtended.call(cls, cls, data);
5453 if (data.onClassExtended) {
5454 clsPrototype.$onExtended = data.onClassExtended;
5455 delete data.onClassExtended;
5460 Class.registerPreprocessor('statics', function(cls, data) {
5461 var statics = data.statics,
5464 for (name in statics) {
5465 if (statics.hasOwnProperty(name)) {
5466 cls[name] = statics[name];
5470 delete data.statics;
5473 Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5474 var statics = data.inheritableStatics,
5476 prototype = cls.prototype,
5479 inheritableStatics = prototype.$inheritableStatics;
5481 if (!inheritableStatics) {
5482 inheritableStatics = prototype.$inheritableStatics = [];
5485 for (name in statics) {
5486 if (statics.hasOwnProperty(name)) {
5487 cls[name] = statics[name];
5488 inheritableStatics.push(name);
5492 delete data.inheritableStatics;
5495 Class.registerPreprocessor('mixins', function(cls, data) {
5496 cls.mixin(data.mixins);
5501 Class.registerPreprocessor('config', function(cls, data) {
5502 var prototype = cls.prototype;
5504 Ext.Object.each(data.config, function(name) {
5505 var cName = name.charAt(0).toUpperCase() + name.substr(1),
5507 apply = 'apply' + cName,
5508 setter = 'set' + cName,
5509 getter = 'get' + cName;
5511 if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5512 data[apply] = function(val) {
5517 if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5518 data[setter] = function(val) {
5519 var ret = this[apply].call(this, val, this[pName]);
5521 if (ret !== undefined) {
5529 if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5530 data[getter] = function() {
5536 Ext.Object.merge(prototype.config, data.config);
5540 Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);
5542 // Backwards compatible
5543 Ext.extend = function(subclass, superclass, members) {
5544 if (arguments.length === 2 && Ext.isObject(superclass)) {
5545 members = superclass;
5546 superclass = subclass;
5553 Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5556 members.extend = superclass;
5557 members.preprocessors = ['extend', 'mixins', 'config', 'statics'];
5560 cls = new Class(subclass, members);
5563 cls = new Class(members);
5566 cls.prototype.override = function(o) {
5568 if (o.hasOwnProperty(m)) {
5580 * @author Jacky Nguyen <jacky@sencha.com>
5581 * @docauthor Jacky Nguyen <jacky@sencha.com>
5582 * @class Ext.ClassManager
5584 Ext.ClassManager manages all classes and handles mapping from string class name to
5585 actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5586 these convenient shorthands:
5588 - {@link Ext#define Ext.define}
5589 - {@link Ext#create Ext.create}
5590 - {@link Ext#widget Ext.widget}
5591 - {@link Ext#getClass Ext.getClass}
5592 - {@link Ext#getClassName Ext.getClassName}
5597 (function(Class, alias) {
5599 var slice = Array.prototype.slice;
5601 var Manager = Ext.ClassManager = {
5606 * All classes which were defined through the ClassManager. Keys are the
5607 * name of the classes and the values are references to the classes.
5620 namespaceRewrites: [{
5629 alternateToName: {},
5635 enableNamespaceParseCache: true,
5638 namespaceParseCache: {},
5644 instantiationCounts: {},
5647 * Checks if a class has already been created.
5649 * @param {String} className
5650 * @return {Boolean} exist
5652 isCreated: function(className) {
5653 var i, ln, part, root, parts;
5655 if (typeof className !== 'string' || className.length < 1) {
5657 sourceClass: "Ext.ClassManager",
5658 sourceMethod: "exist",
5659 msg: "Invalid classname, must be a string and must not be empty"
5663 if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
5668 parts = this.parseNamespace(className);
5670 for (i = 0, ln = parts.length; i < ln; i++) {
5673 if (typeof part !== 'string') {
5676 if (!root || !root[part]) {
5684 Ext.Loader.historyPush(className);
5686 this.existCache[className] = true;
5692 * Supports namespace rewriting
5695 parseNamespace: function(namespace) {
5696 if (typeof namespace !== 'string') {
5698 sourceClass: "Ext.ClassManager",
5699 sourceMethod: "parseNamespace",
5700 msg: "Invalid namespace, must be a string"
5704 var cache = this.namespaceParseCache;
5706 if (this.enableNamespaceParseCache) {
5707 if (cache.hasOwnProperty(namespace)) {
5708 return cache[namespace];
5713 rewrites = this.namespaceRewrites,
5714 rewrite, from, to, i, ln, root = Ext.global;
5716 for (i = 0, ln = rewrites.length; i < ln; i++) {
5717 rewrite = rewrites[i];
5718 from = rewrite.from;
5721 if (namespace === from || namespace.substring(0, from.length) === from) {
5722 namespace = namespace.substring(from.length);
5724 if (typeof to !== 'string') {
5727 parts = parts.concat(to.split('.'));
5736 parts = parts.concat(namespace.split('.'));
5738 if (this.enableNamespaceParseCache) {
5739 cache[namespace] = parts;
5746 * Creates a namespace and assign the `value` to the created object
5748 Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
5750 alert(MyCompany.pkg.Example === someObject); // alerts true
5752 * @param {String} name
5753 * @param {Mixed} value
5756 setNamespace: function(name, value) {
5757 var root = Ext.global,
5758 parts = this.parseNamespace(name),
5762 for (i = 0, ln = parts.length; i < ln; i++) {
5765 if (typeof part !== 'string') {
5782 * The new Ext.ns, supports namespace rewriting
5785 createNamespaces: function() {
5786 var root = Ext.global,
5787 parts, part, i, j, ln, subLn;
5789 for (i = 0, ln = arguments.length; i < ln; i++) {
5790 parts = this.parseNamespace(arguments[i]);
5792 for (j = 0, subLn = parts.length; j < subLn; j++) {
5795 if (typeof part !== 'string') {
5811 * Sets a name reference to a class.
5813 * @param {String} name
5814 * @param {Object} value
5815 * @return {Ext.ClassManager} this
5817 set: function(name, value) {
5818 var targetName = this.getName(value);
5820 this.classes[name] = this.setNamespace(name, value);
5822 if (targetName && targetName !== name) {
5823 this.maps.alternateToName[name] = targetName;
5830 * Retrieve a class by its name.
5832 * @param {String} name
5833 * @return {Class} class
5835 get: function(name) {
5836 if (this.classes.hasOwnProperty(name)) {
5837 return this.classes[name];
5840 var root = Ext.global,
5841 parts = this.parseNamespace(name),
5844 for (i = 0, ln = parts.length; i < ln; i++) {
5847 if (typeof part !== 'string') {
5850 if (!root || !root[part]) {
5862 * Register the alias for a class.
5864 * @param {Class/String} cls a reference to a class or a className
5865 * @param {String} alias Alias to use when referring to this class
5867 setAlias: function(cls, alias) {
5868 var aliasToNameMap = this.maps.aliasToName,
5869 nameToAliasesMap = this.maps.nameToAliases,
5872 if (typeof cls === 'string') {
5875 className = this.getName(cls);
5878 if (alias && aliasToNameMap[alias] !== className) {
5879 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
5880 Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
5881 "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
5884 aliasToNameMap[alias] = className;
5887 if (!nameToAliasesMap[className]) {
5888 nameToAliasesMap[className] = [];
5892 Ext.Array.include(nameToAliasesMap[className], alias);
5899 * Get a reference to the class by its alias.
5901 * @param {String} alias
5902 * @return {Class} class
5904 getByAlias: function(alias) {
5905 return this.get(this.getNameByAlias(alias));
5909 * Get the name of a class by its alias.
5911 * @param {String} alias
5912 * @return {String} className
5914 getNameByAlias: function(alias) {
5915 return this.maps.aliasToName[alias] || '';
5919 * Get the name of a class by its alternate name.
5921 * @param {String} alternate
5922 * @return {String} className
5924 getNameByAlternate: function(alternate) {
5925 return this.maps.alternateToName[alternate] || '';
5929 * Get the aliases of a class by the class name
5931 * @param {String} name
5932 * @return {Array} aliases
5934 getAliasesByName: function(name) {
5935 return this.maps.nameToAliases[name] || [];
5939 * Get the name of the class by its reference or its instance;
5940 * usually invoked by the shorthand {@link Ext#getClassName Ext.getClassName}
5942 Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
5944 * @param {Class/Object} object
5945 * @return {String} className
5948 getName: function(object) {
5949 return object && object.$className || '';
5953 * Get the class of the provided object; returns null if it's not an instance
5954 * of any class created with Ext.define. This is usually invoked by the shorthand {@link Ext#getClass Ext.getClass}
5956 var component = new Ext.Component();
5958 Ext.ClassManager.getClass(component); // returns Ext.Component
5960 * @param {Object} object
5961 * @return {Class} class
5964 getClass: function(object) {
5965 return object && object.self || null;
5969 * Defines a class. This is usually invoked via the alias {@link Ext#define Ext.define}
5971 Ext.ClassManager.create('My.awesome.Class', {
5972 someProperty: 'something',
5973 someMethod: function() { ... }
5978 alert(this === My.awesome.Class); // alerts true
5980 var myInstance = new this();
5983 * @param {String} className The class name to create in string dot-namespaced format, for example:
5984 * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
5985 * It is highly recommended to follow this simple convention:
5987 - The root and the class name are 'CamelCased'
5988 - Everything else is lower-cased
5990 * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid
5991 * strings, except those in the reserved listed below:
5999 - `alternateClassName`
6001 * @param {Function} createdFn Optional callback to execute after the class is created, the execution scope of which
6002 * (`this`) will be the newly created class itself.
6003 * @return {Ext.Base}
6006 create: function(className, data, createdFn) {
6009 if (typeof className !== 'string') {
6012 sourceMethod: "define",
6013 msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6017 data.$className = className;
6019 return new Class(data, function() {
6020 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6021 registeredPostprocessors = manager.postprocessors,
6023 postprocessors = [],
6024 postprocessor, postprocessors, process, i, ln;
6026 delete data.postprocessors;
6028 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6029 postprocessor = postprocessorStack[i];
6031 if (typeof postprocessor === 'string') {
6032 postprocessor = registeredPostprocessors[postprocessor];
6034 if (!postprocessor.always) {
6035 if (data[postprocessor.name] !== undefined) {
6036 postprocessors.push(postprocessor.fn);
6040 postprocessors.push(postprocessor.fn);
6044 postprocessors.push(postprocessor);
6048 process = function(clsName, cls, clsData) {
6049 postprocessor = postprocessors[index++];
6051 if (!postprocessor) {
6052 manager.set(className, cls);
6054 Ext.Loader.historyPush(className);
6057 createdFn.call(cls, cls);
6063 if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6064 process.apply(this, arguments);
6068 process.call(manager, className, this, data);
6073 * Instantiate a class by its alias; usually invoked by the convenient shorthand {@link Ext#createByAlias Ext.createByAlias}
6074 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6075 * attempt to load the class via synchronous loading.
6077 var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6079 * @param {String} alias
6080 * @param {Mixed} args,... Additional arguments after the alias will be passed to the
6081 * class constructor.
6082 * @return {Object} instance
6085 instantiateByAlias: function() {
6086 var alias = arguments[0],
6087 args = slice.call(arguments),
6088 className = this.getNameByAlias(alias);
6091 className = this.maps.aliasToName[alias];
6096 sourceMethod: "createByAlias",
6097 msg: "Cannot create an instance of unrecognized alias: " + alias
6101 if (Ext.global.console) {
6102 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6103 "Ext.require('" + alias + "') above Ext.onReady");
6106 Ext.syncRequire(className);
6109 args[0] = className;
6111 return this.instantiate.apply(this, args);
6115 * Instantiate a class by either full name, alias or alternate name; usually invoked by the convenient
6116 * shorthand {@link Ext#create Ext.create}
6118 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6119 * attempt to load the class via synchronous loading.
6121 * For example, all these three lines return the same result:
6124 var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6127 var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6130 var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6132 * @param {String} name
6133 * @param {Mixed} args,... Additional arguments after the name will be passed to the class' constructor.
6134 * @return {Object} instance
6137 instantiate: function() {
6138 var name = arguments[0],
6139 args = slice.call(arguments, 1),
6143 if (typeof name !== 'function') {
6144 if ((typeof name !== 'string' || name.length < 1)) {
6147 sourceMethod: "create",
6148 msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6152 cls = this.get(name);
6158 // No record of this class name, it's possibly an alias, so look it up
6160 possibleName = this.getNameByAlias(name);
6163 name = possibleName;
6165 cls = this.get(name);
6169 // Still no record of this class name, it's possibly an alternate name, so look it up
6171 possibleName = this.getNameByAlternate(name);
6174 name = possibleName;
6176 cls = this.get(name);
6180 // Still not existing at this point, try to load it via synchronous mode as the last resort
6182 if (Ext.global.console) {
6183 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6184 "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6187 Ext.syncRequire(name);
6189 cls = this.get(name);
6195 sourceMethod: "create",
6196 msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6200 if (typeof cls !== 'function') {
6203 sourceMethod: "create",
6204 msg: "'" + name + "' is a singleton and cannot be instantiated"
6208 if (!this.instantiationCounts[name]) {
6209 this.instantiationCounts[name] = 0;
6212 this.instantiationCounts[name]++;
6214 return this.getInstantiator(args.length)(cls, args);
6222 dynInstantiate: function(name, args) {
6223 args = Ext.Array.from(args, true);
6226 return this.instantiate.apply(this, args);
6233 getInstantiator: function(length) {
6234 if (!this.instantiators[length]) {
6238 for (i = 0; i < length; i++) {
6239 args.push('a['+i+']');
6242 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6245 return this.instantiators[length];
6256 defaultPostprocessors: [],
6259 * Register a post-processor function.
6261 * @param {String} name
6262 * @param {Function} postprocessor
6264 registerPostprocessor: function(name, fn, always) {
6265 this.postprocessors[name] = {
6267 always: always || false,
6275 * Set the default post processors array stack which are applied to every class.
6277 * @param {String/Array} The name of a registered post processor or an array of registered names.
6278 * @return {Ext.ClassManager} this
6280 setDefaultPostprocessors: function(postprocessors) {
6281 this.defaultPostprocessors = Ext.Array.from(postprocessors);
6287 * Insert this post-processor at a specific position in the stack, optionally relative to
6288 * any existing post-processor
6290 * @param {String} name The post-processor name. Note that it needs to be registered with
6291 * {@link Ext.ClassManager#registerPostprocessor} before this
6292 * @param {String} offset The insertion position. Four possible values are:
6293 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6294 * @param {String} relativeName
6295 * @return {Ext.ClassManager} this
6297 setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6298 var defaultPostprocessors = this.defaultPostprocessors,
6301 if (typeof offset === 'string') {
6302 if (offset === 'first') {
6303 defaultPostprocessors.unshift(name);
6307 else if (offset === 'last') {
6308 defaultPostprocessors.push(name);
6313 offset = (offset === 'after') ? 1 : -1;
6316 index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6319 defaultPostprocessors.splice(Math.max(0, index + offset), 0, name);
6326 * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6327 * or class names. Expressions support wildcards:
6329 // returns ['Ext.window.Window']
6330 var window = Ext.ClassManager.getNamesByExpression('widget.window');
6332 // returns ['widget.panel', 'widget.window', ...]
6333 var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6335 // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6336 var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6338 * @param {String} expression
6339 * @return {Array} classNames
6342 getNamesByExpression: function(expression) {
6343 var nameToAliasesMap = this.maps.nameToAliases,
6345 name, alias, aliases, possibleName, regex, i, ln;
6347 if (typeof expression !== 'string' || expression.length < 1) {
6349 sourceClass: "Ext.ClassManager",
6350 sourceMethod: "getNamesByExpression",
6351 msg: "Expression " + expression + " is invalid, must be a non-empty string"
6355 if (expression.indexOf('*') !== -1) {
6356 expression = expression.replace(/\*/g, '(.*?)');
6357 regex = new RegExp('^' + expression + '$');
6359 for (name in nameToAliasesMap) {
6360 if (nameToAliasesMap.hasOwnProperty(name)) {
6361 aliases = nameToAliasesMap[name];
6363 if (name.search(regex) !== -1) {
6367 for (i = 0, ln = aliases.length; i < ln; i++) {
6370 if (alias.search(regex) !== -1) {
6380 possibleName = this.getNameByAlias(expression);
6383 names.push(possibleName);
6385 possibleName = this.getNameByAlternate(expression);
6388 names.push(possibleName);
6390 names.push(expression);
6399 Manager.registerPostprocessor('alias', function(name, cls, data) {
6400 var aliases = data.alias,
6401 widgetPrefix = 'widget.',
6404 if (!(aliases instanceof Array)) {
6405 aliases = [aliases];
6408 for (i = 0, ln = aliases.length; i < ln; i++) {
6411 if (typeof alias !== 'string') {
6414 sourceMethod: "define",
6415 msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"
6419 this.setAlias(cls, alias);
6422 // This is ugly, will change to make use of parseNamespace for alias later on
6423 for (i = 0, ln = aliases.length; i < ln; i++) {
6426 if (alias.substring(0, widgetPrefix.length) === widgetPrefix) {
6427 // Only the first alias with 'widget.' prefix will be used for xtype
6428 cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length);
6434 Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
6435 fn.call(this, name, new cls(), data);
6439 Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
6440 var alternates = data.alternateClassName,
6443 if (!(alternates instanceof Array)) {
6444 alternates = [alternates];
6447 for (i = 0, ln = alternates.length; i < ln; i++) {
6448 alternate = alternates[i];
6450 if (typeof alternate !== 'string') {
6453 sourceMethod: "define",
6454 msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
6458 this.set(alternate, cls);
6462 Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
6466 * Convenient shorthand, see {@link Ext.ClassManager#instantiate}
6470 create: alias(Manager, 'instantiate'),
6474 * API to be stablized
6476 * @param {Mixed} item
6477 * @param {String} namespace
6479 factory: function(item, namespace) {
6480 if (item instanceof Array) {
6483 for (i = 0, ln = item.length; i < ln; i++) {
6484 item[i] = Ext.factory(item[i], namespace);
6490 var isString = (typeof item === 'string');
6492 if (isString || (item instanceof Object && item.constructor === Object)) {
6493 var name, config = {};
6499 name = item.className;
6501 delete config.className;
6504 if (namespace !== undefined && name.indexOf(namespace) === -1) {
6505 name = namespace + '.' + Ext.String.capitalize(name);
6508 return Ext.create(name, config);
6511 if (typeof item === 'function') {
6512 return Ext.create(item);
6519 * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
6521 var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
6522 var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
6528 widget: function(name) {
6529 var args = slice.call(arguments);
6530 args[0] = 'widget.' + name;
6532 return Manager.instantiateByAlias.apply(Manager, args);
6536 * Convenient shorthand, see {@link Ext.ClassManager#instantiateByAlias}
6538 * @method createByAlias
6540 createByAlias: alias(Manager, 'instantiateByAlias'),
6543 * Convenient shorthand for {@link Ext.ClassManager#create}, see detailed {@link Ext.Class explanation}
6547 define: alias(Manager, 'create'),
6550 * Convenient shorthand, see {@link Ext.ClassManager#getName}
6552 * @method getClassName
6554 getClassName: alias(Manager, 'getName'),
6558 * @param {Mixed} object
6560 getDisplayName: function(object) {
6561 if (object.displayName) {
6562 return object.displayName;
6565 if (object.$name && object.$class) {
6566 return Ext.getClassName(object.$class) + '#' + object.$name;
6569 if (object.$className) {
6570 return object.$className;
6577 * Convenient shorthand, see {@link Ext.ClassManager#getClass}
6579 * @method getClassName
6581 getClass: alias(Manager, 'getClass'),
6584 * Creates namespaces to be used for scoping variables and classes so that they are not global.
6585 * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
6587 Ext.namespace('Company', 'Company.data');
6589 // equivalent and preferable to the above syntax
6590 Ext.namespace('Company.data');
6592 Company.Widget = function() { ... };
6594 Company.data.CustomStore = function(config) { ... };
6596 * @param {String} namespace1
6597 * @param {String} namespace2
6598 * @param {String} etc
6599 * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
6605 namespace: alias(Manager, 'createNamespaces')
6608 Ext.createWidget = Ext.widget;
6611 * Convenient alias for {@link Ext#namespace Ext.namespace}
6615 Ext.ns = Ext.namespace;
6617 Class.registerPreprocessor('className', function(cls, data) {
6618 if (data.$className) {
6619 cls.$className = data.$className;
6620 cls.displayName = cls.$className;
6624 Class.setDefaultPreprocessorPosition('className', 'first');
6626 })(Ext.Class, Ext.Function.alias);
6629 * @author Jacky Nguyen <jacky@sencha.com>
6630 * @docauthor Jacky Nguyen <jacky@sencha.com>
6634 Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
6635 via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
6636 approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons of each approach:
6638 # Asynchronous Loading #
6642 + No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index
6644 + Best possible debugging experience: error messages come with the exact file name and line number
6647 + Dependencies need to be specified before-hand
6649 ### Method 1: Explicitly include what you need: ###
6652 Ext.require({String/Array} expressions);
6654 // Example: Single alias
6655 Ext.require('widget.window');
6657 // Example: Single class name
6658 Ext.require('Ext.window.Window');
6660 // Example: Multiple aliases / class names mix
6661 Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
6664 Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
6666 ### Method 2: Explicitly exclude what you don't need: ###
6668 // Syntax: Note that it must be in this chaining format.
6669 Ext.exclude({String/Array} expressions)
6670 .require({String/Array} expressions);
6672 // Include everything except Ext.data.*
6673 Ext.exclude('Ext.data.*').require('*');Â
6675 // Include all widgets except widget.checkbox*,
6676 // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
6677 Ext.exclude('widget.checkbox*').require('widget.*');
6679 # Synchronous Loading on Demand #
6682 + There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js
6686 + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
6687 + Must be from the same domain due to XHR restriction
6688 + Need a web server, same reason as above
6690 There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
6692 Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
6694 Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
6696 Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
6698 Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
6699 existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load the given
6700 class and all its dependencies.
6702 # Hybrid Loading - The Best of Both Worlds #
6704 It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
6706 ### Step 1: Start writing your application using synchronous approach. Ext.Loader will automatically fetch all
6707 dependencies on demand as they're needed during run-time. For example: ###
6709 Ext.onReady(function(){
6710 var window = Ext.createWidget('window', {
6717 title: 'Hello Dialog',
6719 title: 'Navigation',
6734 ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ###
6736 [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code
6738 [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
6740 Simply copy and paste the suggested code above `Ext.onReady`, i.e:
6742 Ext.require('Ext.window.Window');
6743 Ext.require('Ext.layout.container.Border');
6747 Everything should now load via asynchronous mode.
6751 It's important to note that dynamic loading should only be used during development on your local machines.
6752 During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
6753 the whole process of transitioning from / to between development / maintenance and production as easy as
6754 possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies your application
6755 needs in the exact loading sequence. It's as simple as concatenating all files in this array into one,
6756 then include it on top of your application.
6758 This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
6764 (function(Manager, Class, flexSetter, alias) {
6767 dependencyProperties = ['extend', 'mixins', 'requires'],
6770 Loader = Ext.Loader = {
6774 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
6777 * Flag indicating whether there are still files being loaded
6783 * Maintain the queue for all dependencies. Each item in the array is an object of the format:
6785 * requires: [...], // The required classes for this queue item
6786 * callback: function() { ... } // The function to execute when all classes specified in requires exist
6793 * Maintain the list of files that have already been handled so that they never get double-loaded
6799 * Maintain the list of listeners to execute when all required scripts are fully loaded
6805 * Contains optional dependencies to be loaded last
6808 optionalRequires: [],
6811 * Map of fully qualified class names to an array of dependent classes.
6827 hasFileLoadError: false,
6832 classNameToFilePathMap: {},
6835 * An array of class names to keep track of the dependency loading order.
6836 * This is not guaranteed to be the same everytime due to the asynchronous
6837 * nature of the Loader.
6850 * Whether or not to enable the dynamic dependency loading feature
6852 * @cfg {Boolean} enabled
6857 * @cfg {Boolean} disableCaching
6858 * Appends current timestamp to script files to prevent caching
6861 disableCaching: true,
6864 * @cfg {String} disableCachingParam
6865 * The get parameter name for the cache buster's timestamp.
6868 disableCachingParam: '_dc',
6871 * @cfg {Object} paths
6872 * The mapping from namespaces to file paths
6874 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
6875 // loaded from ./layout/Container.js
6877 'My': './src/my_own_folder' // My.layout.Container will be loaded from
6878 // ./src/my_own_folder/layout/Container.js
6880 * Note that all relative paths are relative to the current HTML document.
6881 * If not being specified, for example, <code>Other.awesome.Class</code>
6882 * will simply be loaded from <code>./Other/awesome/Class.js</code>
6890 * Set the configuration for the loader. This should be called right after ext-core.js
6891 * (or ext-core-debug.js) is included in the page, i.e:
6893 <script type="text/javascript" src="ext-core-debug.js"></script>
6894 <script type="text/javascript">
6895 Ext.Loader.setConfig({
6902 <script type="text/javascript">
6905 Ext.onReady(function() {
6906 // application code here
6910 * Refer to {@link Ext.Loader#configs} for the list of possible properties
6912 * @param {Object} config The config object to override the default values in {@link Ext.Loader#config}
6913 * @return {Ext.Loader} this
6916 setConfig: function(name, value) {
6917 if (Ext.isObject(name) && arguments.length === 1) {
6918 Ext.Object.merge(this.config, name);
6921 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
6928 * Get the config value corresponding to the specified name. If no name is given, will return the config object
6929 * @param {String} name The config property name
6930 * @return {Object/Mixed}
6932 getConfig: function(name) {
6934 return this.config[name];
6941 * Sets the path of a namespace.
6944 Ext.Loader.setPath('Ext', '.');
6946 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
6947 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
6948 * @return {Ext.Loader} this
6951 setPath: flexSetter(function(name, path) {
6952 this.config.paths[name] = path;
6958 * Translates a className to a file path by adding the
6959 * the proper prefix and converting the .'s to /'s. For example:
6961 Ext.Loader.setPath('My', '/path/to/My');
6963 alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
6965 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
6967 Ext.Loader.setPath({
6968 'My': '/path/to/lib',
6969 'My.awesome': '/other/path/for/awesome/stuff',
6970 'My.awesome.more': '/more/awesome/path'
6973 alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
6975 alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
6977 alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
6979 alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
6981 * @param {String} className
6982 * @return {String} path
6985 getPath: function(className) {
6987 paths = this.config.paths,
6988 prefix = this.getPrefix(className);
6990 if (prefix.length > 0) {
6991 if (prefix === className) {
6992 return paths[prefix];
6995 path = paths[prefix];
6996 className = className.substring(prefix.length + 1);
6999 if (path.length > 0) {
7003 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7008 * @param {String} className
7010 getPrefix: function(className) {
7011 var paths = this.config.paths,
7012 prefix, deepestPrefix = '';
7014 if (paths.hasOwnProperty(className)) {
7018 for (prefix in paths) {
7019 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7020 if (prefix.length > deepestPrefix.length) {
7021 deepestPrefix = prefix;
7026 return deepestPrefix;
7030 * Refresh all items in the queue. If all dependencies for an item exist during looping,
7031 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7035 refreshQueue: function() {
7036 var ln = this.queue.length,
7037 i, item, j, requires;
7040 this.triggerReady();
7044 for (i = 0; i < ln; i++) {
7045 item = this.queue[i];
7048 requires = item.requires;
7050 // Don't bother checking when the number of files loaded
7051 // is still less than the array length
7052 if (requires.length > this.numLoadedFiles) {
7059 if (Manager.isCreated(requires[j])) {
7060 // Take out from the queue
7061 requires.splice(j, 1);
7066 } while (j < requires.length);
7068 if (item.requires.length === 0) {
7069 this.queue.splice(i, 1);
7070 item.callback.call(item.scope);
7071 this.refreshQueue();
7081 * Inject a script element to document's head, call onLoad and onError accordingly
7084 injectScriptElement: function(url, onLoad, onError, scope) {
7085 var script = document.createElement('script'),
7087 onLoadFn = function() {
7088 me.cleanupScriptElement(script);
7091 onErrorFn = function() {
7092 me.cleanupScriptElement(script);
7093 onError.call(scope);
7096 script.type = 'text/javascript';
7098 script.onload = onLoadFn;
7099 script.onerror = onErrorFn;
7100 script.onreadystatechange = function() {
7101 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7106 this.documentHead.appendChild(script);
7114 cleanupScriptElement: function(script) {
7115 script.onload = null;
7116 script.onreadystatechange = null;
7117 script.onerror = null;
7123 * Load a script file, supports both asynchronous and synchronous approaches
7125 * @param {String} url
7126 * @param {Function} onLoad
7127 * @param {Scope} scope
7128 * @param {Boolean} synchronous
7131 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7133 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7134 fileName = url.split('/').pop(),
7135 isCrossOriginRestricted = false,
7136 xhr, status, onScriptError;
7138 scope = scope || this;
7140 this.isLoading = true;
7143 onScriptError = function() {
7144 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7147 if (!Ext.isReady && Ext.onDocumentReady) {
7148 Ext.onDocumentReady(function() {
7149 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7153 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7157 if (typeof XMLHttpRequest !== 'undefined') {
7158 xhr = new XMLHttpRequest();
7160 xhr = new ActiveXObject('Microsoft.XMLHTTP');
7164 xhr.open('GET', noCacheUrl, false);
7167 isCrossOriginRestricted = true;
7170 status = (xhr.status === 1223) ? 204 : xhr.status;
7172 if (!isCrossOriginRestricted) {
7173 isCrossOriginRestricted = (status === 0);
7176 if (isCrossOriginRestricted
7178 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7179 "being loaded from a different domain or from the local file system whereby cross origin " +
7180 "requests are not allowed due to security reasons. Use asynchronous loading with " +
7181 "Ext.require instead.", synchronous);
7183 else if (status >= 200 && status < 300
7185 // Firebug friendly, file names are still shown even though they're eval'ed code
7186 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7191 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7192 "verify that the file exists. " +
7193 "XHR status code: " + status, synchronous);
7196 // Prevent potential IE memory leak
7202 * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
7203 * Can be chained with more `require` and `exclude` methods, eg:
7205 Ext.exclude('Ext.data.*').require('*');
7207 Ext.exclude('widget.button*').require('widget.*');
7209 * @param {Array} excludes
7210 * @return {Object} object contains `require` method for chaining
7213 exclude: function(excludes) {
7217 require: function(expressions, fn, scope) {
7218 return me.require(expressions, fn, scope, excludes);
7221 syncRequire: function(expressions, fn, scope) {
7222 return me.syncRequire(expressions, fn, scope, excludes);
7228 * Synchronously loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when finishes, within the optional scope. This method is aliased by {@link Ext#syncRequire} for convenience
7229 * @param {String/Array} expressions Can either be a string or an array of string
7230 * @param {Function} fn (Optional) The callback function
7231 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7232 * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
7235 syncRequire: function() {
7236 this.syncModeEnabled = true;
7237 this.require.apply(this, arguments);
7238 this.refreshQueue();
7239 this.syncModeEnabled = false;
7243 * Loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when
7244 * finishes, within the optional scope. This method is aliased by {@link Ext#require Ext.require} for convenience
7245 * @param {String/Array} expressions Can either be a string or an array of string
7246 * @param {Function} fn (Optional) The callback function
7247 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7248 * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
7251 require: function(expressions, fn, scope, excludes) {
7252 var filePath, expression, exclude, className, excluded = {},
7253 excludedClassNames = [],
7254 possibleClassNames = [],
7255 possibleClassName, classNames = [],
7258 expressions = Ext.Array.from(expressions);
7259 excludes = Ext.Array.from(excludes);
7261 fn = fn || Ext.emptyFn;
7263 scope = scope || Ext.global;
7265 for (i = 0, ln = excludes.length; i < ln; i++) {
7266 exclude = excludes[i];
7268 if (typeof exclude === 'string' && exclude.length > 0) {
7269 excludedClassNames = Manager.getNamesByExpression(exclude);
7271 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
7272 excluded[excludedClassNames[j]] = true;
7277 for (i = 0, ln = expressions.length; i < ln; i++) {
7278 expression = expressions[i];
7280 if (typeof expression === 'string' && expression.length > 0) {
7281 possibleClassNames = Manager.getNamesByExpression(expression);
7283 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
7284 possibleClassName = possibleClassNames[j];
7286 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
7287 Ext.Array.include(classNames, possibleClassName);
7293 // If the dynamic dependency feature is not being used, throw an error
7294 // if the dependencies are not defined
7295 if (!this.config.enabled) {
7296 if (classNames.length > 0) {
7298 sourceClass: "Ext.Loader",
7299 sourceMethod: "require",
7300 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
7301 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
7306 if (classNames.length === 0) {
7312 requires: classNames,
7317 classNames = classNames.slice();
7319 for (i = 0, ln = classNames.length; i < ln; i++) {
7320 className = classNames[i];
7322 if (!this.isFileLoaded.hasOwnProperty(className)) {
7323 this.isFileLoaded[className] = false;
7325 filePath = this.getPath(className);
7327 this.classNameToFilePathMap[className] = filePath;
7329 this.numPendingFiles++;
7331 this.loadScriptFile(
7333 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
7334 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
7336 this.syncModeEnabled
7346 * @param {String} className
7347 * @param {String} filePath
7349 onFileLoaded: function(className, filePath) {
7350 this.numLoadedFiles++;
7352 this.isFileLoaded[className] = true;
7354 this.numPendingFiles--;
7356 if (this.numPendingFiles === 0) {
7357 this.refreshQueue();
7360 if (this.numPendingFiles <= 1) {
7361 window.status = "Finished loading all dependencies, onReady fired!";
7364 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
7367 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
7368 var queue = this.queue,
7370 i, ln, j, subLn, missingClasses = [], missingPaths = [];
7372 for (i = 0, ln = queue.length; i < ln; i++) {
7373 requires = queue[i].requires;
7375 for (j = 0, subLn = requires.length; j < ln; j++) {
7376 if (this.isFileLoaded[requires[j]]) {
7377 missingClasses.push(requires[j]);
7382 if (missingClasses.length < 1) {
7386 missingClasses = Ext.Array.filter(missingClasses, function(item) {
7387 return !this.requiresMap.hasOwnProperty(item);
7390 for (i = 0,ln = missingClasses.length; i < ln; i++) {
7391 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
7395 sourceClass: "Ext.Loader",
7396 sourceMethod: "onFileLoaded",
7397 msg: "The following classes are not declared even if their files have been " +
7398 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
7399 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
7407 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
7408 this.numPendingFiles--;
7409 this.hasFileLoadError = true;
7412 sourceClass: "Ext.Loader",
7413 classToLoad: className,
7415 loadingType: isSynchronous ? 'synchronous' : 'async',
7423 addOptionalRequires: function(requires) {
7424 var optionalRequires = this.optionalRequires,
7427 requires = Ext.Array.from(requires);
7429 for (i = 0, ln = requires.length; i < ln; i++) {
7430 require = requires[i];
7432 Ext.Array.include(optionalRequires, require);
7441 triggerReady: function(force) {
7442 var readyListeners = this.readyListeners,
7443 optionalRequires, listener;
7445 if (this.isLoading || force) {
7446 this.isLoading = false;
7448 if (this.optionalRequires.length) {
7449 // Clone then empty the array to eliminate potential recursive loop issue
7450 optionalRequires = Ext.Array.clone(this.optionalRequires);
7452 // Empty the original array
7453 this.optionalRequires.length = 0;
7455 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
7459 while (readyListeners.length) {
7460 listener = readyListeners.shift();
7461 listener.fn.call(listener.scope);
7463 if (this.isLoading) {
7473 * Add a new listener to be executed when all required scripts are fully loaded
7475 * @param {Function} fn The function callback to be executed
7476 * @param {Object} scope The execution scope (<code>this</code>) of the callback function
7477 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
7479 onReady: function(fn, scope, withDomReady, options) {
7482 if (withDomReady !== false && Ext.onDocumentReady) {
7486 Ext.onDocumentReady(oldFn, scope, options);
7490 if (!this.isLoading) {
7494 this.readyListeners.push({
7503 * @param {String} className
7505 historyPush: function(className) {
7506 if (className && this.isFileLoaded.hasOwnProperty(className)) {
7507 Ext.Array.include(this.history, className);
7515 * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of
7516 * {@link Ext.Loader} for examples.
7520 Ext.require = alias(Loader, 'require');
7523 * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
7526 * @method syncRequire
7528 Ext.syncRequire = alias(Loader, 'syncRequire');
7531 * Convenient shortcut to {@link Ext.Loader#exclude}
7535 Ext.exclude = alias(Loader, 'exclude');
7541 Ext.onReady = function(fn, scope, options) {
7542 Loader.onReady(fn, scope, true, options);
7545 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
7548 className = Manager.getName(cls),
7549 i, j, ln, subLn, value, propertyName, propertyValue;
7552 Basically loop through the dependencyProperties, look for string class names and push
7553 them into a stack, regardless of whether the property's value is a string, array or object. For example:
7555 extend: 'Ext.MyClass',
7556 requires: ['Ext.some.OtherClass'],
7558 observable: 'Ext.util.Observable';
7561 which will later be transformed into:
7563 extend: Ext.MyClass,
7564 requires: [Ext.some.OtherClass],
7566 observable: Ext.util.Observable;
7571 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
7572 propertyName = dependencyProperties[i];
7574 if (data.hasOwnProperty(propertyName)) {
7575 propertyValue = data[propertyName];
7577 if (typeof propertyValue === 'string') {
7578 dependencies.push(propertyValue);
7580 else if (propertyValue instanceof Array) {
7581 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
7582 value = propertyValue[j];
7584 if (typeof value === 'string') {
7585 dependencies.push(value);
7590 for (j in propertyValue) {
7591 if (propertyValue.hasOwnProperty(j)) {
7592 value = propertyValue[j];
7594 if (typeof value === 'string') {
7595 dependencies.push(value);
7603 if (dependencies.length === 0) {
7604 // Loader.historyPush(className);
7608 var deadlockPath = [],
7609 requiresMap = Loader.requiresMap,
7613 Automatically detect deadlocks before-hand,
7614 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
7616 - A extends B, then B extends A
7617 - A requires B, B requires C, then C requires A
7619 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
7620 no matter how deep the path is.
7624 requiresMap[className] = dependencies;
7626 detectDeadlock = function(cls) {
7627 deadlockPath.push(cls);
7629 if (requiresMap[cls]) {
7630 if (Ext.Array.contains(requiresMap[cls], className)) {
7632 sourceClass: "Ext.Loader",
7633 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
7634 deadlockPath[1] + "' " + "mutually require each other. Path: " +
7635 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
7639 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
7640 detectDeadlock(requiresMap[cls][i]);
7645 detectDeadlock(className);
7649 Loader.require(dependencies, function() {
7650 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
7651 propertyName = dependencyProperties[i];
7653 if (data.hasOwnProperty(propertyName)) {
7654 propertyValue = data[propertyName];
7656 if (typeof propertyValue === 'string') {
7657 data[propertyName] = Manager.get(propertyValue);
7659 else if (propertyValue instanceof Array) {
7660 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
7661 value = propertyValue[j];
7663 if (typeof value === 'string') {
7664 data[propertyName][j] = Manager.get(value);
7669 for (var k in propertyValue) {
7670 if (propertyValue.hasOwnProperty(k)) {
7671 value = propertyValue[k];
7673 if (typeof value === 'string') {
7674 data[propertyName][k] = Manager.get(value);
7682 continueFn.call(me, cls, data);
7688 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
7690 Manager.registerPostprocessor('uses', function(name, cls, data) {
7691 var uses = Ext.Array.from(data.uses),
7695 for (i = 0, ln = uses.length; i < ln; i++) {
7698 if (typeof item === 'string') {
7703 Loader.addOptionalRequires(items);
7706 Manager.setDefaultPostprocessorPosition('uses', 'last');
7708 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
7715 A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
7716 errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
7717 uses the Ext 4 class system, the Error class can automatically add the source class and method from which
7718 the error was raised. It also includes logic to automatically log the eroor to the console, if available,
7719 with additional metadata about the error. In all cases, the error will always be thrown at the end so that
7720 execution will halt.
7722 Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
7723 handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
7724 although in a real application it's usually a better idea to override the handling function and perform
7725 logging or some other method of reporting the errors in a way that is meaningful to the application.
7727 At its simplest you can simply raise an error as a simple string from within any code:
7731 Ext.Error.raise('Something bad happened!');
7733 If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
7734 displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
7735 additional metadata about the error being raised. The {@link #raise} method can also take a config object.
7736 In this form the `msg` attribute becomes the error description, and any other data added to the config gets
7737 added to the error object and, if the console is available, logged to the console for inspection.
7741 Ext.define('Ext.Foo', {
7742 doSomething: function(option){
7743 if (someCondition === false) {
7745 msg: 'You cannot do that!',
7746 option: option, // whatever was passed into the method
7747 'error code': 100 // other arbitrary info
7753 If a console is available (that supports the `console.dir` function) you'll see console output like:
7755 An error was raised with the following data:
7756 option: Object { foo: "bar"}
7759 msg: "You cannot do that!"
7760 sourceClass: "Ext.Foo"
7761 sourceMethod: "doSomething"
7763 uncaught exception: You cannot do that!
7765 As you can see, the error will report exactly where it was raised and will include as much information as the
7766 raising code can usefully provide.
7768 If you want to handle all application errors globally you can simply override the static {@link handle} method
7769 and provide whatever handling logic you need. If the method returns true then the error is considered handled
7770 and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
7774 Ext.Error.handle = function(err) {
7775 if (err.someProperty == 'NotReallyAnError') {
7776 // maybe log something to the application here if applicable
7779 // any non-true return value (including none) will cause the error to be thrown
7782 * Create a new Error object
7783 * @param {Object} config The config object
7785 * @author Brian Moeskau <brian@sencha.com>
7786 * @docauthor Brian Moeskau <brian@sencha.com>
7788 Ext.Error = Ext.extend(Error, {
7792 Static flag that can be used to globally disable error reporting to the browser if set to true
7793 (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
7794 and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
7795 be preferable to supply a custom error {@link #handle handling} function instead.
7799 Ext.Error.ignore = true;
7807 Raise an error that can include additional data and supports automatic console logging if available.
7808 You can pass a string error message or an object with the `msg` attribute which will be used as the
7809 error message. The object can contain any other name-value attributes (or objects) to be logged
7810 along with the error.
7812 Note that after displaying the error message a JavaScript error will ultimately be thrown so that
7813 execution will halt.
7817 Ext.Error.raise('A simple string error message');
7821 Ext.define('Ext.Foo', {
7822 doSomething: function(option){
7823 if (someCondition === false) {
7825 msg: 'You cannot do that!',
7826 option: option, // whatever was passed into the method
7827 'error code': 100 // other arbitrary info
7832 * @param {String/Object} err The error message string, or an object containing the
7833 * attribute "msg" that will be used as the error message. Any other data included in
7834 * the object will also be logged to the browser console, if available.
7838 raise: function(err){
7840 if (Ext.isString(err)) {
7844 var method = this.raise.caller;
7848 err.sourceMethod = method.$name;
7850 if (method.$owner) {
7851 err.sourceClass = method.$owner.$className;
7855 if (Ext.Error.handle(err) !== true) {
7856 var global = Ext.global,
7857 con = global.console,
7858 msg = Ext.Error.prototype.toString.call(err),
7859 noConsoleMsg = 'An uncaught error was raised: "' + msg +
7860 '". Use Firebug or Webkit console for additional details.';
7864 con.warn('An uncaught error was raised with the following data:');
7868 con.warn(noConsoleMsg);
7874 else if (global.alert){
7875 global.alert(noConsoleMsg);
7878 throw new Ext.Error(err);
7883 Globally handle any Ext errors that may be raised, optionally providing custom logic to
7884 handle different errors individually. Return true from the function to bypass throwing the
7885 error to the browser, otherwise the error will be thrown and execution will halt.
7889 Ext.Error.handle = function(err) {
7890 if (err.someProperty == 'NotReallyAnError') {
7891 // maybe log something to the application here if applicable
7894 // any non-true return value (including none) will cause the error to be thrown
7897 * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes
7898 * that were originally raised with it, plus properties about the method and class from which
7899 * the error originated (if raised from a class that uses the Ext 4 class system).
7904 return Ext.Error.ignore;
7910 * @param {String/Object} config The error message string, or an object containing the
7911 * attribute "msg" that will be used as the error message. Any other data included in
7912 * the object will be applied to the error instance and logged to the browser console, if available.
7914 constructor: function(config){
7915 if (Ext.isString(config)) {
7916 config = { msg: config };
7918 Ext.apply(this, config);
7922 Provides a custom string representation of the error object. This is an override of the base JavaScript
7923 `Object.toString` method, which is useful so that when logged to the browser console, an error object will
7924 be displayed with a useful message instead of `[object Object]`, the default `toString` result.
7926 The default implementation will include the error message along with the raising class and method, if available,
7927 but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
7928 a particular error instance, if you want to provide a custom description that will show up in the console.
7930 * @return {String} The error message. If raised from within the Ext 4 class system, the error message
7931 * will also include the raising class and method names, if available.
7933 toString: function(){
7935 className = me.className ? me.className : '',
7936 methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
7937 msg = me.msg || '(No description provided)';
7939 return className + methodName + msg;
7945 Ext JS - JavaScript Library
7946 Copyright (c) 2006-2011, Sencha Inc.
7947 All rights reserved.
7948 licensing@sencha.com
7952 * Modified version of Douglas Crockford"s json.js that doesn"t
7953 * mess with the Object prototype
7954 * http://www.json.org/js.html
7957 Ext.JSON = new(function() {
7958 var useHasOwn = !! {}.hasOwnProperty,
7959 isNative = function() {
7960 var useNative = null;
7963 if (useNative === null) {
7964 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
7971 return n < 10 ? "0" + n : n;
7973 doDecode = function(json) {
7974 return eval("(" + json + ')');
7976 doEncode = function(o) {
7977 if (!Ext.isDefined(o) || o === null) {
7979 } else if (Ext.isArray(o)) {
7980 return encodeArray(o);
7981 } else if (Ext.isDate(o)) {
7982 return Ext.JSON.encodeDate(o);
7983 } else if (Ext.isString(o)) {
7984 return encodeString(o);
7985 } else if (typeof o == "number") {
7986 //don't use isNumber here, since finite checks happen inside isNumber
7987 return isFinite(o) ? String(o) : "null";
7988 } else if (Ext.isBoolean(o)) {
7990 } else if (Ext.isObject(o)) {
7991 return encodeObject(o);
7992 } else if (typeof o === "function") {
8005 '\x0b': '\\u000b' //ie doesn't handle \v
8007 charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
8008 encodeString = function(s) {
8009 return '"' + s.replace(charToReplace, function(a) {
8011 return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
8014 encodeArray = function(o) {
8016 // Note empty string in case there are no serializable members.
8019 for (i = 0; i < len; i += 1) {
8020 a.push(doEncode(o[i]), ',');
8022 // Overwrite trailing comma (or empty string)
8023 a[a.length - 1] = ']';
8026 encodeObject = function(o) {
8028 // Note empty string in case there are no serializable members.
8031 if (!useHasOwn || o.hasOwnProperty(i)) {
8032 a.push(doEncode(i), ":", doEncode(o[i]), ',');
8035 // Overwrite trailing comma (or empty string)
8036 a[a.length - 1] = '}';
8041 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
8042 * <b>The returned value includes enclosing double quotation marks.</b></p>
8043 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
8044 * <p>To override this:</p><pre><code>
8045 Ext.JSON.encodeDate = function(d) {
8046 return d.format('"Y-m-d"');
8049 * @param {Date} d The Date to encode
8050 * @return {String} The string literal to use in a JSON string.
8052 this.encodeDate = function(o) {
8053 return '"' + o.getFullYear() + "-"
8054 + pad(o.getMonth() + 1) + "-"
8055 + pad(o.getDate()) + "T"
8056 + pad(o.getHours()) + ":"
8057 + pad(o.getMinutes()) + ":"
8058 + pad(o.getSeconds()) + '"';
8062 * Encodes an Object, Array or other value
8063 * @param {Mixed} o The variable to encode
8064 * @return {String} The JSON string
8066 this.encode = function() {
8068 return function(o) {
8070 // setup encoding function on first access
8071 ec = isNative() ? JSON.stringify : doEncode;
8079 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
8080 * @param {String} json The JSON string
8081 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8082 * @return {Object} The resulting object
8084 this.decode = function() {
8086 return function(json, safe) {
8088 // setup decoding function on first access
8089 dc = isNative() ? JSON.parse : doDecode;
8094 if (safe === true) {
8098 sourceClass: "Ext.JSON",
8099 sourceMethod: "decode",
8100 msg: "You're trying to decode and invalid JSON String: " + json
8108 * Shorthand for {@link Ext.JSON#encode}
8109 * @param {Mixed} o The variable to encode
8110 * @return {String} The JSON string
8114 Ext.encode = Ext.JSON.encode;
8116 * Shorthand for {@link Ext.JSON#decode}
8117 * @param {String} json The JSON string
8118 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8119 * @return {Object} The resulting object
8123 Ext.decode = Ext.JSON.decode;
8129 The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
8130 Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
8131 as direct properties of the Ext namespace.
8133 Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
8134 For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
8136 Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
8137 This ensures all scripts have been loaded, preventing dependency issues. For example
8139 Ext.onReady(function(){
8141 renderTo: document.body,
8146 For more information about how to use the Ext classes, see
8148 * <a href="http://www.sencha.com/learn/">The Learning Center</a>
8149 * <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
8150 * <a href="http://www.sencha.com/forum/">The forums</a>
8156 userAgent: navigator.userAgent.toLowerCase(),
8159 BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
8160 isStrict: document.compatMode == "CSS1Compat",
8161 windowId: 'ext-window',
8162 documentId: 'ext-document',
8165 * True when the document is fully initialized and ready for action
8171 * True to automatically uncache orphaned Ext.core.Elements periodically (defaults to true)
8174 enableGarbageCollector: true,
8177 * True to automatically purge event listeners during garbageCollection (defaults to true).
8180 enableListenerCollection: true,
8183 * Generates unique ids. If the element already has an id, it is unchanged
8184 * @param {Mixed} el (optional) The element to generate an id for
8185 * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
8186 * @return {String} The generated Id.
8188 id: function(el, prefix) {
8189 el = Ext.getDom(el, true) || {};
8190 if (el === document) {
8191 el.id = this.documentId;
8193 else if (el === window) {
8194 el.id = this.windowId;
8197 el.id = (prefix || "ext-gen") + (++Ext.idSeed);
8203 * Returns the current document body as an {@link Ext.core.Element}.
8204 * @return Ext.core.Element The document body
8206 getBody: function() {
8207 return Ext.get(document.body || false);
8211 * Returns the current document head as an {@link Ext.core.Element}.
8212 * @return Ext.core.Element The document head
8214 getHead: function() {
8218 if (head == undefined) {
8219 head = Ext.get(document.getElementsByTagName("head")[0]);
8227 * Returns the current HTML document object as an {@link Ext.core.Element}.
8228 * @return Ext.core.Element The document
8230 getDoc: function() {
8231 return Ext.get(document);
8235 * This is shorthand reference to {@link Ext.ComponentManager#get}.
8236 * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
8237 * @param {String} id The component {@link Ext.Component#id id}
8238 * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
8241 getCmp: function(id) {
8242 return Ext.ComponentManager.get(id);
8246 * Returns the current orientation of the mobile device
8247 * @return {String} Either 'portrait' or 'landscape'
8249 getOrientation: function() {
8250 return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
8254 * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
8255 * DOM (if applicable) and calling their destroy functions (if available). This method is primarily
8256 * intended for arguments of type {@link Ext.core.Element} and {@link Ext.Component}, but any subclass of
8257 * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be
8258 * passed into this function in a single call as separate arguments.
8259 * @param {Mixed} arg1 An {@link Ext.core.Element}, {@link Ext.Component}, or an Array of either of these to destroy
8260 * @param {Mixed} arg2 (optional)
8261 * @param {Mixed} etc... (optional)
8263 destroy: function() {
8264 var ln = arguments.length,
8267 for (i = 0; i < ln; i++) {
8270 if (Ext.isArray(arg)) {
8271 this.destroy.apply(this, arg);
8273 else if (Ext.isFunction(arg.destroy)) {
8284 * Execute a callback function in a particular scope. If no function is passed the call is ignored.
8285 * @param {Function} callback The callback to execute
8286 * @param {Object} scope (optional) The scope to execute in
8287 * @param {Array} args (optional) The arguments to pass to the function
8288 * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
8290 callback: function(callback, scope, args, delay){
8291 if(Ext.isFunction(callback)){
8293 scope = scope || window;
8295 Ext.defer(callback, delay, scope, args);
8297 callback.apply(scope, args);
8303 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
8304 * @param {String} value The string to encode
8305 * @return {String} The encoded text
8307 htmlEncode : function(value) {
8308 return Ext.String.htmlEncode(value);
8312 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
8313 * @param {String} value The string to decode
8314 * @return {String} The decoded text
8316 htmlDecode : function(value) {
8317 return Ext.String.htmlDecode(value);
8321 * Appends content to the query string of a URL, handling logic for whether to place
8322 * a question mark or ampersand.
8323 * @param {String} url The URL to append to.
8324 * @param {String} s The content to append to the URL.
8325 * @return (String) The resulting URL
8327 urlAppend : function(url, s) {
8328 if (!Ext.isEmpty(s)) {
8329 return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
8336 Ext.ns = Ext.namespace;
8339 window.undefined = window.undefined;
8342 * Ext core utilities and functions.
8346 var check = function(regex){
8347 return regex.test(Ext.userAgent);
8349 docMode = document.documentMode,
8350 isOpera = check(/opera/),
8351 isOpera10_5 = isOpera && check(/version\/10\.5/),
8352 isChrome = check(/\bchrome\b/),
8353 isWebKit = check(/webkit/),
8354 isSafari = !isChrome && check(/safari/),
8355 isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
8356 isSafari3 = isSafari && check(/version\/3/),
8357 isSafari4 = isSafari && check(/version\/4/),
8358 isIE = !isOpera && check(/msie/),
8359 isIE7 = isIE && (check(/msie 7/) || docMode == 7),
8360 isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
8361 isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
8362 isIE6 = isIE && check(/msie 6/),
8363 isGecko = !isWebKit && check(/gecko/),
8364 isGecko3 = isGecko && check(/rv:1\.9/),
8365 isGecko4 = isGecko && check(/rv:2\.0/),
8366 isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
8367 isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
8368 isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
8369 isWindows = check(/windows|win32/),
8370 isMac = check(/macintosh|mac os x/),
8371 isLinux = check(/linux/),
8374 // remove css image flicker
8376 document.execCommand("BackgroundImageCache", false, true);
8379 Ext.setVersion('extjs', '4.0.0');
8382 * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
8383 * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
8386 SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank',
8389 * True if the {@link Ext.fx.Anim} Class is available
8391 * @property enableFx
8395 * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
8396 * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
8397 * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
8400 scopeResetCSS : Ext.buildSettings.scopeResetCSS,
8403 * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
8404 * Currently not optimized for performance.
8407 enableNestedListenerRemoval : false,
8410 * Indicates whether to use native browser parsing for JSON methods.
8411 * This option is ignored if the browser does not support native JSON methods.
8412 * <b>Note: Native JSON methods will not work with objects that have functions.
8413 * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
8416 USE_NATIVE_JSON : false,
8419 * Return the dom node for the passed String (id), dom node, or Ext.core.Element.
8420 * Optional 'strict' flag is needed for IE since it can return 'name' and
8421 * 'id' elements by using getElementById.
8422 * Here are some examples:
8424 // gets dom node based on id
8425 var elDom = Ext.getDom('elId');
8426 // gets dom node based on the dom node
8427 var elDom1 = Ext.getDom(elDom);
8429 // If we don't know if we are working with an
8430 // Ext.core.Element or a dom node use Ext.getDom
8432 var dom = Ext.getDom(el);
8433 // do something with the dom node
8436 * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
8437 * when this method is called to be successful.
8439 * @return HTMLElement
8441 getDom : function(el, strict) {
8442 if (!el || !document) {
8448 if (typeof el == 'string') {
8449 var e = document.getElementById(el);
8450 // IE returns elements with the 'name' and 'id' attribute.
8451 // we do a strict check to return the element with only the id attribute
8452 if (e && isIE && strict) {
8453 if (el == e.getAttribute('id')) {
8467 * Removes a DOM node from the document.
8468 * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
8469 * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
8470 * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
8471 * will be ignored if passed in.</p>
8472 * @param {HTMLElement} node The node to remove
8474 removeNode : isIE6 || isIE7 ? function() {
8477 if(n && n.tagName != 'BODY'){
8478 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
8479 d = d || document.createElement('div');
8482 delete Ext.cache[n.id];
8486 if (n && n.parentNode && n.tagName != 'BODY') {
8487 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
8488 n.parentNode.removeChild(n);
8489 delete Ext.cache[n.id];
8494 * True if the detected browser is Opera.
8500 * True if the detected browser is Opera 10.5x.
8503 isOpera10_5 : isOpera10_5,
8506 * True if the detected browser uses WebKit.
8509 isWebKit : isWebKit,
8512 * True if the detected browser is Chrome.
8515 isChrome : isChrome,
8518 * True if the detected browser is Safari.
8521 isSafari : isSafari,
8524 * True if the detected browser is Safari 3.x.
8527 isSafari3 : isSafari3,
8530 * True if the detected browser is Safari 4.x.
8533 isSafari4 : isSafari4,
8536 * True if the detected browser is Safari 2.x.
8539 isSafari2 : isSafari2,
8542 * True if the detected browser is Internet Explorer.
8548 * True if the detected browser is Internet Explorer 6.x.
8554 * True if the detected browser is Internet Explorer 7.x.
8560 * True if the detected browser is Internet Explorer 8.x.
8566 * True if the detected browser is Internet Explorer 9.x.
8572 * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
8578 * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
8581 isGecko3 : isGecko3,
8584 * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
8587 isGecko4 : isGecko4,
8590 * True if the detected browser uses FireFox 3.0
8596 * True if the detected browser uses FireFox 3.5
8602 * True if the detected browser uses FireFox 3.6
8608 * True if the detected platform is Linux.
8614 * True if the detected platform is Windows.
8617 isWindows : isWindows,
8620 * True if the detected platform is Mac OS.
8626 * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
8627 * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
8628 * For other browsers it uses an inline data URL.
8631 BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
8634 * <p>Utility method for returning a default value if the passed value is empty.</p>
8635 * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
8637 * <li>undefined</li>
8638 * <li>an empty array</li>
8639 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
8641 * @param {Mixed} value The value to test
8642 * @param {Mixed} defaultValue The value to return if the original value is empty
8643 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
8644 * @return {Mixed} value, if non-empty, else defaultValue
8645 * @deprecated 4.0.0 Use {Ext#valueFrom} instead
8647 value : function(v, defaultValue, allowBlank){
8648 return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
8652 * Escapes the passed string for use in a regular expression
8653 * @param {String} str
8655 * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
8657 escapeRe : function(s) {
8658 return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
8662 * Applies event listeners to elements by selectors when the document is ready.
8663 * The event name is specified with an <tt>@</tt> suffix.
8666 // add a listener for click on all anchors in element with id foo
8667 '#foo a@click' : function(e, t){
8671 // add the same listener to multiple selectors (separated by comma BEFORE the @)
8672 '#foo a, #bar span.some-class@mouseover' : function(){
8677 * @param {Object} obj The list of behaviors to apply
8679 addBehaviors : function(o){
8681 Ext.onReady(function(){
8682 Ext.addBehaviors(o);
8685 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
8690 if ((parts = b.split('@'))[1]) { // for Object prototype breakers
8693 cache[s] = Ext.select(s);
8695 cache[s].on(parts[1], o[b]);
8703 * Utility method for getting the width of the browser scrollbar. This can differ depending on
8704 * operating system settings, such as the theme or font size.
8705 * @param {Boolean} force (optional) true to force a recalculation of the value.
8706 * @return {Number} The width of the scrollbar.
8708 getScrollBarWidth: function(force){
8713 if(force === true || scrollWidth === null){
8715 // When IE9 positions an element offscreen via offsets, the offsetWidth is
8716 // inaccurately reported. For IE9 only, we render on screen before removing.
8717 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets';
8718 // Append our div, do our calculation and then remove it
8719 var div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
8720 child = div.child('div', true);
8721 var w1 = child.offsetWidth;
8722 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
8723 var w2 = child.offsetWidth;
8725 // Need to add 2 to ensure we leave enough space
8726 scrollWidth = w1 - w2 + 2;
8732 * Copies a set of named properties fom the source object to the destination object.
8733 * <p>example:<pre><code>
8734 ImageComponent = Ext.extend(Ext.Component, {
8735 initComponent: function() {
8736 this.autoEl = { tag: 'img' };
8737 MyComponent.superclass.initComponent.apply(this, arguments);
8738 this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
8742 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
8743 * @param {Object} dest The destination object.
8744 * @param {Object} source The source object.
8745 * @param {Array/String} names Either an Array of property names, or a comma-delimited list
8746 * of property names to copy.
8747 * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
8748 * @return {Object} The modified object.
8750 copyTo : function(dest, source, names, usePrototypeKeys){
8751 if(typeof names == 'string'){
8752 names = names.split(/[,;\s]/);
8754 Ext.each(names, function(name){
8755 if(usePrototypeKeys || source.hasOwnProperty(name)){
8756 dest[name] = source[name];
8763 * Attempts to destroy and then remove a set of named properties of the passed object.
8764 * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
8765 * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
8766 * @param {Mixed} etc... More property names to destroy and remove.
8768 destroyMembers : function(o, arg1, arg2, etc){
8769 for (var i = 1, a = arguments, len = a.length; i < len; i++) {
8770 Ext.destroy(o[a[i]]);
8776 * Partitions the set into two sets: a true set and a false set.
8781 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
8787 return val.className == "class1"
8790 // true are those paragraph elements with a className of "class1",
8791 // false set are those that do not have that className.
8793 * @param {Array|NodeList} arr The array to partition
8794 * @param {Function} truth (optional) a function to determine truth. If this is omitted the element
8795 * itself must be able to be evaluated for its truthfulness.
8796 * @return {Array} [true<Array>,false<Array>]
8797 * @deprecated 4.0.0 Will be removed in the next major version
8799 partition : function(arr, truth){
8801 Ext.each(arr, function(v, i, a) {
8802 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
8808 * Invokes a method on each item in an Array.
8811 Ext.invoke(Ext.query("p"), "getAttribute", "id");
8812 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
8814 * @param {Array|NodeList} arr The Array of items to invoke the method on.
8815 * @param {String} methodName The method name to invoke.
8816 * @param {...*} args Arguments to send into the method invocation.
8817 * @return {Array} The results of invoking the method on each item in the array.
8818 * @deprecated 4.0.0 Will be removed in the next major version
8820 invoke : function(arr, methodName){
8822 args = Array.prototype.slice.call(arguments, 2);
8823 Ext.each(arr, function(v,i) {
8824 if (v && typeof v[methodName] == 'function') {
8825 ret.push(v[methodName].apply(v, args));
8827 ret.push(undefined);
8834 * <p>Zips N sets together.</p>
8837 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
8844 return "$" + a + "" + b + "." + c
8846 ); // ["$+12.43", "$-10.15", "$+22.96"]
8848 * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
8849 * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
8850 * @return {Array} The zipped set.
8851 * @deprecated 4.0.0 Will be removed in the next major version
8854 var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
8857 len = Ext.max(Ext.pluck(arrs, "length")),
8860 for (var i = 0; i < len; i++) {
8863 ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
8865 for (var j = 0, aLen = arrs.length; j < aLen; j++){
8866 ret[i].push( arrs[j][i] );
8874 * Turns an array into a sentence, joined by a specified connector - e.g.:
8875 * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
8876 * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
8877 * @param {Array} items The array to create a sentence from
8878 * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
8879 * @return {String} The sentence string
8880 * @deprecated 4.0.0 Will be removed in the next major version
8882 toSentence: function(items, connector) {
8883 var length = items.length;
8888 var head = items.slice(0, length - 1),
8889 tail = items[length - 1];
8891 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
8896 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
8897 * you may want to set this to true.
8907 * @param {Object} config
8909 Ext.application = function(config) {
8910 Ext.require('Ext.app.Application');
8912 Ext.onReady(function() {
8913 Ext.create('Ext.app.Application', config);
8918 * @class Ext.util.Format
8920 This class is a centralized place for formatting functions inside the library. It includes
8921 functions to format various different types of data, such as text, dates and numeric values.
8924 This class contains several options for localization. These can be set once the library has loaded,
8925 all calls to the functions from that point will use the locale settings that were specified.
8932 This class also uses the default date format defined here: {@link Ext.date#defaultFormat}.
8934 __Using with renderers__
8935 There are two helper functions that return a new function that can be used in conjunction with
8940 renderer: Ext.util.Format.dateRenderer('Y-m-d')
8943 renderer: Ext.util.Format.numberRenderer('0.000')
8946 Functions that only take a single argument can also be passed directly:
8949 renderer: Ext.util.Format.usMoney
8951 dataIndex: 'productCode',
8952 renderer: Ext.util.Format.uppercase
8955 __Using with XTemplates__
8956 XTemplates can also directly use Ext.util.Format functions:
8959 'Date: {startDate:date("Y-m-d")}',
8960 'Cost: {cost:usMoney}'
8969 Ext.util.Format = {};
8970 var UtilFormat = Ext.util.Format,
8971 stripTagsRE = /<\/?[^>]+>/gi,
8972 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
8975 // A RegExp to remove from a number format string, all characters except digits and '.'
8976 formatCleanRe = /[^\d\.]/g,
8978 // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
8979 // Created on first use. The local decimal separator character must be initialized for this to be created.
8982 Ext.apply(UtilFormat, {
8985 * @property thousandSeparator
8986 * <p>The character that the {@link #number} function uses as a thousand separator.</p>
8987 * <p>This defaults to <code>,</code>, but may be overridden in a locale file.</p>
8989 thousandSeparator: ',',
8993 * @property decimalSeparator
8994 * <p>The character that the {@link #number} function uses as a decimal point.</p>
8995 * <p>This defaults to <code>.</code>, but may be overridden in a locale file.</p>
8997 decimalSeparator: '.',
9001 * @property currencyPrecision
9002 * <p>The number of decimal places that the {@link #currency} function displays.</p>
9003 * <p>This defaults to <code>2</code>, but may be overridden in a locale file.</p>
9005 currencyPrecision: 2,
9009 * @property currencySign
9010 * <p>The currency sign that the {@link #currency} function displays.</p>
9011 * <p>This defaults to <code>$</code>, but may be overridden in a locale file.</p>
9017 * @property currencyAtEnd
9018 * <p>This may be set to <code>true</code> to make the {@link #currency} function
9019 * append the currency sign to the formatted value.</p>
9020 * <p>This defaults to <code>false</code>, but may be overridden in a locale file.</p>
9022 currencyAtEnd: false,
9025 * Checks a reference and converts it to empty string if it is undefined
9026 * @param {Mixed} value Reference to check
9027 * @return {Mixed} Empty string if converted, otherwise the original value
9029 undef : function(value) {
9030 return value !== undefined ? value : "";
9034 * Checks a reference and converts it to the default value if it's empty
9035 * @param {Mixed} value Reference to check
9036 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
9039 defaultValue : function(value, defaultValue) {
9040 return value !== undefined && value !== '' ? value : defaultValue;
9044 * Returns a substring from within an original string
9045 * @param {String} value The original text
9046 * @param {Number} start The start index of the substring
9047 * @param {Number} length The length of the substring
9048 * @return {String} The substring
9050 substr : function(value, start, length) {
9051 return String(value).substr(start, length);
9055 * Converts a string to all lower case letters
9056 * @param {String} value The text to convert
9057 * @return {String} The converted text
9059 lowercase : function(value) {
9060 return String(value).toLowerCase();
9064 * Converts a string to all upper case letters
9065 * @param {String} value The text to convert
9066 * @return {String} The converted text
9068 uppercase : function(value) {
9069 return String(value).toUpperCase();
9073 * Format a number as US currency
9074 * @param {Number/String} value The numeric value to format
9075 * @return {String} The formatted currency string
9077 usMoney : function(v) {
9078 return UtilFormat.currency(v, '$', 2);
9082 * Format a number as a currency
9083 * @param {Number/String} value The numeric value to format
9084 * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
9085 * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
9086 * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
9087 * @return {String} The formatted currency string
9089 currency: function(v, currencySign, decimals, end) {
9090 var negativeSign = '',
9098 decimals = decimals || UtilFormat.currencyPrecision;
9099 format += format + (decimals > 0 ? '.' : '');
9100 for (; i < decimals; i++) {
9103 v = UtilFormat.number(v, format);
9104 if ((end || UtilFormat.currencyAtEnd) === true) {
9105 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
9107 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
9112 * Formats the passed date using the specified format pattern.
9113 * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
9114 * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
9115 * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9116 * @return {String} The formatted date string.
9118 date: function(v, format) {
9122 if (!Ext.isDate(v)) {
9123 v = new Date(Date.parse(v));
9125 return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
9129 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
9130 * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9131 * @return {Function} The date formatting function
9133 dateRenderer : function(format) {
9134 return function(v) {
9135 return UtilFormat.date(v, format);
9140 * Strips all HTML tags
9141 * @param {Mixed} value The text from which to strip tags
9142 * @return {String} The stripped text
9144 stripTags : function(v) {
9145 return !v ? v : String(v).replace(stripTagsRE, "");
9149 * Strips all script tags
9150 * @param {Mixed} value The text from which to strip script tags
9151 * @return {String} The stripped text
9153 stripScripts : function(v) {
9154 return !v ? v : String(v).replace(stripScriptsRe, "");
9158 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
9159 * @param {Number/String} size The numeric value to format
9160 * @return {String} The formatted file size
9162 fileSize : function(size) {
9164 return size + " bytes";
9165 } else if (size < 1048576) {
9166 return (Math.round(((size*10) / 1024))/10) + " KB";
9168 return (Math.round(((size*10) / 1048576))/10) + " MB";
9173 * It does simple math for use in a template, for example:<pre><code>
9174 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
9176 * @return {Function} A function that operates on the passed value.
9181 return function(v, a){
9183 fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
9190 * Rounds the passed number to the required decimal precision.
9191 * @param {Number/String} value The numeric value to round.
9192 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
9193 * @return {Number} The rounded value.
9195 round : function(value, precision) {
9196 var result = Number(value);
9197 if (typeof precision == 'number') {
9198 precision = Math.pow(10, precision);
9199 result = Math.round(value * precision) / precision;
9205 * <p>Formats the passed number according to the passed format string.</p>
9206 * <p>The number of digits after the decimal separator character specifies the number of
9207 * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
9208 * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
9209 * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
9210 * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
9211 * <p><b>New to Ext4</b></p>
9212 * <p>Locale-specific characters are always used in the formatted output when inserting
9213 * thousand and decimal separators.</p>
9214 * <p>The format string must specify separator characters according to US/UK conventions ("," as the
9215 * thousand separator, and "." as the decimal separator)</p>
9216 * <p>To allow specification of format strings according to local conventions for separator characters, add
9217 * the string <code>/i</code> to the end of the format string.</p>
9218 * <div style="margin-left:40px">examples (123456.789):
9219 * <div style="margin-left:10px">
9220 * 0 - (123456) show only digits, no precision<br>
9221 * 0.00 - (123456.78) show only digits, 2 precision<br>
9222 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
9223 * 0,000 - (123,456) show comma and digits, no precision<br>
9224 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
9225 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
9226 * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
9227 * For example: 0.000,00/i
9229 * @param {Number} v The number to format.
9230 * @param {String} format The way you would like to format this text.
9231 * @return {String} The formatted number.
9234 function(v, formatString) {
9235 if (!formatString) {
9238 v = Ext.Number.from(v, NaN);
9242 var comma = UtilFormat.thousandSeparator,
9243 dec = UtilFormat.decimalSeparator,
9251 // The "/i" suffix allows caller to use a locale-specific formatting string.
9252 // Clean the format string by removing all but numerals and the decimal separator.
9253 // Then split the format string into pre and post decimal segments according to *what* the
9254 // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
9255 if (formatString.substr(formatString.length - 2) == '/i') {
9256 if (!I18NFormatCleanRe) {
9257 I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
9259 formatString = formatString.substr(0, formatString.length - 2);
9261 hasComma = formatString.indexOf(comma) != -1;
9262 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
9264 hasComma = formatString.indexOf(',') != -1;
9265 psplit = formatString.replace(formatCleanRe, '').split('.');
9268 if (1 < psplit.length) {
9269 v = v.toFixed(psplit[1].length);
9270 } else if(2 < psplit.length) {
9272 sourceClass: "Ext.util.Format",
9273 sourceMethod: "number",
9275 formatString: formatString,
9276 msg: "Invalid number format, should have no more than 1 decimal"
9282 var fnum = v.toString();
9284 psplit = fnum.split('.');
9287 var cnum = psplit[0],
9290 m = Math.floor(j / 3),
9291 n = cnum.length % 3 || 3,
9294 for (i = 0; i < j; i += n) {
9299 parr[parr.length] = cnum.substr(i, n);
9302 fnum = parr.join(comma);
9304 fnum += dec + psplit[1];
9308 fnum = psplit[0] + dec + psplit[1];
9312 return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
9316 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
9317 * @param {String} format Any valid number format string for {@link #number}
9318 * @return {Function} The number formatting function
9320 numberRenderer : function(format) {
9321 return function(v) {
9322 return UtilFormat.number(v, format);
9327 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
9328 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
9329 * if the value is 0 or greater than 1.
9330 * @param {Number} value The value to compare against
9331 * @param {String} singular The singular form of the word
9332 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
9334 plural : function(v, s, p) {
9335 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
9339 * Converts newline characters to the HTML tag <br/>
9340 * @param {String} The string value to format.
9341 * @return {String} The string with embedded <br/> tags in place of newlines.
9343 nl2br : function(v) {
9344 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
9348 * Capitalize the given string. See {@link Ext.String#capitalize}.
9350 capitalize: Ext.String.capitalize,
9353 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length.
9354 * See {@link Ext.String#ellipsis}.
9356 ellipsis: Ext.String.ellipsis,
9359 * Formats to a string. See {@link Ext.String#format}
9361 format: Ext.String.format,
9364 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
9365 * See {@link Ext.string#htmlDecode}.
9367 htmlDecode: Ext.String.htmlDecode,
9370 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
9371 * See {@link Ext.String#htmlEncode}.
9373 htmlEncode: Ext.String.htmlEncode,
9376 * Adds left padding to a string. See {@link Ext.String#leftPad}
9378 leftPad: Ext.String.leftPad,
9381 * Trims any whitespace from either side of a string. See {@link Ext.String#trim}.
9383 trim : Ext.String.trim,
9386 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
9387 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
9388 * @param {Number|String} v The encoded margins
9389 * @return {Object} An object with margin sizes for top, right, bottom and left
9391 parseBox : function(box) {
9392 if (Ext.isNumber(box)) {
9393 box = box.toString();
9395 var parts = box.split(' '),
9399 parts[1] = parts[2] = parts[3] = parts[0];
9402 parts[2] = parts[0];
9403 parts[3] = parts[1];
9406 parts[3] = parts[1];
9410 top :parseInt(parts[0], 10) || 0,
9411 right :parseInt(parts[1], 10) || 0,
9412 bottom:parseInt(parts[2], 10) || 0,
9413 left :parseInt(parts[3], 10) || 0
9418 * Escapes the passed string for use in a regular expression
9419 * @param {String} str
9422 escapeRegex : function(s) {
9423 return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
9429 * @class Ext.util.TaskRunner
9430 * Provides the ability to execute one or more arbitrary tasks in a multithreaded
9431 * manner. Generally, you can use the singleton {@link Ext.TaskManager} instead, but
9432 * if needed, you can create separate instances of TaskRunner. Any number of
9433 * separate tasks can be started at any time and will run independently of each
9434 * other. Example usage:
9436 // Start a simple clock task that updates a div once per second
9437 var updateClock = function(){
9438 Ext.fly('clock').update(new Date().format('g:i:s A'));
9442 interval: 1000 //1 second
9444 var runner = new Ext.util.TaskRunner();
9447 // equivalent using TaskManager
9448 Ext.TaskManager.start({
9454 * <p>See the {@link #start} method for details about how to configure a task object.</p>
9455 * Also see {@link Ext.util.DelayedTask}.
9458 * @param {Number} interval (optional) The minimum precision in milliseconds supported by this TaskRunner instance
9463 Ext.util.TaskRunner = function(interval) {
9464 interval = interval || 10;
9471 stopThread = function() {
9478 startThread = function() {
9481 id = setInterval(runTasks, interval);
9486 removeTask = function(t) {
9487 removeQueue.push(t);
9489 t.onStop.apply(t.scope || t);
9494 runTasks = function() {
9495 var rqLen = removeQueue.length,
9496 now = new Date().getTime(),
9500 for (i = 0; i < rqLen; i++) {
9501 Ext.Array.remove(tasks, removeQueue[i]);
9504 if (tasks.length < 1) {
9514 for (; i < len; ++i) {
9516 itime = now - t.taskRunTime;
9517 if (t.interval <= itime) {
9518 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
9519 t.taskRunTime = now;
9520 if (rt === false || t.taskRunCount === t.repeat) {
9525 if (t.duration && t.duration <= (now - t.taskStartTime)) {
9532 * Starts a new task.
9534 * @param {Object} task <p>A config object that supports the following properties:<ul>
9535 * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
9536 * function will be called at each interval and passed the <code>args</code> argument if specified, and the
9537 * current invocation count if not.</p>
9538 * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
9539 * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
9540 * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
9541 * should be invoked.</div></li>
9542 * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
9543 * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
9544 * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
9545 * <code>run</code> function. Defaults to the task config object.</div></li>
9546 * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
9547 * the task before stopping automatically (defaults to indefinite).</div></li>
9548 * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
9549 * stopping automatically (defaults to indefinite).</div></li>
9551 * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
9552 * that calculations based on the repeat count can be performed.</p>
9553 * @return {Object} The task
9555 this.start = function(task) {
9557 task.taskStartTime = new Date().getTime();
9558 task.taskRunTime = 0;
9559 task.taskRunCount = 0;
9565 * Stops an existing running task.
9567 * @param {Object} task The task to stop
9568 * @return {Object} The task
9570 this.stop = function(task) {
9576 * Stops all tasks that are currently running.
9579 this.stopAll = function() {
9581 for (var i = 0, len = tasks.length; i < len; i++) {
9582 if (tasks[i].onStop) {
9592 * @class Ext.TaskManager
9593 * @extends Ext.util.TaskRunner
9594 * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks. See
9595 * {@link Ext.util.TaskRunner} for supported methods and task config properties.
9597 // Start a simple clock task that updates a div once per second
9600 Ext.fly('clock').update(new Date().format('g:i:s A'));
9602 interval: 1000 //1 second
9604 Ext.TaskManager.start(task);
9606 * <p>See the {@link #start} method for details about how to configure a task object.</p>
9609 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
9613 * Determines information about the current platform the application is running on.
9618 init : function(navigator) {
9619 var platforms = this.platforms,
9620 ln = platforms.length,
9623 navigator = navigator || window.navigator;
9625 for (i = 0; i < ln; i++) {
9626 platform = platforms[i];
9627 this[platform.identity] = platform.regex.test(navigator[platform.property]);
9631 * @property Desktop True if the browser is running on a desktop machine
9634 this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
9636 * @property Tablet True if the browser is running on a tablet (iPad)
9638 this.Tablet = this.iPad;
9640 * @property Phone True if the browser is running on a phone.
9643 this.Phone = !this.Desktop && !this.Tablet;
9645 * @property iOS True if the browser is running on iOS
9648 this.iOS = this.iPhone || this.iPad || this.iPod;
9651 * @property Standalone Detects when application has been saved to homescreen.
9654 this.Standalone = !!window.navigator.standalone;
9658 * @property iPhone True when the browser is running on a iPhone
9662 property: 'platform',
9668 * @property iPod True when the browser is running on a iPod
9672 property: 'platform',
9678 * @property iPad True when the browser is running on a iPad
9682 property: 'userAgent',
9688 * @property Blackberry True when the browser is running on a Blackberry
9692 property: 'userAgent',
9693 regex: /Blackberry/i,
9694 identity: 'Blackberry'
9698 * @property Android True when the browser is running on an Android device
9702 property: 'userAgent',
9708 * @property Mac True when the browser is running on a Mac
9712 property: 'platform',
9718 * @property Windows True when the browser is running on Windows
9722 property: 'platform',
9728 * @property Linux True when the browser is running on Linux
9732 property: 'platform',
9741 * @class Ext.supports
9743 * Determines information about features are supported in the current environment
9750 div = doc.createElement('div'),
9756 '<div style="height:30px;width:50px;">',
9757 '<div style="height:20px;width:20px;"></div>',
9759 '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
9760 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
9762 '<div style="float:left; background-color:transparent;"></div>'
9765 doc.body.appendChild(div);
9767 for (i = 0; i < ln; i++) {
9769 this[test.identity] = test.fn.call(this, doc, div);
9772 doc.body.removeChild(div);
9776 * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
9779 CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
9782 * @property ClassList True if document environment supports the HTML5 classList API.
9785 ClassList: !!document.documentElement.classList,
9788 * @property OrientationChange True if the device supports orientation change
9791 OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
9794 * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
9797 DeviceMotion: ('ondevicemotion' in window),
9800 * @property Touch True if the device supports touch
9803 // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
9804 // and Safari 4.0 (they all have 'ontouchstart' in the window object).
9805 Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
9809 * @property Transitions True if the device supports CSS3 Transitions
9813 identity: 'Transitions',
9814 fn: function(doc, div) {
9822 TE = 'TransitionEnd',
9823 transitionEndName = [
9825 'transitionend', //Moz bucks the prefixing convention
9834 for (; i < ln; i++) {
9835 if (div.getStyle(prefix[i] + "TransitionProperty")) {
9836 Ext.supports.CSS3Prefix = prefix[i];
9837 Ext.supports.CSS3TransitionEnd = transitionEndName[i];
9847 * @property RightMargin True if the device supports right margin.
9848 * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
9852 identity: 'RightMargin',
9853 fn: function(doc, div, view) {
9854 view = doc.defaultView;
9855 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
9860 * @property TransparentColor True if the device supports transparent color
9864 identity: 'TransparentColor',
9865 fn: function(doc, div, view) {
9866 view = doc.defaultView;
9867 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
9872 * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
9876 identity: 'ComputedStyle',
9877 fn: function(doc, div, view) {
9878 view = doc.defaultView;
9879 return view && view.getComputedStyle;
9884 * @property SVG True if the device supports SVG
9890 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
9895 * @property Canvas True if the device supports Canvas
9901 return !!doc.createElement('canvas').getContext;
9906 * @property VML True if the device supports VML
9912 var d = doc.createElement("div");
9913 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
9914 return (d.childNodes.length == 2);
9919 * @property Float True if the device supports CSS float
9924 fn: function(doc, div) {
9925 return !!div.lastChild.style.cssFloat;
9930 * @property AudioTag True if the device supports the HTML5 audio tag
9934 identity: 'AudioTag',
9936 return !!doc.createElement('audio').canPlayType;
9941 * @property History True if the device supports HTML5 history
9945 identity: 'History',
9947 return !!(window.history && history.pushState);
9952 * @property CSS3DTransform True if the device supports CSS3DTransform
9956 identity: 'CSS3DTransform',
9958 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
9963 * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
9967 identity: 'CSS3LinearGradient',
9968 fn: function(doc, div) {
9969 var property = 'background-image:',
9970 webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
9971 w3c = 'linear-gradient(left top, black, white)',
9972 moz = '-moz-' + w3c,
9973 options = [property + webkit, property + w3c, property + moz];
9975 div.style.cssText = options.join(';');
9977 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
9982 * @property CSS3BorderRadius True if the device supports CSS3 border radius
9986 identity: 'CSS3BorderRadius',
9987 fn: function(doc, div) {
9988 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
9991 for (i = 0; i < domPrefixes.length; i++) {
9992 if (document.body.style[domPrefixes[i]] !== undefined) {
10001 * @property GeoLocation True if the device supports GeoLocation
10005 identity: 'GeoLocation',
10007 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
10011 * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
10015 identity: 'MouseEnterLeave',
10016 fn: function(doc, div){
10017 return ('onmouseenter' in div && 'onmouseleave' in div);
10021 * @property MouseWheel True if the browser supports the mousewheel event
10025 identity: 'MouseWheel',
10026 fn: function(doc, div) {
10027 return ('onmousewheel' in div);
10031 * @property Opacity True if the browser supports normal css opacity
10035 identity: 'Opacity',
10036 fn: function(doc, div){
10037 // Not a strict equal comparison in case opacity can be converted to a number.
10038 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
10041 div.firstChild.style.cssText = 'opacity:0.73';
10042 return div.firstChild.style.opacity == '0.73';
10046 * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
10050 identity: 'Placeholder',
10051 fn: function(doc) {
10052 return 'placeholder' in doc.createElement('input');
10057 * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight,
10058 * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
10062 identity: 'Direct2DBug',
10064 return Ext.isString(document.body.style.msTransformOrigin);
10068 * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
10072 identity: 'BoundingClientRect',
10073 fn: function(doc, div) {
10074 return Ext.isFunction(div.getBoundingClientRect);
10078 identity: 'IncludePaddingInWidthCalculation',
10079 fn: function(doc, div){
10080 var el = Ext.get(div.childNodes[1].firstChild);
10081 return el.getWidth() == 210;
10085 identity: 'IncludePaddingInHeightCalculation',
10086 fn: function(doc, div){
10087 var el = Ext.get(div.childNodes[1].firstChild);
10088 return el.getHeight() == 210;
10093 * @property ArraySort True if the Array sort native method isn't bugged.
10097 identity: 'ArraySort',
10099 var a = [1,2,3,4,5].sort(function(){ return 0; });
10100 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
10104 * @property Range True if browser support document.createRange native method.
10110 return !!document.createRange;
10114 * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
10118 identity: 'CreateContextualFragment',
10120 var range = Ext.supports.Range ? document.createRange() : false;
10122 return range && !!range.createContextualFragment;
10132 Ext JS - JavaScript Library
10133 Copyright (c) 2006-2011, Sencha Inc.
10134 All rights reserved.
10135 licensing@sencha.com
10138 * @class Ext.core.DomHelper
10139 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10140 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
10141 * from your DOM building code.</p>
10143 * <p><b><u>DomHelper element specification object</u></b></p>
10144 * <p>A specification object is used when creating elements. Attributes of this object
10145 * are assumed to be element attributes, except for 4 special attributes:
10146 * <div class="mdetail-params"><ul>
10147 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
10148 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
10149 * same kind of element definition objects to be created and appended. These can be nested
10150 * as deep as you want.</div></li>
10151 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
10152 * This will end up being either the "class" attribute on a HTML fragment or className
10153 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
10154 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
10157 * <p><b><u>Insertion methods</u></b></p>
10158 * <p>Commonly used insertion methods:
10159 * <div class="mdetail-params"><ul>
10160 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
10161 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
10162 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
10163 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
10164 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
10165 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
10168 * <p><b><u>Example</u></b></p>
10169 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
10170 * element with id <tt>'my-div'</tt>:<br>
10172 var dh = Ext.core.DomHelper; // create shorthand alias
10173 // specification object
10178 // append children after creating
10179 children: [ // may also specify 'cn' instead of 'children'
10180 {tag: 'li', id: 'item0', html: 'List Item 0'},
10181 {tag: 'li', id: 'item1', html: 'List Item 1'},
10182 {tag: 'li', id: 'item2', html: 'List Item 2'}
10185 var list = dh.append(
10186 'my-div', // the context element 'my-div' can either be the id or the actual node
10187 spec // the specification object
10190 * <p>Element creation specification parameters in this class may also be passed as an Array of
10191 * specification objects. This can be used to insert multiple sibling nodes into an existing
10192 * container very efficiently. For example, to add more list items to the example above:<pre><code>
10193 dh.append('my-ul', [
10194 {tag: 'li', id: 'item3', html: 'List Item 3'},
10195 {tag: 'li', id: 'item4', html: 'List Item 4'}
10197 * </code></pre></p>
10199 * <p><b><u>Templating</u></b></p>
10200 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
10201 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
10202 * insert new elements. Revisiting the example above, we could utilize templating this time:
10205 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
10207 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
10209 for(var i = 0; i < 5, i++){
10210 tpl.append(list, [i]); // use template to append to the actual node
10212 * </code></pre></p>
10213 * <p>An example using a template:<pre><code>
10214 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
10216 var tpl = new Ext.core.DomHelper.createTemplate(html);
10217 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
10218 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
10219 * </code></pre></p>
10221 * <p>The same example using named parameters:<pre><code>
10222 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
10224 var tpl = new Ext.core.DomHelper.createTemplate(html);
10225 tpl.append('blog-roll', {
10227 url: 'http://www.edspencer.net/',
10228 text: "Ed's Site"
10230 tpl.append('blog-roll', {
10232 url: 'http://www.dustindiaz.com/',
10233 text: "Dustin's Site"
10235 * </code></pre></p>
10237 * <p><b><u>Compiling Templates</u></b></p>
10238 * <p>Templates are applied using regular expressions. The performance is great, but if
10239 * you are adding a bunch of DOM elements using the same template, you can increase
10240 * performance even further by {@link Ext.Template#compile "compiling"} the template.
10241 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
10242 * broken up at the different variable points and a dynamic function is created and eval'ed.
10243 * The generated function performs string concatenation of these parts and the passed
10244 * variables instead of using regular expressions.
10246 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
10248 var tpl = new Ext.core.DomHelper.createTemplate(html);
10251 //... use template like normal
10252 * </code></pre></p>
10254 * <p><b><u>Performance Boost</u></b></p>
10255 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
10256 * of DOM can significantly boost performance.</p>
10257 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
10258 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
10259 * results in the creation of a text node. Usage:</p>
10261 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
10265 Ext.ns('Ext.core');
10266 Ext.core.DomHelper = function(){
10267 var tempTableEl = null,
10268 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
10269 tableRe = /^table|tbody|tr|td$/i,
10270 confRe = /tag|children|cn|html$/i,
10271 tableElRe = /td|tr|tbody/i,
10274 // kill repeat to save bytes
10275 afterbegin = 'afterbegin',
10276 afterend = 'afterend',
10277 beforebegin = 'beforebegin',
10278 beforeend = 'beforeend',
10281 tbs = ts+'<tbody>',
10282 tbe = '</tbody>'+te,
10283 trs = tbs + '<tr>',
10287 function doInsert(el, o, returnElement, pos, sibling, append){
10288 el = Ext.getDom(el);
10291 newNode = createDom(o, null);
10293 el.appendChild(newNode);
10295 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
10298 newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
10300 return returnElement ? Ext.get(newNode, true) : newNode;
10303 function createDom(o, parentNode){
10311 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
10312 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
10313 for (var i = 0, l = o.length; i < l; i++) {
10314 createDom(o[i], el);
10316 } else if (typeof o == 'string') { // Allow a string as a child spec.
10317 el = doc.createTextNode(o);
10319 el = doc.createElement( o.tag || 'div' );
10320 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
10322 if(!confRe.test(attr)){
10325 el.className = val;
10328 el.setAttribute(attr, val);
10335 Ext.core.DomHelper.applyStyles(el, o.style);
10337 if ((cn = o.children || o.cn)) {
10339 } else if (o.html) {
10340 el.innerHTML = o.html;
10344 parentNode.appendChild(el);
10349 // build as innerHTML where available
10350 function createHtml(o){
10358 if(typeof o == "string"){
10360 } else if (Ext.isArray(o)) {
10361 for (i=0; i < o.length; i++) {
10363 b += createHtml(o[i]);
10367 b += '<' + (o.tag = o.tag || 'div');
10370 if(!confRe.test(attr)){
10371 if (typeof val == "object") {
10372 b += ' ' + attr + '="';
10374 b += key + ':' + val[key] + ';';
10378 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
10382 // Now either just close the tag or try to add children and close the tag.
10383 if (emptyTags.test(o.tag)) {
10387 if ((cn = o.children || o.cn)) {
10388 b += createHtml(cn);
10392 b += '</' + o.tag + '>';
10398 function ieTable(depth, s, h, e){
10399 tempTableEl.innerHTML = [s, h, e].join('');
10403 while(++i < depth){
10404 el = el.firstChild;
10406 // If the result is multiple siblings, then encapsulate them into one fragment.
10407 ns = el.nextSibling;
10409 var df = document.createDocumentFragment();
10411 ns = el.nextSibling;
10412 df.appendChild(el);
10422 * Nasty code for IE's broken table implementation
10424 function insertIntoTable(tag, where, el, html) {
10428 tempTableEl = tempTableEl || document.createElement('div');
10430 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
10431 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
10434 before = where == beforebegin ? el :
10435 where == afterend ? el.nextSibling :
10436 where == afterbegin ? el.firstChild : null;
10438 if (where == beforebegin || where == afterend) {
10439 el = el.parentNode;
10442 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
10443 node = ieTable(4, trs, html, tre);
10444 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
10445 (tag == 'tr' && (where == beforebegin || where == afterend))) {
10446 node = ieTable(3, tbs, html, tbe);
10448 node = ieTable(2, ts, html, te);
10450 el.insertBefore(node, before);
10456 * Fix for IE9 createContextualFragment missing method
10458 function createContextualFragment(html){
10459 var div = document.createElement("div"),
10460 fragment = document.createDocumentFragment(),
10462 length, childNodes;
10464 div.innerHTML = html;
10465 childNodes = div.childNodes;
10466 length = childNodes.length;
10468 for (; i < length; i++) {
10469 fragment.appendChild(childNodes[i].cloneNode(true));
10477 * Returns the markup for the passed Element(s) config.
10478 * @param {Object} o The DOM object spec (and children)
10481 markup : function(o){
10482 return createHtml(o);
10486 * Applies a style specification to an element.
10487 * @param {String/HTMLElement} el The element to apply styles to
10488 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
10489 * a function which returns such a specification.
10491 applyStyles : function(el, styles){
10494 if (typeof styles == "function") {
10495 styles = styles.call();
10497 if (typeof styles == "string") {
10498 styles = Ext.core.Element.parseStyles(styles);
10500 if (typeof styles == "object") {
10501 el.setStyle(styles);
10507 * Inserts an HTML fragment into the DOM.
10508 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
10509 * @param {HTMLElement/TextNode} el The context element
10510 * @param {String} html The HTML fragment
10511 * @return {HTMLElement} The new node
10513 insertHtml : function(where, el, html){
10522 where = where.toLowerCase();
10523 // add these here because they are used in both branches of the condition.
10524 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
10525 hash[afterend] = ['AfterEnd', 'nextSibling'];
10527 // if IE and context element is an HTMLElement
10528 if (el.insertAdjacentHTML) {
10529 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
10533 // add these two to the hash.
10534 hash[afterbegin] = ['AfterBegin', 'firstChild'];
10535 hash[beforeend] = ['BeforeEnd', 'lastChild'];
10536 if ((hashVal = hash[where])) {
10537 el.insertAdjacentHTML(hashVal[0], html);
10538 return el[hashVal[1]];
10540 // if (not IE and context element is an HTMLElement) or TextNode
10542 // we cannot insert anything inside a textnode so...
10543 if (Ext.isTextNode(el)) {
10544 where = where === 'afterbegin' ? 'beforebegin' : where;
10545 where = where === 'beforeend' ? 'afterend' : where;
10547 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
10548 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
10551 range[setStart](el);
10552 frag = range.createContextualFragment(html);
10554 frag = createContextualFragment(html);
10556 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
10557 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
10559 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
10560 if (el.firstChild) {
10562 range[setStart](el[rangeEl]);
10563 frag = range.createContextualFragment(html);
10565 frag = createContextualFragment(html);
10568 if(where == afterbegin){
10569 el.insertBefore(frag, el.firstChild);
10571 el.appendChild(frag);
10574 el.innerHTML = html;
10576 return el[rangeEl];
10580 sourceClass: 'Ext.core.DomHelper',
10581 sourceMethod: 'insertHtml',
10582 htmlToInsert: html,
10584 msg: 'Illegal insertion point reached: "' + where + '"'
10589 * Creates new DOM element(s) and inserts them before el.
10590 * @param {Mixed} el The context element
10591 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
10592 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
10593 * @return {HTMLElement/Ext.core.Element} The new node
10595 insertBefore : function(el, o, returnElement){
10596 return doInsert(el, o, returnElement, beforebegin);
10600 * Creates new DOM element(s) and inserts them after el.
10601 * @param {Mixed} el The context element
10602 * @param {Object} o The DOM object spec (and children)
10603 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
10604 * @return {HTMLElement/Ext.core.Element} The new node
10606 insertAfter : function(el, o, returnElement){
10607 return doInsert(el, o, returnElement, afterend, 'nextSibling');
10611 * Creates new DOM element(s) and inserts them as the first child of el.
10612 * @param {Mixed} el The context element
10613 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
10614 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
10615 * @return {HTMLElement/Ext.core.Element} The new node
10617 insertFirst : function(el, o, returnElement){
10618 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
10622 * Creates new DOM element(s) and appends them to el.
10623 * @param {Mixed} el The context element
10624 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
10625 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
10626 * @return {HTMLElement/Ext.core.Element} The new node
10628 append : function(el, o, returnElement){
10629 return doInsert(el, o, returnElement, beforeend, '', true);
10633 * Creates new DOM element(s) and overwrites the contents of el with them.
10634 * @param {Mixed} el The context element
10635 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
10636 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
10637 * @return {HTMLElement/Ext.core.Element} The new node
10639 overwrite : function(el, o, returnElement){
10640 el = Ext.getDom(el);
10641 el.innerHTML = createHtml(o);
10642 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
10645 createHtml : createHtml,
10648 * Creates new DOM element(s) without inserting them to the document.
10649 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
10650 * @return {HTMLElement} The new uninserted node
10652 createDom: createDom,
10654 /** True to force the use of DOM instead of html fragments @type Boolean */
10658 * Creates a new Ext.Template from the DOM object spec.
10659 * @param {Object} o The DOM object spec (and children)
10660 * @return {Ext.Template} The new template
10662 createTemplate : function(o){
10663 var html = Ext.core.DomHelper.createHtml(o);
10664 return Ext.create('Ext.Template', html);
10671 * This is code is also distributed under MIT license for use
10672 * with jQuery and prototype JavaScript libraries.
10675 * @class Ext.DomQuery
10676 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).
10678 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>
10681 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.
10683 <h4>Element Selectors:</h4>
10685 <li> <b>*</b> any element</li>
10686 <li> <b>E</b> an element with the tag E</li>
10687 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
10688 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
10689 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
10690 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
10692 <h4>Attribute Selectors:</h4>
10693 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
10695 <li> <b>E[foo]</b> has an attribute "foo"</li>
10696 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
10697 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
10698 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
10699 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
10700 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
10701 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
10703 <h4>Pseudo Classes:</h4>
10705 <li> <b>E:first-child</b> E is the first child of its parent</li>
10706 <li> <b>E:last-child</b> E is the last child of its parent</li>
10707 <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>
10708 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
10709 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
10710 <li> <b>E:only-child</b> E is the only child of its parent</li>
10711 <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>
10712 <li> <b>E:first</b> the first E in the resultset</li>
10713 <li> <b>E:last</b> the last E in the resultset</li>
10714 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
10715 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
10716 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
10717 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
10718 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
10719 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
10720 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
10721 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
10722 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
10723 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
10725 <h4>CSS Value Selectors:</h4>
10727 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
10728 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
10729 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
10730 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
10731 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
10732 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
10736 Ext.ns('Ext.core');
10738 Ext.core.DomQuery = Ext.DomQuery = function(){
10743 trimRe = /^\s+|\s+$/g,
10744 tplRe = /\{(\d+)\}/g,
10745 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
10746 tagTokenRe = /^(#)?([\w-\*]+)/,
10747 nthRe = /(\d*)n\+?(\d*)/,
10749 // This is for IE MSXML which does not support expandos.
10750 // IE runs the same speed using setAttribute, however FF slows way down
10751 // and Safari completely fails so they need to continue to use expandos.
10752 isIE = window.ActiveXObject ? true : false,
10755 // this eval is stop the compressor from
10756 // renaming the variable to something shorter
10757 eval("var batch = 30803;");
10759 // Retrieve the child node from a particular
10760 // parent at the specified index.
10761 function child(parent, index){
10763 n = parent.firstChild;
10765 if(n.nodeType == 1){
10775 // retrieve the next element node
10777 while((n = n.nextSibling) && n.nodeType != 1);
10781 // retrieve the previous element node
10783 while((n = n.previousSibling) && n.nodeType != 1);
10787 // Mark each child node with a nodeIndex skipping and
10788 // removing empty text nodes.
10789 function children(parent){
10790 var n = parent.firstChild,
10794 nextNode = n.nextSibling;
10795 // clean worthless empty nodes.
10796 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
10797 parent.removeChild(n);
10799 // add an expando nodeIndex
10800 n.nodeIndex = ++nodeIndex;
10808 // nodeSet - array of nodes
10810 function byClassName(nodeSet, cls){
10814 var result = [], ri = -1;
10815 for(var i = 0, ci; ci = nodeSet[i]; i++){
10816 if((' '+ci.className+' ').indexOf(cls) != -1){
10823 function attrValue(n, attr){
10824 // if its an array, use the first node.
10825 if(!n.tagName && typeof n.length != "undefined"){
10835 if(attr == "class" || attr == "className"){
10836 return n.className;
10838 return n.getAttribute(attr) || n[attr];
10844 // mode - false, /, >, +, ~
10845 // tagName - defaults to "*"
10846 function getNodes(ns, mode, tagName){
10847 var result = [], ri = -1, cs;
10851 tagName = tagName || "*";
10852 // convert to array
10853 if(typeof ns.getElementsByTagName != "undefined"){
10857 // no mode specified, grab all elements by tagName
10860 for(var i = 0, ni; ni = ns[i]; i++){
10861 cs = ni.getElementsByTagName(tagName);
10862 for(var j = 0, ci; ci = cs[j]; j++){
10866 // Direct Child mode (/ or >)
10867 // E > F or E/F all direct children elements of E that have the tag
10868 } else if(mode == "/" || mode == ">"){
10869 var utag = tagName.toUpperCase();
10870 for(var i = 0, ni, cn; ni = ns[i]; i++){
10871 cn = ni.childNodes;
10872 for(var j = 0, cj; cj = cn[j]; j++){
10873 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
10878 // Immediately Preceding mode (+)
10879 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
10880 }else if(mode == "+"){
10881 var utag = tagName.toUpperCase();
10882 for(var i = 0, n; n = ns[i]; i++){
10883 while((n = n.nextSibling) && n.nodeType != 1);
10884 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
10888 // Sibling mode (~)
10889 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
10890 }else if(mode == "~"){
10891 var utag = tagName.toUpperCase();
10892 for(var i = 0, n; n = ns[i]; i++){
10893 while((n = n.nextSibling)){
10894 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
10903 function concat(a, b){
10905 return a.concat(b);
10907 for(var i = 0, l = b.length; i < l; i++){
10908 a[a.length] = b[i];
10913 function byTag(cs, tagName){
10914 if(cs.tagName || cs == document){
10920 var result = [], ri = -1;
10921 tagName = tagName.toLowerCase();
10922 for(var i = 0, ci; ci = cs[i]; i++){
10923 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
10930 function byId(cs, id){
10931 if(cs.tagName || cs == document){
10937 var result = [], ri = -1;
10938 for(var i = 0, ci; ci = cs[i]; i++){
10939 if(ci && ci.id == id){
10947 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
10948 // custom can be "{"
10949 function byAttribute(cs, attr, value, op, custom){
10952 useGetStyle = custom == "{",
10953 fn = Ext.DomQuery.operators[op],
10958 for(var i = 0, ci; ci = cs[i]; i++){
10959 // skip non-element nodes.
10960 if(ci.nodeType != 1){
10963 // only need to do this for the first node
10965 xml = Ext.DomQuery.isXml(ci);
10969 // we only need to change the property names if we're dealing with html nodes, not XML
10972 a = Ext.DomQuery.getStyle(ci, attr);
10973 } else if (attr == "class" || attr == "className"){
10975 } else if (attr == "for"){
10977 } else if (attr == "href"){
10978 // getAttribute href bug
10979 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
10980 a = ci.getAttribute("href", 2);
10982 a = ci.getAttribute(attr);
10985 a = ci.getAttribute(attr);
10987 if((fn && fn(a, value)) || (!fn && a)){
10994 function byPseudo(cs, name, value){
10995 return Ext.DomQuery.pseudos[name](cs, value);
10998 function nodupIEXml(cs){
11001 cs[0].setAttribute("_nodup", d);
11003 for(var i = 1, len = cs.length; i < len; i++){
11005 if(!c.getAttribute("_nodup") != d){
11006 c.setAttribute("_nodup", d);
11010 for(var i = 0, len = cs.length; i < len; i++){
11011 cs[i].removeAttribute("_nodup");
11016 function nodup(cs){
11020 var len = cs.length, c, i, r = cs, cj, ri = -1;
11021 if(!len || typeof cs.nodeType != "undefined" || len == 1){
11024 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
11025 return nodupIEXml(cs);
11029 for(i = 1; c = cs[i]; i++){
11034 for(var j = 0; j < i; j++){
11037 for(j = i+1; cj = cs[j]; j++){
11038 if(cj._nodup != d){
11049 function quickDiffIEXml(c1, c2){
11052 for(var i = 0, len = c1.length; i < len; i++){
11053 c1[i].setAttribute("_qdiff", d);
11055 for(var i = 0, len = c2.length; i < len; i++){
11056 if(c2[i].getAttribute("_qdiff") != d){
11057 r[r.length] = c2[i];
11060 for(var i = 0, len = c1.length; i < len; i++){
11061 c1[i].removeAttribute("_qdiff");
11066 function quickDiff(c1, c2){
11067 var len1 = c1.length,
11073 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
11074 return quickDiffIEXml(c1, c2);
11076 for(var i = 0; i < len1; i++){
11079 for(var i = 0, len = c2.length; i < len; i++){
11080 if(c2[i]._qdiff != d){
11081 r[r.length] = c2[i];
11087 function quickId(ns, mode, root, id){
11089 var d = root.ownerDocument || root;
11090 return d.getElementById(id);
11092 ns = getNodes(ns, mode, "*");
11093 return byId(ns, id);
11097 getStyle : function(el, name){
11098 return Ext.fly(el).getStyle(name);
11101 * Compiles a selector/xpath query into a reusable function. The returned function
11102 * takes one parameter "root" (optional), which is the context node from where the query should start.
11103 * @param {String} selector The selector/xpath query
11104 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
11105 * @return {Function}
11107 compile : function(path, type){
11108 type = type || "select";
11110 // setup fn preamble
11111 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
11114 matchers = Ext.DomQuery.matchers,
11115 matchersLn = matchers.length,
11117 // accept leading mode switch
11118 lmode = path.match(modeRe);
11120 if(lmode && lmode[1]){
11121 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
11122 path = path.replace(lmode[1], "");
11125 // strip leading slashes
11126 while(path.substr(0, 1)=="/"){
11127 path = path.substr(1);
11130 while(path && lastPath != path){
11132 var tokenMatch = path.match(tagTokenRe);
11133 if(type == "select"){
11136 if(tokenMatch[1] == "#"){
11137 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
11139 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
11141 path = path.replace(tokenMatch[0], "");
11142 }else if(path.substr(0, 1) != '@'){
11143 fn[fn.length] = 'n = getNodes(n, mode, "*");';
11145 // type of "simple"
11148 if(tokenMatch[1] == "#"){
11149 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
11151 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
11153 path = path.replace(tokenMatch[0], "");
11156 while(!(modeMatch = path.match(modeRe))){
11157 var matched = false;
11158 for(var j = 0; j < matchersLn; j++){
11159 var t = matchers[j];
11160 var m = path.match(t.re);
11162 fn[fn.length] = t.select.replace(tplRe, function(x, i){
11165 path = path.replace(m[0], "");
11170 // prevent infinite loop on bad selector
11173 sourceClass: 'Ext.DomQuery',
11174 sourceMethod: 'compile',
11175 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
11180 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
11181 path = path.replace(modeMatch[1], "");
11185 fn[fn.length] = "return nodup(n);\n}";
11187 // eval fn and return it
11193 * Selects a group of elements.
11194 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
11195 * @param {Node/String} root (optional) The start of the query (defaults to document).
11196 * @return {Array} An Array of DOM elements which match the selector. If there are
11197 * no matches, and empty Array is returned.
11199 jsSelect: function(path, root, type){
11200 // set root to doc if not specified.
11201 root = root || document;
11203 if(typeof root == "string"){
11204 root = document.getElementById(root);
11206 var paths = path.split(","),
11209 // loop over each selector
11210 for(var i = 0, len = paths.length; i < len; i++){
11211 var subPath = paths[i].replace(trimRe, "");
11212 // compile and place in cache
11213 if(!cache[subPath]){
11214 cache[subPath] = Ext.DomQuery.compile(subPath);
11215 if(!cache[subPath]){
11217 sourceClass: 'Ext.DomQuery',
11218 sourceMethod: 'jsSelect',
11219 msg: subPath + ' is not a valid selector'
11223 var result = cache[subPath](root);
11224 if(result && result != document){
11225 results = results.concat(result);
11229 // if there were multiple selectors, make sure dups
11231 if(paths.length > 1){
11232 return nodup(results);
11237 isXml: function(el) {
11238 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
11239 return docEl ? docEl.nodeName !== "HTML" : false;
11242 select : document.querySelectorAll ? function(path, root, type) {
11243 root = root || document;
11244 if (!Ext.DomQuery.isXml(root)) {
11246 var cs = root.querySelectorAll(path);
11247 return Ext.Array.toArray(cs);
11251 return Ext.DomQuery.jsSelect.call(this, path, root, type);
11252 } : function(path, root, type) {
11253 return Ext.DomQuery.jsSelect.call(this, path, root, type);
11257 * Selects a single element.
11258 * @param {String} selector The selector/xpath query
11259 * @param {Node} root (optional) The start of the query (defaults to document).
11260 * @return {Element} The DOM element which matched the selector.
11262 selectNode : function(path, root){
11263 return Ext.DomQuery.select(path, root)[0];
11267 * Selects the value of a node, optionally replacing null with the defaultValue.
11268 * @param {String} selector The selector/xpath query
11269 * @param {Node} root (optional) The start of the query (defaults to document).
11270 * @param {String} defaultValue
11273 selectValue : function(path, root, defaultValue){
11274 path = path.replace(trimRe, "");
11275 if(!valueCache[path]){
11276 valueCache[path] = Ext.DomQuery.compile(path, "select");
11278 var n = valueCache[path](root), v;
11279 n = n[0] ? n[0] : n;
11281 // overcome a limitation of maximum textnode size
11282 // Rumored to potentially crash IE6 but has not been confirmed.
11283 // http://reference.sitepoint.com/javascript/Node/normalize
11284 // https://developer.mozilla.org/En/DOM/Node.normalize
11285 if (typeof n.normalize == 'function') n.normalize();
11287 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
11288 return ((v === null||v === undefined||v==='') ? defaultValue : v);
11292 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
11293 * @param {String} selector The selector/xpath query
11294 * @param {Node} root (optional) The start of the query (defaults to document).
11295 * @param {Number} defaultValue
11298 selectNumber : function(path, root, defaultValue){
11299 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
11300 return parseFloat(v);
11304 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
11305 * @param {String/HTMLElement/Array} el An element id, element or array of elements
11306 * @param {String} selector The simple selector to test
11307 * @return {Boolean}
11309 is : function(el, ss){
11310 if(typeof el == "string"){
11311 el = document.getElementById(el);
11313 var isArray = Ext.isArray(el),
11314 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
11315 return isArray ? (result.length == el.length) : (result.length > 0);
11319 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
11320 * @param {Array} el An array of elements to filter
11321 * @param {String} selector The simple selector to test
11322 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
11323 * the selector instead of the ones that match
11324 * @return {Array} An Array of DOM elements which match the selector. If there are
11325 * no matches, and empty Array is returned.
11327 filter : function(els, ss, nonMatches){
11328 ss = ss.replace(trimRe, "");
11329 if(!simpleCache[ss]){
11330 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
11332 var result = simpleCache[ss](els);
11333 return nonMatches ? quickDiff(result, els) : result;
11337 * Collection of matching regular expressions and code snippets.
11338 * Each capture group within () will be replace the {} in the select
11339 * statement as specified by their index.
11343 select: 'n = byClassName(n, " {1} ");'
11345 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
11346 select: 'n = byPseudo(n, "{1}", "{2}");'
11348 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
11349 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
11352 select: 'n = byId(n, "{1}");'
11355 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
11360 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
11361 * 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, > <.
11364 "=" : function(a, v){
11367 "!=" : function(a, v){
11370 "^=" : function(a, v){
11371 return a && a.substr(0, v.length) == v;
11373 "$=" : function(a, v){
11374 return a && a.substr(a.length-v.length) == v;
11376 "*=" : function(a, v){
11377 return a && a.indexOf(v) !== -1;
11379 "%=" : function(a, v){
11380 return (a % v) == 0;
11382 "|=" : function(a, v){
11383 return a && (a == v || a.substr(0, v.length+1) == v+'-');
11385 "~=" : function(a, v){
11386 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
11391 Object hash of "pseudo class" filter functions which are used when filtering selections.
11392 Each function is passed two parameters:
11395 An Array of DOM elements to filter.
11398 The argument (if any) supplied in the selector.
11400 A filter function returns an Array of DOM elements which conform to the pseudo class.
11401 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
11402 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
11404 For example, to filter `a` elements to only return links to __external__ resources:
11406 Ext.DomQuery.pseudos.external = function(c, v){
11407 var r = [], ri = -1;
11408 for(var i = 0, ci; ci = c[i]; i++){
11409 // Include in result set only if it's a link to an external resource
11410 if(ci.hostname != location.hostname){
11417 Then external links could be gathered with the following statement:
11419 var externalLinks = Ext.select("a:external");
11424 "first-child" : function(c){
11425 var r = [], ri = -1, n;
11426 for(var i = 0, ci; ci = n = c[i]; i++){
11427 while((n = n.previousSibling) && n.nodeType != 1);
11435 "last-child" : function(c){
11436 var r = [], ri = -1, n;
11437 for(var i = 0, ci; ci = n = c[i]; i++){
11438 while((n = n.nextSibling) && n.nodeType != 1);
11446 "nth-child" : function(c, a) {
11447 var r = [], ri = -1,
11448 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
11449 f = (m[1] || 1) - 0, l = m[2] - 0;
11450 for(var i = 0, n; n = c[i]; i++){
11451 var pn = n.parentNode;
11452 if (batch != pn._batch) {
11454 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
11455 if(cn.nodeType == 1){
11456 cn.nodeIndex = ++j;
11462 if (l == 0 || n.nodeIndex == l){
11465 } else if ((n.nodeIndex + l) % f == 0){
11473 "only-child" : function(c){
11474 var r = [], ri = -1;;
11475 for(var i = 0, ci; ci = c[i]; i++){
11476 if(!prev(ci) && !next(ci)){
11483 "empty" : function(c){
11484 var r = [], ri = -1;
11485 for(var i = 0, ci; ci = c[i]; i++){
11486 var cns = ci.childNodes, j = 0, cn, empty = true;
11487 while(cn = cns[j]){
11489 if(cn.nodeType == 1 || cn.nodeType == 3){
11501 "contains" : function(c, v){
11502 var r = [], ri = -1;
11503 for(var i = 0, ci; ci = c[i]; i++){
11504 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
11511 "nodeValue" : function(c, v){
11512 var r = [], ri = -1;
11513 for(var i = 0, ci; ci = c[i]; i++){
11514 if(ci.firstChild && ci.firstChild.nodeValue == v){
11521 "checked" : function(c){
11522 var r = [], ri = -1;
11523 for(var i = 0, ci; ci = c[i]; i++){
11524 if(ci.checked == true){
11531 "not" : function(c, ss){
11532 return Ext.DomQuery.filter(c, ss, true);
11535 "any" : function(c, selectors){
11536 var ss = selectors.split('|'),
11537 r = [], ri = -1, s;
11538 for(var i = 0, ci; ci = c[i]; i++){
11539 for(var j = 0; s = ss[j]; j++){
11540 if(Ext.DomQuery.is(ci, s)){
11549 "odd" : function(c){
11550 return this["nth-child"](c, "odd");
11553 "even" : function(c){
11554 return this["nth-child"](c, "even");
11557 "nth" : function(c, a){
11558 return c[a-1] || [];
11561 "first" : function(c){
11565 "last" : function(c){
11566 return c[c.length-1] || [];
11569 "has" : function(c, ss){
11570 var s = Ext.DomQuery.select,
11572 for(var i = 0, ci; ci = c[i]; i++){
11573 if(s(ss, ci).length > 0){
11580 "next" : function(c, ss){
11581 var is = Ext.DomQuery.is,
11583 for(var i = 0, ci; ci = c[i]; i++){
11585 if(n && is(n, ss)){
11592 "prev" : function(c, ss){
11593 var is = Ext.DomQuery.is,
11595 for(var i = 0, ci; ci = c[i]; i++){
11597 if(n && is(n, ss)){
11608 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
11609 * @param {String} path The selector/xpath query
11610 * @param {Node} root (optional) The start of the query (defaults to document).
11615 Ext.query = Ext.DomQuery.select;
11618 * @class Ext.core.Element
11619 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
11620 * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
11621 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
11622 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
11623 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
11627 var el = Ext.get("my-div");
11629 // by DOM element reference
11630 var el = Ext.get(myDivElement);
11632 * <b>Animations</b><br />
11633 * <p>When an element is manipulated, by default there is no animation.</p>
11635 var el = Ext.get("my-div");
11640 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
11641 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
11643 // default animation
11644 el.setWidth(100, true);
11647 * <p>To configure the effects, an object literal with animation options to use as the Element animation
11648 * configuration object can also be specified. Note that the supported Element animation configuration
11649 * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects. The supported
11650 * Element animation configuration options are:</p>
11652 Option Default Description
11653 --------- -------- ---------------------------------------------
11654 {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
11655 {@link Ext.fx.Anim#easing easing} easeOut The easing method
11656 {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
11657 {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
11661 // Element animation options object
11663 {@link Ext.fx.Anim#duration duration}: 1,
11664 {@link Ext.fx.Anim#easing easing}: 'elasticIn',
11665 {@link Ext.fx.Anim#callback callback}: this.foo,
11666 {@link Ext.fx.Anim#scope scope}: this
11668 // animation with some options set
11669 el.setWidth(100, opt);
11671 * <p>The Element animation object being used for the animation will be set on the options
11672 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
11674 // using the "anim" property to get the Anim object
11675 if(opt.anim.isAnimated()){
11679 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
11680 * <p><b> Composite (Collections of) Elements</b></p>
11681 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
11682 * @constructor Create a new Element directly.
11683 * @param {String/HTMLElement} element
11684 * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
11687 var DOC = document,
11690 Ext.Element = Ext.core.Element = function(element, forceNew) {
11691 var dom = typeof element == "string" ? DOC.getElementById(element) : element,
11700 if (!forceNew && id && EC[id]) {
11701 // element object already exists
11707 * @type HTMLElement
11712 * The DOM element ID
11715 this.id = id || Ext.id(dom);
11718 var DH = Ext.core.DomHelper,
11719 El = Ext.core.Element;
11724 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
11725 * @param {Object} o The object with the attributes
11726 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
11727 * @return {Ext.core.Element} this
11729 set: function(o, useSet) {
11733 useSet = (useSet !== false) && !!el.setAttribute;
11736 if (o.hasOwnProperty(attr)) {
11738 if (attr == 'style') {
11739 DH.applyStyles(el, val);
11740 } else if (attr == 'cls') {
11741 el.className = val;
11742 } else if (useSet) {
11743 el.setAttribute(attr, val);
11755 * Fires when a mouse click is detected within the element.
11756 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11757 * @param {HtmlElement} t The target of the event.
11758 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11761 * @event contextmenu
11762 * Fires when a right click is detected within the element.
11763 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11764 * @param {HtmlElement} t The target of the event.
11765 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11769 * Fires when a mouse double click is detected within the element.
11770 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11771 * @param {HtmlElement} t The target of the event.
11772 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11776 * Fires when a mousedown is detected within the element.
11777 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11778 * @param {HtmlElement} t The target of the event.
11779 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11783 * Fires when a mouseup is detected within the element.
11784 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11785 * @param {HtmlElement} t The target of the event.
11786 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11790 * Fires when a mouseover is detected within the element.
11791 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11792 * @param {HtmlElement} t The target of the event.
11793 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11797 * Fires when a mousemove is detected with the element.
11798 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11799 * @param {HtmlElement} t The target of the event.
11800 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11804 * Fires when a mouseout is detected with the element.
11805 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11806 * @param {HtmlElement} t The target of the event.
11807 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11810 * @event mouseenter
11811 * Fires when the mouse enters the element.
11812 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11813 * @param {HtmlElement} t The target of the event.
11814 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11817 * @event mouseleave
11818 * Fires when the mouse leaves the element.
11819 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11820 * @param {HtmlElement} t The target of the event.
11821 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11827 * Fires when a keypress is detected within the element.
11828 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11829 * @param {HtmlElement} t The target of the event.
11830 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11834 * Fires when a keydown is detected within the element.
11835 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11836 * @param {HtmlElement} t The target of the event.
11837 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11841 * Fires when a keyup is detected within the element.
11842 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11843 * @param {HtmlElement} t The target of the event.
11844 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11848 // HTML frame/object events
11851 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
11852 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11853 * @param {HtmlElement} t The target of the event.
11854 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11858 * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
11859 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11860 * @param {HtmlElement} t The target of the event.
11861 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11865 * Fires when an object/image is stopped from loading before completely loaded.
11866 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11867 * @param {HtmlElement} t The target of the event.
11868 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11872 * Fires when an object/image/frame cannot be loaded properly.
11873 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11874 * @param {HtmlElement} t The target of the event.
11875 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11879 * Fires when a document view is resized.
11880 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11881 * @param {HtmlElement} t The target of the event.
11882 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11886 * Fires when a document view is scrolled.
11887 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11888 * @param {HtmlElement} t The target of the event.
11889 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11895 * Fires when a user selects some text in a text field, including input and textarea.
11896 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11897 * @param {HtmlElement} t The target of the event.
11898 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11902 * Fires when a control loses the input focus and its value has been modified since gaining focus.
11903 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11904 * @param {HtmlElement} t The target of the event.
11905 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11909 * Fires when a form is submitted.
11910 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11911 * @param {HtmlElement} t The target of the event.
11912 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11916 * Fires when a form is reset.
11917 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11918 * @param {HtmlElement} t The target of the event.
11919 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11923 * Fires when an element receives focus either via the pointing device or by tab navigation.
11924 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11925 * @param {HtmlElement} t The target of the event.
11926 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11930 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
11931 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11932 * @param {HtmlElement} t The target of the event.
11933 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11936 // User Interface events
11938 * @event DOMFocusIn
11939 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
11940 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11941 * @param {HtmlElement} t The target of the event.
11942 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11945 * @event DOMFocusOut
11946 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
11947 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11948 * @param {HtmlElement} t The target of the event.
11949 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11952 * @event DOMActivate
11953 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
11954 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11955 * @param {HtmlElement} t The target of the event.
11956 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11959 // DOM Mutation events
11961 * @event DOMSubtreeModified
11962 * Where supported. Fires when the subtree is modified.
11963 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11964 * @param {HtmlElement} t The target of the event.
11965 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11968 * @event DOMNodeInserted
11969 * Where supported. Fires when a node has been added as a child of another node.
11970 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11971 * @param {HtmlElement} t The target of the event.
11972 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11975 * @event DOMNodeRemoved
11976 * Where supported. Fires when a descendant node of the element is removed.
11977 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11978 * @param {HtmlElement} t The target of the event.
11979 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11982 * @event DOMNodeRemovedFromDocument
11983 * Where supported. Fires when a node is being removed from a document.
11984 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11985 * @param {HtmlElement} t The target of the event.
11986 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11989 * @event DOMNodeInsertedIntoDocument
11990 * Where supported. Fires when a node is being inserted into a document.
11991 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11992 * @param {HtmlElement} t The target of the event.
11993 * @param {Object} o The options configuration passed to the {@link #addListener} call.
11996 * @event DOMAttrModified
11997 * Where supported. Fires when an attribute has been modified.
11998 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
11999 * @param {HtmlElement} t The target of the event.
12000 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12003 * @event DOMCharacterDataModified
12004 * Where supported. Fires when the character data has been modified.
12005 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12006 * @param {HtmlElement} t The target of the event.
12007 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12011 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
12017 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
12018 * @param {String} selector The simple selector to test
12019 * @return {Boolean} True if this element matches the selector, else false
12021 is: function(simpleSelector) {
12022 return Ext.DomQuery.is(this.dom, simpleSelector);
12026 * Tries to focus the element. Any exceptions are caught and ignored.
12027 * @param {Number} defer (optional) Milliseconds to defer the focus
12028 * @return {Ext.core.Element} this
12030 focus: function(defer,
12034 dom = dom || me.dom;
12036 if (Number(defer)) {
12037 Ext.defer(me.focus, defer, null, [null, dom]);
12046 * Tries to blur the element. Any exceptions are caught and ignored.
12047 * @return {Ext.core.Element} this
12057 * Returns the value of the "value" attribute
12058 * @param {Boolean} asNumber true to parse the value as a number
12059 * @return {String/Number}
12061 getValue: function(asNumber) {
12062 var val = this.dom.value;
12063 return asNumber ? parseInt(val, 10) : val;
12067 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
12068 * @param {String} eventName The name of event to handle.
12069 * @param {Function} fn The handler function the event invokes. This function is passed
12070 * the following parameters:<ul>
12071 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
12072 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
12073 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
12074 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
12076 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
12077 * <b>If omitted, defaults to this Element.</b>.
12078 * @param {Object} options (optional) An object containing handler configuration properties.
12079 * This may contain any of the following properties:<ul>
12080 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
12081 * <b>If omitted, defaults to this Element.</b></div></li>
12082 * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
12083 * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
12084 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
12085 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
12086 * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
12087 * <li><b>target</b> Ext.core.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
12088 * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
12089 * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
12090 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
12091 * by the specified number of milliseconds. If the event fires again within that time, the original
12092 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
12095 * <b>Combining Options</b><br>
12096 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
12097 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
12098 * types of listeners:<br>
12100 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
12101 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
12103 el.on('click', this.onClick, this, {
12108 });</code></pre></p>
12110 * <b>Attaching multiple handlers in 1 call</b><br>
12111 * The method also allows for a single argument to be passed which is a config object containing properties
12112 * which specify multiple handlers.</p>
12122 fn: this.onMouseOver,
12126 fn: this.onMouseOut,
12131 * Or a shorthand syntax:<br>
12132 * Code:<pre><code></p>
12134 'click' : this.onClick,
12135 'mouseover' : this.onMouseOver,
12136 'mouseout' : this.onMouseOut,
12139 * </code></pre></p>
12140 * <p><b>delegate</b></p>
12141 * <p>This is a configuration option that you can pass along when registering a handler for
12142 * an event to assist with event delegation. Event delegation is a technique that is used to
12143 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
12144 * for a container element as opposed to each element within a container. By setting this
12145 * configuration option to a simple selector, the target element will be filtered to look for
12146 * a descendant of the target.
12147 * For example:<pre><code>
12148 // using this markup:
12150 <p id='p1'>paragraph one</p>
12151 <p id='p2' class='clickable'>paragraph two</p>
12152 <p id='p3'>paragraph three</p>
12154 // utilize event delegation to registering just one handler on the container element:
12155 el = Ext.get('elId');
12160 console.info(t.id); // 'p2'
12164 // filter the target element to be a descendant with the class 'clickable'
12165 delegate: '.clickable'
12168 * </code></pre></p>
12169 * @return {Ext.core.Element} this
12171 addListener: function(eventName, fn, scope, options) {
12172 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
12177 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
12178 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
12179 * listener, the same scope must be specified here.
12182 el.removeListener('click', this.handlerFn);
12184 el.un('click', this.handlerFn);
12186 * @param {String} eventName The name of the event from which to remove the handler.
12187 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
12188 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
12189 * then this must refer to the same object.
12190 * @return {Ext.core.Element} this
12192 removeListener: function(eventName, fn, scope) {
12193 Ext.EventManager.un(this.dom, eventName, fn, scope || this);
12198 * Removes all previous added listeners from this element
12199 * @return {Ext.core.Element} this
12201 removeAllListeners: function() {
12202 Ext.EventManager.removeAll(this.dom);
12207 * Recursively removes all previous added listeners from this element and its children
12208 * @return {Ext.core.Element} this
12210 purgeAllListeners: function() {
12211 Ext.EventManager.purgeElement(this);
12216 * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
12217 * @param size {Mixed} The size to set
12218 * @param units {String} The units to append to a numeric size value
12220 addUnits: function(size, units) {
12222 // Most common case first: Size is set to a number
12223 if (Ext.isNumber(size)) {
12224 return size + (units || this.defaultUnit || 'px');
12227 // Size set to a value which means "auto"
12228 if (size === "" || size == "auto" || size === undefined || size === null) {
12232 // Otherwise, warn if it's not a valid CSS measurement
12233 if (!unitPattern.test(size)) {
12234 if (Ext.isDefined(Ext.global.console)) {
12235 Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
12243 * Tests various css rules/browsers to determine if this element uses a border box
12244 * @return {Boolean}
12246 isBorderBox: function() {
12247 return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
12251 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
12253 remove: function() {
12259 Ext.removeNode(dom);
12264 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
12265 * @param {Function} overFn The function to call when the mouse enters the Element.
12266 * @param {Function} outFn The function to call when the mouse leaves the Element.
12267 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
12268 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
12269 * @return {Ext.core.Element} this
12271 hover: function(overFn, outFn, scope, options) {
12273 me.on('mouseenter', overFn, scope || me.dom, options);
12274 me.on('mouseleave', outFn, scope || me.dom, options);
12279 * Returns true if this element is an ancestor of the passed element
12280 * @param {HTMLElement/String} el The element to check
12281 * @return {Boolean} True if this element is an ancestor of el, else false
12283 contains: function(el) {
12284 return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
12288 * Returns the value of a namespaced attribute from the element's underlying DOM node.
12289 * @param {String} namespace The namespace in which to look for the attribute
12290 * @param {String} name The attribute name
12291 * @return {String} The attribute value
12294 getAttributeNS: function(ns, name) {
12295 return this.getAttribute(name, ns);
12299 * Returns the value of an attribute from the element's underlying DOM node.
12300 * @param {String} name The attribute name
12301 * @param {String} namespace (optional) The namespace in which to look for the attribute
12302 * @return {String} The attribute value
12304 getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
12305 function(name, ns) {
12309 type = typeof d[ns + ":" + name];
12310 if (type != 'undefined' && type != 'unknown') {
12311 return d[ns + ":" + name] || null;
12315 if (name === "for") {
12318 return d[name] || null;
12319 }: function(name, ns) {
12322 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
12324 return d.getAttribute(name) || d[name] || null;
12328 * Update the innerHTML of this element
12329 * @param {String} html The new HTML
12330 * @return {Ext.core.Element} this
12332 update: function(html) {
12334 this.dom.innerHTML = html;
12340 var ep = El.prototype;
12342 El.addMethods = function(o) {
12347 * Appends an event handler (shorthand for {@link #addListener}).
12348 * @param {String} eventName The name of event to handle.
12349 * @param {Function} fn The handler function the event invokes.
12350 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
12351 * @param {Object} options (optional) An object containing standard {@link #addListener} options
12352 * @member Ext.core.Element
12355 ep.on = ep.addListener;
12358 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
12359 * @param {String} eventName The name of the event from which to remove the handler.
12360 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
12361 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
12362 * then this must refer to the same object.
12363 * @return {Ext.core.Element} this
12364 * @member Ext.core.Element
12367 ep.un = ep.removeListener;
12370 * Removes all previous added listeners from this element
12371 * @return {Ext.core.Element} this
12372 * @member Ext.core.Element
12373 * @method clearListeners
12375 ep.clearListeners = ep.removeAllListeners;
12378 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
12379 * Alias to {@link #remove}.
12380 * @member Ext.core.Element
12383 ep.destroy = ep.remove;
12386 * true to automatically adjust width and height settings for box-model issues (default to true)
12388 ep.autoBoxAdjust = true;
12391 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
12395 * Retrieves Ext.core.Element objects.
12396 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
12397 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
12398 * its ID, use {@link Ext.ComponentManager#get}.</p>
12399 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
12400 * object was recreated with the same id via AJAX or DOM.</p>
12401 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
12402 * @return {Element} The Element object (or null if no matching element was found)
12404 * @member Ext.core.Element
12407 El.get = function(el) {
12414 if (typeof el == "string") {
12416 if (! (elm = DOC.getElementById(el))) {
12419 if (EC[el] && EC[el].el) {
12423 ex = El.addToCache(new El(elm));
12426 } else if (el.tagName) {
12428 if (! (id = el.id)) {
12431 if (EC[id] && EC[id].el) {
12435 ex = El.addToCache(new El(el));
12438 } else if (el instanceof El) {
12440 // refresh dom element in case no longer valid,
12441 // catch case where it hasn't been appended
12442 // If an el instance is passed, don't pass to getElementById without some kind of id
12443 if (Ext.isIE && (el.id == undefined || el.id == '')) {
12446 el.dom = DOC.getElementById(el.id) || el.dom;
12450 } else if (el.isComposite) {
12452 } else if (Ext.isArray(el)) {
12453 return El.select(el);
12454 } else if (el == DOC) {
12455 // create a bogus element object representing the document object
12457 var f = function() {};
12458 f.prototype = El.prototype;
12467 El.addToCache = function(el, id) {
12479 // private method for getting and setting element data
12480 El.data = function(el, key, value) {
12485 var c = EC[el.id].data;
12486 if (arguments.length == 2) {
12489 return (c[key] = value);
12494 // Garbage collection - uncache elements/purge listeners on orphaned elements
12495 // so we don't hold a reference and cause the browser to retain them
12496 function garbageCollect() {
12497 if (!Ext.enableGarbageCollector) {
12498 clearInterval(El.collectorThreadId);
12506 if (!EC.hasOwnProperty(eid)) {
12510 if (o.skipGarbageCollection) {
12515 // -------------------------------------------------------
12516 // Determining what is garbage:
12517 // -------------------------------------------------------
12519 // dom node is null, definitely garbage
12520 // -------------------------------------------------------
12522 // no parentNode == direct orphan, definitely garbage
12523 // -------------------------------------------------------
12524 // !d.offsetParent && !document.getElementById(eid)
12525 // display none elements have no offsetParent so we will
12526 // also try to look it up by it's id. However, check
12527 // offsetParent first so we don't do unneeded lookups.
12528 // This enables collection of elements that are not orphans
12529 // directly, but somewhere up the line they have an orphan
12531 // -------------------------------------------------------
12532 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
12533 if (d && Ext.enableListenerCollection) {
12534 Ext.EventManager.removeAll(d);
12539 // Cleanup IE Object leaks
12543 if (!EC.hasOwnProperty(eid)) {
12548 EC = Ext.cache = t;
12552 El.collectorThreadId = setInterval(garbageCollect, 30000);
12554 var flyFn = function() {};
12555 flyFn.prototype = El.prototype;
12558 El.Flyweight = function(dom) {
12562 El.Flyweight.prototype = new flyFn();
12563 El.Flyweight.prototype.isFlyweight = true;
12564 El._flyweights = {};
12567 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
12568 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
12569 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
12570 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
12571 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
12572 * @param {String/HTMLElement} el The dom node or id
12573 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
12574 * (e.g. internally Ext uses "_global")
12575 * @return {Element} The shared Element object (or null if no matching element was found)
12576 * @member Ext.core.Element
12579 El.fly = function(el, named) {
12581 named = named || '_global';
12582 el = Ext.getDom(el);
12584 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
12585 ret = El._flyweights[named];
12591 * Retrieves Ext.core.Element objects.
12592 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
12593 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
12594 * its ID, use {@link Ext.ComponentManager#get}.</p>
12595 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
12596 * object was recreated with the same id via AJAX or DOM.</p>
12597 * Shorthand of {@link Ext.core.Element#get}
12598 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
12599 * @return {Element} The Element object (or null if no matching element was found)
12606 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
12607 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
12608 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
12609 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
12610 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
12611 * @param {String/HTMLElement} el The dom node or id
12612 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
12613 * (e.g. internally Ext uses "_global")
12614 * @return {Element} The shared Element object (or null if no matching element was found)
12620 // speedy lookup for elements never to box adjust
12621 var noBoxAdjust = Ext.isStrict ? {
12628 if (Ext.isIE || Ext.isGecko) {
12629 noBoxAdjust['button'] = 1;
12634 * @class Ext.core.Element
12636 Ext.core.Element.addMethods({
12638 * 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)
12639 * @param {String} selector The simple selector to test
12640 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
12641 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
12642 * @return {HTMLElement} The matching DOM node (or null if no match was found)
12644 findParent : function(simpleSelector, maxDepth, returnEl) {
12650 maxDepth = maxDepth || 50;
12651 if (isNaN(maxDepth)) {
12652 stopEl = Ext.getDom(maxDepth);
12653 maxDepth = Number.MAX_VALUE;
12655 while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
12656 if (Ext.DomQuery.is(p, simpleSelector)) {
12657 return returnEl ? Ext.get(p) : p;
12666 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
12667 * @param {String} selector The simple selector to test
12668 * @param {Number/Mixed} maxDepth (optional) The max depth to
12669 search as a number or element (defaults to 10 || document.body)
12670 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
12671 * @return {HTMLElement} The matching DOM node (or null if no match was found)
12673 findParentNode : function(simpleSelector, maxDepth, returnEl) {
12674 var p = Ext.fly(this.dom.parentNode, '_internal');
12675 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
12679 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
12680 * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
12681 * @param {String} selector The simple selector to test
12682 * @param {Number/Mixed} maxDepth (optional) The max depth to
12683 search as a number or element (defaults to 10 || document.body)
12684 * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
12686 up : function(simpleSelector, maxDepth) {
12687 return this.findParentNode(simpleSelector, maxDepth, true);
12691 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
12692 * @param {String} selector The CSS selector
12693 * @return {CompositeElement/CompositeElement} The composite element
12695 select : function(selector) {
12696 return Ext.core.Element.select(selector, false, this.dom);
12700 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
12701 * @param {String} selector The CSS selector
12702 * @return {Array} An array of the matched nodes
12704 query : function(selector) {
12705 return Ext.DomQuery.select(selector, this.dom);
12709 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
12710 * @param {String} selector The CSS selector
12711 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
12712 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
12714 down : function(selector, returnDom) {
12715 var n = Ext.DomQuery.selectNode(selector, this.dom);
12716 return returnDom ? n : Ext.get(n);
12720 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
12721 * @param {String} selector The CSS selector
12722 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
12723 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
12725 child : function(selector, returnDom) {
12729 id = Ext.get(me).id;
12731 id = id.replace(/[\.:]/g, "\\$0");
12732 node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
12733 return returnDom ? node : Ext.get(node);
12737 * Gets the parent node for this element, optionally chaining up trying to match a selector
12738 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
12739 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
12740 * @return {Ext.core.Element/HTMLElement} The parent node or null
12742 parent : function(selector, returnDom) {
12743 return this.matchNode('parentNode', 'parentNode', selector, returnDom);
12747 * Gets the next sibling, skipping text nodes
12748 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
12749 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
12750 * @return {Ext.core.Element/HTMLElement} The next sibling or null
12752 next : function(selector, returnDom) {
12753 return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
12757 * Gets the previous sibling, skipping text nodes
12758 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
12759 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
12760 * @return {Ext.core.Element/HTMLElement} The previous sibling or null
12762 prev : function(selector, returnDom) {
12763 return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
12768 * Gets the first child, skipping text nodes
12769 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
12770 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
12771 * @return {Ext.core.Element/HTMLElement} The first child or null
12773 first : function(selector, returnDom) {
12774 return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
12778 * Gets the last child, skipping text nodes
12779 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
12780 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
12781 * @return {Ext.core.Element/HTMLElement} The last child or null
12783 last : function(selector, returnDom) {
12784 return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
12787 matchNode : function(dir, start, selector, returnDom) {
12792 var n = this.dom[start];
12794 if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
12795 return !returnDom ? Ext.get(n) : n;
12804 * @class Ext.core.Element
12806 Ext.core.Element.addMethods({
12808 * Appends the passed element(s) to this element
12809 * @param {String/HTMLElement/Array/Element/CompositeElement} el
12810 * @return {Ext.core.Element} this
12812 appendChild : function(el) {
12813 return Ext.get(el).appendTo(this);
12817 * Appends this element to the passed element
12818 * @param {Mixed} el The new parent element
12819 * @return {Ext.core.Element} this
12821 appendTo : function(el) {
12822 Ext.getDom(el).appendChild(this.dom);
12827 * Inserts this element before the passed element in the DOM
12828 * @param {Mixed} el The element before which this element will be inserted
12829 * @return {Ext.core.Element} this
12831 insertBefore : function(el) {
12832 el = Ext.getDom(el);
12833 el.parentNode.insertBefore(this.dom, el);
12838 * Inserts this element after the passed element in the DOM
12839 * @param {Mixed} el The element to insert after
12840 * @return {Ext.core.Element} this
12842 insertAfter : function(el) {
12843 el = Ext.getDom(el);
12844 el.parentNode.insertBefore(this.dom, el.nextSibling);
12849 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
12850 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
12851 * @return {Ext.core.Element} The new child
12853 insertFirst : function(el, returnDom) {
12855 if (el.nodeType || el.dom || typeof el == 'string') { // element
12856 el = Ext.getDom(el);
12857 this.dom.insertBefore(el, this.dom.firstChild);
12858 return !returnDom ? Ext.get(el) : el;
12860 else { // dh config
12861 return this.createChild(el, this.dom.firstChild, returnDom);
12866 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
12867 * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.
12868 * @param {String} where (optional) 'before' or 'after' defaults to before
12869 * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
12870 * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
12872 insertSibling: function(el, where, returnDom){
12874 isAfter = (where || 'before').toLowerCase() == 'after',
12877 if(Ext.isArray(el)){
12879 Ext.each(el, function(e) {
12880 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
12890 if(el.nodeType || el.dom){
12891 rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
12896 if (isAfter && !me.dom.nextSibling) {
12897 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
12899 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
12906 * Replaces the passed element with this element
12907 * @param {Mixed} el The element to replace
12908 * @return {Ext.core.Element} this
12910 replace : function(el) {
12912 this.insertBefore(el);
12918 * Replaces this element with the passed element
12919 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
12920 * @return {Ext.core.Element} this
12922 replaceWith: function(el){
12925 if(el.nodeType || el.dom || typeof el == 'string'){
12927 me.dom.parentNode.insertBefore(el, me.dom);
12929 el = Ext.core.DomHelper.insertBefore(me.dom, el);
12932 delete Ext.cache[me.id];
12933 Ext.removeNode(me.dom);
12934 me.id = Ext.id(me.dom = el);
12935 Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);
12940 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
12941 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
12942 * automatically generated with the specified attributes.
12943 * @param {HTMLElement} insertBefore (optional) a child element of this element
12944 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
12945 * @return {Ext.core.Element} The new child element
12947 createChild : function(config, insertBefore, returnDom) {
12948 config = config || {tag:'div'};
12949 if (insertBefore) {
12950 return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
12953 return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
12958 * Creates and wraps this element with another element
12959 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
12960 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
12961 * @return {HTMLElement/Element} The newly created wrapper element
12963 wrap : function(config, returnDom) {
12964 var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
12965 d = newEl.dom || newEl;
12967 d.appendChild(this.dom);
12972 * Inserts an html fragment into this element
12973 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
12974 * @param {String} html The HTML fragment
12975 * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
12976 * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
12978 insertHtml : function(where, html, returnEl) {
12979 var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
12980 return returnEl ? Ext.get(el) : el;
12985 * @class Ext.core.Element
12988 Ext.core.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
12989 // local style camelizing for speed
12990 var supports = Ext.supports,
12991 view = document.defaultView,
12992 opacityRe = /alpha\(opacity=(.*)\)/i,
12993 trimRe = /^\s+|\s+$/g,
12996 adjustDirect2DTableRe = /table-row|table-.*-group/,
12997 INTERNAL = '_internal',
12998 PADDING = 'padding',
13004 BOTTOM = '-bottom',
13008 ISCLIPPED = 'isClipped',
13009 OVERFLOW = 'overflow',
13010 OVERFLOWX = 'overflow-x',
13011 OVERFLOWY = 'overflow-y',
13012 ORIGINALCLIP = 'originalClip',
13013 // special markup used throughout Ext when box wrapping elements
13014 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
13015 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
13016 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
13017 data = Ext.core.Element.data;
13019 Ext.override(Ext.core.Element, {
13022 * TODO: Look at this
13024 // private ==> used by Fx
13025 adjustWidth : function(width) {
13027 isNum = (typeof width == 'number');
13029 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13030 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
13032 return (isNum && width < 0) ? 0 : width;
13035 // private ==> used by Fx
13036 adjustHeight : function(height) {
13038 isNum = (typeof height == "number");
13040 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13041 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
13043 return (isNum && height < 0) ? 0 : height;
13048 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
13049 * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
13050 * @return {Ext.core.Element} this
13052 addCls : function(className){
13055 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
13057 if (!Ext.isDefined(className)) {
13060 // Separate case is for speed
13061 if (!Ext.isArray(className)) {
13062 if (typeof className === 'string') {
13063 className = className.replace(trimRe, '').split(spacesRe);
13064 if (className.length === 1) {
13065 className = className[0];
13066 if (!me.hasCls(className)) {
13067 me.dom.className += space + className;
13070 this.addCls(className);
13074 for (i = 0, len = className.length; i < len; i++) {
13076 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
13081 me.dom.className += space + cls.join(" ");
13088 * Removes one or more CSS classes from the element.
13089 * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
13090 * @return {Ext.core.Element} this
13092 removeCls : function(className){
13094 i, idx, len, cls, elClasses;
13095 if (!Ext.isDefined(className)) {
13098 if (!Ext.isArray(className)){
13099 className = className.replace(trimRe, '').split(spacesRe);
13101 if (me.dom && me.dom.className) {
13102 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
13103 for (i = 0, len = className.length; i < len; i++) {
13104 cls = className[i];
13105 if (typeof cls == 'string') {
13106 cls = cls.replace(trimRe, '');
13107 idx = Ext.Array.indexOf(elClasses, cls);
13109 elClasses.splice(idx, 1);
13113 me.dom.className = elClasses.join(" ");
13119 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
13120 * @param {String/Array} className The CSS class to add, or an array of classes
13121 * @return {Ext.core.Element} this
13123 radioCls : function(className){
13124 var cn = this.dom.parentNode.childNodes,
13126 className = Ext.isArray(className) ? className : [className];
13127 for (i = 0, len = cn.length; i < len; i++) {
13129 if (v && v.nodeType == 1) {
13130 Ext.fly(v, '_internal').removeCls(className);
13133 return this.addCls(className);
13137 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
13138 * @param {String} className The CSS class to toggle
13139 * @return {Ext.core.Element} this
13141 toggleCls : Ext.supports.ClassList ?
13142 function(className) {
13143 this.dom.classList.toggle(Ext.String.trim(className));
13146 function(className) {
13147 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
13151 * Checks if the specified CSS class exists on this element's DOM node.
13152 * @param {String} className The CSS class to check for
13153 * @return {Boolean} True if the class exists, else false
13155 hasCls : Ext.supports.ClassList ?
13156 function(className) {
13160 className = className.split(spacesRe);
13161 var ln = className.length,
13163 for (; i < ln; i++) {
13164 if (className[i] && this.dom.classList.contains(className[i])) {
13170 function(className){
13171 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
13175 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
13176 * @param {String} oldClassName The CSS class to replace
13177 * @param {String} newClassName The replacement CSS class
13178 * @return {Ext.core.Element} this
13180 replaceCls : function(oldClassName, newClassName){
13181 return this.removeCls(oldClassName).addCls(newClassName);
13184 isStyle : function(style, val) {
13185 return this.getStyle(style) == val;
13189 * Normalizes currentStyle and computedStyle.
13190 * @param {String} property The style property whose value is returned.
13191 * @return {String} The current value of the style property for this element.
13193 getStyle : function(){
13194 return view && view.getComputedStyle ?
13197 v, cs, out, display;
13199 if(el == document){
13202 prop = Ext.core.Element.normalize(prop);
13203 out = (v = el.style[prop]) ? v :
13204 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
13206 // Ignore cases when the margin is correctly reported as 0, the bug only shows
13208 if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
13209 display = this.getStyle('display');
13210 el.style.display = 'inline-block';
13211 out = view.getComputedStyle(el, '').marginRight;
13212 el.style.display = display;
13215 if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
13216 out = 'transparent';
13224 if (el == document) {
13228 if (prop == 'opacity') {
13229 if (el.style.filter.match) {
13230 m = el.style.filter.match(opacityRe);
13232 var fv = parseFloat(m[1]);
13234 return fv ? fv / 100 : 0;
13240 prop = Ext.core.Element.normalize(prop);
13241 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
13246 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
13247 * are convert to standard 6 digit hex color.
13248 * @param {String} attr The css attribute
13249 * @param {String} defaultValue The default value to use when a valid color isn't found
13250 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
13253 getColor : function(attr, defaultValue, prefix){
13254 var v = this.getStyle(attr),
13255 color = prefix || prefix === '' ? prefix : '#',
13258 if(!v || (/transparent|inherit/.test(v))) {
13259 return defaultValue;
13262 Ext.each(v.slice(4, v.length -1).split(','), function(s){
13263 h = parseInt(s, 10);
13264 color += (h < 16 ? '0' : '') + h.toString(16);
13267 v = v.replace('#', '');
13268 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
13270 return(color.length > 5 ? color.toLowerCase() : defaultValue);
13274 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
13275 * @param {String/Object} property The style property to be set, or an object of multiple styles.
13276 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
13277 * @return {Ext.core.Element} this
13279 setStyle : function(prop, value){
13287 if (!Ext.isObject(prop)) {
13292 for (style in prop) {
13293 if (prop.hasOwnProperty(style)) {
13294 value = Ext.value(prop[style], '');
13295 if (style == 'opacity') {
13296 me.setOpacity(value);
13299 me.dom.style[Ext.core.Element.normalize(style)] = value;
13307 * Set the opacity of the element
13308 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
13309 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
13310 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
13311 * @return {Ext.core.Element} this
13313 setOpacity: function(opacity, animate) {
13323 style = me.dom.style;
13325 if (!animate || !me.anim) {
13326 if (!Ext.supports.Opacity) {
13327 opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
13328 val = style.filter.replace(opacityRe, '').replace(trimRe, '');
13331 style.filter = val + (val.length > 0 ? ' ': '') + opacity;
13334 style.opacity = opacity;
13338 if (!Ext.isObject(animate)) {
13344 me.animate(Ext.applyIf({
13356 * Clears any opacity settings from this element. Required in some cases for IE.
13357 * @return {Ext.core.Element} this
13359 clearOpacity : function(){
13360 var style = this.dom.style;
13361 if(!Ext.supports.Opacity){
13362 if(!Ext.isEmpty(style.filter)){
13363 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
13366 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
13373 * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
13374 * @return {Number} 0 or 1
13376 adjustDirect2DDimension: function(dimension) {
13379 display = me.getStyle('display'),
13380 inlineDisplay = dom.style['display'],
13381 inlinePosition = dom.style['position'],
13382 originIndex = dimension === 'width' ? 0 : 1,
13385 if (display === 'inline') {
13386 dom.style['display'] = 'inline-block';
13389 dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
13391 // floating will contain digits that appears after the decimal point
13392 // if height or width are set to auto we fallback to msTransformOrigin calculation
13393 floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
13395 dom.style['position'] = inlinePosition;
13397 if (display === 'inline') {
13398 dom.style['display'] = inlineDisplay;
13405 * Returns the offset height of the element
13406 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
13407 * @return {Number} The element's height
13409 getHeight: function(contentHeight, preciseHeight) {
13412 hidden = Ext.isIE && me.isStyle('display', 'none'),
13413 height, overflow, style, floating;
13415 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
13416 // We will put the overflow back to it's original value when we are done measuring.
13417 if (Ext.isIEQuirks) {
13419 overflow = style.overflow;
13420 me.setStyle({ overflow: 'hidden'});
13423 height = dom.offsetHeight;
13425 height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
13427 // IE9 Direct2D dimension rounding bug
13428 if (!hidden && Ext.supports.Direct2DBug) {
13429 floating = me.adjustDirect2DDimension('height');
13430 if (preciseHeight) {
13431 height += floating;
13433 else if (floating > 0 && floating < 0.5) {
13438 if (contentHeight) {
13439 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
13442 if (Ext.isIEQuirks) {
13443 me.setStyle({ overflow: overflow});
13453 * Returns the offset width of the element
13454 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
13455 * @return {Number} The element's width
13457 getWidth: function(contentWidth, preciseWidth) {
13460 hidden = Ext.isIE && me.isStyle('display', 'none'),
13461 rect, width, overflow, style, floating, parentPosition;
13463 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
13464 // We will put the overflow back to it's original value when we are done measuring.
13465 if (Ext.isIEQuirks) {
13467 overflow = style.overflow;
13468 me.setStyle({overflow: 'hidden'});
13471 // Fix Opera 10.5x width calculation issues
13472 if (Ext.isOpera10_5) {
13473 if (dom.parentNode.currentStyle.position === 'relative') {
13474 parentPosition = dom.parentNode.style.position;
13475 dom.parentNode.style.position = 'static';
13476 width = dom.offsetWidth;
13477 dom.parentNode.style.position = parentPosition;
13479 width = Math.max(width || 0, dom.offsetWidth);
13481 // Gecko will in some cases report an offsetWidth that is actually less than the width of the
13482 // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
13483 // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
13484 // subpixel measurements so we can force them to always be rounded up. See
13485 // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
13486 } else if (Ext.supports.BoundingClientRect) {
13487 rect = dom.getBoundingClientRect();
13488 width = rect.right - rect.left;
13489 width = preciseWidth ? width : Math.ceil(width);
13491 width = dom.offsetWidth;
13494 width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
13496 // IE9 Direct2D dimension rounding bug
13497 if (!hidden && Ext.supports.Direct2DBug) {
13498 floating = me.adjustDirect2DDimension('width');
13499 if (preciseWidth) {
13502 else if (floating > 0 && floating < 0.5) {
13507 if (contentWidth) {
13508 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
13511 if (Ext.isIEQuirks) {
13512 me.setStyle({ overflow: overflow});
13522 * Set the width of this Element.
13523 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
13524 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
13525 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
13527 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
13528 * @return {Ext.core.Element} this
13530 setWidth : function(width, animate){
13532 width = me.adjustWidth(width);
13533 if (!animate || !me.anim) {
13534 me.dom.style.width = me.addUnits(width);
13537 if (!Ext.isObject(animate)) {
13540 me.animate(Ext.applyIf({
13550 * Set the height of this Element.
13552 // change the height to 200px and animate with default configuration
13553 Ext.fly('elementId').setHeight(200, true);
13555 // change the height to 150px and animate with a custom configuration
13556 Ext.fly('elId').setHeight(150, {
13557 duration : .5, // animation will have a duration of .5 seconds
13558 // will change the content to "finished"
13559 callback: function(){ this.{@link #update}("finished"); }
13562 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
13563 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
13564 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
13566 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
13567 * @return {Ext.core.Element} this
13569 setHeight : function(height, animate){
13571 height = me.adjustHeight(height);
13572 if (!animate || !me.anim) {
13573 me.dom.style.height = me.addUnits(height);
13576 if (!Ext.isObject(animate)) {
13579 me.animate(Ext.applyIf({
13589 * Gets the width of the border(s) for the specified side(s)
13590 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
13591 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
13592 * @return {Number} The width of the sides passed added together
13594 getBorderWidth : function(side){
13595 return this.addStyles(side, borders);
13599 * Gets the width of the padding(s) for the specified side(s)
13600 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
13601 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
13602 * @return {Number} The padding of the sides passed added together
13604 getPadding : function(side){
13605 return this.addStyles(side, paddings);
13609 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
13610 * @return {Ext.core.Element} this
13616 if(!data(dom, ISCLIPPED)){
13617 data(dom, ISCLIPPED, true);
13618 data(dom, ORIGINALCLIP, {
13619 o: me.getStyle(OVERFLOW),
13620 x: me.getStyle(OVERFLOWX),
13621 y: me.getStyle(OVERFLOWY)
13623 me.setStyle(OVERFLOW, HIDDEN);
13624 me.setStyle(OVERFLOWX, HIDDEN);
13625 me.setStyle(OVERFLOWY, HIDDEN);
13631 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
13632 * @return {Ext.core.Element} this
13634 unclip : function(){
13639 if(data(dom, ISCLIPPED)){
13640 data(dom, ISCLIPPED, false);
13641 clip = data(dom, ORIGINALCLIP);
13643 me.setStyle(OVERFLOW, o.o);
13646 me.setStyle(OVERFLOWX, o.x);
13649 me.setStyle(OVERFLOWY, o.y);
13656 addStyles : function(sides, styles){
13658 sidesArr = sides.match(wordsRe),
13660 len = sidesArr.length,
13662 for (; i < len; i++) {
13663 side = sidesArr[i];
13664 size = side && parseInt(this.getStyle(styles[side]), 10);
13666 totalSize += MATH.abs(size);
13675 * More flexible version of {@link #setStyle} for setting style properties.
13676 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
13677 * a function which returns such a specification.
13678 * @return {Ext.core.Element} this
13680 applyStyles : function(style){
13681 Ext.core.DomHelper.applyStyles(this.dom, style);
13686 * Returns an object with properties matching the styles requested.
13687 * For example, el.getStyles('color', 'font-size', 'width') might return
13688 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
13689 * @param {String} style1 A style name
13690 * @param {String} style2 A style name
13691 * @param {String} etc.
13692 * @return {Object} The style object
13694 getStyles : function(){
13696 len = arguments.length,
13699 for(; i < len; ++i) {
13700 style = arguments[i];
13701 styles[style] = this.getStyle(style);
13707 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
13708 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
13709 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
13710 * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}). The markup
13711 * is of this form:</p>
13713 Ext.core.Element.boxMarkup =
13714 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
13715 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
13716 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
13718 * <p>Example usage:</p>
13721 Ext.get("foo").boxWrap();
13723 // You can also add a custom class and use CSS inheritance rules to customize the box look.
13724 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
13725 // for how to create a custom box wrap style.
13726 Ext.get("foo").boxWrap().addCls("x-box-blue");
13728 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
13729 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
13730 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
13731 * also supply all of the necessary rules.
13732 * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
13734 boxWrap : function(cls){
13735 cls = cls || Ext.baseCSSPrefix + 'box';
13736 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
13737 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
13742 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
13743 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
13744 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
13745 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
13746 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
13748 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
13749 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
13750 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
13752 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
13753 * @return {Ext.core.Element} this
13755 setSize : function(width, height, animate){
13757 if (Ext.isObject(width)){ // in case of object from getSize()
13758 height = width.height;
13759 width = width.width;
13761 width = me.adjustWidth(width);
13762 height = me.adjustHeight(height);
13763 if(!animate || !me.anim){
13764 me.dom.style.width = me.addUnits(width);
13765 me.dom.style.height = me.addUnits(height);
13768 if (!Ext.isObject(animate)) {
13771 me.animate(Ext.applyIf({
13782 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
13783 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
13784 * if a height has not been set using CSS.
13787 getComputedHeight : function(){
13789 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
13791 h = parseFloat(me.getStyle('height')) || 0;
13792 if(!me.isBorderBox()){
13793 h += me.getFrameWidth('tb');
13800 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
13801 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
13802 * if a width has not been set using CSS.
13805 getComputedWidth : function(){
13807 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
13810 w = parseFloat(me.getStyle('width')) || 0;
13811 if(!me.isBorderBox()){
13812 w += me.getFrameWidth('lr');
13819 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
13820 for more information about the sides.
13821 * @param {String} sides
13824 getFrameWidth : function(sides, onlyContentBox){
13825 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
13829 * Sets up event handlers to add and remove a css class when the mouse is over this element
13830 * @param {String} className
13831 * @return {Ext.core.Element} this
13833 addClsOnOver : function(className){
13834 var dom = this.dom;
13837 Ext.fly(dom, INTERNAL).addCls(className);
13840 Ext.fly(dom, INTERNAL).removeCls(className);
13847 * Sets up event handlers to add and remove a css class when this element has the focus
13848 * @param {String} className
13849 * @return {Ext.core.Element} this
13851 addClsOnFocus : function(className){
13854 me.on("focus", function(){
13855 Ext.fly(dom, INTERNAL).addCls(className);
13857 me.on("blur", function(){
13858 Ext.fly(dom, INTERNAL).removeCls(className);
13864 * 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)
13865 * @param {String} className
13866 * @return {Ext.core.Element} this
13868 addClsOnClick : function(className){
13869 var dom = this.dom;
13870 this.on("mousedown", function(){
13871 Ext.fly(dom, INTERNAL).addCls(className);
13872 var d = Ext.getDoc(),
13874 Ext.fly(dom, INTERNAL).removeCls(className);
13875 d.removeListener("mouseup", fn);
13877 d.on("mouseup", fn);
13883 * <p>Returns the dimensions of the element available to lay content out in.<p>
13884 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
13885 * example:<pre><code>
13886 var vpSize = Ext.getBody().getViewSize();
13888 // all Windows created afterwards will have a default value of 90% height and 95% width
13889 Ext.Window.override({
13890 width: vpSize.width * 0.9,
13891 height: vpSize.height * 0.95
13893 // To handle window resizing you would have to hook onto onWindowResize.
13896 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
13897 * To obtain the size including scrollbars, use getStyleSize
13899 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
13902 getViewSize : function(){
13905 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
13906 style, overflow, ret;
13908 // If the body, use static methods
13911 width : Ext.core.Element.getViewWidth(),
13912 height : Ext.core.Element.getViewHeight()
13915 // Else use clientHeight/clientWidth
13918 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
13919 // We will put the overflow back to it's original value when we are done measuring.
13920 if (Ext.isIE6 || Ext.isIEQuirks) {
13922 overflow = style.overflow;
13923 me.setStyle({ overflow: 'hidden'});
13926 width : dom.clientWidth,
13927 height : dom.clientHeight
13929 if (Ext.isIE6 || Ext.isIEQuirks) {
13930 me.setStyle({ overflow: overflow });
13937 * <p>Returns the dimensions of the element available to lay content out in.<p>
13939 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
13940 * To obtain the size excluding scrollbars, use getViewSize
13942 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
13945 getStyleSize : function(){
13949 isDoc = (d == doc || d == doc.body),
13953 // If the body, use static methods
13956 width : Ext.core.Element.getViewWidth(),
13957 height : Ext.core.Element.getViewHeight()
13960 // Use Styles if they are set
13961 if(s.width && s.width != 'auto'){
13962 w = parseFloat(s.width);
13963 if(me.isBorderBox()){
13964 w -= me.getFrameWidth('lr');
13967 // Use Styles if they are set
13968 if(s.height && s.height != 'auto'){
13969 h = parseFloat(s.height);
13970 if(me.isBorderBox()){
13971 h -= me.getFrameWidth('tb');
13974 // Use getWidth/getHeight if style not set.
13975 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
13979 * Returns the size of the element.
13980 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
13981 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
13983 getSize : function(contentSize){
13984 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
13988 * Forces the browser to repaint this element
13989 * @return {Ext.core.Element} this
13991 repaint : function(){
13992 var dom = this.dom;
13993 this.addCls(Ext.baseCSSPrefix + 'repaint');
13994 setTimeout(function(){
13995 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
14001 * Disables text selection for this element (normalized across browsers)
14002 * @return {Ext.core.Element} this
14004 unselectable : function(){
14006 me.dom.unselectable = "on";
14008 me.swallowEvent("selectstart", true);
14009 me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
14010 me.addCls(Ext.baseCSSPrefix + 'unselectable');
14016 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
14017 * then it returns the calculated width of the sides (see getPadding)
14018 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
14019 * @return {Object/Number}
14021 getMargin : function(side){
14023 hash = {t:"top", l:"left", r:"right", b: "bottom"},
14028 for (key in me.margins){
14029 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
14033 return me.addStyles.call(me, side, me.margins);
14039 * @class Ext.core.Element
14042 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
14046 Ext.core.Element.VISIBILITY = 1;
14048 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
14052 Ext.core.Element.DISPLAY = 2;
14055 * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
14060 Ext.core.Element.OFFSETS = 3;
14063 Ext.core.Element.ASCLASS = 4;
14066 * Defaults to 'x-hide-nosize'
14070 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
14072 Ext.core.Element.addMethods(function(){
14073 var El = Ext.core.Element,
14074 OPACITY = "opacity",
14075 VISIBILITY = "visibility",
14076 DISPLAY = "display",
14078 OFFSETS = "offsets",
14079 ASCLASS = "asclass",
14082 ORIGINALDISPLAY = 'originalDisplay',
14083 VISMODE = 'visibilityMode',
14084 ISVISIBLE = 'isVisible',
14086 getDisplay = function(dom){
14087 var d = data(dom, ORIGINALDISPLAY);
14088 if(d === undefined){
14089 data(dom, ORIGINALDISPLAY, d = '');
14093 getVisMode = function(dom){
14094 var m = data(dom, VISMODE);
14095 if(m === undefined){
14096 data(dom, VISMODE, m = 1);
14103 * The element's default display mode (defaults to "")
14106 originalDisplay : "",
14107 visibilityMode : 1,
14110 * Sets the element's visibility mode. When setVisible() is called it
14111 * will use this to determine whether to set the visibility or the display property.
14112 * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
14113 * @return {Ext.core.Element} this
14115 setVisibilityMode : function(visMode){
14116 data(this.dom, VISMODE, visMode);
14121 * Checks whether the element is currently visible using both visibility and display properties.
14122 * @return {Boolean} True if the element is currently visible, else false
14124 isVisible : function() {
14127 visible = data(dom, ISVISIBLE);
14129 if(typeof visible == 'boolean'){ //return the cached value if registered
14132 //Determine the current state based on display states
14133 visible = !me.isStyle(VISIBILITY, HIDDEN) &&
14134 !me.isStyle(DISPLAY, NONE) &&
14135 !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
14137 data(dom, ISVISIBLE, visible);
14142 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
14143 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
14144 * @param {Boolean} visible Whether the element is visible
14145 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
14146 * @return {Ext.core.Element} this
14148 setVisible : function(visible, animate){
14149 var me = this, isDisplay, isVisibility, isOffsets, isNosize,
14151 visMode = getVisMode(dom);
14154 // hideMode string override
14155 if (typeof animate == 'string'){
14158 visMode = El.DISPLAY;
14161 visMode = El.VISIBILITY;
14164 visMode = El.OFFSETS;
14168 visMode = El.ASCLASS;
14171 me.setVisibilityMode(visMode);
14175 if (!animate || !me.anim) {
14176 if(visMode == El.ASCLASS ){
14178 me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
14180 } else if (visMode == El.DISPLAY){
14182 return me.setDisplayed(visible);
14184 } else if (visMode == El.OFFSETS){
14187 // Remember position for restoring, if we are not already hidden by offsets.
14188 if (!me.hideModeStyles) {
14189 me.hideModeStyles = {
14190 position: me.getStyle('position'),
14191 top: me.getStyle('top'),
14192 left: me.getStyle('left')
14195 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
14198 // Only "restore" as position if we have actually been hidden using offsets.
14199 // Calling setVisible(true) on a positioned element should not reposition it.
14200 else if (me.hideModeStyles) {
14201 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
14202 delete me.hideModeStyles;
14207 // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
14208 dom.style.visibility = visible ? '' : HIDDEN;
14211 // closure for composites
14213 me.setOpacity(0.01);
14214 me.setVisible(true);
14216 if (!Ext.isObject(animate)) {
14222 me.animate(Ext.applyIf({
14223 callback: function() {
14224 visible || me.setVisible(false).setOpacity(1);
14227 opacity: (visible) ? 1 : 0
14231 data(dom, ISVISIBLE, visible); //set logical visibility state
14238 * Determine if the Element has a relevant height and width available based
14239 * upon current logical visibility state
14241 hasMetrics : function(){
14242 var dom = this.dom;
14243 return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
14247 * Toggles the element's visibility or display, depending on visibility mode.
14248 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
14249 * @return {Ext.core.Element} this
14251 toggle : function(animate){
14253 me.setVisible(!me.isVisible(), me.anim(animate));
14258 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
14259 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
14260 * @return {Ext.core.Element} this
14262 setDisplayed : function(value) {
14263 if(typeof value == "boolean"){
14264 value = value ? getDisplay(this.dom) : NONE;
14266 this.setStyle(DISPLAY, value);
14271 fixDisplay : function(){
14273 if (me.isStyle(DISPLAY, NONE)) {
14274 me.setStyle(VISIBILITY, HIDDEN);
14275 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
14276 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
14277 me.setStyle(DISPLAY, "block");
14283 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
14284 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14285 * @return {Ext.core.Element} this
14287 hide : function(animate){
14288 // hideMode override
14289 if (typeof animate == 'string'){
14290 this.setVisible(false, animate);
14293 this.setVisible(false, this.anim(animate));
14298 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
14299 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14300 * @return {Ext.core.Element} this
14302 show : function(animate){
14303 // hideMode override
14304 if (typeof animate == 'string'){
14305 this.setVisible(true, animate);
14308 this.setVisible(true, this.anim(animate));
14314 * @class Ext.core.Element
14316 Ext.applyIf(Ext.core.Element.prototype, {
14317 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
14318 animate: function(config) {
14321 me = Ext.get(me.dom);
14323 if (Ext.fx.Manager.hasFxBlock(me.id)) {
14326 Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
14330 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
14331 anim: function(config) {
14332 if (!Ext.isObject(config)) {
14333 return (config) ? {} : false;
14337 duration = config.duration || Ext.fx.Anim.prototype.duration,
14338 easing = config.easing || 'ease',
14341 if (config.stopAnimation) {
14342 me.stopAnimation();
14345 Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
14347 // Clear any 'paused' defaults.
14348 Ext.fx.Manager.setFxDefaults(me.id, {
14354 remove: config.remove,
14355 alternate: config.alternate || false,
14356 duration: duration,
14358 callback: config.callback,
14359 listeners: config.listeners,
14360 iterations: config.iterations || 1,
14361 scope: config.scope,
14362 block: config.block,
14363 concurrent: config.concurrent,
14364 delay: config.delay || 0,
14366 keyframes: config.keyframes,
14367 from: config.from || {},
14368 to: Ext.apply({}, config)
14370 Ext.apply(animConfig.to, config.to);
14372 // Anim API properties - backward compat
14373 delete animConfig.to.to;
14374 delete animConfig.to.from;
14375 delete animConfig.to.remove;
14376 delete animConfig.to.alternate;
14377 delete animConfig.to.keyframes;
14378 delete animConfig.to.iterations;
14379 delete animConfig.to.listeners;
14380 delete animConfig.to.target;
14381 delete animConfig.to.paused;
14382 delete animConfig.to.callback;
14383 delete animConfig.to.scope;
14384 delete animConfig.to.duration;
14385 delete animConfig.to.easing;
14386 delete animConfig.to.concurrent;
14387 delete animConfig.to.block;
14388 delete animConfig.to.stopAnimation;
14389 delete animConfig.to.delay;
14394 * Slides the element into view. An anchor point can be optionally passed to set the point of
14395 * origin for the slide effect. This function automatically handles wrapping the element with
14396 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
14399 // default: slide the element in from the top
14402 // custom: slide the element in from the right with a 2-second duration
14403 el.slideIn('r', { duration: 2 });
14405 // common config options shown with default values
14411 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
14412 * @param {Object} options (optional) Object literal with any of the Fx config options
14413 * @return {Ext.core.Element} The Element
14415 slideIn: function(anchor, obj, slideOut) {
14417 elStyle = me.dom.style,
14418 beforeAnim, wrapAnim;
14420 anchor = anchor || "t";
14423 beforeAnim = function() {
14424 var animScope = this,
14425 listeners = obj.listeners,
14426 box, position, restoreSize, wrap, anim;
14433 if ((anchor == 't' || anchor == 'b') && box.height == 0) {
14434 box.height = me.dom.scrollHeight;
14436 else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
14437 box.width = me.dom.scrollWidth;
14440 position = me.getPositioning();
14441 me.setSize(box.width, box.height);
14445 visibility: slideOut ? 'visible' : 'hidden'
14448 wrap.setPositioning(position);
14449 if (wrap.isStyle('position', 'static')) {
14450 wrap.position('relative');
14452 me.clearPositioning('auto');
14455 // This element is temporarily positioned absolute within its wrapper.
14456 // Restore to its default, CSS-inherited visibility setting.
14457 // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
14460 position: 'absolute'
14463 wrap.setSize(box.width, box.height);
14470 width: box.width + 'px',
14474 width: box.width + 'px',
14475 height: box.height + 'px'
14478 elStyle.bottom = '0px';
14484 height: box.height + 'px'
14487 width: box.width + 'px',
14488 height: box.height + 'px'
14491 elStyle.right = '0px';
14496 x: box.x + box.width,
14498 height: box.height + 'px'
14502 width: box.width + 'px',
14503 height: box.height + 'px'
14510 y: box.y + box.height,
14511 width: box.width + 'px',
14516 width: box.width + 'px',
14517 height: box.height + 'px'
14530 width: box.width + 'px',
14531 height: box.height + 'px'
14534 elStyle.bottom = '0px';
14535 elStyle.right = '0px';
14540 x: box.x + box.width,
14546 width: box.width + 'px',
14547 height: box.height + 'px'
14550 elStyle.right = '0px';
14555 x: box.x + box.width,
14556 y: box.y + box.height,
14563 width: box.width + 'px',
14564 height: box.height + 'px'
14571 y: box.y + box.height,
14577 width: box.width + 'px',
14578 height: box.height + 'px'
14581 elStyle.bottom = '0px';
14586 wrapAnim = Ext.apply({}, obj);
14587 delete wrapAnim.listeners;
14588 wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
14591 easing: 'ease-out',
14592 from: slideOut ? anim.to : anim.from,
14593 to: slideOut ? anim.from : anim.to
14596 // In the absence of a callback, this listener MUST be added first
14597 wrapAnim.on('afteranimate', function() {
14599 me.setPositioning(position);
14600 if (obj.useDisplay) {
14601 me.setDisplayed(false);
14607 me.clearPositioning();
14608 me.setPositioning(position);
14611 wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
14614 me.setSize(box.width, box.height);
14617 // Add configured listeners after
14619 wrapAnim.on(listeners);
14624 duration: obj.duration ? obj.duration * 2 : 1000,
14631 if (wrapAnim && wrapAnim.running) {
14643 * Slides the element out of view. An anchor point can be optionally passed to set the end point
14644 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
14645 * 'hidden') but block elements will still take up space in the document. The element must be removed
14646 * from the DOM using the 'remove' config option if desired. This function automatically handles
14647 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
14650 // default: slide the element out to the top
14653 // custom: slide the element out to the right with a 2-second duration
14654 el.slideOut('r', { duration: 2 });
14656 // common config options shown with default values
14664 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
14665 * @param {Object} options (optional) Object literal with any of the Fx config options
14666 * @return {Ext.core.Element} The Element
14668 slideOut: function(anchor, o) {
14669 return this.slideIn(anchor, o, true);
14673 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
14674 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
14680 // common config options shown with default values
14687 * @param {Object} options (optional) Object literal with any of the Fx config options
14688 * @return {Ext.core.Element} The Element
14691 puff: function(obj) {
14694 obj = Ext.applyIf(obj || {}, {
14695 easing: 'ease-out',
14700 beforeAnim = function() {
14704 var box = me.getBox(),
14705 fontSize = me.getStyle('fontSize'),
14706 position = me.getPositioning();
14708 width: box.width * 2,
14709 height: box.height * 2,
14710 x: box.x - (box.width / 2),
14711 y: box.y - (box.height /2),
14715 this.on('afteranimate',function() {
14717 if (obj.useDisplay) {
14718 me.setDisplayed(false);
14723 me.setPositioning(position);
14724 me.setStyle({fontSize: fontSize});
14730 duration: obj.duration,
14731 easing: obj.easing,
14742 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
14743 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
14744 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
14750 // all config options shown with default values
14758 * @param {Object} options (optional) Object literal with any of the Fx config options
14759 * @return {Ext.core.Element} The Element
14761 switchOff: function(obj) {
14765 obj = Ext.applyIf(obj || {}, {
14772 beforeAnim = function() {
14773 var animScope = this,
14774 size = me.getSize(),
14776 keyframe, position;
14779 position = me.getPositioning();
14781 keyframe = Ext.create('Ext.fx.Animator', {
14783 duration: obj.duration,
14784 easing: obj.easing,
14791 y: xy[1] + size.height / 2
14795 x: xy[0] + size.width / 2
14799 keyframe.on('afteranimate', function() {
14800 if (obj.useDisplay) {
14801 me.setDisplayed(false);
14806 me.setPositioning(position);
14812 duration: (obj.duration * 2),
14823 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
14826 // default: a single light blue ripple
14829 // custom: 3 red ripples lasting 3 seconds total
14830 el.frame("#ff0000", 3, { duration: 3 });
14832 // common config options shown with default values
14833 el.frame("#C3DAF9", 1, {
14834 duration: 1 //duration of each individual ripple.
14835 // Note: Easing is not configurable and will be ignored if included
14838 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
14839 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
14840 * @param {Object} options (optional) Object literal with any of the Fx config options
14841 * @return {Ext.core.Element} The Element
14843 frame : function(color, count, obj){
14847 color = color || '#C3DAF9';
14848 count = count || 1;
14851 beforeAnim = function() {
14853 var animScope = this,
14855 proxy = Ext.getBody().createChild({
14857 position : 'absolute',
14858 'pointer-events': 'none',
14860 border : '0px solid ' + color
14864 proxyAnim = Ext.create('Ext.fx.Anim', {
14866 duration: obj.duration || 1000,
14873 height: box.height,
14881 height: box.height + 40,
14882 width: box.width + 40
14885 proxyAnim.on('afteranimate', function() {
14892 duration: (obj.duration * 2) || 2000,
14903 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
14904 * ending point of the effect.
14907 // default: slide the element downward while fading out
14910 // custom: slide the element out to the right with a 2-second duration
14911 el.ghost('r', { duration: 2 });
14913 // common config options shown with default values
14919 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
14920 * @param {Object} options (optional) Object literal with any of the Fx config options
14921 * @return {Ext.core.Element} The Element
14923 ghost: function(anchor, obj) {
14927 anchor = anchor || "b";
14928 beforeAnim = function() {
14929 var width = me.getWidth(),
14930 height = me.getHeight(),
14932 position = me.getPositioning(),
14938 to.y = xy[1] - height;
14941 to.x = xy[0] - width;
14944 to.x = xy[0] + width;
14947 to.y = xy[1] + height;
14950 to.x = xy[0] - width;
14951 to.y = xy[1] - height;
14954 to.x = xy[0] - width;
14955 to.y = xy[1] + height;
14958 to.x = xy[0] + width;
14959 to.y = xy[1] + height;
14962 to.x = xy[0] + width;
14963 to.y = xy[1] - height;
14967 this.on('afteranimate', function () {
14971 me.setPositioning(position);
14976 me.animate(Ext.applyIf(obj || {}, {
14978 easing: 'ease-out',
14989 * Highlights the Element by setting a color (applies to the background-color by default, but can be
14990 * changed using the "attr" config option) and then fading back to the original color. If no original
14991 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
14994 // default: highlight background to yellow
14997 // custom: highlight foreground text to blue for 2 seconds
14998 el.highlight("0000ff", { attr: 'color', duration: 2 });
15000 // common config options shown with default values
15001 el.highlight("ffff9c", {
15002 attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
15003 endColor: (current color) or "ffffff",
15008 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
15009 * @param {Object} options (optional) Object literal with any of the Fx config options
15010 * @return {Ext.core.Element} The Element
15012 highlight: function(color, o) {
15016 restore, to, attr, lns, event, fn;
15019 lns = o.listeners || {};
15020 attr = o.attr || 'backgroundColor';
15021 from[attr] = color || 'ffff9c';
15025 to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
15031 // Don't apply directly on lns, since we reference it in our own callbacks below
15032 o.listeners = Ext.apply(Ext.apply({}, lns), {
15033 beforeanimate: function() {
15034 restore = dom.style[attr];
15038 event = lns.beforeanimate;
15040 fn = event.fn || event;
15041 return fn.apply(event.scope || lns.scope || window, arguments);
15044 afteranimate: function() {
15046 dom.style[attr] = restore;
15049 event = lns.afteranimate;
15051 fn = event.fn || event;
15052 fn.apply(event.scope || lns.scope || window, arguments);
15057 me.animate(Ext.apply({}, o, {
15068 * Creates a pause before any subsequent queued effects begin. If there are
15069 * no effects queued after the pause it will have no effect.
15074 * @param {Number} seconds The length of time to pause (in seconds)
15075 * @return {Ext.Element} The Element
15077 pause: function(ms) {
15079 Ext.fx.Manager.setFxDefaults(me.id, {
15086 * Fade an element in (from transparent to opaque). The ending opacity can be specified
15087 * using the <tt>{@link #endOpacity}</tt> config option.
15090 // default: fade in from opacity 0 to 100%
15093 // custom: fade in from opacity 0 to 75% over 2 seconds
15094 el.fadeIn({ endOpacity: .75, duration: 2});
15096 // common config options shown with default values
15098 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
15103 * @param {Object} options (optional) Object literal with any of the Fx config options
15104 * @return {Ext.Element} The Element
15106 fadeIn: function(o) {
15107 this.animate(Ext.apply({}, o, {
15114 * Fade an element out (from opaque to transparent). The ending opacity can be specified
15115 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
15116 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
15119 // default: fade out from the element's current opacity to 0
15122 // custom: fade out from the element's current opacity to 25% over 2 seconds
15123 el.fadeOut({ endOpacity: .25, duration: 2});
15125 // common config options shown with default values
15127 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
15134 * @param {Object} options (optional) Object literal with any of the Fx config options
15135 * @return {Ext.Element} The Element
15137 fadeOut: function(o) {
15138 this.animate(Ext.apply({}, o, {
15146 * Animates the transition of an element's dimensions from a starting height/width
15147 * to an ending height/width. This method is a convenience implementation of {@link shift}.
15150 // change height and width to 100x100 pixels
15151 el.scale(100, 100);
15153 // common config options shown with default values. The height and width will default to
15154 // the element's existing values if passed as null.
15156 [element's width],
15157 [element's height], {
15163 * @param {Number} width The new width (pass undefined to keep the original width)
15164 * @param {Number} height The new height (pass undefined to keep the original height)
15165 * @param {Object} options (optional) Object literal with any of the Fx config options
15166 * @return {Ext.Element} The Element
15168 scale: function(w, h, o) {
15169 this.animate(Ext.apply({}, o, {
15178 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
15179 * Any of these properties not specified in the config object will not be changed. This effect
15180 * requires that at least one new dimension, position or opacity setting must be passed in on
15181 * the config object in order for the function to have any effect.
15184 // slide the element horizontally to x position 200 while changing the height and opacity
15185 el.shift({ x: 200, height: 50, opacity: .8 });
15187 // common config options shown with default values.
15189 width: [element's width],
15190 height: [element's height],
15191 x: [element's x position],
15192 y: [element's y position],
15193 opacity: [element's opacity],
15198 * @param {Object} options Object literal with any of the Fx config options
15199 * @return {Ext.Element} The Element
15201 shift: function(config) {
15202 this.animate(config);
15208 * @class Ext.core.Element
15210 Ext.applyIf(Ext.core.Element, {
15211 unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
15212 camelRe: /(-[a-z])/gi,
15213 opacityRe: /alpha\(opacity=(.*)\)/i,
15214 cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
15216 defaultUnit : "px",
15217 borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
15218 paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
15219 margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
15221 // Reference the prototype's version of the method. Signatures are identical.
15222 addUnits : Ext.core.Element.prototype.addUnits,
15225 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
15226 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
15228 * @param {Number|String} box The encoded margins
15229 * @return {Object} An object with margin sizes for top, right, bottom and left
15231 parseBox : function(box) {
15232 if (Ext.isObject(box)) {
15235 right: box.right || 0,
15236 bottom: box.bottom || 0,
15237 left: box.left || 0
15240 if (typeof box != 'string') {
15241 box = box.toString();
15243 var parts = box.split(' '),
15247 parts[1] = parts[2] = parts[3] = parts[0];
15249 else if (ln == 2) {
15250 parts[2] = parts[0];
15251 parts[3] = parts[1];
15253 else if (ln == 3) {
15254 parts[3] = parts[1];
15258 top :parseFloat(parts[0]) || 0,
15259 right :parseFloat(parts[1]) || 0,
15260 bottom:parseFloat(parts[2]) || 0,
15261 left :parseFloat(parts[3]) || 0
15268 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
15269 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
15271 * @param {Number|String} box The encoded margins
15272 * @param {String} units The type of units to add
15273 * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
15275 unitizeBox : function(box, units) {
15276 var A = this.addUnits,
15277 B = this.parseBox(box);
15279 return A(B.top, units) + ' ' +
15280 A(B.right, units) + ' ' +
15281 A(B.bottom, units) + ' ' +
15287 camelReplaceFn : function(m, a) {
15288 return a.charAt(1).toUpperCase();
15292 * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
15295 * <li>border-width -> borderWidth</li>
15296 * <li>padding-top -> paddingTop</li>
15299 * @param {String} prop The property to normalize
15300 * @return {String} The normalized string
15302 normalize : function(prop) {
15303 if (prop == 'float') {
15304 prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
15306 return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
15310 * Retrieves the document height
15312 * @return {Number} documentHeight
15314 getDocumentHeight: function() {
15315 return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
15319 * Retrieves the document width
15321 * @return {Number} documentWidth
15323 getDocumentWidth: function() {
15324 return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
15328 * Retrieves the viewport height of the window.
15330 * @return {Number} viewportHeight
15332 getViewportHeight: function(){
15333 return window.innerHeight;
15337 * Retrieves the viewport width of the window.
15339 * @return {Number} viewportWidth
15341 getViewportWidth : function() {
15342 return window.innerWidth;
15346 * Retrieves the viewport size of the window.
15348 * @return {Object} object containing width and height properties
15350 getViewSize : function() {
15352 width: window.innerWidth,
15353 height: window.innerHeight
15358 * Retrieves the current orientation of the window. This is calculated by
15359 * determing if the height is greater than the width.
15361 * @return {String} Orientation of window: 'portrait' or 'landscape'
15363 getOrientation : function() {
15364 if (Ext.supports.OrientationChange) {
15365 return (window.orientation == 0) ? 'portrait' : 'landscape';
15368 return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
15372 * Returns the top Element that is located at the passed coordinates
15374 * @param {Number} x The x coordinate
15375 * @param {Number} x The y coordinate
15376 * @return {String} The found Element
15378 fromPoint: function(x, y) {
15379 return Ext.get(document.elementFromPoint(x, y));
15383 * Converts a CSS string into an object with a property for each style.
15385 * The sample code below would return an object with 2 properties, one
15386 * for background-color and one for color.</p>
15388 var css = 'background-color: red;color: blue; ';
15389 console.log(Ext.core.Element.parseStyles(css));
15392 * @param {String} styles A CSS string
15393 * @return {Object} styles
15395 parseStyles: function(styles){
15397 cssRe = this.cssRe,
15401 // Since we're using the g flag on the regex, we need to set the lastIndex.
15402 // This automatically happens on some implementations, but not others, see:
15403 // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
15404 // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
15405 cssRe.lastIndex = 0;
15406 while ((matches = cssRe.exec(styles))) {
15407 out[matches[1]] = matches[2];
15415 * @class Ext.CompositeElementLite
15416 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
15417 * members, or to perform collective actions upon the whole set.</p>
15418 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
15419 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
15420 * Example:<pre><code>
15421 var els = Ext.select("#some-el div.some-class");
15422 // or select directly from an existing element
15423 var el = Ext.get('some-el');
15424 el.select('div.some-class');
15426 els.setWidth(100); // all elements become 100 width
15427 els.hide(true); // all elements fade out and hide
15429 els.setWidth(100).hide(true);
15432 Ext.CompositeElementLite = function(els, root){
15434 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
15435 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
15436 * to augment the capabilities of the CompositeElementLite class may use it when adding
15437 * methods to the class.</p>
15438 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
15439 * following siblings of selected elements, the code would be</p><code><pre>
15440 Ext.override(Ext.CompositeElementLite, {
15441 nextAll: function() {
15442 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
15444 // Loop through all elements in this Composite, accumulating
15445 // an Array of all siblings.
15446 for (i = 0; i < l; i++) {
15447 for (n = els[i].nextSibling; n; n = n.nextSibling) {
15452 // Add all found siblings to this Composite
15453 return this.add(r);
15457 * @property elements
15459 this.elements = [];
15460 this.add(els, root);
15461 this.el = new Ext.core.Element.Flyweight();
15464 Ext.CompositeElementLite.prototype = {
15468 getElement : function(el){
15469 // Set the shared flyweight dom property to the current element
15477 transformElement : function(el){
15478 return Ext.getDom(el);
15482 * Returns the number of elements in this Composite.
15485 getCount : function(){
15486 return this.elements.length;
15489 * Adds elements to this Composite object.
15490 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
15491 * @return {CompositeElement} This Composite object.
15493 add : function(els, root){
15495 elements = me.elements;
15499 if(typeof els == "string"){
15500 els = Ext.core.Element.selectorFunction(els, root);
15501 }else if(els.isComposite){
15502 els = els.elements;
15503 }else if(!Ext.isIterable(els)){
15507 for(var i = 0, len = els.length; i < len; ++i){
15508 elements.push(me.transformElement(els[i]));
15513 invoke : function(fn, args){
15520 for(i = 0; i < len; i++) {
15523 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
15529 * Returns a flyweight Element of the dom element object at the specified index
15530 * @param {Number} index
15531 * @return {Ext.core.Element}
15533 item : function(index){
15535 el = me.elements[index],
15539 out = me.getElement(el);
15544 // fixes scope with flyweight
15545 addListener : function(eventName, handler, scope, opt){
15546 var els = this.elements,
15550 for(i = 0; i<len; i++) {
15553 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
15559 * <p>Calls the passed function for each element in this composite.</p>
15560 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
15561 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
15562 * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
15563 * a reference to the dom node, use el.dom.</b></div></li>
15564 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
15565 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
15567 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
15568 * @return {CompositeElement} this
15570 each : function(fn, scope){
15576 for(i = 0; i<len; i++) {
15579 e = this.getElement(e);
15580 if(fn.call(scope || e, e, me, i) === false){
15589 * Clears this Composite and adds the elements passed.
15590 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
15591 * @return {CompositeElement} this
15593 fill : function(els){
15601 * Filters this composite to only elements that match the passed selector.
15602 * @param {String/Function} selector A string CSS selector or a comparison function.
15603 * The comparison function will be called with the following arguments:<ul>
15604 * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
15605 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
15607 * @return {CompositeElement} this
15609 filter : function(selector){
15612 fn = Ext.isFunction(selector) ? selector
15614 return el.is(selector);
15617 me.each(function(el, self, i) {
15618 if (fn(el, i) !== false) {
15619 els[els.length] = me.transformElement(el);
15628 * Find the index of the passed element within the composite collection.
15629 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
15630 * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
15632 indexOf : function(el){
15633 return Ext.Array.indexOf(this.elements, this.transformElement(el));
15637 * Replaces the specified element with the passed element.
15638 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
15640 * @param {Mixed} replacement The id of an element or the Element itself.
15641 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
15642 * @return {CompositeElement} this
15644 replaceElement : function(el, replacement, domReplace){
15645 var index = !isNaN(el) ? el : this.indexOf(el),
15648 replacement = Ext.getDom(replacement);
15650 d = this.elements[index];
15651 d.parentNode.insertBefore(replacement, d);
15654 this.elements.splice(index, 1, replacement);
15660 * Removes all elements.
15662 clear : function(){
15663 this.elements = [];
15667 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
15671 * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
15672 * This is called twice - once immediately below, and once again after additional Ext.core.Element
15673 * are added in Ext JS
15675 Ext.CompositeElementLite.importElementMethods = function() {
15677 ElProto = Ext.core.Element.prototype,
15678 CelProto = Ext.CompositeElementLite.prototype;
15680 for (fnName in ElProto) {
15681 if (typeof ElProto[fnName] == 'function'){
15682 (function(fnName) {
15683 CelProto[fnName] = CelProto[fnName] || function() {
15684 return this.invoke(fnName, arguments);
15686 }).call(CelProto, fnName);
15692 Ext.CompositeElementLite.importElementMethods();
15695 Ext.core.Element.selectorFunction = Ext.DomQuery.select;
15699 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
15700 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
15701 * {@link Ext.CompositeElementLite CompositeElementLite} object.
15702 * @param {String/Array} selector The CSS selector or an array of elements
15703 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
15704 * @return {CompositeElementLite/CompositeElement}
15705 * @member Ext.core.Element
15708 Ext.core.Element.select = function(selector, root){
15710 if(typeof selector == "string"){
15711 els = Ext.core.Element.selectorFunction(selector, root);
15712 }else if(selector.length !== undefined){
15716 sourceClass: "Ext.core.Element",
15717 sourceMethod: "select",
15718 selector: selector,
15720 msg: "Invalid selector specified: " + selector
15723 return new Ext.CompositeElementLite(els);
15726 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
15727 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
15728 * {@link Ext.CompositeElementLite CompositeElementLite} object.
15729 * @param {String/Array} selector The CSS selector or an array of elements
15730 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
15731 * @return {CompositeElementLite/CompositeElement}
15735 Ext.select = Ext.core.Element.select;
15738 * @class Ext.util.DelayedTask
15740 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
15741 * performing setTimeout where a new timeout cancels the old timeout. When called, the
15742 * task will wait the specified time period before executing. If durng that time period,
15743 * the task is called again, the original call will be cancelled. This continues so that
15744 * the function is only called a single time for each iteration.
15746 * This method is especially useful for things like detecting whether a user has finished
15747 * typing in a text field. An example would be performing validation on a keypress. You can
15748 * use this class to buffer the keypress events for a certain number of milliseconds, and
15749 * perform only if they stop for that amount of time.
15753 * var task = new Ext.util.DelayedTask(function(){
15754 * alert(Ext.getDom('myInputField').value.length);
15757 * // Wait 500ms before calling our function. If the user presses another key
15758 * // during that 500ms, it will be cancelled and we'll wait another 500ms.
15759 * Ext.get('myInputField').on('keypress', function(){
15760 * task.{@link #delay}(500);
15763 * Note that we are using a DelayedTask here to illustrate a point. The configuration
15764 * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
15765 * also setup a delayed task for you to buffer events.
15767 * @constructor The parameters to this constructor serve as defaults and are not required.
15768 * @param {Function} fn (optional) The default function to call.
15769 * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
15770 * function is called. If not specified, <code>this</code> will refer to the browser window.
15771 * @param {Array} args (optional) The default Array of arguments.
15773 Ext.util.DelayedTask = function(fn, scope, args) {
15776 call = function() {
15779 fn.apply(scope, args || []);
15783 * Cancels any pending timeout and queues a new one
15784 * @param {Number} delay The milliseconds to delay
15785 * @param {Function} newFn (optional) Overrides function passed to constructor
15786 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
15787 * is specified, <code>this</code> will refer to the browser window.
15788 * @param {Array} newArgs (optional) Overrides args passed to constructor
15790 this.delay = function(delay, newFn, newScope, newArgs) {
15793 scope = newScope || scope;
15794 args = newArgs || args;
15795 id = setInterval(call, delay);
15799 * Cancel the last queued timeout
15801 this.cancel = function(){
15808 Ext.require('Ext.util.DelayedTask', function() {
15810 Ext.util.Event = Ext.extend(Object, (function() {
15811 function createBuffered(handler, listener, o, scope) {
15812 listener.task = new Ext.util.DelayedTask();
15813 return function() {
15814 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
15818 function createDelayed(handler, listener, o, scope) {
15819 return function() {
15820 var task = new Ext.util.DelayedTask();
15821 if (!listener.tasks) {
15822 listener.tasks = [];
15824 listener.tasks.push(task);
15825 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
15829 function createSingle(handler, listener, o, scope) {
15830 return function() {
15831 listener.ev.removeListener(listener.fn, scope);
15832 return handler.apply(scope, arguments);
15839 constructor: function(observable, name) {
15841 this.observable = observable;
15842 this.listeners = [];
15845 addListener: function(fn, scope, options) {
15848 scope = scope || me.observable;
15852 sourceClass: Ext.getClassName(this.observable),
15853 sourceMethod: "addListener",
15854 msg: "The specified callback function is undefined"
15858 if (!me.isListening(fn, scope)) {
15859 listener = me.createListener(fn, scope, options);
15861 // if we are currently firing this event, don't disturb the listener loop
15862 me.listeners = me.listeners.slice(0);
15864 me.listeners.push(listener);
15868 createListener: function(fn, scope, o) {
15870 scope = scope || this.observable;
15880 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
15881 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
15883 handler = createSingle(handler, listener, o, scope);
15886 handler = createDelayed(handler, listener, o, scope);
15889 handler = createBuffered(handler, listener, o, scope);
15892 listener.fireFn = handler;
15896 findListener: function(fn, scope) {
15897 var listeners = this.listeners,
15898 i = listeners.length,
15903 listener = listeners[i];
15905 s = listener.scope;
15906 if (listener.fn == fn && (s == scope || s == this.observable)) {
15915 isListening: function(fn, scope) {
15916 return this.findListener(fn, scope) !== -1;
15919 removeListener: function(fn, scope) {
15924 index = me.findListener(fn, scope);
15926 listener = me.listeners[index];
15929 me.listeners = me.listeners.slice(0);
15932 // cancel and remove a buffered handler that hasn't fired yet
15933 if (listener.task) {
15934 listener.task.cancel();
15935 delete listener.task;
15938 // cancel and remove all delayed handlers that haven't fired yet
15939 k = listener.tasks && listener.tasks.length;
15942 listener.tasks[k].cancel();
15944 delete listener.tasks;
15947 // remove this listener from the listeners array
15948 me.listeners.splice(index, 1);
15955 // Iterate to stop any buffered/delayed events
15956 clearListeners: function() {
15957 var listeners = this.listeners,
15958 i = listeners.length;
15961 this.removeListener(listeners[i].fn, listeners[i].scope);
15967 listeners = me.listeners,
15968 count = listeners.length,
15975 for (i = 0; i < count; i++) {
15976 listener = listeners[i];
15977 args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
15979 args.push(listener.o);
15981 if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
15982 return (me.firing = false);
15994 * @class Ext.EventManager
15995 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
15996 * several useful events directly.
15997 * See {@link Ext.EventObject} for more details on normalized event objects.
16000 Ext.EventManager = {
16002 // --------------------- onReady ---------------------
16005 * Check if we have bound our global onReady listener
16008 hasBoundOnReady: false,
16011 * Check if fireDocReady has been called
16014 hasFiredReady: false,
16017 * Timer for the document ready event in old IE versions
16020 readyTimeout: null,
16023 * Checks if we have bound an onreadystatechange event
16026 hasOnReadyStateChange: false,
16029 * Holds references to any onReady functions
16032 readyEvent: new Ext.util.Event(),
16035 * Check the ready state for old IE versions
16037 * @return {Boolean} True if the document is ready
16039 checkReadyState: function(){
16040 var me = Ext.EventManager;
16042 if(window.attachEvent){
16043 // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
16044 if (window != top) {
16048 document.documentElement.doScroll('left');
16055 if (document.readyState == 'complete') {
16059 me.readyTimeout = setTimeout(arguments.callee, 2);
16064 * Binds the appropriate browser event for checking if the DOM has loaded.
16067 bindReadyEvent: function(){
16068 var me = Ext.EventManager;
16069 if (me.hasBoundOnReady) {
16073 if (document.addEventListener) {
16074 document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
16075 // fallback, load will ~always~ fire
16076 window.addEventListener('load', me.fireDocReady, false);
16078 // check if the document is ready, this will also kick off the scroll checking timer
16079 if (!me.checkReadyState()) {
16080 document.attachEvent('onreadystatechange', me.checkReadyState);
16081 me.hasOnReadyStateChange = true;
16083 // fallback, onload will ~always~ fire
16084 window.attachEvent('onload', me.fireDocReady, false);
16086 me.hasBoundOnReady = true;
16090 * We know the document is loaded, so trigger any onReady events.
16093 fireDocReady: function(){
16094 var me = Ext.EventManager;
16096 // only unbind these events once
16097 if (!me.hasFiredReady) {
16098 me.hasFiredReady = true;
16100 if (document.addEventListener) {
16101 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
16102 window.removeEventListener('load', me.fireDocReady, false);
16104 if (me.readyTimeout !== null) {
16105 clearTimeout(me.readyTimeout);
16107 if (me.hasOnReadyStateChange) {
16108 document.detachEvent('onreadystatechange', me.checkReadyState);
16110 window.detachEvent('onload', me.fireDocReady);
16112 Ext.supports.init();
16114 if (!Ext.isReady) {
16115 Ext.isReady = true;
16116 me.onWindowUnload();
16117 me.readyEvent.fire();
16122 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
16123 * accessed shorthanded as Ext.onReady().
16124 * @param {Function} fn The method the event invokes.
16125 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
16126 * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
16128 onDocumentReady: function(fn, scope, options){
16129 options = options || {};
16130 var me = Ext.EventManager,
16131 readyEvent = me.readyEvent;
16133 // force single to be true so our event is only ever fired once.
16134 options.single = true;
16136 // Document already loaded, let's just fire it
16138 readyEvent.addListener(fn, scope, options);
16141 options.delay = options.delay || 1;
16142 readyEvent.addListener(fn, scope, options);
16143 me.bindReadyEvent();
16148 // --------------------- event binding ---------------------
16151 * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
16154 stoppedMouseDownEvent: new Ext.util.Event(),
16157 * Options to parse for the 4th argument to addListener.
16160 propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
16163 * Get the id of the element. If one has not been assigned, automatically assign it.
16164 * @param {Mixed} element The element to get the id for.
16165 * @return {String} id
16167 getId : function(element) {
16168 var skipGarbageCollection = false,
16171 element = Ext.getDom(element);
16173 if (element === document || element === window) {
16174 id = element === document ? Ext.documentId : Ext.windowId;
16177 id = Ext.id(element);
16179 // skip garbage collection for special elements (window, document, iframes)
16180 if (element && (element.getElementById || element.navigator)) {
16181 skipGarbageCollection = true;
16184 if (!Ext.cache[id]){
16185 Ext.core.Element.addToCache(new Ext.core.Element(element), id);
16186 if (skipGarbageCollection) {
16187 Ext.cache[id].skipGarbageCollection = true;
16194 * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
16196 * @param {Object} element The element the event is for
16197 * @param {Object} event The event configuration
16198 * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
16200 prepareListenerConfig: function(element, config, isRemove){
16202 propRe = me.propRe,
16205 // loop over all the keys in the object
16206 for (key in config) {
16207 if (config.hasOwnProperty(key)) {
16208 // if the key is something else then an event option
16209 if (!propRe.test(key)) {
16210 value = config[key];
16211 // if the value is a function it must be something like click: function(){}, scope: this
16212 // which means that there might be multiple event listeners with shared options
16213 if (Ext.isFunction(value)) {
16215 args = [element, key, value, config.scope, config];
16217 // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
16218 args = [element, key, value.fn, value.scope, value];
16221 if (isRemove === true) {
16222 me.removeListener.apply(this, args);
16224 me.addListener.apply(me, args);
16232 * Normalize cross browser event differences
16234 * @param {Object} eventName The event name
16235 * @param {Object} fn The function to execute
16236 * @return {Object} The new event name/function
16238 normalizeEvent: function(eventName, fn){
16239 if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
16241 fn = Ext.Function.createInterceptor(fn, this.contains, this);
16243 eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
16244 } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
16245 eventName = 'DOMMouseScroll';
16248 eventName: eventName,
16254 * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
16256 * @param {Object} event
16258 contains: function(event){
16259 var parent = event.browserEvent.currentTarget,
16260 child = this.getRelatedTarget(event);
16262 if (parent && parent.firstChild) {
16264 if (child === parent) {
16267 child = child.parentNode;
16268 if (child && (child.nodeType != 1)) {
16277 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
16278 * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
16279 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
16280 * @param {String} eventName The name of the event to listen for.
16281 * @param {Function} handler The handler function the event invokes. This function is passed
16282 * the following parameters:<ul>
16283 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
16284 * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
16285 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
16286 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
16288 * @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>.
16289 * @param {Object} options (optional) An object containing handler configuration properties.
16290 * This may contain any of the following properties:<ul>
16291 * <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>
16292 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
16293 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
16294 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
16295 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
16296 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
16297 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
16298 * <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>
16299 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
16300 * by the specified number of milliseconds. If the event fires again within that time, the original
16301 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
16302 * <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>
16304 * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
16306 addListener: function(element, eventName, fn, scope, options){
16307 // Check if we've been passed a "config style" event.
16308 if (Ext.isObject(eventName)) {
16309 this.prepareListenerConfig(element, eventName);
16313 var dom = Ext.getDom(element),
16319 sourceClass: 'Ext.EventManager',
16320 sourceMethod: 'addListener',
16321 targetElement: element,
16322 eventName: eventName,
16323 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
16328 sourceClass: 'Ext.EventManager',
16329 sourceMethod: 'addListener',
16330 targetElement: element,
16331 eventName: eventName,
16332 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
16336 // create the wrapper function
16337 options = options || {};
16339 bind = this.normalizeEvent(eventName, fn);
16340 wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
16343 if (dom.attachEvent) {
16344 dom.attachEvent('on' + bind.eventName, wrap);
16346 dom.addEventListener(bind.eventName, wrap, options.capture || false);
16349 if (dom == document && eventName == 'mousedown') {
16350 this.stoppedMouseDownEvent.addListener(wrap);
16353 // add all required data into the event cache
16354 this.getEventListenerCache(dom, eventName).push({
16362 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
16363 * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
16364 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
16365 * @param {String} eventName The name of the event.
16366 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
16367 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
16368 * then this must refer to the same object.
16370 removeListener : function(element, eventName, fn, scope) {
16371 // handle our listener config object syntax
16372 if (Ext.isObject(eventName)) {
16373 this.prepareListenerConfig(element, eventName, true);
16377 var dom = Ext.getDom(element),
16378 cache = this.getEventListenerCache(dom, eventName),
16379 bindName = this.normalizeEvent(eventName).eventName,
16380 i = cache.length, j,
16381 listener, wrap, tasks;
16385 listener = cache[i];
16387 if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
16388 wrap = listener.wrap;
16390 // clear buffered calls
16392 clearTimeout(wrap.task);
16396 // clear delayed calls
16397 j = wrap.tasks && wrap.tasks.length;
16400 clearTimeout(wrap.tasks[j]);
16405 if (dom.detachEvent) {
16406 dom.detachEvent('on' + bindName, wrap);
16408 dom.removeEventListener(bindName, wrap, false);
16411 if (wrap && dom == document && eventName == 'mousedown') {
16412 this.stoppedMouseDownEvent.removeListener(wrap);
16415 // remove listener from cache
16416 cache.splice(i, 1);
16422 * Removes all event handers from an element. Typically you will use {@link Ext.core.Element#removeAllListeners}
16423 * directly on an Element in favor of calling this version.
16424 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
16426 removeAll : function(element){
16427 var dom = Ext.getDom(element),
16432 cache = this.getElementEventCache(dom);
16434 for (ev in cache) {
16435 if (cache.hasOwnProperty(ev)) {
16436 this.removeListener(dom, ev);
16439 Ext.cache[dom.id].events = {};
16443 * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
16444 * directly on an Element in favor of calling this version.
16445 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
16446 * @param {String} eventName (optional) The name of the event.
16448 purgeElement : function(element, eventName) {
16449 var dom = Ext.getDom(element),
16453 this.removeListener(dom, eventName);
16456 this.removeAll(dom);
16459 if(dom && dom.childNodes) {
16460 for(len = element.childNodes.length; i < len; i++) {
16461 this.purgeElement(element.childNodes[i], eventName);
16467 * Create the wrapper function for the event
16469 * @param {HTMLElement} dom The dom element
16470 * @param {String} ename The event name
16471 * @param {Function} fn The function to execute
16472 * @param {Object} scope The scope to execute callback in
16473 * @param {Object} o The options
16475 createListenerWrap : function(dom, ename, fn, scope, options) {
16476 options = !Ext.isObject(options) ? {} : options;
16478 var f = ['if(!Ext) {return;}'],
16481 if(options.buffer || options.delay || options.freezeEvent) {
16482 f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
16484 f.push('e = Ext.EventObject.setEvent(e);');
16487 if (options.delegate) {
16488 f.push('var t = e.getTarget("' + options.delegate + '", this);');
16489 f.push('if(!t) {return;}');
16491 f.push('var t = e.target;');
16494 if (options.target) {
16495 f.push('if(e.target !== options.target) {return;}');
16498 if(options.stopEvent) {
16499 f.push('e.stopEvent();');
16501 if(options.preventDefault) {
16502 f.push('e.preventDefault();');
16504 if(options.stopPropagation) {
16505 f.push('e.stopPropagation();');
16509 if(options.normalized === false) {
16510 f.push('e = e.browserEvent;');
16513 if(options.buffer) {
16514 f.push('(wrap.task && clearTimeout(wrap.task));');
16515 f.push('wrap.task = setTimeout(function(){');
16518 if(options.delay) {
16519 f.push('wrap.tasks = wrap.tasks || [];');
16520 f.push('wrap.tasks.push(setTimeout(function(){');
16523 // finally call the actual handler fn
16524 f.push('fn.call(scope || dom, e, t, options);');
16526 if(options.single) {
16527 f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
16530 if(options.delay) {
16531 f.push('}, ' + options.delay + '));');
16534 if(options.buffer) {
16535 f.push('}, ' + options.buffer + ');');
16538 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
16540 return function wrap(e, args) {
16541 gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
16546 * Get the event cache for a particular element for a particular event
16548 * @param {HTMLElement} element The element
16549 * @param {Object} eventName The event name
16550 * @return {Array} The events for the element
16552 getEventListenerCache : function(element, eventName) {
16553 var eventCache = this.getElementEventCache(element);
16554 return eventCache[eventName] || (eventCache[eventName] = []);
16558 * Gets the event cache for the object
16560 * @param {HTMLElement} element The element
16561 * @return {Object} The event cache for the object
16563 getElementEventCache : function(element) {
16564 var elementCache = Ext.cache[this.getId(element)];
16565 return elementCache.events || (elementCache.events = {});
16568 // --------------------- utility methods ---------------------
16569 mouseLeaveRe: /(mouseout|mouseleave)/,
16570 mouseEnterRe: /(mouseover|mouseenter)/,
16573 * Stop the event (preventDefault and stopPropagation)
16574 * @param {Event} The event to stop
16576 stopEvent: function(event) {
16577 this.stopPropagation(event);
16578 this.preventDefault(event);
16582 * Cancels bubbling of the event.
16583 * @param {Event} The event to stop bubbling.
16585 stopPropagation: function(event) {
16586 event = event.browserEvent || event;
16587 if (event.stopPropagation) {
16588 event.stopPropagation();
16590 event.cancelBubble = true;
16595 * Prevents the browsers default handling of the event.
16596 * @param {Event} The event to prevent the default
16598 preventDefault: function(event) {
16599 event = event.browserEvent || event;
16600 if (event.preventDefault) {
16601 event.preventDefault();
16603 event.returnValue = false;
16604 // Some keys events require setting the keyCode to -1 to be prevented
16606 // all ctrl + X and F1 -> F12
16607 if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
16608 event.keyCode = -1;
16611 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
16617 * Gets the related target from the event.
16618 * @param {Object} event The event
16619 * @return {HTMLElement} The related target.
16621 getRelatedTarget: function(event) {
16622 event = event.browserEvent || event;
16623 var target = event.relatedTarget;
16625 if (this.mouseLeaveRe.test(event.type)) {
16626 target = event.toElement;
16627 } else if (this.mouseEnterRe.test(event.type)) {
16628 target = event.fromElement;
16631 return this.resolveTextNode(target);
16635 * Gets the x coordinate from the event
16636 * @param {Object} event The event
16637 * @return {Number} The x coordinate
16639 getPageX: function(event) {
16640 return this.getXY(event)[0];
16644 * Gets the y coordinate from the event
16645 * @param {Object} event The event
16646 * @return {Number} The y coordinate
16648 getPageY: function(event) {
16649 return this.getXY(event)[1];
16653 * Gets the x & ycoordinate from the event
16654 * @param {Object} event The event
16655 * @return {Array} The x/y coordinate
16657 getPageXY: function(event) {
16658 event = event.browserEvent || event;
16659 var x = event.pageX,
16661 doc = document.documentElement,
16662 body = document.body;
16664 // pageX/pageY not available (undefined, not null), use clientX/clientY instead
16665 if (!x && x !== 0) {
16666 x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
16667 y = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
16673 * Gets the target of the event.
16674 * @param {Object} event The event
16675 * @return {HTMLElement} target
16677 getTarget: function(event) {
16678 event = event.browserEvent || event;
16679 return this.resolveTextNode(event.target || event.srcElement);
16683 * Resolve any text nodes accounting for browser differences.
16685 * @param {HTMLElement} node The node
16686 * @return {HTMLElement} The resolved node
16688 // 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.
16689 resolveTextNode: Ext.isGecko ?
16694 // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
16695 var s = HTMLElement.prototype.toString.call(node);
16696 if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
16699 return node.nodeType == 3 ? node.parentNode: node;
16700 }: function(node) {
16701 return node && node.nodeType == 3 ? node.parentNode: node;
16704 // --------------------- custom event binding ---------------------
16706 // Keep track of the current width/height
16711 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
16712 * passes new viewport width and height to handlers.
16713 * @param {Function} fn The handler function the window resize event invokes.
16714 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
16715 * @param {boolean} options Options object as passed to {@link Ext.core.Element#addListener}
16717 onWindowResize: function(fn, scope, options){
16718 var resize = this.resizeEvent;
16720 this.resizeEvent = resize = new Ext.util.Event();
16721 this.on(window, 'resize', this.fireResize, this, {buffer: 100});
16723 resize.addListener(fn, scope, options);
16727 * Fire the resize event.
16730 fireResize: function(){
16732 w = Ext.core.Element.getViewWidth(),
16733 h = Ext.core.Element.getViewHeight();
16735 //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
16736 if(me.curHeight != h || me.curWidth != w){
16739 me.resizeEvent.fire(w, h);
16744 * Removes the passed window resize listener.
16745 * @param {Function} fn The method the event invokes
16746 * @param {Object} scope The scope of handler
16748 removeResizeListener: function(fn, scope){
16749 if (this.resizeEvent) {
16750 this.resizeEvent.removeListener(fn, scope);
16754 onWindowUnload: function() {
16755 var unload = this.unloadEvent;
16757 this.unloadEvent = unload = new Ext.util.Event();
16758 this.addListener(window, 'unload', this.fireUnload, this);
16763 * Fires the unload event for items bound with onWindowUnload
16766 fireUnload: function() {
16767 // wrap in a try catch, could have some problems during unload
16769 this.removeUnloadListener();
16770 // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
16771 if (Ext.isGecko3) {
16772 var gridviews = Ext.ComponentQuery.query('gridview'),
16774 ln = gridviews.length;
16775 for (; i < ln; i++) {
16776 gridviews[i].scrollToTop();
16779 // Purge all elements in the cache
16782 for (el in cache) {
16783 if (cache.hasOwnProperty(el)) {
16784 Ext.EventManager.removeAll(el);
16792 * Removes the passed window unload listener.
16793 * @param {Function} fn The method the event invokes
16794 * @param {Object} scope The scope of handler
16796 removeUnloadListener: function(){
16797 if (this.unloadEvent) {
16798 this.removeListener(window, 'unload', this.fireUnload);
16803 * note 1: IE fires ONLY the keydown event on specialkey autorepeat
16804 * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
16805 * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
16808 useKeyDown: Ext.isWebKit ?
16809 parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
16810 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
16813 * Indicates which event to use for getting key presses.
16814 * @return {String} The appropriate event name.
16816 getKeyEvent: function(){
16817 return this.useKeyDown ? 'keydown' : 'keypress';
16822 * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
16826 Ext.onReady = function(fn, scope, options) {
16827 Ext.Loader.onReady(fn, scope, true, options);
16831 * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
16833 * @method onDocumentReady
16835 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
16838 * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
16839 * @member Ext.EventManager
16842 Ext.EventManager.on = Ext.EventManager.addListener;
16845 * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
16846 * @member Ext.EventManager
16849 Ext.EventManager.un = Ext.EventManager.removeListener;
16852 var initExtCss = function() {
16853 // find the body element
16854 var bd = document.body || document.getElementsByTagName('body')[0],
16855 baseCSSPrefix = Ext.baseCSSPrefix,
16864 html = bd.parentNode;
16866 //Let's keep this human readable!
16868 cls.push(baseCSSPrefix + 'ie');
16871 cls.push(baseCSSPrefix + 'ie6');
16874 cls.push(baseCSSPrefix + 'ie7');
16877 cls.push(baseCSSPrefix + 'ie8');
16880 cls.push(baseCSSPrefix + 'ie9');
16883 cls.push(baseCSSPrefix + 'gecko');
16885 if (Ext.isGecko3) {
16886 cls.push(baseCSSPrefix + 'gecko3');
16888 if (Ext.isGecko4) {
16889 cls.push(baseCSSPrefix + 'gecko4');
16892 cls.push(baseCSSPrefix + 'opera');
16894 if (Ext.isWebKit) {
16895 cls.push(baseCSSPrefix + 'webkit');
16897 if (Ext.isSafari) {
16898 cls.push(baseCSSPrefix + 'safari');
16900 if (Ext.isSafari2) {
16901 cls.push(baseCSSPrefix + 'safari2');
16903 if (Ext.isSafari3) {
16904 cls.push(baseCSSPrefix + 'safari3');
16906 if (Ext.isSafari4) {
16907 cls.push(baseCSSPrefix + 'safari4');
16909 if (Ext.isChrome) {
16910 cls.push(baseCSSPrefix + 'chrome');
16913 cls.push(baseCSSPrefix + 'mac');
16916 cls.push(baseCSSPrefix + 'linux');
16918 if (!Ext.supports.CSS3BorderRadius) {
16919 cls.push(baseCSSPrefix + 'nbr');
16921 if (!Ext.supports.CSS3LinearGradient) {
16922 cls.push(baseCSSPrefix + 'nlg');
16924 if (!Ext.scopeResetCSS) {
16925 cls.push(baseCSSPrefix + 'reset');
16928 // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
16930 if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
16931 Ext.isBorderBox = false;
16934 Ext.isBorderBox = true;
16937 htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
16938 if (!Ext.isStrict) {
16939 htmlCls.push(baseCSSPrefix + 'quirks');
16940 if (Ext.isIE && !Ext.isStrict) {
16941 Ext.isIEQuirks = true;
16944 Ext.fly(html, '_internal').addCls(htmlCls);
16947 Ext.fly(bd, '_internal').addCls(cls);
16951 Ext.onReady(initExtCss);
16955 * @class Ext.EventObject
16957 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
16958 wraps the browser's native event-object normalizing cross-browser differences,
16959 such as which mouse button is clicked, keys pressed, mechanisms to stop
16960 event-propagation along with a method to prevent default actions from taking place.
16964 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
16965 e.preventDefault();
16966 var target = e.getTarget(); // same as t (the target HTMLElement)
16970 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.core.Element}
16971 myDiv.on( // 'on' is shorthand for addListener
16972 "click", // perform an action on click of myDiv
16973 handleClick // reference to the action handler
16976 // other methods to do the same:
16977 Ext.EventManager.on("myDiv", 'click', handleClick);
16978 Ext.EventManager.addListener("myDiv", 'click', handleClick);
16983 Ext.define('Ext.EventObjectImpl', {
16984 uses: ['Ext.util.Point'],
16986 /** Key constant @type Number */
16988 /** Key constant @type Number */
16990 /** Key constant @type Number */
16992 /** Key constant @type Number */
16994 /** Key constant @type Number */
16996 /** Key constant @type Number */
16998 /** Key constant @type Number */
17000 /** Key constant @type Number */
17002 /** Key constant @type Number */
17004 /** Key constant @type Number */
17006 /** Key constant @type Number */
17008 /** Key constant @type Number */
17010 /** Key constant @type Number */
17012 /** Key constant @type Number */
17014 /** Key constant @type Number */
17016 /** Key constant @type Number */
17018 /** Key constant @type Number */
17020 /** Key constant @type Number */
17022 /** Key constant @type Number */
17024 /** Key constant @type Number */
17026 /** Key constant @type Number */
17028 /** Key constant @type Number */
17030 /** Key constant @type Number */
17032 /** Key constant @type Number */
17034 /** Key constant @type Number */
17036 /** Key constant @type Number */
17038 /** Key constant @type Number */
17040 /** Key constant @type Number */
17042 /** Key constant @type Number */
17044 /** Key constant @type Number */
17046 /** Key constant @type Number */
17048 /** Key constant @type Number */
17050 /** Key constant @type Number */
17052 /** Key constant @type Number */
17054 /** Key constant @type Number */
17056 /** Key constant @type Number */
17058 /** Key constant @type Number */
17060 /** Key constant @type Number */
17062 /** Key constant @type Number */
17064 /** Key constant @type Number */
17066 /** Key constant @type Number */
17068 /** Key constant @type Number */
17070 /** Key constant @type Number */
17072 /** Key constant @type Number */
17074 /** Key constant @type Number */
17076 /** Key constant @type Number */
17078 /** Key constant @type Number */
17080 /** Key constant @type Number */
17082 /** Key constant @type Number */
17084 /** Key constant @type Number */
17086 /** Key constant @type Number */
17088 /** Key constant @type Number */
17090 /** Key constant @type Number */
17092 /** Key constant @type Number */
17094 /** Key constant @type Number */
17096 /** Key constant @type Number */
17098 /** Key constant @type Number */
17100 /** Key constant @type Number */
17102 /** Key constant @type Number */
17104 /** Key constant @type Number */
17106 /** Key constant @type Number */
17108 /** Key constant @type Number */
17110 /** Key constant @type Number */
17112 /** Key constant @type Number */
17114 /** Key constant @type Number */
17116 /** Key constant @type Number */
17118 /** Key constant @type Number */
17120 /** Key constant @type Number */
17122 /** Key constant @type Number */
17124 /** Key constant @type Number */
17126 /** Key constant @type Number */
17128 /** Key constant @type Number */
17130 /** Key constant @type Number */
17132 /** Key constant @type Number */
17134 /** Key constant @type Number */
17136 /** Key constant @type Number */
17138 /** Key constant @type Number */
17140 /** Key constant @type Number */
17142 /** Key constant @type Number */
17144 /** Key constant @type Number */
17146 /** Key constant @type Number */
17148 /** Key constant @type Number */
17150 /** Key constant @type Number */
17152 /** Key constant @type Number */
17154 /** Key constant @type Number */
17156 /** Key constant @type Number */
17158 /** Key constant @type Number */
17162 * Simple click regex
17165 clickRe: /(dbl)?click/,
17166 // safari keypress events for special keys return bad keycodes
17170 63235: 39, // right
17173 63276: 33, // page up
17174 63277: 34, // page down
17175 63272: 46, // delete
17179 // normalize button clicks, don't see any way to feature detect this.
17180 btnMap: Ext.isIE ? {
17190 constructor: function(event, freezeEvent){
17192 this.setEvent(event.browserEvent || event, freezeEvent);
17196 setEvent: function(event, freezeEvent){
17197 var me = this, button, options;
17199 if (event == me || (event && event.browserEvent)) { // already wrapped
17202 me.browserEvent = event;
17204 // normalize buttons
17205 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
17206 if (me.clickRe.test(event.type) && button == -1) {
17212 shiftKey: event.shiftKey,
17213 // mac metaKey behaves like ctrlKey
17214 ctrlKey: event.ctrlKey || event.metaKey || false,
17215 altKey: event.altKey,
17216 // in getKey these will be normalized for the mac
17217 keyCode: event.keyCode,
17218 charCode: event.charCode,
17219 // cache the targets for the delayed and or buffered events
17220 target: Ext.EventManager.getTarget(event),
17221 relatedTarget: Ext.EventManager.getRelatedTarget(event),
17222 currentTarget: event.currentTarget,
17223 xy: (freezeEvent ? me.getXY() : null)
17237 Ext.apply(me, options);
17242 * Stop the event (preventDefault and stopPropagation)
17244 stopEvent: function(){
17245 this.stopPropagation();
17246 this.preventDefault();
17250 * Prevents the browsers default handling of the event.
17252 preventDefault: function(){
17253 if (this.browserEvent) {
17254 Ext.EventManager.preventDefault(this.browserEvent);
17259 * Cancels bubbling of the event.
17261 stopPropagation: function(){
17262 var browserEvent = this.browserEvent;
17264 if (browserEvent) {
17265 if (browserEvent.type == 'mousedown') {
17266 Ext.EventManager.stoppedMouseDownEvent.fire(this);
17268 Ext.EventManager.stopPropagation(browserEvent);
17273 * Gets the character code for the event.
17276 getCharCode: function(){
17277 return this.charCode || this.keyCode;
17281 * Returns a normalized keyCode for the event.
17282 * @return {Number} The key code
17284 getKey: function(){
17285 return this.normalizeKey(this.keyCode || this.charCode);
17289 * Normalize key codes across browsers
17291 * @param {Number} key The key code
17292 * @return {Number} The normalized code
17294 normalizeKey: function(key){
17295 // can't feature detect this
17296 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
17300 * Gets the x coordinate of the event.
17302 * @deprecated 4.0 Replaced by {@link #getX}
17304 getPageX: function(){
17305 return this.getX();
17309 * Gets the y coordinate of the event.
17311 * @deprecated 4.0 Replaced by {@link #getY}
17313 getPageY: function(){
17314 return this.getY();
17318 * Gets the x coordinate of the event.
17322 return this.getXY()[0];
17326 * Gets the y coordinate of the event.
17330 return this.getXY()[1];
17334 * Gets the page coordinates of the event.
17335 * @return {Array} The xy values like [x, y]
17337 getXY: function() {
17340 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
17346 * Gets the target for the event.
17347 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
17348 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
17349 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
17350 * @return {HTMLelement}
17352 getTarget : function(selector, maxDepth, returnEl){
17354 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
17356 return returnEl ? Ext.get(this.target) : this.target;
17360 * Gets the related target.
17361 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
17362 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
17363 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
17364 * @return {HTMLElement}
17366 getRelatedTarget : function(selector, maxDepth, returnEl){
17368 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
17370 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
17374 * Normalizes mouse wheel delta across browsers
17375 * @return {Number} The delta
17377 getWheelDelta : function(){
17378 var event = this.browserEvent,
17381 if (event.wheelDelta) { /* IE/Opera. */
17382 delta = event.wheelDelta / 120;
17383 } else if (event.detail){ /* Mozilla case. */
17384 delta = -event.detail / 3;
17390 * 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.
17391 * Example usage:<pre><code>
17392 // Handle click on any child of an element
17393 Ext.getBody().on('click', function(e){
17394 if(e.within('some-el')){
17395 alert('Clicked on a child of some-el!');
17399 // Handle click directly on an element, ignoring clicks on child nodes
17400 Ext.getBody().on('click', function(e,t){
17401 if((t.id == 'some-el') && !e.within(t, true)){
17402 alert('Clicked directly on some-el!');
17406 * @param {Mixed} el The id, DOM element or Ext.core.Element to check
17407 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
17408 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
17409 * @return {Boolean}
17411 within : function(el, related, allowEl){
17413 var t = related ? this.getRelatedTarget() : this.getTarget(),
17417 result = Ext.fly(el).contains(t);
17418 if (!result && allowEl) {
17419 result = t == Ext.getDom(el);
17428 * Checks if the key pressed was a "navigation" key
17429 * @return {Boolean} True if the press is a navigation keypress
17431 isNavKeyPress : function(){
17433 k = this.normalizeKey(me.keyCode);
17435 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
17442 * Checks if the key pressed was a "special" key
17443 * @return {Boolean} True if the press is a special keypress
17445 isSpecialKey : function(){
17446 var k = this.normalizeKey(this.keyCode);
17447 return (this.type == 'keypress' && this.ctrlKey) ||
17448 this.isNavKeyPress() ||
17449 (k == this.BACKSPACE) || // Backspace
17450 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
17451 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
17455 * Returns a point object that consists of the object coordinates.
17456 * @return {Ext.util.Point} point
17458 getPoint : function(){
17459 var xy = this.getXY();
17460 return Ext.create('Ext.util.Point', xy[0], xy[1]);
17464 * Returns true if the control, meta, shift or alt key was pressed during this event.
17465 * @return {Boolean}
17467 hasModifier : function(){
17468 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
17472 * Injects a DOM event using the data in this object and (optionally) a new target.
17473 * This is a low-level technique and not likely to be used by application code. The
17474 * currently supported event types are:
17475 * <p><b>HTMLEvents</b></p>
17486 * <p><b>MouseEvents</b></p>
17489 * <li>dblclick</li>
17490 * <li>mousedown</li>
17492 * <li>mouseover</li>
17493 * <li>mousemove</li>
17494 * <li>mouseout</li>
17496 * <p><b>UIEvents</b></p>
17499 * <li>focusout</li>
17500 * <li>activate</li>
17504 * @param {Element/HTMLElement} target If specified, the target for the event. This
17505 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
17506 * is used to determine the target.
17508 injectEvent: function () {
17510 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
17512 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
17514 // IE9 has createEvent, but this code causes major problems with htmleditor (it
17515 // blocks all mouse events and maybe more). TODO
17517 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
17519 createHtmlEvent: function (doc, type, bubbles, cancelable) {
17520 var event = doc.createEvent('HTMLEvents');
17522 event.initEvent(type, bubbles, cancelable);
17526 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
17527 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
17528 button, relatedTarget) {
17529 var event = doc.createEvent('MouseEvents'),
17530 view = doc.defaultView || window;
17532 if (event.initMouseEvent) {
17533 event.initMouseEvent(type, bubbles, cancelable, view, detail,
17534 clientX, clientY, clientX, clientY, ctrlKey, altKey,
17535 shiftKey, metaKey, button, relatedTarget);
17536 } else { // old Safari
17537 event = doc.createEvent('UIEvents');
17538 event.initEvent(type, bubbles, cancelable);
17540 event.detail = detail;
17541 event.screenX = clientX;
17542 event.screenY = clientY;
17543 event.clientX = clientX;
17544 event.clientY = clientY;
17545 event.ctrlKey = ctrlKey;
17546 event.altKey = altKey;
17547 event.metaKey = metaKey;
17548 event.shiftKey = shiftKey;
17549 event.button = button;
17550 event.relatedTarget = relatedTarget;
17556 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
17557 var event = doc.createEvent('UIEvents'),
17558 view = doc.defaultView || window;
17560 event.initUIEvent(type, bubbles, cancelable, view, detail);
17564 fireEvent: function (target, type, event) {
17565 target.dispatchEvent(event);
17568 fixTarget: function (target) {
17569 // Safari3 doesn't have window.dispatchEvent()
17570 if (target == window && !target.dispatchEvent) {
17577 } else if (document.createEventObject) { // else if (IE)
17578 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
17581 createHtmlEvent: function (doc, type, bubbles, cancelable) {
17582 var event = doc.createEventObject();
17583 event.bubbles = bubbles;
17584 event.cancelable = cancelable;
17588 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
17589 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
17590 button, relatedTarget) {
17591 var event = doc.createEventObject();
17592 event.bubbles = bubbles;
17593 event.cancelable = cancelable;
17594 event.detail = detail;
17595 event.screenX = clientX;
17596 event.screenY = clientY;
17597 event.clientX = clientX;
17598 event.clientY = clientY;
17599 event.ctrlKey = ctrlKey;
17600 event.altKey = altKey;
17601 event.shiftKey = shiftKey;
17602 event.metaKey = metaKey;
17603 event.button = crazyIEButtons[button] || button;
17604 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
17608 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
17609 var event = doc.createEventObject();
17610 event.bubbles = bubbles;
17611 event.cancelable = cancelable;
17615 fireEvent: function (target, type, event) {
17616 target.fireEvent('on' + type, event);
17619 fixTarget: function (target) {
17620 if (target == document) {
17621 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
17622 // IE6,IE7 cannot properly call document.fireEvent()
17623 return document.documentElement;
17635 load: [false, false],
17636 unload: [false, false],
17637 select: [true, false],
17638 change: [true, false],
17639 submit: [true, true],
17640 reset: [true, false],
17641 resize: [true, false],
17642 scroll: [true, false]
17644 function (name, value) {
17645 var bubbles = value[0], cancelable = value[1];
17646 dispatchers[name] = function (targetEl, srcEvent) {
17647 var e = API.createHtmlEvent(name, bubbles, cancelable);
17648 API.fireEvent(targetEl, name, e);
17655 function createMouseEventDispatcher (type, detail) {
17656 var cancelable = (type != 'mousemove');
17657 return function (targetEl, srcEvent) {
17658 var xy = srcEvent.getXY(),
17659 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
17660 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
17661 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
17662 srcEvent.relatedTarget);
17663 API.fireEvent(targetEl, type, e);
17667 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
17668 function (eventName) {
17669 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
17676 focusin: [true, false],
17677 focusout: [true, false],
17678 activate: [true, true],
17679 focus: [false, false],
17680 blur: [false, false]
17682 function (name, value) {
17683 var bubbles = value[0], cancelable = value[1];
17684 dispatchers[name] = function (targetEl, srcEvent) {
17685 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
17686 API.fireEvent(targetEl, name, e);
17692 // not even sure what ancient browsers fall into this category...
17694 dispatchers = {}; // never mind all those we just built :P
17697 fixTarget: function (t) {
17703 function cannotInject (target, srcEvent) {
17704 // TODO log something
17707 return function (target) {
17709 dispatcher = dispatchers[me.type] || cannotInject,
17710 t = target ? (target.dom || target) : me.getTarget();
17712 t = API.fixTarget(t);
17715 }() // call to produce method
17719 Ext.EventObject = new Ext.EventObjectImpl();
17725 * @class Ext.core.Element
17728 var doc = document,
17729 isCSS1 = doc.compatMode == "CSS1Compat",
17730 ELEMENT = Ext.core.Element,
17731 fly = function(el){
17733 _fly = new Ext.core.Element.Flyweight();
17739 Ext.apply(ELEMENT, {
17740 isAncestor : function(p, c) {
17747 return p.contains(c);
17748 } else if (p.compareDocumentPosition) {
17749 return !!(p.compareDocumentPosition(c) & 16);
17751 while ((c = c.parentNode)) {
17752 ret = c == p || ret;
17759 getViewWidth : function(full) {
17760 return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
17763 getViewHeight : function(full) {
17764 return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
17767 getDocumentHeight: function() {
17768 return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
17771 getDocumentWidth: function() {
17772 return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
17775 getViewportHeight: function(){
17777 (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
17781 getViewportWidth : function() {
17782 return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
17783 Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
17786 getY : function(el) {
17787 return ELEMENT.getXY(el)[1];
17790 getX : function(el) {
17791 return ELEMENT.getXY(el)[0];
17794 getXY : function(el) {
17805 bd = (doc.body || doc.documentElement),
17808 el = Ext.getDom(el);
17811 hasAbsolute = fly(el).isStyle("position", "absolute");
17813 if (el.getBoundingClientRect) {
17814 b = el.getBoundingClientRect();
17815 scroll = fly(document).getScroll();
17816 ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
17825 hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
17828 y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
17829 x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
17831 if (p != el && !pe.isStyle('overflow','visible')) {
17836 p = p.offsetParent;
17839 if (Ext.isSafari && hasAbsolute) {
17840 x -= bd.offsetLeft;
17844 if (Ext.isGecko && !hasAbsolute) {
17846 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
17847 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
17851 while (p && p != bd) {
17852 if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
17864 setXY : function(el, xy) {
17865 (el = Ext.fly(el, '_setXY')).position();
17867 var pts = el.translatePoints(xy),
17868 style = el.dom.style,
17872 if (!isNaN(pts[pos])) {
17873 style[pos] = pts[pos] + "px";
17878 setX : function(el, x) {
17879 ELEMENT.setXY(el, [x, false]);
17882 setY : function(el, y) {
17883 ELEMENT.setXY(el, [false, y]);
17887 * Serializes a DOM form into a url encoded string
17888 * @param {Object} form The form
17889 * @return {String} The url encoded form
17891 serializeForm: function(form) {
17892 var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
17894 encoder = encodeURIComponent,
17900 Ext.each(fElements, function(element){
17901 name = element.name;
17902 type = element.type;
17904 if (!element.disabled && name) {
17905 if (/select-(one|multiple)/i.test(type)) {
17906 Ext.each(element.options, function(opt){
17907 if (opt.selected) {
17908 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
17909 data += String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
17912 } else if (!(/file|undefined|reset|button/i.test(type))) {
17913 if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
17914 data += encoder(name) + '=' + encoder(element.value) + '&';
17915 hasSubmit = /submit/i.test(type);
17920 return data.substr(0, data.length - 1);
17926 * @class Ext.core.Element
17929 Ext.core.Element.addMethods({
17932 * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
17933 * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
17934 * back in, the function is not called.
17935 * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
17936 * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
17937 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
17938 * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
17939 // Hide the menu if the mouse moves out for 250ms or more
17940 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
17943 // Remove mouseleave monitor on menu destroy
17944 this.menuEl.un(this.mouseLeaveMonitor);
17947 monitorMouseLeave: function(delay, handler, scope) {
17951 mouseleave: function(e) {
17952 timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
17954 mouseenter: function() {
17955 clearTimeout(timer);
17965 * Stops the specified event(s) from bubbling and optionally prevents the default action
17966 * @param {String/Array} eventName an event / array of events to stop from bubbling
17967 * @param {Boolean} preventDefault (optional) true to prevent the default action too
17968 * @return {Ext.core.Element} this
17970 swallowEvent : function(eventName, preventDefault) {
17973 e.stopPropagation();
17974 if (preventDefault) {
17975 e.preventDefault();
17979 if (Ext.isArray(eventName)) {
17980 Ext.each(eventName, function(e) {
17985 me.on(eventName, fn);
17990 * Create an event handler on this element such that when the event fires and is handled by this element,
17991 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
17992 * @param {String} eventName The type of event to relay
17993 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
17994 * for firing the relayed event
17996 relayEvent : function(eventName, observable) {
17997 this.on(eventName, function(e) {
17998 observable.fireEvent(eventName, e);
18003 * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
18004 * @param {Boolean} forceReclean (optional) By default the element
18005 * keeps track if it has been cleaned already so
18006 * you can call this over and over. However, if you update the element and
18007 * need to force a reclean, you can pass true.
18009 clean : function(forceReclean) {
18012 n = dom.firstChild,
18016 if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
18021 nx = n.nextSibling;
18022 if (n.nodeType == 3) {
18023 // Remove empty/whitespace text nodes
18024 if (!(/\S/.test(n.nodeValue))) {
18025 dom.removeChild(n);
18026 // Combine adjacent text nodes
18027 } else if (nx && nx.nodeType == 3) {
18028 n.appendData(Ext.String.trim(nx.data));
18029 dom.removeChild(nx);
18030 nx = n.nextSibling;
18031 n.nodeIndex = ++ni;
18034 // Recursively clean
18035 Ext.fly(n).clean();
18036 n.nodeIndex = ++ni;
18041 Ext.core.Element.data(dom, 'isCleaned', true);
18046 * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
18047 * parameter as {@link Ext.ElementLoader#load}
18048 * @return {Ext.core.Element} this
18050 load : function(options) {
18051 this.getLoader().load(options);
18056 * Gets this element's {@link Ext.ElementLoader ElementLoader}
18057 * @return {Ext.ElementLoader} The loader
18059 getLoader : function() {
18060 var dom = this.dom,
18061 data = Ext.core.Element.data,
18062 loader = data(dom, 'loader');
18065 loader = Ext.create('Ext.ElementLoader', {
18068 data(dom, 'loader', loader);
18074 * Update the innerHTML of this element, optionally searching for and processing scripts
18075 * @param {String} html The new HTML
18076 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
18077 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
18078 * @return {Ext.core.Element} this
18080 update : function(html, loadScripts, callback) {
18092 if (loadScripts !== true) {
18093 dom.innerHTML = html;
18094 Ext.callback(callback, me);
18099 html += '<span id="' + id + '"></span>';
18101 interval = setInterval(function(){
18102 if (!document.getElementById(id)) {
18105 clearInterval(interval);
18106 var DOC = document,
18107 hd = DOC.getElementsByTagName("head")[0],
18108 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
18109 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
18110 typeRe = /\stype=([\'\"])(.*?)\1/i,
18118 while ((match = re.exec(html))) {
18120 srcMatch = attrs ? attrs.match(srcRe) : false;
18121 if (srcMatch && srcMatch[2]) {
18122 s = DOC.createElement("script");
18123 s.src = srcMatch[2];
18124 typeMatch = attrs.match(typeRe);
18125 if (typeMatch && typeMatch[2]) {
18126 s.type = typeMatch[2];
18129 } else if (match[2] && match[2].length > 0) {
18130 if (window.execScript) {
18131 window.execScript(match[2]);
18133 window.eval(match[2]);
18138 el = DOC.getElementById(id);
18140 Ext.removeNode(el);
18142 Ext.callback(callback, me);
18144 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
18148 // inherit docs, overridden so we can add removeAnchor
18149 removeAllListeners : function() {
18150 this.removeAnchor();
18151 Ext.EventManager.removeAll(this.dom);
18156 * Creates a proxy element of this element
18157 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
18158 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
18159 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
18160 * @return {Ext.core.Element} The new proxy element
18162 createProxy : function(config, renderTo, matchBox) {
18163 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
18166 proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
18167 Ext.core.DomHelper.insertBefore(me.dom, config, true);
18169 proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
18171 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
18172 proxy.setBox(me.getBox());
18177 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
18180 * @class Ext.core.Element
18182 Ext.core.Element.addMethods({
18184 * Gets the x,y coordinates specified by the anchor position on the element.
18185 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
18186 * for details on supported anchor positions.
18187 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
18188 * of page coordinates
18189 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
18190 * {width: (target width), height: (target height)} (defaults to the element's current size)
18191 * @return {Array} [x, y] An array containing the element's x and y coordinates
18193 getAnchorXY : function(anchor, local, s){
18194 //Passing a different size is useful for pre-calculating anchors,
18195 //especially for anchored animations that change the el size.
18196 anchor = (anchor || "tl").toLowerCase();
18200 vp = me.dom == document.body || me.dom == document,
18201 w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
18202 h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
18206 scroll = me.getScroll(),
18207 extraX = vp ? scroll.left : !local ? o[0] : 0,
18208 extraY = vp ? scroll.top : !local ? o[1] : 0,
18210 c : [r(w * 0.5), r(h * 0.5)],
18211 t : [r(w * 0.5), 0],
18212 l : [0, r(h * 0.5)],
18213 r : [w, r(h * 0.5)],
18214 b : [r(w * 0.5), h],
18222 return [xy[0] + extraX, xy[1] + extraY];
18226 * Anchors an element to another element and realigns it when the window is resized.
18227 * @param {Mixed} element The element to align to.
18228 * @param {String} position The position to align to.
18229 * @param {Array} offsets (optional) Offset the positioning by [x, y]
18230 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
18231 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
18232 * is a number, it is used as the buffer delay (defaults to 50ms).
18233 * @param {Function} callback The function to call after the animation finishes
18234 * @return {Ext.core.Element} this
18236 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
18239 scroll = !Ext.isEmpty(monitorScroll),
18240 action = function(){
18241 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
18242 Ext.callback(callback, Ext.fly(dom));
18244 anchor = this.getAnchor();
18246 // previous listener anchor, remove it
18247 this.removeAnchor();
18248 Ext.apply(anchor, {
18253 Ext.EventManager.onWindowResize(action, null);
18256 Ext.EventManager.on(window, 'scroll', action, null,
18257 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
18259 action.call(me); // align immediately
18264 * Remove any anchor to this element. See {@link #anchorTo}.
18265 * @return {Ext.core.Element} this
18267 removeAnchor : function(){
18269 anchor = this.getAnchor();
18271 if(anchor && anchor.fn){
18272 Ext.EventManager.removeResizeListener(anchor.fn);
18274 Ext.EventManager.un(window, 'scroll', anchor.fn);
18282 getAnchor : function(){
18283 var data = Ext.core.Element.data,
18288 var anchor = data(dom, '_anchor');
18291 anchor = data(dom, '_anchor', {});
18296 getAlignVector: function(el, spec, offset) {
18298 side = {t:"top", l:"left", r:"right", b: "bottom"},
18299 thisRegion = me.getRegion(),
18303 if(!el || !el.dom){
18305 sourceClass: 'Ext.core.Element',
18306 sourceMethod: 'getAlignVector',
18307 msg: 'Attempted to align an element that doesn\'t exist'
18311 elRegion = el.getRegion();
18315 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
18316 * supported position values.
18317 * @param {Mixed} element The element to align to.
18318 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
18319 * @param {Array} offsets (optional) Offset the positioning by [x, y]
18320 * @return {Array} [x, y]
18322 getAlignToXY : function(el, p, o){
18325 if(!el || !el.dom){
18327 sourceClass: 'Ext.core.Element',
18328 sourceMethod: 'getAlignToXY',
18329 msg: 'Attempted to align an element that doesn\'t exist'
18334 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
18342 //constrain the aligned el to viewport if necessary
18346 dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
18347 dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
18355 docElement = doc.documentElement,
18356 docBody = doc.body,
18357 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
18358 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
18359 c = false, //constrain to viewport
18362 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
18366 sourceClass: 'Ext.core.Element',
18367 sourceMethod: 'getAlignToXY',
18371 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
18379 //Subtract the aligned el's internal xy from the target's offset xy
18380 //plus custom offset to get the aligned el's new offset xy
18381 a1 = me.getAnchorXY(p1, true);
18382 a2 = el.getAnchorXY(p2, false);
18384 x = a2[0] - a1[0] + o[0];
18385 y = a2[1] - a1[1] + o[1];
18389 h = me.getHeight();
18390 r = el.getRegion();
18391 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
18392 //perpendicular to the vp border, allow the aligned el to slide on that border,
18393 //otherwise swap the aligned el to the opposite border of the target.
18394 p1y = p1.charAt(0);
18395 p1x = p1.charAt(p1.length-1);
18396 p2y = p2.charAt(0);
18397 p2x = p2.charAt(p2.length-1);
18398 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
18399 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
18402 if (x + w > dw + scrollX) {
18403 x = swapX ? r.left-w : dw+scrollX-w;
18406 x = swapX ? r.right : scrollX;
18408 if (y + h > dh + scrollY) {
18409 y = swapY ? r.top-h : dh+scrollY-h;
18412 y = swapY ? r.bottom : scrollY;
18419 * Aligns this element with another element relative to the specified anchor points. If the other element is the
18420 * document it aligns it to the viewport.
18421 * The position parameter is optional, and can be specified in any one of the following formats:
18423 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
18424 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
18425 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
18426 * deprecated in favor of the newer two anchor syntax below</i>.</li>
18427 * <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
18428 * element's anchor point, and the second value is used as the target's anchor point.</li>
18430 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
18431 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
18432 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
18433 * that specified in order to enforce the viewport constraints.
18434 * Following are all of the supported anchor positions:
18437 ----- -----------------------------
18438 tl The top left corner (default)
18439 t The center of the top edge
18440 tr The top right corner
18441 l The center of the left edge
18442 c In the center of the element
18443 r The center of the right edge
18444 bl The bottom left corner
18445 b The center of the bottom edge
18446 br The bottom right corner
18450 // align el to other-el using the default positioning ("tl-bl", non-constrained)
18451 el.alignTo("other-el");
18453 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
18454 el.alignTo("other-el", "tr?");
18456 // align the bottom right corner of el with the center left edge of other-el
18457 el.alignTo("other-el", "br-l?");
18459 // align the center of el with the bottom left corner of other-el and
18460 // adjust the x position by -6 pixels (and the y position by 0)
18461 el.alignTo("other-el", "c-bl", [-6, 0]);
18463 * @param {Mixed} element The element to align to.
18464 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
18465 * @param {Array} offsets (optional) Offset the positioning by [x, y]
18466 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
18467 * @return {Ext.core.Element} this
18469 alignTo : function(element, position, offsets, animate){
18471 return me.setXY(me.getAlignToXY(element, position, offsets),
18472 me.anim && !!animate ? me.anim(animate) : false);
18475 // private ==> used outside of core
18476 adjustForConstraints : function(xy, parent) {
18477 var vector = this.getConstrainVector(parent, xy);
18479 xy[0] += vector[0];
18480 xy[1] += vector[1];
18486 * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
18487 * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
18488 * <p>Priority is given to constraining the top and left within the constraint.</p>
18489 * <p>The constraint may either be an existing element into which this element is to be constrained, or
18490 * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
18491 * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
18492 * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
18493 * of using this Element's current position;
18494 * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
18495 * vector by which this element must be translated. Otherwise, <code>false</code>.
18497 getConstrainVector: function(constrainTo, proposedPosition) {
18498 if (!(constrainTo instanceof Ext.util.Region)) {
18499 constrainTo = Ext.get(constrainTo).getViewRegion();
18501 var thisRegion = this.getRegion(),
18503 shadowSize = this.shadow && this.shadow.offset,
18504 overflowed = false;
18506 // Shift this region to occupy the proposed position
18507 if (proposedPosition) {
18508 thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
18511 // Reduce the constrain region to allow for shadow
18512 // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
18514 constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
18517 // Constrain the X coordinate by however much this Element overflows
18518 if (thisRegion.right > constrainTo.right) {
18520 vector[0] = (constrainTo.right - thisRegion.right); // overflowed the right
18522 if (thisRegion.left + vector[0] < constrainTo.left) {
18524 vector[0] = (constrainTo.left - thisRegion.left); // overflowed the left
18527 // Constrain the Y coordinate by however much this Element overflows
18528 if (thisRegion.bottom > constrainTo.bottom) {
18530 vector[1] = (constrainTo.bottom - thisRegion.bottom); // overflowed the bottom
18532 if (thisRegion.top + vector[1] < constrainTo.top) {
18534 vector[1] = (constrainTo.top - thisRegion.top); // overflowed the top
18536 return overflowed ? vector : false;
18540 * Calculates the x, y to center this element on the screen
18541 * @return {Array} The x, y values [x, y]
18543 getCenterXY : function(){
18544 return this.getAlignToXY(document, 'c-c');
18548 * Centers the Element in either the viewport, or another Element.
18549 * @param {Mixed} centerIn (optional) The element in which to center the element.
18551 center : function(centerIn){
18552 return this.alignTo(centerIn || document, 'c-c');
18557 * @class Ext.core.Element
18561 var ELEMENT = Ext.core.Element,
18566 POSITION = "position",
18568 RELATIVE = "relative",
18570 ZINDEX = "z-index";
18572 Ext.override(Ext.core.Element, {
18574 * 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).
18575 * @return {Number} The X position of the element
18578 return ELEMENT.getX(this.dom);
18582 * 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).
18583 * @return {Number} The Y position of the element
18586 return ELEMENT.getY(this.dom);
18590 * 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).
18591 * @return {Array} The XY position of the element
18593 getXY : function(){
18594 return ELEMENT.getXY(this.dom);
18598 * 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.
18599 * @param {Mixed} element The element to get the offsets from.
18600 * @return {Array} The XY page offsets (e.g. [100, -200])
18602 getOffsetsTo : function(el){
18603 var o = this.getXY(),
18604 e = Ext.fly(el, '_internal').getXY();
18605 return [o[0]-e[0],o[1]-e[1]];
18609 * 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).
18610 * @param {Number} The X position of the element
18611 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
18612 * @return {Ext.core.Element} this
18614 setX : function(x, animate){
18615 return this.setXY([x, this.getY()], animate);
18619 * 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).
18620 * @param {Number} The Y position of the element
18621 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
18622 * @return {Ext.core.Element} this
18624 setY : function(y, animate){
18625 return this.setXY([this.getX(), y], animate);
18629 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
18630 * @param {String} left The left CSS property value
18631 * @return {Ext.core.Element} this
18633 setLeft : function(left){
18634 this.setStyle(LEFT, this.addUnits(left));
18639 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
18640 * @param {String} top The top CSS property value
18641 * @return {Ext.core.Element} this
18643 setTop : function(top){
18644 this.setStyle(TOP, this.addUnits(top));
18649 * Sets the element's CSS right style.
18650 * @param {String} right The right CSS property value
18651 * @return {Ext.core.Element} this
18653 setRight : function(right){
18654 this.setStyle(RIGHT, this.addUnits(right));
18659 * Sets the element's CSS bottom style.
18660 * @param {String} bottom The bottom CSS property value
18661 * @return {Ext.core.Element} this
18663 setBottom : function(bottom){
18664 this.setStyle(BOTTOM, this.addUnits(bottom));
18669 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
18670 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
18671 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
18672 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
18673 * @return {Ext.core.Element} this
18675 setXY: function(pos, animate) {
18677 if (!animate || !me.anim) {
18678 ELEMENT.setXY(me.dom, pos);
18681 if (!Ext.isObject(animate)) {
18684 me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
18690 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
18691 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
18692 * @param {Number} x X value for new position (coordinates are page-based)
18693 * @param {Number} y Y value for new position (coordinates are page-based)
18694 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
18695 * @return {Ext.core.Element} this
18697 setLocation : function(x, y, animate){
18698 return this.setXY([x, y], animate);
18702 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
18703 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
18704 * @param {Number} x X value for new position (coordinates are page-based)
18705 * @param {Number} y Y value for new position (coordinates are page-based)
18706 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
18707 * @return {Ext.core.Element} this
18709 moveTo : function(x, y, animate){
18710 return this.setXY([x, y], animate);
18714 * Gets the left X coordinate
18715 * @param {Boolean} local True to get the local css position instead of page coordinate
18718 getLeft : function(local){
18719 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
18723 * Gets the right X coordinate of the element (element X position + element width)
18724 * @param {Boolean} local True to get the local css position instead of page coordinate
18727 getRight : function(local){
18729 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
18733 * Gets the top Y coordinate
18734 * @param {Boolean} local True to get the local css position instead of page coordinate
18737 getTop : function(local) {
18738 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
18742 * Gets the bottom Y coordinate of the element (element Y position + element height)
18743 * @param {Boolean} local True to get the local css position instead of page coordinate
18746 getBottom : function(local){
18748 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
18752 * Initializes positioning on this element. If a desired position is not passed, it will make the
18753 * the element positioned relative IF it is not already positioned.
18754 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
18755 * @param {Number} zIndex (optional) The zIndex to apply
18756 * @param {Number} x (optional) Set the page X position
18757 * @param {Number} y (optional) Set the page Y position
18759 position : function(pos, zIndex, x, y) {
18762 if (!pos && me.isStyle(POSITION, STATIC)){
18763 me.setStyle(POSITION, RELATIVE);
18765 me.setStyle(POSITION, pos);
18768 me.setStyle(ZINDEX, zIndex);
18771 me.setXY([x || false, y || false]);
18776 * Clear positioning back to the default when the document was loaded
18777 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
18778 * @return {Ext.core.Element} this
18780 clearPositioning : function(value){
18781 value = value || '';
18794 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
18795 * snapshot before performing an update and then restoring the element.
18798 getPositioning : function(){
18799 var l = this.getStyle(LEFT);
18800 var t = this.getStyle(TOP);
18802 "position" : this.getStyle(POSITION),
18804 "right" : l ? "" : this.getStyle(RIGHT),
18806 "bottom" : t ? "" : this.getStyle(BOTTOM),
18807 "z-index" : this.getStyle(ZINDEX)
18812 * Set positioning with an object returned by getPositioning().
18813 * @param {Object} posCfg
18814 * @return {Ext.core.Element} this
18816 setPositioning : function(pc){
18818 style = me.dom.style;
18822 if(pc.right == AUTO){
18825 if(pc.bottom == AUTO){
18833 * Translates the passed page coordinates into left/top css values for this element
18834 * @param {Number/Array} x The page x or an array containing [x, y]
18835 * @param {Number} y (optional) The page y, required if x is not an array
18836 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
18838 translatePoints: function(x, y) {
18839 if (Ext.isArray(x)) {
18844 relative = me.isStyle(POSITION, RELATIVE),
18846 left = parseInt(me.getStyle(LEFT), 10),
18847 top = parseInt(me.getStyle(TOP), 10);
18849 if (!Ext.isNumber(left)) {
18850 left = relative ? 0 : me.dom.offsetLeft;
18852 if (!Ext.isNumber(top)) {
18853 top = relative ? 0 : me.dom.offsetTop;
18855 left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
18856 top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
18864 * 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.
18865 * @param {Object} box The box to fill {x, y, width, height}
18866 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
18867 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
18868 * @return {Ext.core.Element} this
18870 setBox: function(box, adjust, animate) {
18874 if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
18875 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
18876 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
18878 me.setBounds(box.x, box.y, w, h, animate);
18883 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
18884 * set another Element's size/location to match this element.
18885 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
18886 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
18887 * @return {Object} box An object in the format<pre><code>
18889 x: <Element's X position>,
18890 y: <Element's Y position>,
18891 width: <Element's width>,
18892 height: <Element's height>,
18893 bottom: <Element's lower bound>,
18894 right: <Element's rightmost bound>
18897 * The returned object may also be addressed as an Array where index 0 contains the X position
18898 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
18900 getBox: function(contentBox, local) {
18905 getBorderWidth = me.getBorderWidth,
18906 getPadding = me.getPadding,
18907 l, r, t, b, w, h, bx;
18911 left = parseInt(me.getStyle("left"), 10) || 0;
18912 top = parseInt(me.getStyle("top"), 10) || 0;
18916 h = me.getHeight();
18927 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
18928 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
18929 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
18930 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
18936 width: w - (l + r),
18937 height: h - (t + b)
18940 bx.right = bx.x + bx.width;
18941 bx.bottom = bx.y + bx.height;
18946 * Move this element relative to its current position.
18947 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
18948 * @param {Number} distance How far to move the element in pixels
18949 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
18950 * @return {Ext.core.Element} this
18952 move: function(direction, distance, animate) {
18957 left = [x - distance, y],
18958 right = [x + distance, y],
18959 top = [x, y - distance],
18960 bottom = [x, y + distance],
18974 direction = direction.toLowerCase();
18975 me.moveTo(hash[direction][0], hash[direction][1], animate);
18979 * Quick set left and top adding default units
18980 * @param {String} left The left CSS property value
18981 * @param {String} top The top CSS property value
18982 * @return {Ext.core.Element} this
18984 setLeftTop: function(left, top) {
18986 style = me.dom.style;
18987 style.left = me.addUnits(left);
18988 style.top = me.addUnits(top);
18993 * Returns the region of this element.
18994 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
18995 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
18997 getRegion: function() {
18998 return this.getPageBox(true);
19002 * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
19003 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
19005 getViewRegion: function() {
19007 isBody = me.dom === document.body,
19008 scroll, pos, top, left, width, height;
19010 // For the body we want to do some special logic
19012 scroll = me.getScroll();
19013 left = scroll.left;
19015 width = Ext.core.Element.getViewportWidth();
19016 height = Ext.core.Element.getViewportHeight();
19020 left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
19021 top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
19022 width = me.getWidth(true);
19023 height = me.getHeight(true);
19026 return Ext.create('Ext.util.Region', top, left + width, top + height, left);
19030 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
19031 * set another Element's size/location to match this element.
19032 * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
19033 * @return {Object} box An object in the format<pre><code>
19035 x: <Element's X position>,
19036 y: <Element's Y position>,
19037 width: <Element's width>,
19038 height: <Element's height>,
19039 bottom: <Element's lower bound>,
19040 right: <Element's rightmost bound>
19043 * The returned object may also be addressed as an Array where index 0 contains the X position
19044 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
19046 getPageBox : function(getRegion) {
19049 isDoc = el === document.body,
19050 w = isDoc ? Ext.core.Element.getViewWidth() : el.offsetWidth,
19051 h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
19059 return Ext.create('Ext.util.Region', t, r, b, l);
19074 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
19075 * @param {Number} x X value for new position (coordinates are page-based)
19076 * @param {Number} y Y value for new position (coordinates are page-based)
19077 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
19078 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
19079 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
19081 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
19082 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
19083 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
19085 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19086 * @return {Ext.core.Element} this
19088 setBounds: function(x, y, width, height, animate) {
19090 if (!animate || !me.anim) {
19091 me.setSize(width, height);
19092 me.setLocation(x, y);
19094 if (!Ext.isObject(animate)) {
19097 me.animate(Ext.applyIf({
19101 width: me.adjustWidth(width),
19102 height: me.adjustHeight(height)
19110 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
19111 * @param {Ext.util.Region} region The region to fill
19112 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19113 * @return {Ext.core.Element} this
19115 setRegion: function(region, animate) {
19116 return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
19122 * @class Ext.core.Element
19124 Ext.override(Ext.core.Element, {
19126 * Returns true if this element is scrollable.
19127 * @return {Boolean}
19129 isScrollable : function(){
19130 var dom = this.dom;
19131 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
19135 * Returns the current scroll position of the element.
19136 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
19138 getScroll : function() {
19142 docElement = doc.documentElement,
19147 if (d == doc || d == body) {
19148 if (Ext.isIE && Ext.isStrict) {
19149 l = docElement.scrollLeft;
19150 t = docElement.scrollTop;
19152 l = window.pageXOffset;
19153 t = window.pageYOffset;
19156 left: l || (body ? body.scrollLeft : 0),
19157 top : t || (body ? body.scrollTop : 0)
19161 left: d.scrollLeft,
19170 * 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().
19171 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
19172 * @param {Number} value The new scroll value
19173 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19174 * @return {Element} this
19176 scrollTo : function(side, value, animate) {
19177 //check if we're scrolling top or left
19178 var top = /top/i.test(side),
19183 if (!animate || !me.anim) {
19184 // just setting the value, so grab the direction
19185 prop = 'scroll' + (top ? 'Top' : 'Left');
19189 if (!Ext.isObject(animate)) {
19192 obj['scroll' + (top ? 'Top' : 'Left')] = value;
19193 me.animate(Ext.applyIf({
19201 * Scrolls this element into view within the passed container.
19202 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
19203 * string (id), dom node, or Ext.core.Element.
19204 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
19205 * @return {Ext.core.Element} this
19207 scrollIntoView : function(container, hscroll) {
19208 container = Ext.getDom(container) || Ext.getBody().dom;
19210 offsets = this.getOffsetsTo(container),
19212 left = offsets[0] + container.scrollLeft,
19213 top = offsets[1] + container.scrollTop,
19214 bottom = top + el.offsetHeight,
19215 right = left + el.offsetWidth,
19217 ctClientHeight = container.clientHeight,
19218 ctScrollTop = parseInt(container.scrollTop, 10),
19219 ctScrollLeft = parseInt(container.scrollLeft, 10),
19220 ctBottom = ctScrollTop + ctClientHeight,
19221 ctRight = ctScrollLeft + container.clientWidth;
19223 if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
19224 container.scrollTop = top;
19225 } else if (bottom > ctBottom) {
19226 container.scrollTop = bottom - ctClientHeight;
19228 // corrects IE, other browsers will ignore
19229 container.scrollTop = container.scrollTop;
19231 if (hscroll !== false) {
19232 if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
19233 container.scrollLeft = left;
19235 else if (right > ctRight) {
19236 container.scrollLeft = right - container.clientWidth;
19238 container.scrollLeft = container.scrollLeft;
19244 scrollChildIntoView : function(child, hscroll) {
19245 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
19249 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
19250 * within this element's scrollable range.
19251 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
19252 * @param {Number} distance How far to scroll the element in pixels
19253 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19254 * @return {Boolean} Returns true if a scroll was triggered or false if the element
19255 * was scrolled as far as it could go.
19257 scroll : function(direction, distance, animate) {
19258 if (!this.isScrollable()) {
19262 l = el.scrollLeft, t = el.scrollTop,
19263 w = el.scrollWidth, h = el.scrollHeight,
19264 cw = el.clientWidth, ch = el.clientHeight,
19265 scrolled = false, v,
19267 l: Math.min(l + distance, w-cw),
19268 r: v = Math.max(l - distance, 0),
19269 t: Math.max(t - distance, 0),
19270 b: Math.min(t + distance, h-ch)
19275 direction = direction.substr(0, 1);
19276 if ((v = hash[direction]) > -1) {
19278 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
19284 * @class Ext.core.Element
19286 Ext.core.Element.addMethods(
19288 var VISIBILITY = "visibility",
19289 DISPLAY = "display",
19292 XMASKED = Ext.baseCSSPrefix + "masked",
19293 XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
19294 data = Ext.core.Element.data;
19298 * Checks whether the element is currently visible using both visibility and display properties.
19299 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
19300 * @return {Boolean} True if the element is currently visible, else false
19302 isVisible : function(deep) {
19303 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
19304 p = this.dom.parentNode;
19306 if (deep !== true || !vis) {
19310 while (p && !(/^body/i.test(p.tagName))) {
19311 if (!Ext.fly(p, '_isVisible').isVisible()) {
19320 * Returns true if display is not "none"
19321 * @return {Boolean}
19323 isDisplayed : function() {
19324 return !this.isStyle(DISPLAY, NONE);
19328 * Convenience method for setVisibilityMode(Element.DISPLAY)
19329 * @param {String} display (optional) What to set display to when visible
19330 * @return {Ext.core.Element} this
19332 enableDisplayMode : function(display) {
19333 this.setVisibilityMode(Ext.core.Element.DISPLAY);
19335 if (!Ext.isEmpty(display)) {
19336 data(this.dom, 'originalDisplay', display);
19343 * Puts a mask over this element to disable user interaction. Requires core.css.
19344 * This method can only be applied to elements which accept child nodes.
19345 * @param {String} msg (optional) A message to display in the mask
19346 * @param {String} msgCls (optional) A css class to apply to the msg element
19347 * @return {Element} The mask element
19349 mask : function(msg, msgCls) {
19352 setExpression = dom.style.setExpression,
19353 dh = Ext.core.DomHelper,
19354 EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
19358 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
19359 me.addCls(XMASKEDRELATIVE);
19361 el = data(dom, 'maskMsg');
19365 el = data(dom, 'mask');
19370 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
19371 data(dom, 'mask', mask);
19373 me.addCls(XMASKED);
19374 mask.setDisplayed(true);
19376 if (typeof msg == 'string') {
19377 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
19378 data(dom, 'maskMsg', mm);
19379 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
19380 mm.dom.firstChild.innerHTML = msg;
19381 mm.setDisplayed(true);
19384 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
19385 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
19386 // In normal use cases an element will be masked for a limited period of time.
19387 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
19388 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
19389 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
19390 mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
19393 // Some versions and modes of IE subtract top+bottom padding when calculating height.
19394 // Different versions from those which make the same error for width!
19395 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
19396 mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
19398 // ie will not expand full height automatically
19399 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
19400 mask.setSize(undefined, me.getHeight());
19406 * Removes a previously applied mask.
19408 unmask : function() {
19411 mask = data(dom, 'mask'),
19412 maskMsg = data(dom, 'maskMsg');
19415 // Remove resource-intensive CSS expressions as soon as they are not required.
19416 if (mask.dom.style.clearExpression) {
19417 mask.dom.style.clearExpression('width');
19418 mask.dom.style.clearExpression('height');
19422 data(dom, 'maskMsg', undefined);
19426 data(dom, 'mask', undefined);
19427 me.removeCls([XMASKED, XMASKEDRELATIVE]);
19431 * Returns true if this element is masked. Also re-centers any displayed message within the mask.
19432 * @return {Boolean}
19434 isMasked : function() {
19436 mask = data(me.dom, 'mask'),
19437 maskMsg = data(me.dom, 'maskMsg');
19439 if (mask && mask.isVisible()) {
19441 maskMsg.center(me);
19449 * Creates an iframe shim for this element to keep selects and other windowed objects from
19451 * @return {Ext.core.Element} The new shim element
19453 createShim : function() {
19454 var el = document.createElement('iframe'),
19457 el.frameBorder = '0';
19458 el.className = Ext.baseCSSPrefix + 'shim';
19459 el.src = Ext.SSL_SECURE_URL;
19460 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
19461 shim.autoBoxAdjust = false;
19468 * @class Ext.core.Element
19470 Ext.core.Element.addMethods({
19472 * Convenience method for constructing a KeyMap
19473 * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
19474 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
19475 * @param {Function} fn The function to call
19476 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
19477 * @return {Ext.util.KeyMap} The KeyMap created
19479 addKeyListener : function(key, fn, scope){
19481 if(typeof key != 'object' || Ext.isArray(key)){
19497 return Ext.create('Ext.util.KeyMap', this, config);
19501 * Creates a KeyMap for this element
19502 * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
19503 * @return {Ext.util.KeyMap} The KeyMap created
19505 addKeyMap : function(config){
19506 return Ext.create('Ext.util.KeyMap', this, config);
19510 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
19511 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
19512 Ext.CompositeElementLite.importElementMethods();
19515 * @class Ext.CompositeElementLite
19517 Ext.apply(Ext.CompositeElementLite.prototype, {
19518 addElements : function(els, root){
19522 if(typeof els == "string"){
19523 els = Ext.core.Element.selectorFunction(els, root);
19525 var yels = this.elements;
19526 Ext.each(els, function(e) {
19527 yels.push(Ext.get(e));
19533 * Returns the first Element
19534 * @return {Ext.core.Element}
19536 first : function(){
19537 return this.item(0);
19541 * Returns the last Element
19542 * @return {Ext.core.Element}
19545 return this.item(this.getCount()-1);
19549 * Returns true if this composite contains the passed element
19550 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
19553 contains : function(el){
19554 return this.indexOf(el) != -1;
19558 * Removes the specified element(s).
19559 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
19560 * or an array of any of those.
19561 * @param {Boolean} removeDom (optional) True to also remove the element from the document
19562 * @return {CompositeElement} this
19564 removeElement : function(keys, removeDom){
19566 els = this.elements,
19568 Ext.each(keys, function(val){
19569 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
19574 Ext.removeNode(el);
19577 els.splice(val, 1);
19585 * @class Ext.CompositeElement
19586 * @extends Ext.CompositeElementLite
19587 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
19588 * members, or to perform collective actions upon the whole set.</p>
19589 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
19590 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
19591 * <p>All methods return <i>this</i> and can be chained.</p>
19594 var els = Ext.select("#some-el div.some-class", true);
19595 // or select directly from an existing element
19596 var el = Ext.get('some-el');
19597 el.select('div.some-class', true);
19599 els.setWidth(100); // all elements become 100 width
19600 els.hide(true); // all elements fade out and hide
19602 els.setWidth(100).hide(true);
19605 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
19607 constructor : function(els, root){
19608 this.elements = [];
19609 this.add(els, root);
19613 getElement : function(el){
19614 // In this case just return it, since we already have a reference to it
19619 transformElement : function(el){
19620 return Ext.get(el);
19624 * Adds elements to this composite.
19625 * @param {String/Array} els A string CSS selector, an array of elements or an element
19626 * @return {CompositeElement} this
19630 * Returns the Element object at the specified index
19631 * @param {Number} index
19632 * @return {Ext.core.Element}
19636 * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
19637 * @param {Function} fn
19639 The function to be called with each
19640 `element`. If the supplied function returns <tt>false</tt>,
19641 iteration stops. This function is called with the following arguments:
19643 - `element` : __Ext.core.Element++
19644 The element at the current `index` in the `composite`
19646 - `composite` : __Object__
19649 - `index` : __Number__
19650 The current index within the `composite`
19652 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
19653 * Defaults to the <code>element</code> at the current <code>index</code>
19654 * within the composite.
19655 * @return {CompositeElement} this
19661 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
19662 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
19663 * {@link Ext.CompositeElementLite CompositeElementLite} object.
19664 * @param {String/Array} selector The CSS selector or an array of elements
19665 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
19666 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
19667 * @return {CompositeElementLite/CompositeElement}
19668 * @member Ext.core.Element
19671 Ext.core.Element.select = function(selector, unique, root){
19673 if(typeof selector == "string"){
19674 els = Ext.core.Element.selectorFunction(selector, root);
19675 }else if(selector.length !== undefined){
19679 sourceClass: "Ext.core.Element",
19680 sourceMethod: "select",
19681 selector: selector,
19684 msg: "Invalid selector specified: " + selector
19687 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
19691 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
19692 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
19693 * {@link Ext.CompositeElementLite CompositeElementLite} object.
19694 * @param {String/Array} selector The CSS selector or an array of elements
19695 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
19696 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
19697 * @return {CompositeElementLite/CompositeElement}
19701 Ext.select = Ext.core.Element.select;
19705 Ext JS - JavaScript Library
19706 Copyright (c) 2006-2011, Sencha Inc.
19707 All rights reserved.
19708 licensing@sencha.com
19710 (function(){ var data = {
19711 "nameToAliasesMap":{
19712 "Ext.AbstractComponent":[""
19714 "Ext.AbstractManager":[""
19716 "Ext.AbstractPlugin":[""
19720 "Ext.ComponentLoader":[""
19722 "Ext.ComponentManager":[""
19724 "Ext.ComponentQuery":[""
19726 "Ext.ElementLoader":[""
19730 "Ext.ModelManager":[""
19732 "Ext.PluginManager":[""
19736 "Ext.XTemplate":[""
19738 "Ext.app.Application":[""
19740 "Ext.app.Controller":[""
19742 "Ext.app.EventBus":[""
19744 "Ext.chart.Callout":[""
19746 "Ext.chart.Chart":["widget.chart"
19748 "Ext.chart.Highlight":[""
19750 "Ext.chart.Label":[""
19752 "Ext.chart.Legend":[""
19754 "Ext.chart.LegendItem":[""
19756 "Ext.chart.Mask":[""
19758 "Ext.chart.MaskLayer":[""
19760 "Ext.chart.Navigation":[""
19762 "Ext.chart.Shape":[""
19764 "Ext.chart.Tip":[""
19766 "Ext.chart.TipSurface":[""
19768 "Ext.chart.axis.Abstract":[""
19770 "Ext.chart.axis.Axis":[""
19772 "Ext.chart.axis.Category":["axis.category"
19774 "Ext.chart.axis.Gauge":["axis.gauge"
19776 "Ext.chart.axis.Numeric":["axis.numeric"
19778 "Ext.chart.axis.Radial":["axis.radial"
19780 "Ext.chart.axis.Time":["axis.time"
19782 "Ext.chart.series.Area":["series.area"
19784 "Ext.chart.series.Bar":["series.bar"
19786 "Ext.chart.series.Cartesian":[""
19788 "Ext.chart.series.Column":["series.column"
19790 "Ext.chart.series.Gauge":["series.gauge"
19792 "Ext.chart.series.Line":["series.line"
19794 "Ext.chart.series.Pie":["series.pie"
19796 "Ext.chart.series.Radar":["series.radar"
19798 "Ext.chart.series.Scatter":["series.scatter"
19800 "Ext.chart.series.Series":[""
19802 "Ext.chart.theme.Base":[""
19804 "Ext.chart.theme.Theme":[""
19806 "Ext.container.AbstractContainer":[""
19808 "Ext.data.AbstractStore":[""
19810 "Ext.data.ArrayStore":["store.array"
19812 "Ext.data.Association":[""
19814 "Ext.data.Batch":[""
19816 "Ext.data.BelongsToAssociation":["association.belongsto"
19818 "Ext.data.BufferStore":["store.buffer"
19820 "Ext.data.Connection":[""
19822 "Ext.data.DirectStore":["store.direct"
19824 "Ext.data.Errors":[""
19826 "Ext.data.Field":["data.field"
19828 "Ext.data.HasManyAssociation":["association.hasmany"
19830 "Ext.data.JsonP":[""
19832 "Ext.data.JsonPStore":["store.jsonp"
19834 "Ext.data.JsonStore":["store.json"
19836 "Ext.data.Model":[""
19838 "Ext.data.NodeInterface":[""
19840 "Ext.data.NodeStore":["store.node"
19842 "Ext.data.Operation":[""
19844 "Ext.data.Request":[""
19846 "Ext.data.ResultSet":[""
19848 "Ext.data.SortTypes":[""
19850 "Ext.data.Store":["store.store"
19852 "Ext.data.StoreManager":[""
19854 "Ext.data.Tree":["data.tree"
19856 "Ext.data.TreeStore":["store.tree"
19858 "Ext.data.Types":[""
19860 "Ext.data.validations":[""
19862 "Ext.data.XmlStore":["store.xml"
19864 "Ext.data.proxy.Ajax":["proxy.ajax"
19866 "Ext.data.proxy.Client":[""
19868 "Ext.data.proxy.Direct":["proxy.direct"
19870 "Ext.data.proxy.JsonP":["proxy.jsonp",
19873 "Ext.data.proxy.LocalStorage":["proxy.localstorage"
19875 "Ext.data.proxy.Memory":["proxy.memory"
19877 "Ext.data.proxy.Proxy":["proxy.proxy"
19879 "Ext.data.proxy.Rest":["proxy.rest"
19881 "Ext.data.proxy.Server":["proxy.server"
19883 "Ext.data.proxy.SessionStorage":["proxy.sessionstorage"
19885 "Ext.data.proxy.WebStorage":[""
19887 "Ext.data.reader.Array":["reader.array"
19889 "Ext.data.reader.Json":["reader.json"
19891 "Ext.data.reader.Reader":[""
19893 "Ext.data.reader.Xml":["reader.xml"
19895 "Ext.data.writer.Json":["writer.json"
19897 "Ext.data.writer.Writer":["writer.base"
19899 "Ext.data.writer.Xml":["writer.xml"
19901 "Ext.direct.Event":["direct.event"
19903 "Ext.direct.ExceptionEvent":["direct.exception"
19905 "Ext.direct.JsonProvider":["direct.jsonprovider"
19907 "Ext.direct.Manager":[""
19909 "Ext.direct.PollingProvider":["direct.pollingprovider"
19911 "Ext.direct.Provider":["direct.provider"
19913 "Ext.direct.RemotingEvent":["direct.rpc"
19915 "Ext.direct.RemotingMethod":[""
19917 "Ext.direct.RemotingProvider":["direct.remotingprovider"
19919 "Ext.direct.Transaction":["direct.transaction"
19921 "Ext.draw.Color":[""
19923 "Ext.draw.Component":["widget.draw"
19925 "Ext.draw.CompositeSprite":[""
19927 "Ext.draw.Draw":[""
19929 "Ext.draw.Matrix":[""
19931 "Ext.draw.Sprite":[""
19933 "Ext.draw.SpriteDD":[""
19935 "Ext.draw.Surface":[""
19937 "Ext.draw.engine.Svg":[""
19939 "Ext.draw.engine.Vml":[""
19943 "Ext.fx.Animator":[""
19945 "Ext.fx.CubicBezier":[""
19947 "Ext.fx.Easing":[],
19948 "Ext.fx.Manager":[""
19950 "Ext.fx.PropertyHandler":[""
19954 "Ext.fx.target.Component":[""
19956 "Ext.fx.target.CompositeElement":[""
19958 "Ext.fx.target.CompositeElementCSS":[""
19960 "Ext.fx.target.CompositeSprite":[""
19962 "Ext.fx.target.Element":[""
19964 "Ext.fx.target.ElementCSS":[""
19966 "Ext.fx.target.Sprite":[""
19968 "Ext.fx.target.Target":[""
19970 "Ext.layout.Layout":[""
19972 "Ext.layout.component.AbstractDock":[""
19974 "Ext.layout.component.Auto":["layout.autocomponent"
19976 "Ext.layout.component.Component":[""
19978 "Ext.layout.component.Draw":["layout.draw"
19980 "Ext.layout.container.AbstractCard":[""
19982 "Ext.layout.container.AbstractContainer":[""
19984 "Ext.layout.container.AbstractFit":[""
19986 "Ext.layout.container.Auto":["layout.auto",
19987 "layout.autocontainer"
19989 "Ext.panel.AbstractPanel":[""
19991 "Ext.selection.DataViewModel":[""
19993 "Ext.selection.Model":[""
19995 "Ext.state.CookieProvider":[""
19997 "Ext.state.LocalStorageProvider":["state.localstorage"
19999 "Ext.state.Manager":[""
20001 "Ext.state.Provider":[""
20003 "Ext.state.Stateful":[""
20005 "Ext.util.AbstractMixedCollection":[""
20007 "Ext.util.Filter":[""
20009 "Ext.util.Grouper":[""
20011 "Ext.util.HashMap":[""
20013 "Ext.util.Inflector":[""
20015 "Ext.util.MixedCollection":[""
20017 "Ext.util.Observable":[""
20019 "Ext.util.Offset":[""
20021 "Ext.util.Point":[""
20023 "Ext.util.Region":[""
20025 "Ext.util.Sortable":[""
20027 "Ext.util.Sorter":[""
20029 "Ext.view.AbstractView":[""
20033 "Ext.Component":["widget.component",
20036 "Ext.Editor":["widget.editor"
20038 "Ext.FocusManager":[""
20040 "Ext.Img":["widget.image",
20041 "widget.imagecomponent"
20045 "Ext.ProgressBar":["widget.progressbar"
20049 "Ext.ShadowPool":[""
20051 "Ext.ZIndexManager":[""
20053 "Ext.button.Button":["widget.button"
20055 "Ext.button.Cycle":["widget.cycle"
20057 "Ext.button.Split":["widget.splitbutton"
20059 "Ext.container.ButtonGroup":["widget.buttongroup"
20061 "Ext.container.Container":["widget.container"
20063 "Ext.container.Viewport":["widget.viewport"
20067 "Ext.dd.DDProxy":[""
20069 "Ext.dd.DDTarget":[""
20071 "Ext.dd.DragDrop":[""
20073 "Ext.dd.DragDropManager":[""
20075 "Ext.dd.DragSource":[""
20077 "Ext.dd.DragTracker":[""
20079 "Ext.dd.DragZone":[""
20081 "Ext.dd.DropTarget":[""
20083 "Ext.dd.DropZone":[""
20085 "Ext.dd.Registry":[""
20087 "Ext.dd.ScrollManager":[""
20089 "Ext.dd.StatusProxy":[""
20091 "Ext.flash.Component":["widget.flash"
20093 "Ext.form.Basic":[""
20095 "Ext.form.CheckboxGroup":["widget.checkboxgroup"
20097 "Ext.form.CheckboxManager":[""
20099 "Ext.form.FieldAncestor":[""
20101 "Ext.form.FieldContainer":["widget.fieldcontainer"
20103 "Ext.form.FieldSet":["widget.fieldset"
20105 "Ext.form.Label":["widget.label"
20107 "Ext.form.Labelable":[""
20109 "Ext.form.Panel":["widget.form"
20111 "Ext.form.RadioGroup":["widget.radiogroup"
20113 "Ext.form.RadioManager":[""
20115 "Ext.form.action.Action":[""
20117 "Ext.form.action.DirectLoad":["formaction.directload"
20119 "Ext.form.action.DirectSubmit":["formaction.directsubmit"
20121 "Ext.form.action.Load":["formaction.load"
20123 "Ext.form.action.StandardSubmit":["formaction.standardsubmit"
20125 "Ext.form.action.Submit":["formaction.submit"
20127 "Ext.form.field.Base":["widget.field"
20129 "Ext.form.field.Checkbox":["widget.checkboxfield",
20132 "Ext.form.field.ComboBox":["widget.combobox",
20135 "Ext.form.field.Date":["widget.datefield"
20137 "Ext.form.field.Display":["widget.displayfield"
20139 "Ext.form.field.Field":[""
20141 "Ext.form.field.File":["widget.filefield",
20142 "widget.fileuploadfield"
20144 "Ext.form.field.Hidden":["widget.hiddenfield",
20147 "Ext.form.field.HtmlEditor":["widget.htmleditor"
20149 "Ext.form.field.Number":["widget.numberfield"
20151 "Ext.form.field.Picker":["widget.pickerfield"
20153 "Ext.form.field.Radio":["widget.radiofield",
20156 "Ext.form.field.Spinner":["widget.spinnerfield"
20158 "Ext.form.field.Text":["widget.textfield"
20160 "Ext.form.field.TextArea":["widget.textareafield",
20163 "Ext.form.field.Time":["widget.timefield"
20165 "Ext.form.field.Trigger":["widget.triggerfield",
20168 "Ext.form.field.VTypes":[""
20170 "Ext.grid.CellEditor":[""
20172 "Ext.grid.ColumnLayout":["layout.gridcolumn"
20174 "Ext.grid.Lockable":[""
20176 "Ext.grid.LockingView":[""
20178 "Ext.grid.PagingScroller":["widget.paginggridscroller"
20180 "Ext.grid.Panel":["widget.gridpanel",
20183 "Ext.grid.RowEditor":[""
20185 "Ext.grid.RowNumberer":["widget.rownumberer"
20187 "Ext.grid.Scroller":["widget.gridscroller"
20189 "Ext.grid.View":["widget.gridview"
20191 "Ext.grid.ViewDropZone":[""
20193 "Ext.grid.column.Action":["widget.actioncolumn"
20195 "Ext.grid.column.Boolean":["widget.booleancolumn"
20197 "Ext.grid.column.Column":["widget.gridcolumn"
20199 "Ext.grid.column.Date":["widget.datecolumn"
20201 "Ext.grid.column.Number":["widget.numbercolumn"
20203 "Ext.grid.column.Template":["widget.templatecolumn"
20205 "Ext.grid.feature.AbstractSummary":["feature.abstractsummary"
20207 "Ext.grid.feature.Chunking":["feature.chunking"
20209 "Ext.grid.feature.Feature":["feature.feature"
20211 "Ext.grid.feature.Grouping":["feature.grouping"
20213 "Ext.grid.feature.GroupingSummary":["feature.groupingsummary"
20215 "Ext.grid.feature.RowBody":["feature.rowbody"
20217 "Ext.grid.feature.RowWrap":["feature.rowwrap"
20219 "Ext.grid.feature.Summary":["feature.summary"
20221 "Ext.grid.header.Container":["widget.headercontainer"
20223 "Ext.grid.header.DragZone":[""
20225 "Ext.grid.header.DropZone":[""
20227 "Ext.grid.plugin.CellEditing":["plugin.cellediting"
20229 "Ext.grid.plugin.DragDrop":["plugin.gridviewdragdrop"
20231 "Ext.grid.plugin.Editing":["editing.editing"
20233 "Ext.grid.plugin.HeaderReorderer":["plugin.gridheaderreorderer"
20235 "Ext.grid.plugin.HeaderResizer":["plugin.gridheaderresizer"
20237 "Ext.grid.plugin.RowEditing":["plugin.rowediting"
20239 "Ext.grid.property.Grid":[""
20241 "Ext.grid.property.HeaderContainer":[""
20243 "Ext.grid.property.Property":[""
20245 "Ext.grid.property.Store":[""
20247 "Ext.layout.component.Body":["layout.body"
20249 "Ext.layout.component.BoundList":["layout.boundlist"
20251 "Ext.layout.component.Button":["layout.button"
20253 "Ext.layout.component.Dock":["layout.dock"
20255 "Ext.layout.component.Editor":["layout.editor"
20257 "Ext.layout.component.FieldSet":["layout.fieldset"
20259 "Ext.layout.component.ProgressBar":["layout.progressbar"
20261 "Ext.layout.component.Tab":["layout.tab"
20263 "Ext.layout.component.Tip":["layout.tip"
20265 "Ext.layout.component.field.Field":["layout.field"
20267 "Ext.layout.component.field.File":["layout.filefield"
20269 "Ext.layout.component.field.HtmlEditor":["layout.htmleditor"
20271 "Ext.layout.component.field.Slider":["layout.sliderfield"
20273 "Ext.layout.component.field.Text":["layout.textfield"
20275 "Ext.layout.component.field.TextArea":["layout.textareafield"
20277 "Ext.layout.component.field.Trigger":["layout.triggerfield"
20279 "Ext.layout.container.Absolute":["layout.absolute"
20281 "Ext.layout.container.Accordion":["layout.accordion"
20283 "Ext.layout.container.Anchor":["layout.anchor"
20285 "Ext.layout.container.Border":["layout.border"
20287 "Ext.layout.container.Box":["layout.box"
20289 "Ext.layout.container.Card":["layout.card"
20291 "Ext.layout.container.CheckboxGroup":["layout.checkboxgroup"
20293 "Ext.layout.container.Column":["layout.column"
20295 "Ext.layout.container.Container":[""
20297 "Ext.layout.container.Fit":["layout.fit"
20299 "Ext.layout.container.HBox":["layout.hbox"
20301 "Ext.layout.container.Table":["layout.table"
20303 "Ext.layout.container.VBox":["layout.vbox"
20305 "Ext.layout.container.boxOverflow.Menu":[""
20307 "Ext.layout.container.boxOverflow.None":[""
20309 "Ext.layout.container.boxOverflow.Scroller":[""
20311 "Ext.menu.CheckItem":["widget.menucheckitem"
20313 "Ext.menu.ColorPicker":["widget.colormenu"
20315 "Ext.menu.DatePicker":["widget.datemenu"
20317 "Ext.menu.Item":["widget.menuitem"
20319 "Ext.menu.KeyNav":[""
20321 "Ext.menu.Manager":[""
20323 "Ext.menu.Menu":["widget.menu"
20325 "Ext.menu.Separator":["widget.menuseparator"
20329 "Ext.panel.Header":["widget.header"
20331 "Ext.panel.Panel":["widget.panel"
20333 "Ext.panel.Proxy":[""
20335 "Ext.panel.Table":["widget.tablepanel"
20337 "Ext.panel.Tool":["widget.tool"
20339 "Ext.picker.Color":["widget.colorpicker"
20341 "Ext.picker.Date":["widget.datepicker"
20343 "Ext.picker.Month":["widget.monthpicker"
20345 "Ext.picker.Time":["widget.timepicker"
20347 "Ext.resizer.Handle":[""
20349 "Ext.resizer.Resizer":[""
20351 "Ext.resizer.ResizeTracker":[""
20353 "Ext.resizer.Splitter":["widget.splitter"
20355 "Ext.resizer.SplitterTracker":[""
20357 "Ext.selection.CellModel":["selection.cellmodel"
20359 "Ext.selection.CheckboxModel":[""
20361 "Ext.selection.RowModel":["selection.rowmodel"
20363 "Ext.selection.TreeModel":["selection.treemodel"
20365 "Ext.slider.Multi":["widget.multislider"
20367 "Ext.slider.Single":["widget.slider",
20368 "widget.sliderfield"
20370 "Ext.slider.Thumb":[""
20372 "Ext.slider.Tip":["widget.slidertip"
20374 "Ext.tab.Bar":["widget.tabbar"
20376 "Ext.tab.Panel":["widget.tabpanel"
20378 "Ext.tab.Tab":["widget.tab"
20380 "Ext.tip.QuickTip":[""
20382 "Ext.tip.QuickTipManager":[""
20386 "Ext.tip.ToolTip":["widget.tooltip"
20388 "Ext.toolbar.Fill":["widget.tbfill"
20390 "Ext.toolbar.Item":["widget.tbitem"
20392 "Ext.toolbar.Paging":["widget.pagingtoolbar"
20394 "Ext.toolbar.Separator":["widget.tbseparator"
20396 "Ext.toolbar.Spacer":["widget.tbspacer"
20398 "Ext.toolbar.TextItem":["widget.tbtext"
20400 "Ext.toolbar.Toolbar":["widget.toolbar"
20402 "Ext.tree.Column":["widget.treecolumn"
20404 "Ext.tree.Panel":["widget.treepanel"
20406 "Ext.tree.View":["widget.treeview"
20408 "Ext.tree.ViewDragZone":[""
20410 "Ext.tree.ViewDropZone":[""
20412 "Ext.tree.plugin.TreeViewDragDrop":["plugin.treeviewdragdrop"
20414 "Ext.util.Animate":[""
20416 "Ext.util.ClickRepeater":[""
20418 "Ext.util.ComponentDragger":[""
20420 "Ext.util.Cookies":[""
20424 "Ext.util.Floating":[""
20426 "Ext.util.History":[""
20428 "Ext.util.KeyMap":[""
20430 "Ext.util.KeyNav":[""
20432 "Ext.util.TextMetrics":[""
20434 "Ext.view.BoundList":["widget.boundlist"
20436 "Ext.view.BoundListKeyNav":[""
20438 "Ext.view.DragZone":[""
20440 "Ext.view.DropZone":[""
20442 "Ext.view.Table":["widget.tableview"
20444 "Ext.view.TableChunker":[""
20446 "Ext.view.View":["widget.dataview"
20448 "Ext.window.MessageBox":["widget.messagebox"
20450 "Ext.window.Window":["widget.window"
20453 "alternateToNameMap":{
20454 "Ext.ComponentMgr":"Ext.ComponentManager",
20455 "Ext.ModelMgr":"Ext.ModelManager",
20456 "Ext.PluginMgr":"Ext.PluginManager",
20457 "Ext.chart.Axis":"Ext.chart.axis.Axis",
20458 "Ext.chart.CategoryAxis":"Ext.chart.axis.Category",
20459 "Ext.chart.NumericAxis":"Ext.chart.axis.Numeric",
20460 "Ext.chart.TimeAxis":"Ext.chart.axis.Time",
20461 "Ext.data.Record":"Ext.data.Model",
20462 "Ext.data.XmlStore":"Ext.data.XmlStore",
20463 "Ext.data.ClientProxy":"Ext.data.proxy.Client",
20464 "Ext.data.DirectProxy":"Ext.data.proxy.Direct",
20465 "Ext.data.ScriptTagProxy":"Ext.data.proxy.JsonP",
20466 "Ext.data.LocalStorageProxy":"Ext.data.proxy.LocalStorage",
20467 "Ext.data.MemoryProxy":"Ext.data.proxy.Memory",
20468 "Ext.data.RestProxy":"Ext.data.proxy.Rest",
20469 "Ext.data.ServerProxy":"Ext.data.proxy.Server",
20470 "Ext.data.SessionStorageProxy":"Ext.data.proxy.SessionStorage",
20471 "Ext.data.WebStorageProxy":"Ext.data.proxy.WebStorage",
20472 "Ext.data.ArrayReader":"Ext.data.reader.Array",
20473 "Ext.data.JsonReader":"Ext.data.reader.Json",
20474 "Ext.data.XmlReader":"Ext.data.reader.Xml",
20475 "Ext.data.JsonWriter":"Ext.data.writer.Json",
20476 "Ext.data.XmlWriter":"Ext.data.writer.Xml",
20477 "Ext.Direct.Transaction":"Ext.direct.Transaction",
20478 "Ext.AbstractStoreSelectionModel":"Ext.selection.Model",
20479 "Ext.view.AbstractView":"Ext.view.AbstractView",
20480 "Ext.FocusMgr":"Ext.FocusManager",
20481 "Ext.WindowGroup":"Ext.ZIndexManager",
20482 "Ext.Button":"Ext.button.Button",
20483 "Ext.CycleButton":"Ext.button.Cycle",
20484 "Ext.SplitButton":"Ext.button.Split",
20485 "Ext.ButtonGroup":"Ext.container.ButtonGroup",
20486 "Ext.Container":"Ext.container.Container",
20487 "Ext.Viewport":"Ext.container.Viewport",
20488 "Ext.FlashComponent":"Ext.flash.Component",
20489 "Ext.form.BasicForm":"Ext.form.Basic",
20490 "Ext.form.Action":"Ext.form.action.Action",
20491 "Ext.form.Action.DirectLoad":"Ext.form.action.DirectLoad",
20492 "Ext.form.Action.DirectSubmit":"Ext.form.action.DirectSubmit",
20493 "Ext.form.Action.Load":"Ext.form.action.Load",
20494 "Ext.form.Action.Submit":"Ext.form.action.Submit",
20495 "Ext.form.Checkbox":"Ext.form.field.Checkbox",
20496 "Ext.form.ComboBox":"Ext.form.field.ComboBox",
20497 "Ext.form.Hidden":"Ext.form.field.Hidden",
20498 "Ext.form.HtmlEditor":"Ext.form.field.HtmlEditor",
20499 "Ext.form.Picker":"Ext.form.field.Picker",
20500 "Ext.form.Radio":"Ext.form.field.Radio",
20501 "Ext.form.Spinner":"Ext.form.field.Spinner",
20502 "Ext.form.TextArea":"Ext.form.field.TextArea",
20503 "Ext.grid.ActionColumn":"Ext.grid.column.Action",
20504 "Ext.grid.BooleanColumn":"Ext.grid.column.Boolean",
20505 "Ext.grid.Column":"Ext.grid.column.Column",
20506 "Ext.grid.DateColumn":"Ext.grid.column.Date",
20507 "Ext.grid.NumberColumn":"Ext.grid.column.Number",
20508 "Ext.grid.TemplateColumn":"Ext.grid.column.Template",
20509 "Ext.grid.PropertyGrid":"Ext.grid.property.Grid",
20510 "Ext.grid.PropertyColumnModel":"Ext.grid.property.HeaderContainer",
20511 "Ext.PropGridProperty":"Ext.grid.property.Property",
20512 "Ext.grid.PropertyStore":"Ext.grid.property.Store",
20513 "Ext.layout.AbsoluteLayout":"Ext.layout.container.Absolute",
20514 "Ext.layout.AccordionLayout":"Ext.layout.container.Accordion",
20515 "Ext.layout.AnchorLayout":"Ext.layout.container.Anchor",
20516 "Ext.layout.BorderLayout":"Ext.layout.container.Border",
20517 "Ext.layout.BoxLayout":"Ext.layout.container.Box",
20518 "Ext.layout.CardLayout":"Ext.layout.container.Card",
20519 "Ext.layout.ColumnLayout":"Ext.layout.container.Column",
20520 "Ext.layout.ContainerLayout":"Ext.layout.container.Container",
20521 "Ext.layout.FitLayout":"Ext.layout.container.Fit",
20522 "Ext.layout.HBoxLayout":"Ext.layout.container.HBox",
20523 "Ext.layout.TableLayout":"Ext.layout.container.Table",
20524 "Ext.layout.VBoxLayout":"Ext.layout.container.VBox",
20525 "Ext.layout.boxOverflow.Menu":"Ext.layout.container.boxOverflow.Menu",
20526 "Ext.layout.boxOverflow.None":"Ext.layout.container.boxOverflow.None",
20527 "Ext.layout.boxOverflow.Scroller":"Ext.layout.container.boxOverflow.Scroller",
20528 "Ext.menu.TextItem":"Ext.menu.Item",
20529 "Ext.menu.MenuMgr":"Ext.menu.Manager",
20530 "Ext.Panel":"Ext.panel.Panel",
20531 "Ext.dd.PanelProxy":"Ext.panel.Proxy",
20532 "Ext.ColorPalette":"Ext.picker.Color",
20533 "Ext.DatePicker":"Ext.picker.Date",
20534 "Ext.MonthPicker":"Ext.picker.Month",
20535 "Ext.Resizable":"Ext.resizer.Resizer",
20536 "Ext.slider.MultiSlider":"Ext.slider.Multi",
20537 "Ext.QuickTip":"Ext.tip.QuickTip",
20538 "Ext.Tip":"Ext.tip.Tip",
20539 "Ext.ToolTip":"Ext.tip.ToolTip",
20540 "Ext.Toolbar.Fill":"Ext.toolbar.Fill",
20541 "Ext.Toolbar.Item":"Ext.toolbar.Item",
20542 "Ext.PagingToolbar":"Ext.toolbar.Paging",
20543 "Ext.Toolbar.Separator":"Ext.toolbar.Separator",
20544 "Ext.Toolbar.Spacer":"Ext.toolbar.Spacer",
20545 "Ext.Toolbar.TextItem":"Ext.toolbar.TextItem",
20546 "Ext.Toolbar":"Ext.toolbar.Toolbar",
20547 "Ext.History":"Ext.util.History",
20548 "Ext.KeyMap":"Ext.util.KeyMap",
20549 "Ext.KeyNav":"Ext.util.KeyNav",
20550 "Ext.BoundList":"Ext.view.BoundList",
20551 "Ext.view.View":"Ext.view.View",
20552 "Ext.MessageBox":"Ext.window.MessageBox",
20553 "Ext.Window":"Ext.window.Window"
20555 };var scripts = document.getElementsByTagName('script'),
20559 for (i = 0, ln = scripts.length; i < ln; i++) {
20560 src = scripts[i].src;
20562 match = src.match(/ext(-debug)?\.js$/);
20565 path = src.substring(0, src.length - match[0].length);
20570 var nameToAliasesMap = data.nameToAliasesMap,
20571 alternateToNameMap = data.alternateToNameMap,
20572 classManager = Ext.ClassManager,
20575 for (name in nameToAliasesMap) {
20576 if (nameToAliasesMap.hasOwnProperty(name)) {
20577 aliases = nameToAliasesMap[name];
20579 if (aliases.length > 0) {
20580 for (i = 0, ln = aliases.length; i < ln; i++) {
20581 classManager.setAlias(name, aliases[i]);
20585 classManager.setAlias(name, null);
20590 Ext.Object.merge(classManager.maps.alternateToName, alternateToNameMap);
20592 Ext.Loader.setConfig({
20594 disableCaching: true,
20596 'Ext': path + 'src'