X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/builds/ext-foundation-debug.js
diff --git a/builds/ext-foundation-debug.js b/builds/ext-foundation-debug.js
new file mode 100644
index 00000000..94333963
--- /dev/null
+++ b/builds/ext-foundation-debug.js
@@ -0,0 +1,7944 @@
+/*
+Ext JS - JavaScript Library
+Copyright (c) 2006-2011, Sencha Inc.
+All rights reserved.
+licensing@sencha.com
+*/
+/**
+ * @class Ext
+ * @singleton
+ */
+(function() {
+ var global = this,
+ objectPrototype = Object.prototype,
+ toString = Object.prototype.toString,
+ enumerables = true,
+ enumerablesTest = { toString: 1 },
+ i;
+
+ if (typeof Ext === 'undefined') {
+ global.Ext = {};
+ }
+
+ Ext.global = global;
+
+ for (i in enumerablesTest) {
+ enumerables = null;
+ }
+
+ if (enumerables) {
+ enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'constructor'];
+ }
+
+ /**
+ * An array containing extra enumerables for old browsers
+ * @type Array
+ */
+ Ext.enumerables = enumerables;
+
+ /**
+ * Copies all the properties of config to the specified object.
+ * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
+ * {@link Ext.Object#merge} instead.
+ * @param {Object} object The receiver of the properties
+ * @param {Object} config The source of the properties
+ * @param {Object} defaults A different object that will also be applied for default values
+ * @return {Object} returns obj
+ */
+ Ext.apply = function(object, config, defaults) {
+ if (defaults) {
+ Ext.apply(object, defaults);
+ }
+
+ if (object && config && typeof config === 'object') {
+ var i, j, k;
+
+ for (i in config) {
+ object[i] = config[i];
+ }
+
+ if (enumerables) {
+ for (j = enumerables.length; j--;) {
+ k = enumerables[j];
+ if (config.hasOwnProperty(k)) {
+ object[k] = config[k];
+ }
+ }
+ }
+ }
+
+ return object;
+ };
+
+ Ext.buildSettings = Ext.apply({
+ baseCSSPrefix: 'x-',
+ scopeResetCSS: false
+ }, Ext.buildSettings || {});
+
+ Ext.apply(Ext, {
+ /**
+ * A reusable empty function
+ */
+ emptyFn: function() {},
+
+ baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
+
+ /**
+ * Copies all the properties of config to object if they don't already exist.
+ * @function
+ * @param {Object} object The receiver of the properties
+ * @param {Object} config The source of the properties
+ * @return {Object} returns obj
+ */
+ applyIf: function(object, config) {
+ var property;
+
+ if (object) {
+ for (property in config) {
+ if (object[property] === undefined) {
+ object[property] = config[property];
+ }
+ }
+ }
+
+ return object;
+ },
+
+ /**
+ * Iterates either an array or an object. This method delegates to
+ * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
+ *
+ * @param {Object/Array} object The object or array to be iterated.
+ * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
+ * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
+ * type that is being iterated.
+ * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
+ * Defaults to the object being iterated itself.
+ * @markdown
+ */
+ iterate: function(object, fn, scope) {
+ if (Ext.isEmpty(object)) {
+ return;
+ }
+
+ if (scope === undefined) {
+ scope = object;
+ }
+
+ if (Ext.isIterable(object)) {
+ Ext.Array.each.call(Ext.Array, object, fn, scope);
+ }
+ else {
+ Ext.Object.each.call(Ext.Object, object, fn, scope);
+ }
+ }
+ });
+
+ Ext.apply(Ext, {
+
+ /**
+ * This method deprecated. Use {@link Ext#define Ext.define} instead.
+ * @function
+ * @param {Function} superclass
+ * @param {Object} overrides
+ * @return {Function} The subclass constructor from the overrides parameter, or a generated one if not provided.
+ * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
+ */
+ extend: function() {
+ // inline overrides
+ var objectConstructor = objectPrototype.constructor,
+ inlineOverrides = function(o) {
+ for (var m in o) {
+ if (!o.hasOwnProperty(m)) {
+ continue;
+ }
+ this[m] = o[m];
+ }
+ };
+
+ return function(subclass, superclass, overrides) {
+ // First we check if the user passed in just the superClass with overrides
+ if (Ext.isObject(superclass)) {
+ overrides = superclass;
+ superclass = subclass;
+ subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
+ superclass.apply(this, arguments);
+ };
+ }
+
+ if (!superclass) {
+ Ext.Error.raise({
+ sourceClass: 'Ext',
+ sourceMethod: 'extend',
+ msg: 'Attempting to extend from a class which has not been loaded on the page.'
+ });
+ }
+
+ // We create a new temporary class
+ var F = function() {},
+ subclassProto, superclassProto = superclass.prototype;
+
+ F.prototype = superclassProto;
+ subclassProto = subclass.prototype = new F();
+ subclassProto.constructor = subclass;
+ subclass.superclass = superclassProto;
+
+ if (superclassProto.constructor === objectConstructor) {
+ superclassProto.constructor = superclass;
+ }
+
+ subclass.override = function(overrides) {
+ Ext.override(subclass, overrides);
+ };
+
+ subclassProto.override = inlineOverrides;
+ subclassProto.proto = subclassProto;
+
+ subclass.override(overrides);
+ subclass.extend = function(o) {
+ return Ext.extend(subclass, o);
+ };
+
+ return subclass;
+ };
+ }(),
+
+ /**
+ * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
+
+ Ext.define('My.cool.Class', {
+ sayHi: function() {
+ alert('Hi!');
+ }
+ }
+
+ Ext.override(My.cool.Class, {
+ sayHi: function() {
+ alert('About to say...');
+
+ this.callOverridden();
+ }
+ });
+
+ var cool = new My.cool.Class();
+ cool.sayHi(); // alerts 'About to say...'
+ // alerts 'Hi!'
+
+ * Please note that `this.callOverridden()` only works if the class was previously
+ * created with {@link Ext#define)
+ *
+ * @param {Object} cls The class to override
+ * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
+ * containing one or more methods.
+ * @method override
+ * @markdown
+ */
+ override: function(cls, overrides) {
+ if (cls.prototype.$className) {
+ return cls.override(overrides);
+ }
+ else {
+ Ext.apply(cls.prototype, overrides);
+ }
+ }
+ });
+
+ // A full set of static methods to do type checking
+ Ext.apply(Ext, {
+
+ /**
+ * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
+ * value (second argument) otherwise.
+ *
+ * @param {Mixed} value The value to test
+ * @param {Mixed} defaultValue The value to return if the original value is empty
+ * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
+ * @return {Mixed} value, if non-empty, else defaultValue
+ */
+ valueFrom: function(value, defaultValue, allowBlank){
+ return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
+ },
+
+ /**
+ * Returns the type of the given variable in string format. List of possible values are:
+ *
+ * - `undefined`: If the given value is `undefined`
+ * - `null`: If the given value is `null`
+ * - `string`: If the given value is a string
+ * - `number`: If the given value is a number
+ * - `boolean`: If the given value is a boolean value
+ * - `date`: If the given value is a `Date` object
+ * - `function`: If the given value is a function reference
+ * - `object`: If the given value is an object
+ * - `array`: If the given value is an array
+ * - `regexp`: If the given value is a regular expression
+ * - `element`: If the given value is a DOM Element
+ * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
+ * - `whitespace`: If the given value is a DOM text node and contains only whitespace
+ *
+ * @param {Mixed} value
+ * @return {String}
+ * @markdown
+ */
+ typeOf: function(value) {
+ if (value === null) {
+ return 'null';
+ }
+
+ var type = typeof value;
+
+ if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
+ return type;
+ }
+
+ var typeToString = toString.call(value);
+
+ switch(typeToString) {
+ case '[object Array]':
+ return 'array';
+ case '[object Date]':
+ return 'date';
+ case '[object Boolean]':
+ return 'boolean';
+ case '[object Number]':
+ return 'number';
+ case '[object RegExp]':
+ return 'regexp';
+ }
+
+ if (type === 'function') {
+ return 'function';
+ }
+
+ if (type === 'object') {
+ if (value.nodeType !== undefined) {
+ if (value.nodeType === 3) {
+ return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
+ }
+ else {
+ return 'element';
+ }
+ }
+
+ return 'object';
+ }
+
+ Ext.Error.raise({
+ sourceClass: 'Ext',
+ sourceMethod: 'typeOf',
+ msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
+ });
+ },
+
+ /**
+ * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
+ *
+ * - `null`
+ * - `undefined`
+ * - a zero-length array
+ * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
+ *
+ * @param {Mixed} value The value to test
+ * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
+ * @return {Boolean}
+ * @markdown
+ */
+ isEmpty: function(value, allowEmptyString) {
+ return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
+ },
+
+ /**
+ * Returns true if the passed value is a JavaScript Array, false otherwise.
+ *
+ * @param {Mixed} target The target to test
+ * @return {Boolean}
+ */
+ isArray: ('isArray' in Array) ? Array.isArray : function(value) {
+ return toString.call(value) === '[object Array]';
+ },
+
+ /**
+ * Returns true if the passed value is a JavaScript Date object, false otherwise.
+ * @param {Object} object The object to test
+ * @return {Boolean}
+ */
+ isDate: function(value) {
+ return toString.call(value) === '[object Date]';
+ },
+
+ /**
+ * Returns true if the passed value is a JavaScript Object, false otherwise.
+ * @param {Mixed} value The value to test
+ * @return {Boolean}
+ */
+ isObject: (toString.call(null) === '[object Object]') ?
+ function(value) {
+ return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.nodeType === undefined;
+ } :
+ function(value) {
+ return toString.call(value) === '[object Object]';
+ },
+
+ /**
+ * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
+ * @param {Mixed} value The value to test
+ * @return {Boolean}
+ */
+ isPrimitive: function(value) {
+ var type = typeof value;
+
+ return type === 'string' || type === 'number' || type === 'boolean';
+ },
+
+ /**
+ * Returns true if the passed value is a JavaScript Function, false otherwise.
+ * @param {Mixed} value The value to test
+ * @return {Boolean}
+ */
+ isFunction:
+ // Safari 3.x and 4.x returns 'function' for typeof Creates a delegate function, optionally with a bound scope which, when called, buffers
+ * the execution of the passed function for the configured number of milliseconds.
+ * If called again within that period, the impending invocation will be canceled, and the
+ * timeout period will begin again. Creates a throttled version of the passed function which, when called repeatedly and
+ * rapidly, invokes the passed function only after a certain interval has elapsed since the
+ * previous invocation. This is useful for wrapping functions which may be called repeatedly, such as
+ * a handler of a mouse move event when the processing is expensive. Developer-written, custom formats may be used by supplying both a formatting and a parsing function
+ * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}. An object hash in which each property is a date parsing function. The property name is the
+ * format string which that function parses. This object is automatically populated with date parsing functions as
+ * date formats are requested for Ext standard formatting strings. Custom parsing functions may be inserted into this object, keyed by a name which from then on
+ * may be used as a format string to {@link #parse}.
+ * Example: A parsing function should return a Date object, and is passed the following parameters:
+ * @param {String/Number} target The version to compare with
+ * @return {Boolean} True if this version matches the target, false otherwise
+ */
+ match: function(target) {
+ target = String(target);
+ return this.version.substr(0, target.length) === target;
+ },
+
+ /**
+ * Returns this format: [major, minor, patch, build, release]. Useful for comparison
+ * @return {Array}
+ */
+ toArray: function() {
+ return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
+ },
+
+ /**
+ * Returns shortVersion version without dots and release
+ * @return {String}
+ */
+ getShortVersion: function() {
+ return this.shortVersion;
+ }
+ });
+
+ Ext.apply(Version, {
+ // @private
+ releaseValueMap: {
+ 'dev': -6,
+ 'alpha': -5,
+ 'a': -5,
+ 'beta': -4,
+ 'b': -4,
+ 'rc': -3,
+ '#': -2,
+ 'p': -1,
+ 'pl': -1
+ },
+
+ /**
+ * Converts a version component to a comparable value
+ *
+ * @static
+ * @param {Mixed} value The value to convert
+ * @return {Mixed}
+ */
+ getComponentValue: function(value) {
+ return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
+ },
+
+ /**
+ * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
+ * they are handled in the following order:
+ * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
+ *
+ * @static
+ * @param {String} current The current version to compare to
+ * @param {String} target The target version to compare to
+ * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
+ */
+ compare: function(current, target) {
+ var currentValue, targetValue, i;
+
+ current = new Version(current).toArray();
+ target = new Version(target).toArray();
+
+ for (i = 0; i < Math.max(current.length, target.length); i++) {
+ currentValue = this.getComponentValue(current[i]);
+ targetValue = this.getComponentValue(target[i]);
+
+ if (currentValue < targetValue) {
+ return -1;
+ } else if (currentValue > targetValue) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+ });
+
+ Ext.apply(Ext, {
+ /**
+ * @private
+ */
+ versions: {},
+
+ /**
+ * @private
+ */
+ lastRegisteredVersion: null,
+
+ /**
+ * Set version number for the given package name.
+ *
+ * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
+ * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
+ * @return {Ext}
+ */
+ setVersion: function(packageName, version) {
+ Ext.versions[packageName] = new Version(version);
+ Ext.lastRegisteredVersion = Ext.versions[packageName];
+
+ return this;
+ },
+
+ /**
+ * Get the version number of the supplied package name; will return the last registered version
+ * (last Ext.setVersion call) if there's no package name given.
+ *
+ * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
+ * @return {Ext.Version} The version
+ */
+ getVersion: function(packageName) {
+ if (packageName === undefined) {
+ return Ext.lastRegisteredVersion;
+ }
+
+ return Ext.versions[packageName];
+ },
+
+ /**
+ * Create a closure for deprecated code.
+ *
+ // This means Ext.oldMethod is only supported in 4.0.0beta and older.
+ // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
+ // the closure will not be invoked
+ Ext.deprecate('extjs', '4.0.0beta', function() {
+ Ext.oldMethod = Ext.newMethod;
+
+ ...
+ });
+
+ * @param {String} packageName The package name
+ * @param {String} since The last version before it's deprecated
+ * @param {Function} closure The callback function to be executed with the specified version is less than the current version
+ * @param {Object} scope The execution scope (this) if the closure
+ * @markdown
+ */
+ deprecate: function(packageName, since, closure, scope) {
+ if (Version.compare(Ext.getVersion(packageName), since) < 1) {
+ closure.call(scope);
+ }
+ }
+ }); // End Versioning
+
+ Ext.setVersion('core', version);
+
+})();
+
+/**
+ * @class Ext.String
+ *
+ * A collection of useful static methods to deal with strings
+ * @singleton
+ */
+
+Ext.String = {
+ 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,
+ escapeRe: /('|\\)/g,
+ formatRe: /\{(\d+)\}/g,
+ escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
+
+ /**
+ * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
+ * @param {String} value The string to encode
+ * @return {String} The encoded text
+ */
+ htmlEncode: (function() {
+ var entities = {
+ '&': '&',
+ '>': '>',
+ '<': '<',
+ '"': '"'
+ }, keys = [], p, regex;
+
+ for (p in entities) {
+ keys.push(p);
+ }
+
+ regex = new RegExp('(' + keys.join('|') + ')', 'g');
+
+ return function(value) {
+ return (!value) ? value : String(value).replace(regex, function(match, capture) {
+ return entities[capture];
+ });
+ };
+ })(),
+
+ /**
+ * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
+ * @param {String} value The string to decode
+ * @return {String} The decoded text
+ */
+ htmlDecode: (function() {
+ var entities = {
+ '&': '&',
+ '>': '>',
+ '<': '<',
+ '"': '"'
+ }, keys = [], p, regex;
+
+ for (p in entities) {
+ keys.push(p);
+ }
+
+ regex = new RegExp('(' + keys.join('|') + '|[0-9]{1,5};' + ')', 'g');
+
+ return function(value) {
+ return (!value) ? value : String(value).replace(regex, function(match, capture) {
+ if (capture in entities) {
+ return entities[capture];
+ } else {
+ return String.fromCharCode(parseInt(capture.substr(2), 10));
+ }
+ });
+ };
+ })(),
+
+ /**
+ * Appends content to the query string of a URL, handling logic for whether to place
+ * a question mark or ampersand.
+ * @param {String} url The URL to append to.
+ * @param {String} string The content to append to the URL.
+ * @return (String) The resulting URL
+ */
+ urlAppend : function(url, string) {
+ if (!Ext.isEmpty(string)) {
+ return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
+ }
+
+ return url;
+ },
+
+ /**
+ * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
+ * @example
+var s = ' foo bar ';
+alert('-' + s + '-'); //alerts "- foo bar -"
+alert('-' + Ext.String.trim(s) + '-'); //alerts "-foo bar-"
+
+ * @param {String} string The string to escape
+ * @return {String} The trimmed string
+ */
+ trim: function(string) {
+ return string.replace(Ext.String.trimRegex, "");
+ },
+
+ /**
+ * Capitalize the given string
+ * @param {String} string
+ * @return {String}
+ */
+ capitalize: function(string) {
+ return string.charAt(0).toUpperCase() + string.substr(1);
+ },
+
+ /**
+ * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
+ * @param {String} value The string to truncate
+ * @param {Number} length The maximum length to allow before truncating
+ * @param {Boolean} word True to try to find a common word break
+ * @return {String} The converted text
+ */
+ ellipsis: function(value, len, word) {
+ if (value && value.length > len) {
+ if (word) {
+ var vs = value.substr(0, len - 2),
+ index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
+ if (index !== -1 && index >= (len - 15)) {
+ return vs.substr(0, index) + "...";
+ }
+ }
+ return value.substr(0, len - 3) + "...";
+ }
+ return value;
+ },
+
+ /**
+ * Escapes the passed string for use in a regular expression
+ * @param {String} string
+ * @return {String}
+ */
+ escapeRegex: function(string) {
+ return string.replace(Ext.String.escapeRegexRe, "\\$1");
+ },
+
+ /**
+ * Escapes the passed string for ' and \
+ * @param {String} string The string to escape
+ * @return {String} The escaped string
+ */
+ escape: function(string) {
+ return string.replace(Ext.String.escapeRe, "\\$1");
+ },
+
+ /**
+ * Utility function that allows you to easily switch a string between two alternating values. The passed value
+ * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
+ * they are already different, the first value passed in is returned. Note that this method returns the new value
+ * but does not change the current string.
+ *
+ * var version = new Ext.Version('1.0.2beta');
+ * console.log(version.match(1)); // True
+ * console.log(version.match(1.0)); // True
+ * console.log(version.match('1.0.2')); // True
+ * console.log(version.match('1.0.2RC')); // False
+ *
+ * @param {String} string The current string
+ * @param {String} value The value to compare to the current string
+ * @param {String} other The new value to use if the string already equals the first value passed in
+ * @return {String} The new value
+ */
+ toggle: function(string, value, other) {
+ return string === value ? other : value;
+ },
+
+ /**
+ * Pads the left side of a string with a specified character. This is especially useful
+ * for normalizing number and date strings. Example usage:
+ *
+ *
+ // alternate sort directions
+ sort = Ext.String.toggle(sort, 'ASC', 'DESC');
+
+ // instead of conditional logic:
+ sort = (sort == 'ASC' ? 'DESC' : 'ASC');
+
+ * @param {String} string The original string
+ * @param {Number} size The total length of the output string
+ * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
+ * @return {String} The padded string
+ */
+ leftPad: function(string, size, character) {
+ var result = String(string);
+ character = character || " ";
+ while (result.length < size) {
+ result = character + result;
+ }
+ return result;
+ },
+
+ /**
+ * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
+ * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
+ *
+var s = Ext.String.leftPad('123', 5, '0');
+// s now contains the string: '00123'
+
+ * @param {String} string The tokenized string to be formatted
+ * @param {String} value1 The value to replace token {0}
+ * @param {String} value2 Etc...
+ * @return {String} The formatted string
+ */
+ format: function(format) {
+ var args = Ext.Array.toArray(arguments, 1);
+ return format.replace(Ext.String.formatRe, function(m, i) {
+ return args[i];
+ });
+ }
+};
+
+/**
+ * @class Ext.Number
+ *
+ * A collection of useful static methods to deal with numbers
+ * @singleton
+ */
+
+(function() {
+
+var isToFixedBroken = (0.9).toFixed() !== '1';
+
+Ext.Number = {
+ /**
+ * Checks whether or not the current number is within a desired range. If the number is already within the
+ * range it is returned, otherwise the min or max value is returned depending on which side of the range is
+ * exceeded. Note that this method returns the constrained value but does not change the current number.
+ * @param {Number} number The number to check
+ * @param {Number} min The minimum number in the range
+ * @param {Number} max The maximum number in the range
+ * @return {Number} The constrained value if outside the range, otherwise the current value
+ */
+ constrain: function(number, min, max) {
+ number = parseFloat(number);
+
+ if (!isNaN(min)) {
+ number = Math.max(number, min);
+ }
+ if (!isNaN(max)) {
+ number = Math.min(number, max);
+ }
+ return number;
+ },
+
+ /**
+ * Formats a number using fixed-point notation
+ * @param {Number} value The number to format
+ * @param {Number} precision The number of digits to show after the decimal point
+ */
+ toFixed: function(value, precision) {
+ if (isToFixedBroken) {
+ precision = precision || 0;
+ var pow = Math.pow(10, precision);
+ return (Math.round(value * pow) / pow).toFixed(precision);
+ }
+
+ return value.toFixed(precision);
+ },
+
+ /**
+ * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
+ * it is not.
+
+Ext.Number.from('1.23', 1); // returns 1.23
+Ext.Number.from('abc', 1); // returns 1
+
+ * @param {Mixed} value
+ * @param {Number} defaultValue The value to return if the original value is non-numeric
+ * @return {Number} value, if numeric, defaultValue otherwise
+ */
+ from: function(value, defaultValue) {
+ if (isFinite(value)) {
+ value = parseFloat(value);
+ }
+
+ return !isNaN(value) ? value : defaultValue;
+ }
+};
+
+})();
+
+/**
+ * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead
+ *
+ * @deprecated 4.0.0 Replaced by Ext.Number.from
+ * @member Ext
+ * @method num
+ */
+Ext.num = function() {
+ return Ext.Number.from.apply(this, arguments);
+};
+/**
+ * @author Jacky Nguyen
+var cls = 'my-class', text = 'Some text';
+var s = Ext.String.format('<div class="{0}">{1}</div>', cls, text);
+// s now contains the string: '<div class="my-class">Some text</div>'
+
name
and value
.
+ * The wrapped function then allows "flexible" value setting of either:
+ *
+ *
+ *
+ *
+ * For example:
+ * name
and value
as 2 arguments
+ * @param {Function} setter
+ * @returns {Function} flexSetter
+ */
+ flexSetter: function(fn) {
+ return function(a, b) {
+ var k, i;
+
+ if (a === null) {
+ return this;
+ }
+
+ if (typeof a !== 'string') {
+ for (k in a) {
+ if (a.hasOwnProperty(k)) {
+ fn.call(this, k, a[k]);
+ }
+ }
+
+ if (Ext.enumerables) {
+ for (i = Ext.enumerables.length; i--;) {
+ k = Ext.enumerables[i];
+ if (a.hasOwnProperty(k)) {
+ fn.call(this, k, a[k]);
+ }
+ }
+ }
+ } else {
+ fn.call(this, a, b);
+ }
+
+ return this;
+ };
+ },
+
+ /**
+ * Create a new function from the provided
+var setValue = Ext.Function.flexSetter(function(name, value) {
+ this[name] = value;
+});
+
+// Afterwards
+// Setting a single name - value
+setValue('name1', 'value1');
+
+// Settings multiple name - value pairs
+setValue({
+ name1: 'value1',
+ name2: 'value2',
+ name3: 'value3'
+});
+ *
fn
, change this
to the provided scope, optionally
+ * overrides arguments for the call. (Defaults to the arguments passed by the caller)
+ *
+ * @param {Function} fn The function to delegate.
+ * @param {Object} scope (optional) The scope (this
reference) in which the function is executed.
+ * If omitted, defaults to the browser window.
+ * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
+ * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
+ * if a number the args are inserted at the specified position
+ * @return {Function} The new function
+ */
+ bind: function(fn, scope, args, appendArgs) {
+ var method = fn,
+ applyArgs;
+
+ return function() {
+ var callArgs = args || arguments;
+
+ if (appendArgs === true) {
+ callArgs = Array.prototype.slice.call(arguments, 0);
+ callArgs = callArgs.concat(args);
+ }
+ else if (Ext.isNumber(appendArgs)) {
+ callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
+ applyArgs = [appendArgs, 0].concat(args); // create method call params
+ Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
+ }
+
+ return method.apply(scope || window, callArgs);
+ };
+ },
+
+ /**
+ * Create a new function from the provided fn
, the arguments of which are pre-set to `args`.
+ * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
+ * This is especially useful when creating callbacks.
+ * For example:
+ *
+ var originalFunction = function(){
+ alert(Ext.Array.from(arguments).join(' '));
+ };
+
+ var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
+
+ callback(); // alerts 'Hello World'
+ callback('by Me'); // alerts 'Hello World by Me'
+
+ * @param {Function} fn The original function
+ * @param {Array} args The arguments to pass to new callback
+ * @param {Object} scope (optional) The scope (this
reference) in which the function is executed.
+ * @return {Function} The new callback function
+ */
+ pass: function(fn, args, scope) {
+ if (args) {
+ args = Ext.Array.from(args);
+ }
+
+ return function() {
+ return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
+ };
+ },
+
+ /**
+ * Create an alias to the provided method property with name methodName
of object
.
+ * Note that the execution scope will still be bound to the provided object
itself.
+ *
+ * @param {Object/Function} object
+ * @param {String} methodName
+ * @return {Function} aliasFn
+ */
+ alias: function(object, methodName) {
+ return function() {
+ return object[methodName].apply(object, arguments);
+ };
+ },
+
+ /**
+ * Creates an interceptor function. The passed function is called before the original one. If it returns false,
+ * the original one is not called. The resulting function returns the results of the original function.
+ * The passed function is called with the parameters of the original function. Example usage:
+ *
+ * @param {Function} origFn The original function.
+ * @param {Function} newFn The function to call before the original
+ * @param {Object} scope (optional) The scope (
+var sayHi = function(name){
+ alert('Hi, ' + name);
+}
+
+sayHi('Fred'); // alerts "Hi, Fred"
+
+// create a new function that validates input without
+// directly modifying the original function:
+var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
+ return name == 'Brian';
+});
+
+sayHiToFriend('Fred'); // no alert
+sayHiToFriend('Brian'); // alerts "Hi, Brian"
+
this
reference) in which the passed function is executed.
+ * If omitted, defaults to the scope in which the original function is called or the browser window.
+ * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
+ * @return {Function} The new function
+ */
+ createInterceptor: function(origFn, newFn, scope, returnValue) {
+ var method = origFn;
+ if (!Ext.isFunction(newFn)) {
+ return origFn;
+ }
+ else {
+ return function() {
+ var me = this,
+ args = arguments;
+ newFn.target = me;
+ newFn.method = origFn;
+ return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
+ };
+ }
+ },
+
+ /**
+ * Creates a delegate (callback) which, when called, executes after a specific delay.
+ * @param {Function} fn The function which will be called on a delay when the returned function is called.
+ * Optionally, a replacement (or additional) argument list may be specified.
+ * @param {Number} delay The number of milliseconds to defer execution by whenever called.
+ * @param {Object} scope (optional) The scope (this
reference) used by the function at execution time.
+ * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
+ * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
+ * if a number the args are inserted at the specified position.
+ * @return {Function} A function which, when called, executes the original function after the specified delay.
+ */
+ createDelayed: function(fn, delay, scope, args, appendArgs) {
+ if (scope || args) {
+ fn = Ext.Function.bind(fn, scope, args, appendArgs);
+ }
+ return function() {
+ var me = this;
+ setTimeout(function() {
+ fn.apply(me, arguments);
+ }, delay);
+ };
+ },
+
+ /**
+ * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
+ *
+ * @param {Function} fn The function to defer.
+ * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
+ * @param {Object} scope (optional) The scope (
+var sayHi = function(name){
+ alert('Hi, ' + name);
+}
+
+// executes immediately:
+sayHi('Fred');
+
+// executes after 2 seconds:
+Ext.Function.defer(sayHi, 2000, this, ['Fred']);
+
+// this syntax is sometimes useful for deferring
+// execution of an anonymous function:
+Ext.Function.defer(function(){
+ alert('Anonymous');
+}, 100);
+
this
reference) in which the function is executed.
+ * If omitted, defaults to the browser window.
+ * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
+ * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
+ * if a number the args are inserted at the specified position
+ * @return {Number} The timeout id that can be used with clearTimeout
+ */
+ defer: function(fn, millis, obj, args, appendArgs) {
+ fn = Ext.Function.bind(fn, obj, args, appendArgs);
+ if (millis > 0) {
+ return setTimeout(fn, millis);
+ }
+ fn();
+ return 0;
+ },
+
+ /**
+ * Create a combined function call sequence of the original function + the passed function.
+ * The resulting function returns the results of the original function.
+ * The passed function is called with the parameters of the original function. Example usage:
+ *
+ *
+ *
+ * @param {Function} origFn The original function.
+ * @param {Function} newFn The function to sequence
+ * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
+ * If omitted, defaults to the scope in which the original function is called or the browser window.
+ * @return {Function} The new function
+ */
+ createSequence: function(origFn, newFn, scope) {
+ if (!Ext.isFunction(newFn)) {
+ return origFn;
+ }
+ else {
+ return function() {
+ var retval = origFn.apply(this || window, arguments);
+ newFn.apply(scope || this || window, arguments);
+ return retval;
+ };
+ }
+ },
+
+ /**
+ *
+var sayHi = function(name){
+ alert('Hi, ' + name);
+}
+
+sayHi('Fred'); // alerts "Hi, Fred"
+
+var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
+ alert('Bye, ' + name);
+});
+
+sayGoodbye('Fred'); // both alerts show
+ *
this
reference) in which
+ * the passed function is executed. If omitted, defaults to the scope specified by the caller.
+ * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
+ * passed by the caller.
+ * @return {Function} A function which invokes the passed function after buffering for the specified time.
+ */
+ createBuffered: function(fn, buffer, scope, args) {
+ return function(){
+ var timerId;
+ return function() {
+ var me = this;
+ if (timerId) {
+ clearInterval(timerId);
+ timerId = null;
+ }
+ timerId = setTimeout(function(){
+ fn.apply(scope || me, args || arguments);
+ }, buffer);
+ };
+ }();
+ },
+
+ /**
+ * this
reference) in which
+ * the passed function is executed. If omitted, defaults to the scope specified by the caller.
+ * @returns {Function} A function which invokes the passed function at the specified interval.
+ */
+ createThrottled: function(fn, interval, scope) {
+ var lastCallTime, elapsed, lastArgs, timer, execute = function() {
+ fn.apply(scope || this, lastArgs);
+ lastCallTime = new Date().getTime();
+ };
+
+ return function() {
+ elapsed = new Date().getTime() - lastCallTime;
+ lastArgs = arguments;
+
+ clearTimeout(timer);
+ if (!lastCallTime || (elapsed >= interval)) {
+ execute();
+ } else {
+ timer = setTimeout(execute, interval - elapsed);
+ }
+ };
+ }
+};
+
+/**
+ * Shorthand for {@link Ext.Function#defer}
+ * @member Ext
+ * @method defer
+ */
+Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
+
+/**
+ * Shorthand for {@link Ext.Function#pass}
+ * @member Ext
+ * @method pass
+ */
+Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
+
+/**
+ * Shorthand for {@link Ext.Function#bind}
+ * @member Ext
+ * @method bind
+ */
+Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
+
+/**
+ * @author Jacky Nguyen
+Format Description Example returned values
+------ ----------------------------------------------------------------------- -----------------------
+ d Day of the month, 2 digits with leading zeros 01 to 31
+ D A short textual representation of the day of the week Mon to Sun
+ j Day of the month without leading zeros 1 to 31
+ l A full textual representation of the day of the week Sunday to Saturday
+ N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
+ S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
+ w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
+ z The day of the year (starting from 0) 0 to 364 (365 in leap years)
+ W ISO-8601 week number of year, weeks starting on Monday 01 to 53
+ F A full textual representation of a month, such as January or March January to December
+ m Numeric representation of a month, with leading zeros 01 to 12
+ M A short textual representation of a month Jan to Dec
+ n Numeric representation of a month, without leading zeros 1 to 12
+ t Number of days in the given month 28 to 31
+ L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
+ o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
+ belongs to the previous or next year, that year is used instead)
+ Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
+ y A two digit representation of a year Examples: 99 or 03
+ a Lowercase Ante meridiem and Post meridiem am or pm
+ A Uppercase Ante meridiem and Post meridiem AM or PM
+ g 12-hour format of an hour without leading zeros 1 to 12
+ G 24-hour format of an hour without leading zeros 0 to 23
+ h 12-hour format of an hour with leading zeros 01 to 12
+ H 24-hour format of an hour with leading zeros 00 to 23
+ i Minutes, with leading zeros 00 to 59
+ s Seconds, with leading zeros 00 to 59
+ u Decimal fraction of a second Examples:
+ (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
+ 100 (i.e. 0.100s) or
+ 999 (i.e. 0.999s) or
+ 999876543210 (i.e. 0.999876543210s)
+ O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
+ P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
+ T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
+ Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
+ c ISO 8601 date
+ Notes: Examples:
+ 1) If unspecified, the month / day defaults to the current month / day, 1991 or
+ the time defaults to midnight, while the timezone defaults to the 1992-10 or
+ browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
+ and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
+ are optional. 1995-07-18T17:21:28-02:00 or
+ 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
+ least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
+ of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
+ Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
+ date-time granularity which are supported, or see 2000-02-13T21:25:33
+ http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
+ U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
+ MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
+ \/Date(1238606590509+0800)\/
+
+ *
+ * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
+ *
+ *
+ * Here are some standard date/time patterns that you might find helpful. They
+ * are not part of the source of Ext.Date, but to use them you can simply copy this
+ * block of code into any script that is included after Ext.Date and they will also become
+ * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
+ *
+// Sample date:
+// 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
+
+var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
+console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
+console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
+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
+
+ *
+ * Example usage:
+ *
+Ext.Date.patterns = {
+ ISO8601Long:"Y-m-d H:i:s",
+ ISO8601Short:"Y-m-d",
+ ShortDate: "n/j/Y",
+ LongDate: "l, F d, Y",
+ FullDateTime: "l, F d, Y g:i:s A",
+ MonthDay: "F d",
+ ShortTime: "g:i A",
+ LongTime: "g:i:s A",
+ SortableDateTime: "Y-m-d\\TH:i:s",
+ UniversalSortableDateTime: "Y-m-d H:i:sO",
+ YearMonth: "F, Y"
+};
+
+ *
+var dt = new Date();
+console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
+
+ *
+Ext.Date.parseFunctions['x-date-format'] = myDateParser;
+
+ *
date
: Stringstrict
: Boolean
To enable Dates to also be formatted according to that format, a corresponding + * formatting function must be placed into the {@link #formatFunctions} property. + * @property parseFunctions + * @static + * @type Object + */ + parseFunctions: { + "MS": function(input, strict) { + // note: the timezone offset is ignored since the MS Ajax server sends + // a UTC milliseconds-since-Unix-epoch value (negative values are allowed) + var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/'); + var r = (input || '').match(re); + return r? new Date(((r[1] || '') + r[2]) * 1) : null; + } + }, + parseRegexes: [], + + /** + *
An object hash in which each property is a date formatting function. The property name is the + * format string which corresponds to the produced formatted date string.
+ *This object is automatically populated with date formatting functions as + * date formats are requested for Ext standard formatting strings.
+ *Custom formatting functions may be inserted into this object, keyed by a name which from then on + * may be used as a format string to {@link #format}. Example:
+Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
+
+ * A formatting function should return a string representation of the passed Date object, and is passed the following parameters:
date
: DateTo enable date strings to also be parsed according to that format, a corresponding + * parsing function must be placed into the {@link #parseFunctions} property. + * @property formatFunctions + * @static + * @type Object + */ + formatFunctions: { + "MS": function() { + // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF)) + return '\\/Date(' + this.getTime() + ')\\/'; + } + }, + + y2kYear : 50, + + /** + * Date interval constant + * @static + * @type String + */ + MILLI : "ms", + + /** + * Date interval constant + * @static + * @type String + */ + SECOND : "s", + + /** + * Date interval constant + * @static + * @type String + */ + MINUTE : "mi", + + /** Date interval constant + * @static + * @type String + */ + HOUR : "h", + + /** + * Date interval constant + * @static + * @type String + */ + DAY : "d", + + /** + * Date interval constant + * @static + * @type String + */ + MONTH : "mo", + + /** + * Date interval constant + * @static + * @type String + */ + YEAR : "y", + + /** + *
An object hash containing default date values used during date parsing.
+ *The following properties are available:
y
: Numberm
: Numberd
: Numberh
: Numberi
: Numbers
: Numberms
: NumberOverride these properties to customize the default date values used by the {@link #parse} method.
+ *Note: In countries which experience Daylight Saving Time (i.e. DST), the h, i, s + * and ms properties may coincide with the exact time in which DST takes effect. + * It is the responsiblity of the developer to account for this.
+ * Example Usage: + *
+// set default day value to the first day of the month
+Ext.Date.defaults.d = 1;
+
+// parse a February date string containing only year and month values.
+// setting the default day value to 1 prevents weird date rollover issues
+// when attempting to parse the following date string on, for example, March 31st 2009.
+Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
+
+ * @property defaults
+ * @static
+ * @type Object
+ */
+ defaults: {},
+
+ /**
+ * An array of textual day names.
+ * Override these values for international dates.
+ * Example:
+ *
+Ext.Date.dayNames = [
+ 'SundayInYourLang',
+ 'MondayInYourLang',
+ ...
+];
+
+ * @type Array
+ * @static
+ */
+ dayNames : [
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday"
+ ],
+
+ /**
+ * An array of textual month names.
+ * Override these values for international dates.
+ * Example:
+ *
+Ext.Date.monthNames = [
+ 'JanInYourLang',
+ 'FebInYourLang',
+ ...
+];
+
+ * @type Array
+ * @static
+ */
+ monthNames : [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December"
+ ],
+
+ /**
+ * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
+ * Override these values for international dates.
+ * Example:
+ *
+Ext.Date.monthNumbers = {
+ 'ShortJanNameInYourLang':0,
+ 'ShortFebNameInYourLang':1,
+ ...
+};
+
+ * @type Object
+ * @static
+ */
+ monthNumbers : {
+ Jan:0,
+ Feb:1,
+ Mar:2,
+ Apr:3,
+ May:4,
+ Jun:5,
+ Jul:6,
+ Aug:7,
+ Sep:8,
+ Oct:9,
+ Nov:10,
+ Dec:11
+ },
+ /**
+ * The date format string that the {@link #dateRenderer} and {@link #date} functions use. + * see {@link #Date} for details.
+ *This defaults to m/d/Y
, but may be overridden in a locale file.
+Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
+console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
+
+ * @type Object
+ * @static
+ */
+ formatCodes : {
+ d: "Ext.String.leftPad(this.getDate(), 2, '0')",
+ D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
+ j: "this.getDate()",
+ l: "Ext.Date.dayNames[this.getDay()]",
+ N: "(this.getDay() ? this.getDay() : 7)",
+ S: "Ext.Date.getSuffix(this)",
+ w: "this.getDay()",
+ z: "Ext.Date.getDayOfYear(this)",
+ W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
+ F: "Ext.Date.monthNames[this.getMonth()]",
+ m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
+ M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
+ n: "(this.getMonth() + 1)",
+ t: "Ext.Date.getDaysInMonth(this)",
+ L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
+ o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
+ Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
+ y: "('' + this.getFullYear()).substring(2, 4)",
+ a: "(this.getHours() < 12 ? 'am' : 'pm')",
+ A: "(this.getHours() < 12 ? 'AM' : 'PM')",
+ g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
+ G: "this.getHours()",
+ h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
+ H: "Ext.String.leftPad(this.getHours(), 2, '0')",
+ i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
+ s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
+ u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
+ O: "Ext.Date.getGMTOffset(this)",
+ P: "Ext.Date.getGMTOffset(this, true)",
+ T: "Ext.Date.getTimezone(this)",
+ Z: "(this.getTimezoneOffset() * -60)",
+
+ c: function() { // ISO-8601 -- GMT format
+ for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
+ var e = c.charAt(i);
+ code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
+ }
+ return code.join(" + ");
+ },
+ /*
+ c: function() { // ISO-8601 -- UTC format
+ return [
+ "this.getUTCFullYear()", "'-'",
+ "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
+ "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
+ "'T'",
+ "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
+ "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
+ "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
+ "'Z'"
+ ].join(" + ");
+ },
+ */
+
+ U: "Math.round(this.getTime() / 1000)"
+ },
+
+ /**
+ * Checks if the passed Date parameters will cause a javascript Date "rollover".
+ * @param {Number} year 4-digit year
+ * @param {Number} month 1-based month-of-year
+ * @param {Number} day Day of month
+ * @param {Number} hour (optional) Hour
+ * @param {Number} minute (optional) Minute
+ * @param {Number} second (optional) Second
+ * @param {Number} millisecond (optional) Millisecond
+ * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
+ * @static
+ */
+ isValid : function(y, m, d, h, i, s, ms) {
+ // setup defaults
+ h = h || 0;
+ i = i || 0;
+ s = s || 0;
+ ms = ms || 0;
+
+ // Special handling for year < 100
+ var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
+
+ return y == dt.getFullYear() &&
+ m == dt.getMonth() + 1 &&
+ d == dt.getDate() &&
+ h == dt.getHours() &&
+ i == dt.getMinutes() &&
+ s == dt.getSeconds() &&
+ ms == dt.getMilliseconds();
+ },
+
+ /**
+ * Parses the passed string using the specified date format.
+ * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
+ * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
+ * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
+ * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
+ * Keep in mind that the input date string must precisely match the specified format string
+ * in order for the parse operation to be successful (failed parse operations return a null value).
+ * Example:
+//dt = Fri May 25 2007 (current date)
+var dt = new Date();
+
+//dt = Thu May 25 2006 (today's month/day in 2006)
+dt = Ext.Date.parse("2006", "Y");
+
+//dt = Sun Jan 15 2006 (all date parts specified)
+dt = Ext.Date.parse("2006-01-15", "Y-m-d");
+
+//dt = Sun Jan 15 2006 15:20:01
+dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
+
+// attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
+dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
+
+ * @param {String} input The raw date string.
+ * @param {String} format The expected date string format.
+ * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
+ (defaults to false). Invalid date strings will return null when parsed.
+ * @return {Date} The parsed Date.
+ * @static
+ */
+ parse : function(input, format, strict) {
+ var p = utilDate.parseFunctions;
+ if (p[format] == null) {
+ utilDate.createParser(format);
+ }
+ return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
+ },
+
+ // Backwards compat
+ parseDate: function(input, format, strict){
+ return utilDate.parse(input, format, strict);
+ },
+
+
+ // private
+ getFormatCode : function(character) {
+ var f = utilDate.formatCodes[character];
+
+ if (f) {
+ f = typeof f == 'function'? f() : f;
+ utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
+ }
+
+ // note: unknown characters are treated as literals
+ return f || ("'" + Ext.String.escape(character) + "'");
+ },
+
+ // private
+ createFormat : function(format) {
+ var code = [],
+ special = false,
+ ch = '';
+
+ for (var i = 0; i < format.length; ++i) {
+ ch = format.charAt(i);
+ if (!special && ch == "\\") {
+ special = true;
+ } else if (special) {
+ special = false;
+ code.push("'" + Ext.String.escape(ch) + "'");
+ } else {
+ code.push(utilDate.getFormatCode(ch));
+ }
+ }
+ utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
+ },
+
+ // private
+ createParser : (function() {
+ var code = [
+ "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
+ "def = Ext.Date.defaults,",
+ "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
+
+ "if(results){",
+ "{1}",
+
+ "if(u != null){", // i.e. unix time is defined
+ "v = new Date(u * 1000);", // give top priority to UNIX time
+ "}else{",
+ // create Date object representing midnight of the current day;
+ // this will provide us with our date defaults
+ // (note: clearTime() handles Daylight Saving Time automatically)
+ "dt = Ext.Date.clearTime(new Date);",
+
+ // date calculations (note: these calculations create a dependency on Ext.Number.from())
+ "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
+ "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
+ "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
+
+ // time calculations (note: these calculations create a dependency on Ext.Number.from())
+ "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
+ "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
+ "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
+ "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
+
+ "if(z >= 0 && y >= 0){",
+ // both the year and zero-based day of year are defined and >= 0.
+ // these 2 values alone provide sufficient info to create a full date object
+
+ // create Date object representing January 1st for the given year
+ // handle years < 100 appropriately
+ "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
+
+ // then add day of year, checking for Date "rollover" if necessary
+ "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
+ "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
+ "v = null;", // invalid date, so return null
+ "}else{",
+ // plain old Date object
+ // handle years < 100 properly
+ "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
+ "}",
+ "}",
+ "}",
+
+ "if(v){",
+ // favour UTC offset over GMT offset
+ "if(zz != null){",
+ // reset to UTC, then add offset
+ "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
+ "}else if(o){",
+ // reset to GMT, then add offset
+ "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
+ "}",
+ "}",
+
+ "return v;"
+ ].join('\n');
+
+ return function(format) {
+ var regexNum = utilDate.parseRegexes.length,
+ currentGroup = 1,
+ calc = [],
+ regex = [],
+ special = false,
+ ch = "";
+
+ for (var i = 0; i < format.length; ++i) {
+ ch = format.charAt(i);
+ if (!special && ch == "\\") {
+ special = true;
+ } else if (special) {
+ special = false;
+ regex.push(Ext.String.escape(ch));
+ } else {
+ var obj = utilDate.formatCodeToRegex(ch, currentGroup);
+ currentGroup += obj.g;
+ regex.push(obj.s);
+ if (obj.g && obj.c) {
+ calc.push(obj.c);
+ }
+ }
+ }
+
+ utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
+ utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
+ };
+ })(),
+
+ // private
+ parseCodes : {
+ /*
+ * Notes:
+ * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
+ * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
+ * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
+ */
+ d: {
+ g:1,
+ c:"d = parseInt(results[{0}], 10);\n",
+ s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
+ },
+ j: {
+ g:1,
+ c:"d = parseInt(results[{0}], 10);\n",
+ s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
+ },
+ D: function() {
+ for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
+ return {
+ g:0,
+ c:null,
+ s:"(?:" + a.join("|") +")"
+ };
+ },
+ l: function() {
+ return {
+ g:0,
+ c:null,
+ s:"(?:" + utilDate.dayNames.join("|") + ")"
+ };
+ },
+ N: {
+ g:0,
+ c:null,
+ s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
+ },
+ S: {
+ g:0,
+ c:null,
+ s:"(?:st|nd|rd|th)"
+ },
+ w: {
+ g:0,
+ c:null,
+ s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
+ },
+ z: {
+ g:1,
+ c:"z = parseInt(results[{0}], 10);\n",
+ s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
+ },
+ W: {
+ g:0,
+ c:null,
+ s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
+ },
+ F: function() {
+ return {
+ g:1,
+ c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
+ s:"(" + utilDate.monthNames.join("|") + ")"
+ };
+ },
+ M: function() {
+ for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
+ return Ext.applyIf({
+ s:"(" + a.join("|") + ")"
+ }, utilDate.formatCodeToRegex("F"));
+ },
+ m: {
+ g:1,
+ c:"m = parseInt(results[{0}], 10) - 1;\n",
+ s:"(\\d{2})" // month number with leading zeros (01 - 12)
+ },
+ n: {
+ g:1,
+ c:"m = parseInt(results[{0}], 10) - 1;\n",
+ s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
+ },
+ t: {
+ g:0,
+ c:null,
+ s:"(?:\\d{2})" // no. of days in the month (28 - 31)
+ },
+ L: {
+ g:0,
+ c:null,
+ s:"(?:1|0)"
+ },
+ o: function() {
+ return utilDate.formatCodeToRegex("Y");
+ },
+ Y: {
+ g:1,
+ c:"y = parseInt(results[{0}], 10);\n",
+ s:"(\\d{4})" // 4-digit year
+ },
+ y: {
+ g:1,
+ c:"var ty = parseInt(results[{0}], 10);\n"
+ + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
+ s:"(\\d{1,2})"
+ },
+ /**
+ * In the am/pm parsing routines, we allow both upper and lower case
+ * even though it doesn't exactly match the spec. It gives much more flexibility
+ * in being able to specify case insensitive regexes.
+ */
+ a: {
+ g:1,
+ c:"if (/(am)/i.test(results[{0}])) {\n"
+ + "if (!h || h == 12) { h = 0; }\n"
+ + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
+ s:"(am|pm|AM|PM)"
+ },
+ A: {
+ g:1,
+ c:"if (/(am)/i.test(results[{0}])) {\n"
+ + "if (!h || h == 12) { h = 0; }\n"
+ + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
+ s:"(AM|PM|am|pm)"
+ },
+ g: function() {
+ return utilDate.formatCodeToRegex("G");
+ },
+ G: {
+ g:1,
+ c:"h = parseInt(results[{0}], 10);\n",
+ s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
+ },
+ h: function() {
+ return utilDate.formatCodeToRegex("H");
+ },
+ H: {
+ g:1,
+ c:"h = parseInt(results[{0}], 10);\n",
+ s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
+ },
+ i: {
+ g:1,
+ c:"i = parseInt(results[{0}], 10);\n",
+ s:"(\\d{2})" // minutes with leading zeros (00 - 59)
+ },
+ s: {
+ g:1,
+ c:"s = parseInt(results[{0}], 10);\n",
+ s:"(\\d{2})" // seconds with leading zeros (00 - 59)
+ },
+ u: {
+ g:1,
+ c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
+ s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
+ },
+ O: {
+ g:1,
+ c:[
+ "o = results[{0}];",
+ "var sn = o.substring(0,1),", // get + / - sign
+ "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
+ "mn = o.substring(3,5) % 60;", // get minutes
+ "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
+ ].join("\n"),
+ s: "([+\-]\\d{4})" // GMT offset in hrs and mins
+ },
+ P: {
+ g:1,
+ c:[
+ "o = results[{0}];",
+ "var sn = o.substring(0,1),", // get + / - sign
+ "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
+ "mn = o.substring(4,6) % 60;", // get minutes
+ "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
+ ].join("\n"),
+ s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
+ },
+ T: {
+ g:0,
+ c:null,
+ s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
+ },
+ Z: {
+ g:1,
+ c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
+ + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
+ s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
+ },
+ c: function() {
+ var calc = [],
+ arr = [
+ utilDate.formatCodeToRegex("Y", 1), // year
+ utilDate.formatCodeToRegex("m", 2), // month
+ utilDate.formatCodeToRegex("d", 3), // day
+ utilDate.formatCodeToRegex("h", 4), // hour
+ utilDate.formatCodeToRegex("i", 5), // minute
+ utilDate.formatCodeToRegex("s", 6), // second
+ {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)
+ {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
+ "if(results[8]) {", // timezone specified
+ "if(results[8] == 'Z'){",
+ "zz = 0;", // UTC
+ "}else if (results[8].indexOf(':') > -1){",
+ utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
+ "}else{",
+ utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
+ "}",
+ "}"
+ ].join('\n')}
+ ];
+
+ for (var i = 0, l = arr.length; i < l; ++i) {
+ calc.push(arr[i].c);
+ }
+
+ return {
+ g:1,
+ c:calc.join(""),
+ s:[
+ arr[0].s, // year (required)
+ "(?:", "-", arr[1].s, // month (optional)
+ "(?:", "-", arr[2].s, // day (optional)
+ "(?:",
+ "(?:T| )?", // time delimiter -- either a "T" or a single blank space
+ 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
+ "(?::", arr[5].s, ")?", // seconds (optional)
+ "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
+ "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
+ ")?",
+ ")?",
+ ")?"
+ ].join("")
+ };
+ },
+ U: {
+ g:1,
+ c:"u = parseInt(results[{0}], 10);\n",
+ s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
+ }
+ },
+
+ //Old Ext.Date prototype methods.
+ // private
+ dateFormat: function(date, format) {
+ return utilDate.format(date, format);
+ },
+
+ /**
+ * Formats a date given the supplied format string.
+ * @param {Date} date The date to format
+ * @param {String} format The format string
+ * @return {String} The formatted date
+ */
+ format: function(date, format) {
+ if (utilDate.formatFunctions[format] == null) {
+ utilDate.createFormat(format);
+ }
+ var result = utilDate.formatFunctions[format].call(date);
+ return result + '';
+ },
+
+ /**
+ * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
+ *
+ * Note: The date string returned by the javascript Date object's toString() method varies
+ * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
+ * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
+ * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
+ * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
+ * from the GMT offset portion of the date string.
+ * @param {Date} date The date
+ * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
+ */
+ getTimezone : function(date) {
+ // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
+ //
+ // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
+ // 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)
+ // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
+ // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
+ // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
+ //
+ // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
+ // step 1: (?:\((.*)\) -- find timezone in parentheses
+ // 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
+ // step 3: remove all non uppercase characters found in step 1 and 2
+ return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
+ },
+
+ /**
+ * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
+ * @param {Date} date The date
+ * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
+ * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
+ */
+ getGMTOffset : function(date, colon) {
+ var offset = date.getTimezoneOffset();
+ return (offset > 0 ? "-" : "+")
+ + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
+ + (colon ? ":" : "")
+ + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
+ },
+
+ /**
+ * Get the numeric day number of the year, adjusted for leap year.
+ * @param {Date} date The date
+ * @return {Number} 0 to 364 (365 in leap years).
+ */
+ getDayOfYear: function(date) {
+ var num = 0,
+ d = Ext.Date.clone(date),
+ m = date.getMonth(),
+ i;
+
+ for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
+ num += utilDate.getDaysInMonth(d);
+ }
+ return num + date.getDate() - 1;
+ },
+
+ /**
+ * Get the numeric ISO-8601 week number of the year.
+ * (equivalent to the format specifier 'W', but without a leading zero).
+ * @param {Date} date The date
+ * @return {Number} 1 to 53
+ */
+ getWeekOfYear : (function() {
+ // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
+ var ms1d = 864e5, // milliseconds in a day
+ ms7d = 7 * ms1d; // milliseconds in a week
+
+ return function(date) { // return a closure so constants get calculated only once
+ var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
+ AWN = Math.floor(DC3 / 7), // an Absolute Week Number
+ Wyr = new Date(AWN * ms7d).getUTCFullYear();
+
+ return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
+ };
+ })(),
+
+ /**
+ * Checks if the current date falls within a leap year.
+ * @param {Date} date The date
+ * @return {Boolean} True if the current date falls within a leap year, false otherwise.
+ */
+ isLeapYear : function(date) {
+ var year = date.getFullYear();
+ return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
+ },
+
+ /**
+ * Get the first day of the current month, adjusted for leap year. The returned value
+ * is the numeric day index within the week (0-6) which can be used in conjunction with
+ * the {@link #monthNames} array to retrieve the textual day name.
+ * Example:
+ *
+var dt = new Date('1/10/2007'),
+ firstDay = Ext.Date.getFirstDayOfMonth(dt);
+console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
+ *
+ * @param {Date} date The date
+ * @return {Number} The day number (0-6).
+ */
+ getFirstDayOfMonth : function(date) {
+ var day = (date.getDay() - (date.getDate() - 1)) % 7;
+ return (day < 0) ? (day + 7) : day;
+ },
+
+ /**
+ * Get the last day of the current month, adjusted for leap year. The returned value
+ * is the numeric day index within the week (0-6) which can be used in conjunction with
+ * the {@link #monthNames} array to retrieve the textual day name.
+ * Example:
+ *
+var dt = new Date('1/10/2007'),
+ lastDay = Ext.Date.getLastDayOfMonth(dt);
+console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
+ *
+ * @param {Date} date The date
+ * @return {Number} The day number (0-6).
+ */
+ getLastDayOfMonth : function(date) {
+ return utilDate.getLastDateOfMonth(date).getDay();
+ },
+
+
+ /**
+ * Get the date of the first day of the month in which this date resides.
+ * @param {Date} date The date
+ * @return {Date}
+ */
+ getFirstDateOfMonth : function(date) {
+ return new Date(date.getFullYear(), date.getMonth(), 1);
+ },
+
+ /**
+ * Get the date of the last day of the month in which this date resides.
+ * @param {Date} date The date
+ * @return {Date}
+ */
+ getLastDateOfMonth : function(date) {
+ return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
+ },
+
+ /**
+ * Get the number of days in the current month, adjusted for leap year.
+ * @param {Date} date The date
+ * @return {Number} The number of days in the month.
+ */
+ getDaysInMonth: (function() {
+ var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+ return function(date) { // return a closure for efficiency
+ var m = date.getMonth();
+
+ return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
+ };
+ })(),
+
+ /**
+ * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
+ * @param {Date} date The date
+ * @return {String} 'st, 'nd', 'rd' or 'th'.
+ */
+ getSuffix : function(date) {
+ switch (date.getDate()) {
+ case 1:
+ case 21:
+ case 31:
+ return "st";
+ case 2:
+ case 22:
+ return "nd";
+ case 3:
+ case 23:
+ return "rd";
+ default:
+ return "th";
+ }
+ },
+
+ /**
+ * Creates and returns a new Date instance with the exact same date value as the called instance.
+ * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
+ * variable will also be changed. When the intention is to create a new variable that will not
+ * modify the original instance, you should create a clone.
+ *
+ * Example of correctly cloning a date:
+ *
+//wrong way:
+var orig = new Date('10/1/2006');
+var copy = orig;
+copy.setDate(5);
+console.log(orig); //returns 'Thu Oct 05 2006'!
+
+//correct way:
+var orig = new Date('10/1/2006'),
+ copy = Ext.Date.clone(orig);
+copy.setDate(5);
+console.log(orig); //returns 'Thu Oct 01 2006'
+ *
+ * @param {Date} date The date
+ * @return {Date} The new Date instance.
+ */
+ clone : function(date) {
+ return new Date(date.getTime());
+ },
+
+ /**
+ * Checks if the current date is affected by Daylight Saving Time (DST).
+ * @param {Date} date The date
+ * @return {Boolean} True if the current date is affected by DST.
+ */
+ isDST : function(date) {
+ // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
+ // courtesy of @geoffrey.mcgill
+ return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
+ },
+
+ /**
+ * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
+ * automatically adjusting for Daylight Saving Time (DST) where applicable.
+ * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
+ * @param {Date} date The date
+ * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
+ * @return {Date} this or the clone.
+ */
+ clearTime : function(date, clone) {
+ if (clone) {
+ return Ext.Date.clearTime(Ext.Date.clone(date));
+ }
+
+ // get current date before clearing time
+ var d = date.getDate();
+
+ // clear time
+ date.setHours(0);
+ date.setMinutes(0);
+ date.setSeconds(0);
+ date.setMilliseconds(0);
+
+ if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
+ // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
+ // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
+
+ // increment hour until cloned date == current date
+ for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
+
+ date.setDate(d);
+ date.setHours(c.getHours());
+ }
+
+ return date;
+ },
+
+ /**
+ * Provides a convenient method for performing basic date arithmetic. This method
+ * does not modify the Date instance being called - it creates and returns
+ * a new Date instance containing the resulting date value.
+ *
+ * Examples:
+ *
+// Basic usage:
+var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
+console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
+
+// Negative values will be subtracted:
+var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
+console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
+
+ *
+ *
+ * @param {Date} date The date to modify
+ * @param {String} interval A valid date interval enum value.
+ * @param {Number} value The amount to add to the current date.
+ * @return {Date} The new Date instance.
+ */
+ add : function(date, interval, value) {
+ var d = Ext.Date.clone(date),
+ Date = Ext.Date;
+ if (!interval || value === 0) return d;
+
+ switch(interval.toLowerCase()) {
+ case Ext.Date.MILLI:
+ d.setMilliseconds(d.getMilliseconds() + value);
+ break;
+ case Ext.Date.SECOND:
+ d.setSeconds(d.getSeconds() + value);
+ break;
+ case Ext.Date.MINUTE:
+ d.setMinutes(d.getMinutes() + value);
+ break;
+ case Ext.Date.HOUR:
+ d.setHours(d.getHours() + value);
+ break;
+ case Ext.Date.DAY:
+ d.setDate(d.getDate() + value);
+ break;
+ case Ext.Date.MONTH:
+ var day = date.getDate();
+ if (day > 28) {
+ day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
+ }
+ d.setDate(day);
+ d.setMonth(date.getMonth() + value);
+ break;
+ case Ext.Date.YEAR:
+ d.setFullYear(date.getFullYear() + value);
+ break;
+ }
+ return d;
+ },
+
+ /**
+ * Checks if a date falls on or between the given start and end dates.
+ * @param {Date} date The date to check
+ * @param {Date} start Start date
+ * @param {Date} end End date
+ * @return {Boolean} true if this date falls on or between the given start and end dates.
+ */
+ between : function(date, start, end) {
+ var t = date.getTime();
+ return start.getTime() <= t && t <= end.getTime();
+ },
+
+ //Maintains compatibility with old static and prototype window.Date methods.
+ compat: function() {
+ var nativeDate = window.Date,
+ p, u,
+ 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'],
+ proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
+
+ //Append statics
+ Ext.Array.forEach(statics, function(s) {
+ nativeDate[s] = utilDate[s];
+ });
+
+ //Append to prototype
+ Ext.Array.forEach(proto, function(s) {
+ nativeDate.prototype[s] = function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(this);
+ return utilDate[s].apply(utilDate, args);
+ };
+ });
+ }
+};
+
+var utilDate = Ext.Date;
+
+})();
+
+/**
+ * @author Jacky Nguyen Other.awesome.Class
+ * will simply be loaded from ./Other/awesome/Class.js
+ */
+ paths: {
+ 'Ext': '.'
+ }
+ },
+
+ /**
+ * Set the configuration for the loader. This should be called right after ext-core.js
+ * (or ext-core-debug.js) is included in the page, i.e:
+
+
+
+
+ * Refer to {@link Ext.Loader#configs} for the list of possible properties
+ *
+ * @param {Object} config The config object to override the default values in {@link Ext.Loader#config}
+ * @return {Ext.Loader} this
+ * @markdown
+ */
+ setConfig: function(name, value) {
+ if (Ext.isObject(name) && arguments.length === 1) {
+ Ext.Object.merge(this.config, name);
+ }
+ else {
+ this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
+ }
+
+ return this;
+ },
+
+ /**
+ * Get the config value corresponding to the specified name. If no name is given, will return the config object
+ * @param {String} name The config property name
+ * @return {Object/Mixed}
+ */
+ getConfig: function(name) {
+ if (name) {
+ return this.config[name];
+ }
+
+ return this.config;
+ },
+
+ /**
+ * Sets the path of a namespace.
+ * For Example:
+
+ Ext.Loader.setPath('Ext', '.');
+
+ * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
+ * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
+ * @return {Ext.Loader} this
+ * @markdown
+ */
+ setPath: flexSetter(function(name, path) {
+ this.config.paths[name] = path;
+
+ return this;
+ }),
+
+ /**
+ * Translates a className to a file path by adding the
+ * the proper prefix and converting the .'s to /'s. For example:
+
+ Ext.Loader.setPath('My', '/path/to/My');
+
+ alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
+
+ * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
+
+ Ext.Loader.setPath({
+ 'My': '/path/to/lib',
+ 'My.awesome': '/other/path/for/awesome/stuff',
+ 'My.awesome.more': '/more/awesome/path'
+ });
+
+ alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
+
+ alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
+
+ alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
+
+ alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
+
+ * @param {String} className
+ * @return {String} path
+ * @markdown
+ */
+ getPath: function(className) {
+ var path = '',
+ paths = this.config.paths,
+ prefix = this.getPrefix(className);
+
+ if (prefix.length > 0) {
+ if (prefix === className) {
+ return paths[prefix];
+ }
+
+ path = paths[prefix];
+ className = className.substring(prefix.length + 1);
+ }
+
+ if (path.length > 0) {
+ path += '/';
+ }
+
+ return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
+ },
+
+ /**
+ * @private
+ * @param {String} className
+ */
+ getPrefix: function(className) {
+ var paths = this.config.paths,
+ prefix, deepestPrefix = '';
+
+ if (paths.hasOwnProperty(className)) {
+ return className;
+ }
+
+ for (prefix in paths) {
+ if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
+ if (prefix.length > deepestPrefix.length) {
+ deepestPrefix = prefix;
+ }
+ }
+ }
+
+ return deepestPrefix;
+ },
+
+ /**
+ * Refresh all items in the queue. If all dependencies for an item exist during looping,
+ * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
+ * empty
+ * @private
+ */
+ refreshQueue: function() {
+ var ln = this.queue.length,
+ i, item, j, requires;
+
+ if (ln === 0) {
+ this.triggerReady();
+ return;
+ }
+
+ for (i = 0; i < ln; i++) {
+ item = this.queue[i];
+
+ if (item) {
+ requires = item.requires;
+
+ // Don't bother checking when the number of files loaded
+ // is still less than the array length
+ if (requires.length > this.numLoadedFiles) {
+ continue;
+ }
+
+ j = 0;
+
+ do {
+ if (Manager.isCreated(requires[j])) {
+ // Take out from the queue
+ requires.splice(j, 1);
+ }
+ else {
+ j++;
+ }
+ } while (j < requires.length);
+
+ if (item.requires.length === 0) {
+ this.queue.splice(i, 1);
+ item.callback.call(item.scope);
+ this.refreshQueue();
+ break;
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Inject a script element to document's head, call onLoad and onError accordingly
+ * @private
+ */
+ injectScriptElement: function(url, onLoad, onError, scope) {
+ var script = document.createElement('script'),
+ me = this,
+ onLoadFn = function() {
+ me.cleanupScriptElement(script);
+ onLoad.call(scope);
+ },
+ onErrorFn = function() {
+ me.cleanupScriptElement(script);
+ onError.call(scope);
+ };
+
+ script.type = 'text/javascript';
+ script.src = url;
+ script.onload = onLoadFn;
+ script.onerror = onErrorFn;
+ script.onreadystatechange = function() {
+ if (this.readyState === 'loaded' || this.readyState === 'complete') {
+ onLoadFn();
+ }
+ };
+
+ this.documentHead.appendChild(script);
+
+ return script;
+ },
+
+ /**
+ * @private
+ */
+ cleanupScriptElement: function(script) {
+ script.onload = null;
+ script.onreadystatechange = null;
+ script.onerror = null;
+
+ return this;
+ },
+
+ /**
+ * Load a script file, supports both asynchronous and synchronous approaches
+ *
+ * @param {String} url
+ * @param {Function} onLoad
+ * @param {Scope} scope
+ * @param {Boolean} synchronous
+ * @private
+ */
+ loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
+ var me = this,
+ noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
+ fileName = url.split('/').pop(),
+ isCrossOriginRestricted = false,
+ xhr, status, onScriptError;
+
+ scope = scope || this;
+
+ this.isLoading = true;
+
+ if (!synchronous) {
+ onScriptError = function() {
+ onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
+ };
+
+ if (!Ext.isReady && Ext.onDocumentReady) {
+ Ext.onDocumentReady(function() {
+ me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
+ });
+ }
+ else {
+ this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
+ }
+ }
+ else {
+ if (typeof XMLHttpRequest !== 'undefined') {
+ xhr = new XMLHttpRequest();
+ } else {
+ xhr = new ActiveXObject('Microsoft.XMLHTTP');
+ }
+
+ try {
+ xhr.open('GET', noCacheUrl, false);
+ xhr.send(null);
+ } catch (e) {
+ isCrossOriginRestricted = true;
+ }
+
+ status = (xhr.status === 1223) ? 204 : xhr.status;
+
+ if (!isCrossOriginRestricted) {
+ isCrossOriginRestricted = (status === 0);
+ }
+
+ if (isCrossOriginRestricted
+ ) {
+ onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
+ "being loaded from a different domain or from the local file system whereby cross origin " +
+ "requests are not allowed due to security reasons. Use asynchronous loading with " +
+ "Ext.require instead.", synchronous);
+ }
+ else if (status >= 200 && status < 300
+ ) {
+ // Firebug friendly, file names are still shown even though they're eval'ed code
+ new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
+
+ onLoad.call(scope);
+ }
+ else {
+ onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
+ "verify that the file exists. " +
+ "XHR status code: " + status, synchronous);
+ }
+
+ // Prevent potential IE memory leak
+ xhr = null;
+ }
+ },
+
+ /**
+ * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
+ * Can be chained with more `require` and `exclude` methods, eg:
+
+ Ext.exclude('Ext.data.*').require('*');
+
+ Ext.exclude('widget.button*').require('widget.*');
+
+ * @param {Array} excludes
+ * @return {Object} object contains `require` method for chaining
+ * @markdown
+ */
+ exclude: function(excludes) {
+ var me = this;
+
+ return {
+ require: function(expressions, fn, scope) {
+ return me.require(expressions, fn, scope, excludes);
+ },
+
+ syncRequire: function(expressions, fn, scope) {
+ return me.syncRequire(expressions, fn, scope, excludes);
+ }
+ };
+ },
+
+ /**
+ * 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
+ * @param {String/Array} expressions Can either be a string or an array of string
+ * @param {Function} fn (Optional) The callback function
+ * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
+ * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
+ * @markdown
+ */
+ syncRequire: function() {
+ this.syncModeEnabled = true;
+ this.require.apply(this, arguments);
+ this.refreshQueue();
+ this.syncModeEnabled = false;
+ },
+
+ /**
+ * 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#require Ext.require} for convenience
+ * @param {String/Array} expressions Can either be a string or an array of string
+ * @param {Function} fn (Optional) The callback function
+ * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
+ * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
+ * @markdown
+ */
+ require: function(expressions, fn, scope, excludes) {
+ var filePath, expression, exclude, className, excluded = {},
+ excludedClassNames = [],
+ possibleClassNames = [],
+ possibleClassName, classNames = [],
+ i, j, ln, subLn;
+
+ expressions = Ext.Array.from(expressions);
+ excludes = Ext.Array.from(excludes);
+
+ fn = fn || Ext.emptyFn;
+
+ scope = scope || Ext.global;
+
+ for (i = 0, ln = excludes.length; i < ln; i++) {
+ exclude = excludes[i];
+
+ if (typeof exclude === 'string' && exclude.length > 0) {
+ excludedClassNames = Manager.getNamesByExpression(exclude);
+
+ for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
+ excluded[excludedClassNames[j]] = true;
+ }
+ }
+ }
+
+ for (i = 0, ln = expressions.length; i < ln; i++) {
+ expression = expressions[i];
+
+ if (typeof expression === 'string' && expression.length > 0) {
+ possibleClassNames = Manager.getNamesByExpression(expression);
+
+ for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
+ possibleClassName = possibleClassNames[j];
+
+ if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
+ Ext.Array.include(classNames, possibleClassName);
+ }
+ }
+ }
+ }
+
+ // If the dynamic dependency feature is not being used, throw an error
+ // if the dependencies are not defined
+ if (!this.config.enabled) {
+ if (classNames.length > 0) {
+ Ext.Error.raise({
+ sourceClass: "Ext.Loader",
+ sourceMethod: "require",
+ msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
+ "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
+ });
+ }
+ }
+
+ if (classNames.length === 0) {
+ fn.call(scope);
+ return this;
+ }
+
+ this.queue.push({
+ requires: classNames,
+ callback: fn,
+ scope: scope
+ });
+
+ classNames = classNames.slice();
+
+ for (i = 0, ln = classNames.length; i < ln; i++) {
+ className = classNames[i];
+
+ if (!this.isFileLoaded.hasOwnProperty(className)) {
+ this.isFileLoaded[className] = false;
+
+ filePath = this.getPath(className);
+
+ this.classNameToFilePathMap[className] = filePath;
+
+ this.numPendingFiles++;
+
+ this.loadScriptFile(
+ filePath,
+ Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
+ Ext.Function.pass(this.onFileLoadError, [className, filePath]),
+ this,
+ this.syncModeEnabled
+ );
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * @private
+ * @param {String} className
+ * @param {String} filePath
+ */
+ onFileLoaded: function(className, filePath) {
+ this.numLoadedFiles++;
+
+ this.isFileLoaded[className] = true;
+
+ this.numPendingFiles--;
+
+ if (this.numPendingFiles === 0) {
+ this.refreshQueue();
+ }
+
+ if (this.numPendingFiles <= 1) {
+ window.status = "Finished loading all dependencies, onReady fired!";
+ }
+ else {
+ window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
+ }
+
+ if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
+ var queue = this.queue,
+ requires,
+ i, ln, j, subLn, missingClasses = [], missingPaths = [];
+
+ for (i = 0, ln = queue.length; i < ln; i++) {
+ requires = queue[i].requires;
+
+ for (j = 0, subLn = requires.length; j < ln; j++) {
+ if (this.isFileLoaded[requires[j]]) {
+ missingClasses.push(requires[j]);
+ }
+ }
+ }
+
+ if (missingClasses.length < 1) {
+ return;
+ }
+
+ missingClasses = Ext.Array.filter(missingClasses, function(item) {
+ return !this.requiresMap.hasOwnProperty(item);
+ }, this);
+
+ for (i = 0,ln = missingClasses.length; i < ln; i++) {
+ missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
+ }
+
+ Ext.Error.raise({
+ sourceClass: "Ext.Loader",
+ sourceMethod: "onFileLoaded",
+ msg: "The following classes are not declared even if their files have been " +
+ "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
+ "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
+ });
+ }
+ },
+
+ /**
+ * @private
+ */
+ onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
+ this.numPendingFiles--;
+ this.hasFileLoadError = true;
+
+ Ext.Error.raise({
+ sourceClass: "Ext.Loader",
+ classToLoad: className,
+ loadPath: filePath,
+ loadingType: isSynchronous ? 'synchronous' : 'async',
+ msg: errorMessage
+ });
+ },
+
+ /**
+ * @private
+ */
+ addOptionalRequires: function(requires) {
+ var optionalRequires = this.optionalRequires,
+ i, ln, require;
+
+ requires = Ext.Array.from(requires);
+
+ for (i = 0, ln = requires.length; i < ln; i++) {
+ require = requires[i];
+
+ Ext.Array.include(optionalRequires, require);
+ }
+
+ return this;
+ },
+
+ /**
+ * @private
+ */
+ triggerReady: function(force) {
+ var readyListeners = this.readyListeners,
+ optionalRequires, listener;
+
+ if (this.isLoading || force) {
+ this.isLoading = false;
+
+ if (this.optionalRequires.length) {
+ // Clone then empty the array to eliminate potential recursive loop issue
+ optionalRequires = Ext.Array.clone(this.optionalRequires);
+
+ // Empty the original array
+ this.optionalRequires.length = 0;
+
+ this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
+ return this;
+ }
+
+ while (readyListeners.length) {
+ listener = readyListeners.shift();
+ listener.fn.call(listener.scope);
+
+ if (this.isLoading) {
+ return this;
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Add a new listener to be executed when all required scripts are fully loaded
+ *
+ * @param {Function} fn The function callback to be executed
+ * @param {Object} scope The execution scope (this
) of the callback function
+ * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
+ */
+ onReady: function(fn, scope, withDomReady, options) {
+ var oldFn;
+
+ if (withDomReady !== false && Ext.onDocumentReady) {
+ oldFn = fn;
+
+ fn = function() {
+ Ext.onDocumentReady(oldFn, scope, options);
+ };
+ }
+
+ if (!this.isLoading) {
+ fn.call(scope);
+ }
+ else {
+ this.readyListeners.push({
+ fn: fn,
+ scope: scope
+ });
+ }
+ },
+
+ /**
+ * @private
+ * @param {String} className
+ */
+ historyPush: function(className) {
+ if (className && this.isFileLoaded.hasOwnProperty(className)) {
+ Ext.Array.include(this.history, className);
+ }
+
+ return this;
+ }
+ };
+
+ /**
+ * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of
+ * {@link Ext.Loader} for examples.
+ * @member Ext
+ * @method require
+ */
+ Ext.require = alias(Loader, 'require');
+
+ /**
+ * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
+ *
+ * @member Ext
+ * @method syncRequire
+ */
+ Ext.syncRequire = alias(Loader, 'syncRequire');
+
+ /**
+ * Convenient shortcut to {@link Ext.Loader#exclude}
+ * @member Ext
+ * @method exclude
+ */
+ Ext.exclude = alias(Loader, 'exclude');
+
+ /**
+ * @member Ext
+ * @method onReady
+ */
+ Ext.onReady = function(fn, scope, options) {
+ Loader.onReady(fn, scope, true, options);
+ };
+
+ Class.registerPreprocessor('loader', function(cls, data, continueFn) {
+ var me = this,
+ dependencies = [],
+ className = Manager.getName(cls),
+ i, j, ln, subLn, value, propertyName, propertyValue;
+
+ /*
+ Basically loop through the dependencyProperties, look for string class names and push
+ them into a stack, regardless of whether the property's value is a string, array or object. For example:
+ {
+ extend: 'Ext.MyClass',
+ requires: ['Ext.some.OtherClass'],
+ mixins: {
+ observable: 'Ext.util.Observable';
+ }
+ }
+ which will later be transformed into:
+ {
+ extend: Ext.MyClass,
+ requires: [Ext.some.OtherClass],
+ mixins: {
+ observable: Ext.util.Observable;
+ }
+ }
+ */
+
+ for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
+ propertyName = dependencyProperties[i];
+
+ if (data.hasOwnProperty(propertyName)) {
+ propertyValue = data[propertyName];
+
+ if (typeof propertyValue === 'string') {
+ dependencies.push(propertyValue);
+ }
+ else if (propertyValue instanceof Array) {
+ for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
+ value = propertyValue[j];
+
+ if (typeof value === 'string') {
+ dependencies.push(value);
+ }
+ }
+ }
+ else {
+ for (j in propertyValue) {
+ if (propertyValue.hasOwnProperty(j)) {
+ value = propertyValue[j];
+
+ if (typeof value === 'string') {
+ dependencies.push(value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (dependencies.length === 0) {
+// Loader.historyPush(className);
+ return;
+ }
+
+ var deadlockPath = [],
+ requiresMap = Loader.requiresMap,
+ detectDeadlock;
+
+ /*
+ Automatically detect deadlocks before-hand,
+ will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
+
+ - A extends B, then B extends A
+ - A requires B, B requires C, then C requires A
+
+ The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
+ no matter how deep the path is.
+ */
+
+ if (className) {
+ requiresMap[className] = dependencies;
+
+ detectDeadlock = function(cls) {
+ deadlockPath.push(cls);
+
+ if (requiresMap[cls]) {
+ if (Ext.Array.contains(requiresMap[cls], className)) {
+ Ext.Error.raise({
+ sourceClass: "Ext.Loader",
+ msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
+ deadlockPath[1] + "' " + "mutually require each other. Path: " +
+ deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
+ });
+ }
+
+ for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
+ detectDeadlock(requiresMap[cls][i]);
+ }
+ }
+ };
+
+ detectDeadlock(className);
+ }
+
+
+ Loader.require(dependencies, function() {
+ for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
+ propertyName = dependencyProperties[i];
+
+ if (data.hasOwnProperty(propertyName)) {
+ propertyValue = data[propertyName];
+
+ if (typeof propertyValue === 'string') {
+ data[propertyName] = Manager.get(propertyValue);
+ }
+ else if (propertyValue instanceof Array) {
+ for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
+ value = propertyValue[j];
+
+ if (typeof value === 'string') {
+ data[propertyName][j] = Manager.get(value);
+ }
+ }
+ }
+ else {
+ for (var k in propertyValue) {
+ if (propertyValue.hasOwnProperty(k)) {
+ value = propertyValue[k];
+
+ if (typeof value === 'string') {
+ data[propertyName][k] = Manager.get(value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ continueFn.call(me, cls, data);
+ });
+
+ return false;
+ }, true);
+
+ Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
+
+ Manager.registerPostprocessor('uses', function(name, cls, data) {
+ var uses = Ext.Array.from(data.uses),
+ items = [],
+ i, ln, item;
+
+ for (i = 0, ln = uses.length; i < ln; i++) {
+ item = uses[i];
+
+ if (typeof item === 'string') {
+ items.push(item);
+ }
+ }
+
+ Loader.addOptionalRequires(items);
+ });
+
+ Manager.setDefaultPostprocessorPosition('uses', 'last');
+
+})(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
+
+/**
+ * @class Ext.Error
+ * @private
+ * @extends Error
+
+A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
+errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
+uses the Ext 4 class system, the Error class can automatically add the source class and method from which
+the error was raised. It also includes logic to automatically log the eroor to the console, if available,
+with additional metadata about the error. In all cases, the error will always be thrown at the end so that
+execution will halt.
+
+Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
+handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
+although in a real application it's usually a better idea to override the handling function and perform
+logging or some other method of reporting the errors in a way that is meaningful to the application.
+
+At its simplest you can simply raise an error as a simple string from within any code:
+
+#Example usage:#
+
+ Ext.Error.raise('Something bad happened!');
+
+If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
+displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
+additional metadata about the error being raised. The {@link #raise} method can also take a config object.
+In this form the `msg` attribute becomes the error description, and any other data added to the config gets
+added to the error object and, if the console is available, logged to the console for inspection.
+
+#Example usage:#
+
+ Ext.define('Ext.Foo', {
+ doSomething: function(option){
+ if (someCondition === false) {
+ Ext.Error.raise({
+ msg: 'You cannot do that!',
+ option: option, // whatever was passed into the method
+ 'error code': 100 // other arbitrary info
+ });
+ }
+ }
+ });
+
+If a console is available (that supports the `console.dir` function) you'll see console output like:
+
+ An error was raised with the following data:
+ option: Object { foo: "bar"}
+ foo: "bar"
+ error code: 100
+ msg: "You cannot do that!"
+ sourceClass: "Ext.Foo"
+ sourceMethod: "doSomething"
+
+ uncaught exception: You cannot do that!
+
+As you can see, the error will report exactly where it was raised and will include as much information as the
+raising code can usefully provide.
+
+If you want to handle all application errors globally you can simply override the static {@link handle} method
+and provide whatever handling logic you need. If the method returns true then the error is considered handled
+and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
+
+#Example usage:#
+
+ Ext.Error.handle = function(err) {
+ if (err.someProperty == 'NotReallyAnError') {
+ // maybe log something to the application here if applicable
+ return true;
+ }
+ // any non-true return value (including none) will cause the error to be thrown
+ }
+
+ * Create a new Error object
+ * @param {Object} config The config object
+ * @markdown
+ * @author Brian Moeskau