- * @class Ext.Array
*
* A set of useful static methods to deal with arrays; provide missing methods for older browsers.
-
- * @singleton
- * @markdown
*/
(function() {
var arrayPrototype = Array.prototype,
slice = arrayPrototype.slice,
+ supportsSplice = function () {
+ var array = [],
+ lengthBefore,
+ j = 20;
+
+ if (!array.splice) {
+ return false;
+ }
+
+ // This detects a bug in IE8 splice method:
+ // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
+
+ while (j--) {
+ array.push("A");
+ }
+
+ array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");
+
+ lengthBefore = array.length; //41
+ array.splice(13, 0, "XXX"); // add one element
+
+ if (lengthBefore+1 != array.length) {
+ return false;
+ }
+ // end IE8 bug
+
+ return true;
+ }(),
supportsForEach = 'forEach' in arrayPrototype,
supportsMap = 'map' in arrayPrototype,
supportsIndexOf = 'indexOf' in arrayPrototype,
@@ -1213,6 +1296,7 @@ Ext.num = function() {
}(),
supportsSliceOnNodeList = true,
ExtArray;
+
try {
// IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
if (typeof document !== 'undefined') {
@@ -1222,50 +1306,172 @@ Ext.num = function() {
supportsSliceOnNodeList = false;
}
- ExtArray = Ext.Array = {
- /*
- * Iterates an array or an iterable value and invoke the given callback function for each item.
+ function fixArrayIndex (array, index) {
+ return (index < 0) ? Math.max(0, array.length + index)
+ : Math.min(array.length, index);
+ }
+
+ /*
+ Does the same work as splice, but with a slightly more convenient signature. The splice
+ method has bugs in IE8, so this is the implementation we use on that platform.
+
+ The rippling of items in the array can be tricky. Consider two use cases:
+
+ index=2
+ removeCount=2
+ /=====\
+ +---+---+---+---+---+---+---+---+
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ +---+---+---+---+---+---+---+---+
+ / \/ \/ \/ \
+ / /\ /\ /\ \
+ / / \/ \/ \ +--------------------------+
+ / / /\ /\ +--------------------------+ \
+ / / / \/ +--------------------------+ \ \
+ / / / /+--------------------------+ \ \ \
+ / / / / \ \ \ \
+ v v v v v v v v
+ +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
+ | 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
+ +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
+ A B \=========/
+ insert=[a,b,c]
+
+ In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
+ that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
+ must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
+ */
+ function replaceSim (array, index, removeCount, insert) {
+ var add = insert ? insert.length : 0,
+ length = array.length,
+ pos = fixArrayIndex(array, index);
+
+ // we try to use Array.push when we can for efficiency...
+ if (pos === length) {
+ if (add) {
+ array.push.apply(array, insert);
+ }
+ } else {
+ var remove = Math.min(removeCount, length - pos),
+ tailOldPos = pos + remove,
+ tailNewPos = tailOldPos + add - remove,
+ tailCount = length - tailOldPos,
+ lengthAfterRemove = length - remove,
+ i;
- var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
+ if (tailNewPos < tailOldPos) { // case A
+ for (i = 0; i < tailCount; ++i) {
+ array[tailNewPos+i] = array[tailOldPos+i];
+ }
+ } else if (tailNewPos > tailOldPos) { // case B
+ for (i = tailCount; i--; ) {
+ array[tailNewPos+i] = array[tailOldPos+i];
+ }
+ } // else, add == remove (nothing to do)
- Ext.Array.each(countries, function(name, index, countriesItSelf) {
- console.log(name);
- });
+ if (add && pos === lengthAfterRemove) {
+ array.length = lengthAfterRemove; // truncate array
+ array.push.apply(array, insert);
+ } else {
+ array.length = lengthAfterRemove + add; // reserves space
+ for (i = 0; i < add; ++i) {
+ array[pos+i] = insert[i];
+ }
+ }
+ }
- var sum = function() {
- var sum = 0;
+ return array;
+ }
- Ext.Array.each(arguments, function(value) {
- sum += value;
- });
+ function replaceNative (array, index, removeCount, insert) {
+ if (insert && insert.length) {
+ if (index < array.length) {
+ array.splice.apply(array, [index, removeCount].concat(insert));
+ } else {
+ array.push.apply(array, insert);
+ }
+ } else {
+ array.splice(index, removeCount);
+ }
+ return array;
+ }
- return sum;
- };
+ function eraseSim (array, index, removeCount) {
+ return replaceSim(array, index, removeCount);
+ }
- sum(1, 2, 3); // returns 6
+ function eraseNative (array, index, removeCount) {
+ array.splice(index, removeCount);
+ return array;
+ }
- * The iteration can be stopped by returning false in the function callback.
+ function spliceSim (array, index, removeCount) {
+ var pos = fixArrayIndex(array, index),
+ removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
- Ext.Array.each(countries, function(name, index, countriesItSelf) {
- if (name === 'Singapore') {
- return false; // break here
+ if (arguments.length < 4) {
+ replaceSim(array, pos, removeCount);
+ } else {
+ replaceSim(array, pos, removeCount, slice.call(arguments, 3));
}
- });
- * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this
- * argument is not iterable, the callback function is called once.
- * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
- * the current `index`. Arguments passed to this callback function are:
+ return removed;
+ }
+
+ function spliceNative (array) {
+ return array.splice.apply(array, slice.call(arguments, 1));
+ }
+
+ var erase = supportsSplice ? eraseNative : eraseSim,
+ replace = supportsSplice ? replaceNative : replaceSim,
+ splice = supportsSplice ? spliceNative : spliceSim;
-- `item`: {Mixed} The item at the current `index` in the passed `array`
-- `index`: {Number} The current `index` within the `array`
-- `allItems`: {Array/NodeList/Mixed} The `array` passed as the first argument to `Ext.Array.each`
+ // NOTE: from here on, use erase, replace or splice (not native methods)...
+ ExtArray = Ext.Array = {
+ /**
+ * Iterates an array or an iterable value and invoke the given callback function for each item.
+ *
+ * var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
+ *
+ * Ext.Array.each(countries, function(name, index, countriesItSelf) {
+ * console.log(name);
+ * });
+ *
+ * var sum = function() {
+ * var sum = 0;
+ *
+ * Ext.Array.each(arguments, function(value) {
+ * sum += value;
+ * });
+ *
+ * return sum;
+ * };
+ *
+ * sum(1, 2, 3); // returns 6
+ *
+ * The iteration can be stopped by returning false in the function callback.
+ *
+ * Ext.Array.each(countries, function(name, index, countriesItSelf) {
+ * if (name === 'Singapore') {
+ * return false; // break here
+ * }
+ * });
+ *
+ * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
+ *
+ * @param {Array/NodeList/Object} iterable The value to be iterated. If this
+ * argument is not iterable, the callback function is called once.
+ * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
+ * the current `index`.
+ * @param {Object} fn.item The item at the current `index` in the passed `array`
+ * @param {Number} fn.index The current `index` within the `array`
+ * @param {Array} fn.allItems The `array` itself which was passed as the first argument
+ * @param {Boolean} fn.return Return false to stop iteration.
* @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
* @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
* Defaults false
* @return {Boolean} See description for the `fn` parameter.
- * @markdown
*/
each: function(array, fn, scope, reverse) {
array = ExtArray.from(array);
@@ -1293,20 +1499,16 @@ Ext.num = function() {
/**
* Iterates an array and invoke the given callback function for each item. Note that this will simply
- * delegate to the native Array.prototype.forEach method if supported.
- * It doesn't support stopping the iteration by returning false in the callback function like
- * {@link Ext.Array#each}. However, performance could be much better in modern browsers comparing with
- * {@link Ext.Array#each}
+ * delegate to the native Array.prototype.forEach method if supported. It doesn't support stopping the
+ * iteration by returning false in the callback function like {@link Ext.Array#each}. However, performance
+ * could be much better in modern browsers comparing with {@link Ext.Array#each}
*
* @param {Array} array The array to iterate
- * @param {Function} fn The function callback, to be invoked these arguments:
- *
-- `item`: {Mixed} The item at the current `index` in the passed `array`
-- `index`: {Number} The current `index` within the `array`
-- `allItems`: {Array} The `array` itself which was passed as the first argument
-
+ * @param {Function} fn The callback function.
+ * @param {Object} fn.item The item at the current `index` in the passed `array`
+ * @param {Number} fn.index The current `index` within the `array`
+ * @param {Array} fn.allItems The `array` itself which was passed as the first argument
* @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
- * @markdown
*/
forEach: function(array, fn, scope) {
if (supportsForEach) {
@@ -1326,10 +1528,9 @@ Ext.num = function() {
* missing arrayPrototype.indexOf in Internet Explorer.
*
* @param {Array} array The array to check
- * @param {Mixed} item The item to look for
+ * @param {Object} item The item to look for
* @param {Number} from (Optional) The index at which to begin the search
* @return {Number} The index of item in the array (or -1 if it is not found)
- * @markdown
*/
indexOf: function(array, item, from) {
if (supportsIndexOf) {
@@ -1351,9 +1552,8 @@ Ext.num = function() {
* Checks whether or not the given `array` contains the specified `item`
*
* @param {Array} array The array to check
- * @param {Mixed} item The item to look for
+ * @param {Object} item The item to look for
* @return {Boolean} True if the array contains the item, false otherwise
- * @markdown
*/
contains: function(array, item) {
if (supportsIndexOf) {
@@ -1373,28 +1573,29 @@ Ext.num = function() {
/**
* Converts any iterable (numeric indices and a length property) into a true array.
-
-function test() {
- var args = Ext.Array.toArray(arguments),
- fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
-
- alert(args.join(' '));
- alert(fromSecondToLastArgs.join(' '));
-}
-
-test('just', 'testing', 'here'); // alerts 'just testing here';
- // alerts 'testing here';
-
-Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
-Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
-Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
-
- * @param {Mixed} iterable the iterable object to be turned into a true Array.
+ *
+ * function test() {
+ * var args = Ext.Array.toArray(arguments),
+ * fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
+ *
+ * alert(args.join(' '));
+ * alert(fromSecondToLastArgs.join(' '));
+ * }
+ *
+ * test('just', 'testing', 'here'); // alerts 'just testing here';
+ * // alerts 'testing here';
+ *
+ * Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
+ * Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
+ * Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
+ *
+ * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
+ *
+ * @param {Object} iterable the iterable object to be turned into a true Array.
* @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
* @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
* index of the iterable value
* @return {Array} array
- * @markdown
*/
toArray: function(iterable, start, end){
if (!iterable || !iterable.length) {
@@ -1425,9 +1626,9 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Plucks the value of a property from each item in the Array. Example:
*
- Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
-
- * @param {Array|NodeList} array The Array of items to pluck the value from.
+ * Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
+ *
+ * @param {Array/NodeList} array The Array of items to pluck the value from.
* @param {String} propertyName The property name to pluck from each element.
* @return {Array} The value from each item in the Array.
*/
@@ -1446,6 +1647,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Creates a new array with the results of calling a provided function on every element in this array.
+ *
* @param {Array} array
* @param {Function} fn Callback function for each item
* @param {Object} scope Callback function scope
@@ -1533,7 +1735,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
*
- * @see Ext.Array.filter
+ * See {@link Ext.Array#filter}
+ *
* @param {Array} array
* @return {Array} results
*/
@@ -1580,6 +1783,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Creates a new array with all of the elements of this array for which
* the provided filtering function returns true.
+ *
* @param {Array} array
* @param {Function} fn Callback function for each item
* @param {Object} scope Callback function scope
@@ -1611,11 +1815,10 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
* - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
* - An array with one item which is the given value, otherwise
*
- * @param {Array/Mixed} value The value to convert to an array if it's not already is an array
- * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary,
+ * @param {Object} value The value to convert to an array if it's not already is an array
+ * @param {Boolean} newReference (Optional) True to clone the given array and return a new reference if necessary,
* defaults to false
* @return {Array} array
- * @markdown
*/
from: function(value, newReference) {
if (value === undefined || value === null) {
@@ -1637,14 +1840,14 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
* Removes the specified item from the array if it exists
*
* @param {Array} array The array
- * @param {Mixed} item The item to remove
+ * @param {Object} item The item to remove
* @return {Array} The passed array itself
*/
remove: function(array, item) {
var index = ExtArray.indexOf(array, item);
if (index !== -1) {
- array.splice(index, 1);
+ erase(array, index, 1);
}
return array;
@@ -1654,8 +1857,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
* Push an item into the array only if the array doesn't contain it yet
*
* @param {Array} array The array
- * @param {Mixed} item The item to include
- * @return {Array} The passed array itself
+ * @param {Object} item The item to include
*/
include: function(array, item) {
if (!ExtArray.contains(array, item)) {
@@ -1676,9 +1878,13 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
},
/**
- * Merge multiple arrays into one with unique items. Alias to {@link Ext.Array#union}.
+ * Merge multiple arrays into one with unique items.
*
- * @param {Array} array,...
+ * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
+ *
+ * @param {Array} array1
+ * @param {Array} array2
+ * @param {Array} etc
* @return {Array} merged
*/
merge: function() {
@@ -1696,7 +1902,9 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Merge multiple arrays into one with unique items that exist in all of the arrays.
*
- * @param {Array} array,...
+ * @param {Array} array1
+ * @param {Array} array2
+ * @param {Array} etc
* @return {Array} intersect
*/
intersect: function() {
@@ -1716,8 +1924,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
}
}
- minArray = Ext.Array.unique(minArray);
- arrays.splice(x, 1);
+ minArray = ExtArray.unique(minArray);
+ erase(arrays, x, 1);
// Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
// an item in the small array, we're likely to find it before reaching the end
@@ -1745,8 +1953,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Perform a set difference A-B by subtracting all items in array B from array A.
*
- * @param {Array} array A
- * @param {Array} array B
+ * @param {Array} arrayA
+ * @param {Array} arrayB
* @return {Array} difference
*/
difference: function(arrayA, arrayB) {
@@ -1757,7 +1965,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
for (i = 0,lnB = arrayB.length; i < lnB; i++) {
for (j = 0; j < ln; j++) {
if (clone[j] === arrayB[i]) {
- clone.splice(j, 1);
+ erase(clone, j, 1);
j--;
ln--;
}
@@ -1767,6 +1975,39 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
return clone;
},
+ /**
+ * Returns a shallow copy of a part of an array. This is equivalent to the native
+ * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
+ * is "arguments" since the arguments object does not supply a slice method but can
+ * be the context object to Array.prototype.slice.
+ *
+ * @param {Array} array The array (or arguments object).
+ * @param {Number} begin The index at which to begin. Negative values are offsets from
+ * the end of the array.
+ * @param {Number} end The index at which to end. The copied items do not include
+ * end. Negative values are offsets from the end of the array. If end is omitted,
+ * all items up to the end of the array are copied.
+ * @return {Array} The copied piece of the array.
+ */
+ // Note: IE6 will return [] on slice.call(x, undefined).
+ slice: ([1,2].slice(1, undefined).length ?
+ function (array, begin, end) {
+ return slice.call(array, begin, end);
+ } :
+ // at least IE6 uses arguments.length for variadic signature
+ function (array, begin, end) {
+ // After tested for IE 6, the one below is of the best performance
+ // see http://jsperf.com/slice-fix
+ if (typeof begin === 'undefined') {
+ return slice.call(array);
+ }
+ if (typeof end === 'undefined') {
+ return slice.call(array, begin);
+ }
+ return slice.call(array, begin, end);
+ }
+ ),
+
/**
* Sorts the elements of an Array.
* By default, this method sorts the elements alphabetically and ascending.
@@ -1813,8 +2054,9 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Recursively flattens into 1-d Array. Injects Arrays inline.
+ *
* @param {Array} array The array to flatten
- * @return {Array} The new, flattened array.
+ * @return {Array} The 1-d array.
*/
flatten: function(array) {
var worker = [];
@@ -1840,10 +2082,11 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
/**
* Returns the minimum value in the Array.
- * @param {Array|NodeList} array The Array from which to select the minimum value.
+ *
+ * @param {Array/NodeList} array The Array from which to select the minimum value.
* @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
- * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
- * @return {Mixed} minValue The minimum value
+ * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
+ * @return {Object} minValue The minimum value
*/
min: function(array, comparisonFn) {
var min = array[0],
@@ -1868,11 +2111,12 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
},
/**
- * Returns the maximum value in the Array
- * @param {Array|NodeList} array The Array from which to select the maximum value.
+ * Returns the maximum value in the Array.
+ *
+ * @param {Array/NodeList} array The Array from which to select the maximum value.
* @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
- * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
- * @return {Mixed} maxValue The maximum value
+ * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
+ * @return {Object} maxValue The maximum value
*/
max: function(array, comparisonFn) {
var max = array[0],
@@ -1897,7 +2141,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
},
/**
- * Calculates the mean of all items in the array
+ * Calculates the mean of all items in the array.
+ *
* @param {Array} array The Array to calculate the mean value of.
* @return {Number} The mean.
*/
@@ -1906,7 +2151,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
},
/**
- * Calculates the sum of all items in the given array
+ * Calculates the sum of all items in the given array.
+ *
* @param {Array} array The Array to calculate the sum value of.
* @return {Number} The sum.
*/
@@ -1921,98 +2167,162 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
}
return sum;
- }
+ },
+
+ //
+ _replaceSim: replaceSim, // for unit testing
+ _spliceSim: spliceSim,
+ //
+
+ /**
+ * Removes items from an array. This is functionally equivalent to the splice method
+ * of Array, but works around bugs in IE8's splice method and does not copy the
+ * removed elements in order to return them (because very often they are ignored).
+ *
+ * @param {Array} array The Array on which to replace.
+ * @param {Number} index The index in the array at which to operate.
+ * @param {Number} removeCount The number of items to remove at index.
+ * @return {Array} The array passed.
+ * @method
+ */
+ erase: erase,
+
+ /**
+ * Inserts items in to an array.
+ *
+ * @param {Array} array The Array on which to replace.
+ * @param {Number} index The index in the array at which to operate.
+ * @param {Array} items The array of items to insert at index.
+ * @return {Array} The array passed.
+ */
+ insert: function (array, index, items) {
+ return replace(array, index, 0, items);
+ },
+
+ /**
+ * Replaces items in an array. This is functionally equivalent to the splice method
+ * of Array, but works around bugs in IE8's splice method and is often more convenient
+ * to call because it accepts an array of items to insert rather than use a variadic
+ * argument list.
+ *
+ * @param {Array} array The Array on which to replace.
+ * @param {Number} index The index in the array at which to operate.
+ * @param {Number} removeCount The number of items to remove at index (can be 0).
+ * @param {Array} insert (optional) An array of items to insert at index.
+ * @return {Array} The array passed.
+ * @method
+ */
+ replace: replace,
+ /**
+ * Replaces items in an array. This is equivalent to the splice method of Array, but
+ * works around bugs in IE8's splice method. The signature is exactly the same as the
+ * splice method except that the array is the first argument. All arguments following
+ * removeCount are inserted in the array at index.
+ *
+ * @param {Array} array The Array on which to replace.
+ * @param {Number} index The index in the array at which to operate.
+ * @param {Number} removeCount The number of items to remove at index (can be 0).
+ * @return {Array} An array containing the removed items.
+ * @method
+ */
+ splice: splice
};
/**
- * Convenient alias to {@link Ext.Array#each}
+ * @method
* @member Ext
- * @method each
+ * @alias Ext.Array#each
*/
- Ext.each = Ext.Array.each;
+ Ext.each = ExtArray.each;
/**
- * Alias to {@link Ext.Array#merge}.
+ * @method
* @member Ext.Array
- * @method union
+ * @alias Ext.Array#merge
*/
- Ext.Array.union = Ext.Array.merge;
+ ExtArray.union = ExtArray.merge;
/**
* Old alias to {@link Ext.Array#min}
* @deprecated 4.0.0 Use {@link Ext.Array#min} instead
+ * @method
* @member Ext
- * @method min
+ * @alias Ext.Array#min
*/
- Ext.min = Ext.Array.min;
+ Ext.min = ExtArray.min;
/**
* Old alias to {@link Ext.Array#max}
* @deprecated 4.0.0 Use {@link Ext.Array#max} instead
+ * @method
* @member Ext
- * @method max
+ * @alias Ext.Array#max
*/
- Ext.max = Ext.Array.max;
+ Ext.max = ExtArray.max;
/**
* Old alias to {@link Ext.Array#sum}
* @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
+ * @method
* @member Ext
- * @method sum
+ * @alias Ext.Array#sum
*/
- Ext.sum = Ext.Array.sum;
+ Ext.sum = ExtArray.sum;
/**
* Old alias to {@link Ext.Array#mean}
* @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
+ * @method
* @member Ext
- * @method mean
+ * @alias Ext.Array#mean
*/
- Ext.mean = Ext.Array.mean;
+ Ext.mean = ExtArray.mean;
/**
* Old alias to {@link Ext.Array#flatten}
* @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
+ * @method
* @member Ext
- * @method flatten
+ * @alias Ext.Array#flatten
*/
- Ext.flatten = Ext.Array.flatten;
+ Ext.flatten = ExtArray.flatten;
/**
- * Old alias to {@link Ext.Array#clean Ext.Array.clean}
- * @deprecated 4.0.0 Use {@link Ext.Array.clean} instead
+ * Old alias to {@link Ext.Array#clean}
+ * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
+ * @method
* @member Ext
- * @method clean
+ * @alias Ext.Array#clean
*/
- Ext.clean = Ext.Array.clean;
+ Ext.clean = ExtArray.clean;
/**
- * Old alias to {@link Ext.Array#unique Ext.Array.unique}
- * @deprecated 4.0.0 Use {@link Ext.Array.unique} instead
+ * Old alias to {@link Ext.Array#unique}
+ * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
+ * @method
* @member Ext
- * @method unique
+ * @alias Ext.Array#unique
*/
- Ext.unique = Ext.Array.unique;
+ Ext.unique = ExtArray.unique;
/**
* Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
* @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
+ * @method
* @member Ext
- * @method pluck
+ * @alias Ext.Array#pluck
*/
- Ext.pluck = Ext.Array.pluck;
+ Ext.pluck = ExtArray.pluck;
/**
- * Convenient alias to {@link Ext.Array#toArray Ext.Array.toArray}
- * @param {Iterable} the iterable object to be turned into a true Array.
+ * @method
* @member Ext
- * @method toArray
- * @return {Array} array
+ * @alias Ext.Array#toArray
*/
Ext.toArray = function() {
return ExtArray.toArray.apply(ExtArray, arguments);
- }
+ };
})();
/**
@@ -2021,36 +2331,33 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
* A collection of useful static methods to deal with function callbacks
* @singleton
*/
-
Ext.Function = {
/**
* A very commonly used method throughout the framework. It acts as a wrapper around another method
- * which originally accepts 2 arguments for name
and value
.
+ * which originally accepts 2 arguments for `name` and `value`.
* The wrapped function then allows "flexible" value setting of either:
*
- *
- * name
and value
as 2 arguments
- * - one single object argument with multiple key - value pairs
- *
+ * - `name` and `value` as 2 arguments
+ * - one single object argument with multiple key - value pairs
*
* For example:
- *
-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'
-});
- *
+ *
+ * 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'
+ * });
+ *
* @param {Function} setter
* @returns {Function} flexSetter
*/
@@ -2085,33 +2392,40 @@ setValue({
};
},
- /**
- * Create a new function from the provided fn
, change this
to the provided scope, optionally
+ /**
+ * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
* overrides arguments for the call. (Defaults to the arguments passed by the caller)
*
+ * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
+ *
* @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 {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) {
+ if (arguments.length === 2) {
+ return function() {
+ return fn.apply(scope, arguments);
+ }
+ }
+
var method = fn,
- applyArgs;
+ slice = Array.prototype.slice;
return function() {
var callArgs = args || arguments;
if (appendArgs === true) {
- callArgs = Array.prototype.slice.call(arguments, 0);
+ callArgs = 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
+ else if (typeof appendArgs == 'number') {
+ callArgs = slice.call(arguments, 0); // copy arguments first
+ Ext.Array.insert(callArgs, appendArgs, args);
}
return method.apply(scope || window, callArgs);
@@ -2119,23 +2433,26 @@ setValue({
},
/**
- * Create a new function from the provided fn
, the arguments of which are pre-set to `args`.
+ * 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'
-
+ * 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'
+ *
+ * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
+ *
* @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.
+ * @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) {
@@ -2149,8 +2466,8 @@ setValue({
},
/**
- * 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.
+ * 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
@@ -2166,27 +2483,27 @@ setValue({
* 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:
- *
-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"
-
+ *
+ * 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"
+ *
* @param {Function} origFn The original function.
* @param {Function} newFn The function to call before the original
- * @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.
- * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
+ * @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.**
+ * @param {Object} 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) {
@@ -2206,16 +2523,17 @@ sayHiToFriend('Brian'); // alerts "Hi, Brian"
},
/**
- * 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.
- */
+ * 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);
@@ -2230,27 +2548,30 @@ sayHiToFriend('Brian'); // alerts "Hi, Brian"
/**
* Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
- *
-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);
-
+ *
+ * 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);
+ *
+ * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
+ *
* @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 (this
reference) in which the function is executed.
- * If omitted, defaults to the browser window.
+ * @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 (`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
@@ -2270,23 +2591,21 @@ Ext.Function.defer(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:
*
- *
-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
- *
+ * 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
*
* @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.
+ * @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
*/
@@ -2304,15 +2623,15 @@ sayGoodbye('Fred'); // both alerts show
},
/**
- * Creates a delegate function, optionally with a bound scope which, when called, buffers
+ * 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.
+ * timeout period will begin again.
*
* @param {Function} fn The function to invoke on a buffered timer.
* @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
* function.
- * @param {Object} scope (optional) The scope (this
reference) in which
+ * @param {Object} scope (optional) The scope (`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.
@@ -2324,7 +2643,7 @@ sayGoodbye('Fred'); // both alerts show
return function() {
var me = this;
if (timerId) {
- clearInterval(timerId);
+ clearTimeout(timerId);
timerId = null;
}
timerId = setTimeout(function(){
@@ -2335,16 +2654,16 @@ sayGoodbye('Fred'); // both alerts show
},
/**
- * Creates a throttled version of the passed function which, when called repeatedly and
+ * 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.
+ * 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.
+ * 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.
*
- * @param fn {Function} The function to execute at a regular time interval.
- * @param interval {Number} The interval in milliseconds on which the passed function is executed.
- * @param scope (optional) The scope (this
reference) in which
+ * @param {Function} fn The function to execute at a regular time interval.
+ * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
+ * @param {Object} scope (optional) The scope (`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.
*/
@@ -2365,27 +2684,101 @@ sayGoodbye('Fred'); // both alerts show
timer = setTimeout(execute, interval - elapsed);
}
};
+ },
+
+ /**
+ * Adds behavior to an existing method that is executed before the
+ * original behavior of the function. For example:
+ *
+ * var soup = {
+ * contents: [],
+ * add: function(ingredient) {
+ * this.contents.push(ingredient);
+ * }
+ * };
+ * Ext.Function.interceptBefore(soup, "add", function(ingredient){
+ * if (!this.contents.length && ingredient !== "water") {
+ * // Always add water to start with
+ * this.contents.push("water");
+ * }
+ * });
+ * soup.add("onions");
+ * soup.add("salt");
+ * soup.contents; // will contain: water, onions, salt
+ *
+ * @param {Object} object The target object
+ * @param {String} methodName Name of the method to override
+ * @param {Function} fn Function with the new behavior. It will
+ * be called with the same arguments as the original method. The
+ * return value of this function will be the return value of the
+ * new method.
+ * @return {Function} The new function just created.
+ */
+ interceptBefore: function(object, methodName, fn) {
+ var method = object[methodName] || Ext.emptyFn;
+
+ return object[methodName] = function() {
+ var ret = fn.apply(this, arguments);
+ method.apply(this, arguments);
+
+ return ret;
+ };
+ },
+
+ /**
+ * Adds behavior to an existing method that is executed after the
+ * original behavior of the function. For example:
+ *
+ * var soup = {
+ * contents: [],
+ * add: function(ingredient) {
+ * this.contents.push(ingredient);
+ * }
+ * };
+ * Ext.Function.interceptAfter(soup, "add", function(ingredient){
+ * // Always add a bit of extra salt
+ * this.contents.push("salt");
+ * });
+ * soup.add("water");
+ * soup.add("onions");
+ * soup.contents; // will contain: water, salt, onions, salt
+ *
+ * @param {Object} object The target object
+ * @param {String} methodName Name of the method to override
+ * @param {Function} fn Function with the new behavior. It will
+ * be called with the same arguments as the original method. The
+ * return value of this function will be the return value of the
+ * new method.
+ * @return {Function} The new function just created.
+ */
+ interceptAfter: function(object, methodName, fn) {
+ var method = object[methodName] || Ext.emptyFn;
+
+ return object[methodName] = function() {
+ method.apply(this, arguments);
+ return fn.apply(this, arguments);
+ };
}
};
/**
- * Shorthand for {@link Ext.Function#defer}
+ * @method
* @member Ext
- * @method defer
+ * @alias Ext.Function#defer
*/
Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
/**
- * Shorthand for {@link Ext.Function#pass}
+ * @method
* @member Ext
- * @method pass
+ * @alias Ext.Function#pass
*/
Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
/**
- * Shorthand for {@link Ext.Function#bind}
+ * @method
* @member Ext
- * @method bind
+ * @alias Ext.Function#bind
*/
Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
@@ -2394,7 +2787,7 @@ Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
* @docauthor Jacky Nguyen
* @class Ext.Object
*
- * A collection of useful static methods to deal with objects
+ * A collection of useful static methods to deal with objects.
*
* @singleton
*/
@@ -2404,41 +2797,41 @@ Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
var ExtObject = Ext.Object = {
/**
- * Convert a `name` - `value` pair to an array of objects with support for nested structures; useful to construct
+ * Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
* query strings. For example:
-
- var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
-
- // objects then equals:
- [
- { name: 'hobbies', value: 'reading' },
- { name: 'hobbies', value: 'cooking' },
- { name: 'hobbies', value: 'swimming' },
- ];
-
- var objects = Ext.Object.toQueryObjects('dateOfBirth', {
- day: 3,
- month: 8,
- year: 1987,
- extra: {
- hour: 4
- minute: 30
- }
- }, true); // Recursive
-
- // objects then equals:
- [
- { name: 'dateOfBirth[day]', value: 3 },
- { name: 'dateOfBirth[month]', value: 8 },
- { name: 'dateOfBirth[year]', value: 1987 },
- { name: 'dateOfBirth[extra][hour]', value: 4 },
- { name: 'dateOfBirth[extra][minute]', value: 30 },
- ];
-
+ *
+ * var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
+ *
+ * // objects then equals:
+ * [
+ * { name: 'hobbies', value: 'reading' },
+ * { name: 'hobbies', value: 'cooking' },
+ * { name: 'hobbies', value: 'swimming' },
+ * ];
+ *
+ * var objects = Ext.Object.toQueryObjects('dateOfBirth', {
+ * day: 3,
+ * month: 8,
+ * year: 1987,
+ * extra: {
+ * hour: 4
+ * minute: 30
+ * }
+ * }, true); // Recursive
+ *
+ * // objects then equals:
+ * [
+ * { name: 'dateOfBirth[day]', value: 3 },
+ * { name: 'dateOfBirth[month]', value: 8 },
+ * { name: 'dateOfBirth[year]', value: 1987 },
+ * { name: 'dateOfBirth[extra][hour]', value: 4 },
+ * { name: 'dateOfBirth[extra][minute]', value: 30 },
+ * ];
+ *
* @param {String} name
- * @param {Mixed} value
- * @param {Boolean} recursive
- * @markdown
+ * @param {Object/Array} value
+ * @param {Boolean} [recursive=false] True to traverse object recursively
+ * @return {Array}
*/
toQueryObjects: function(name, value, recursive) {
var self = ExtObject.toQueryObjects,
@@ -2484,37 +2877,35 @@ var ExtObject = Ext.Object = {
},
/**
- * Takes an object and converts it to an encoded query string
-
-- Non-recursive:
-
- Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
- Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
- Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
- Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
- Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
-
-- Recursive:
-
- Ext.Object.toQueryString({
- username: 'Jacky',
- dateOfBirth: {
- day: 1,
- month: 2,
- year: 1911
- },
- hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
- }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
- // username=Jacky
- // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
- // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
-
+ * Takes an object and converts it to an encoded query string.
+ *
+ * Non-recursive:
+ *
+ * Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
+ * Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
+ * Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
+ * Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
+ * Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
+ *
+ * Recursive:
+ *
+ * Ext.Object.toQueryString({
+ * username: 'Jacky',
+ * dateOfBirth: {
+ * day: 1,
+ * month: 2,
+ * year: 1911
+ * },
+ * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
+ * }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
+ * // username=Jacky
+ * // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
+ * // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
*
* @param {Object} object The object to encode
- * @param {Boolean} recursive (optional) Whether or not to interpret the object in recursive format.
- * (PHP / Ruby on Rails servers and similar). Defaults to false
+ * @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
+ * (PHP / Ruby on Rails servers and similar).
* @return {String} queryString
- * @markdown
*/
toQueryString: function(object, recursive) {
var paramObjects = [],
@@ -2547,33 +2938,32 @@ var ExtObject = Ext.Object = {
/**
* Converts a query string back into an object.
*
-- Non-recursive:
-
- Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
- Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
- Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
- Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
-
-- Recursive:
-
- Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true);
-
- // returns
- {
- username: 'Jacky',
- dateOfBirth: {
- day: '1',
- month: '2',
- year: '1911'
- },
- hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
- }
-
- * @param {String} queryString The query string to decode
- * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by
- * PHP / Ruby on Rails servers and similar. Defaults to false
- * @return {Object}
- */
+ * Non-recursive:
+ *
+ * Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
+ * Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
+ * Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
+ * Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
+ *
+ * Recursive:
+ *
+ * Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true);
+ * // returns
+ * {
+ * username: 'Jacky',
+ * dateOfBirth: {
+ * day: '1',
+ * month: '2',
+ * year: '1911'
+ * },
+ * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
+ * }
+ *
+ * @param {String} queryString The query string to decode
+ * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
+ * PHP / Ruby on Rails servers and similar.
+ * @return {Object}
+ */
fromQueryString: function(queryString, recursive) {
var parts = queryString.replace(/^\?/, '').split('&'),
object = {},
@@ -2664,32 +3054,29 @@ var ExtObject = Ext.Object = {
},
/**
- * Iterate through an object and invoke the given callback function for each iteration. The iteration can be stop
- * by returning `false` in the callback function. For example:
-
- var person = {
- name: 'Jacky'
- hairColor: 'black'
- loves: ['food', 'sleeping', 'wife']
- };
-
- Ext.Object.each(person, function(key, value, myself) {
- console.log(key + ":" + value);
-
- if (key === 'hairColor') {
- return false; // stop the iteration
- }
- });
-
+ * Iterates through an object and invokes the given callback function for each iteration.
+ * The iteration can be stopped by returning `false` in the callback function. For example:
+ *
+ * var person = {
+ * name: 'Jacky'
+ * hairColor: 'black'
+ * loves: ['food', 'sleeping', 'wife']
+ * };
+ *
+ * Ext.Object.each(person, function(key, value, myself) {
+ * console.log(key + ":" + value);
+ *
+ * if (key === 'hairColor') {
+ * return false; // stop the iteration
+ * }
+ * });
+ *
* @param {Object} object The object to iterate
- * @param {Function} fn The callback function. Passed arguments for each iteration are:
-
-- {String} `key`
-- {Mixed} `value`
-- {Object} `object` The object itself
-
- * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
- * @markdown
+ * @param {Function} fn The callback function.
+ * @param {String} fn.key
+ * @param {Object} fn.value
+ * @param {Object} fn.object The object itself
+ * @param {Object} [scope] The execution scope (`this`) of the callback function
*/
each: function(object, fn, scope) {
for (var property in object) {
@@ -2703,44 +3090,43 @@ var ExtObject = Ext.Object = {
/**
* Merges any number of objects recursively without referencing them or their children.
-
- var extjs = {
- companyName: 'Ext JS',
- products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
- isSuperCool: true
- office: {
- size: 2000,
- location: 'Palo Alto',
- isFun: true
- }
- };
-
- var newStuff = {
- companyName: 'Sencha Inc.',
- products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
- office: {
- size: 40000,
- location: 'Redwood City'
- }
- };
-
- var sencha = Ext.Object.merge(extjs, newStuff);
-
- // extjs and sencha then equals to
- {
- companyName: 'Sencha Inc.',
- products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
- isSuperCool: true
- office: {
- size: 30000,
- location: 'Redwood City'
- isFun: true
- }
- }
-
- * @param {Object} object,...
+ *
+ * var extjs = {
+ * companyName: 'Ext JS',
+ * products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
+ * isSuperCool: true
+ * office: {
+ * size: 2000,
+ * location: 'Palo Alto',
+ * isFun: true
+ * }
+ * };
+ *
+ * var newStuff = {
+ * companyName: 'Sencha Inc.',
+ * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
+ * office: {
+ * size: 40000,
+ * location: 'Redwood City'
+ * }
+ * };
+ *
+ * var sencha = Ext.Object.merge(extjs, newStuff);
+ *
+ * // extjs and sencha then equals to
+ * {
+ * companyName: 'Sencha Inc.',
+ * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
+ * isSuperCool: true
+ * office: {
+ * size: 30000,
+ * location: 'Redwood City'
+ * isFun: true
+ * }
+ * }
+ *
+ * @param {Object...} object Any number of objects to merge.
* @return {Object} merged The object that is created as a result of merging all the objects passed in.
- * @markdown
*/
merge: function(source, key, value) {
if (typeof key === 'string') {
@@ -2779,17 +3165,16 @@ var ExtObject = Ext.Object = {
/**
* Returns the first matching key corresponding to the given value.
* If no matching value is found, null is returned.
-
- var person = {
- name: 'Jacky',
- loves: 'food'
- };
-
- alert(Ext.Object.getKey(sencha, 'loves')); // alerts 'food'
-
+ *
+ * var person = {
+ * name: 'Jacky',
+ * loves: 'food'
+ * };
+ *
+ * alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
+ *
* @param {Object} object
* @param {Object} value The value to find
- * @markdown
*/
getKey: function(object, value) {
for (var property in object) {
@@ -2803,15 +3188,14 @@ var ExtObject = Ext.Object = {
/**
* Gets all values of the given object as an array.
-
- var values = Ext.Object.getValues({
- name: 'Jacky',
- loves: 'food'
- }); // ['Jacky', 'food']
-
+ *
+ * var values = Ext.Object.getValues({
+ * name: 'Jacky',
+ * loves: 'food'
+ * }); // ['Jacky', 'food']
+ *
* @param {Object} object
* @return {Array} An array of values from the object
- * @markdown
*/
getValues: function(object) {
var values = [],
@@ -2828,14 +3212,15 @@ var ExtObject = Ext.Object = {
/**
* Gets all keys of the given object as an array.
-
- var values = Ext.Object.getKeys({
- name: 'Jacky',
- loves: 'food'
- }); // ['name', 'loves']
-
+ *
+ * var values = Ext.Object.getKeys({
+ * name: 'Jacky',
+ * loves: 'food'
+ * }); // ['name', 'loves']
+ *
* @param {Object} object
- * @return {Array} An array of keys from the object
+ * @return {String[]} An array of keys from the object
+ * @method
*/
getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
var keys = [],
@@ -2852,15 +3237,14 @@ var ExtObject = Ext.Object = {
/**
* Gets the total number of this object's own properties
-
- var size = Ext.Object.getSize({
- name: 'Jacky',
- loves: 'food'
- }); // size equals 2
-
+ *
+ * var size = Ext.Object.getSize({
+ * name: 'Jacky',
+ * loves: 'food'
+ * }); // size equals 2
+ *
* @param {Object} object
* @return {Number} size
- * @markdown
*/
getSize: function(object) {
var size = 0,
@@ -2878,19 +3262,21 @@ var ExtObject = Ext.Object = {
/**
- * A convenient alias method for {@link Ext.Object#merge}
+ * A convenient alias method for {@link Ext.Object#merge}.
*
* @member Ext
* @method merge
+ * @alias Ext.Object#merge
*/
Ext.merge = Ext.Object.merge;
/**
- * A convenient alias method for {@link Ext.Object#toQueryString}
+ * Alias for {@link Ext.Object#toQueryString}.
*
* @member Ext
* @method urlEncode
- * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead
+ * @alias Ext.Object#toQueryString
+ * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
*/
Ext.urlEncode = function() {
var args = Ext.Array.from(arguments),
@@ -2906,11 +3292,12 @@ Ext.urlEncode = function() {
};
/**
- * A convenient alias method for {@link Ext.Object#fromQueryString}
+ * Alias for {@link Ext.Object#fromQueryString}.
*
* @member Ext
* @method urlDecode
- * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead
+ * @alias Ext.Object#fromQueryString
+ * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
*/
Ext.urlDecode = function() {
return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
@@ -3051,6 +3438,7 @@ Ext.Date = {
/**
* Returns the current timestamp
* @return {Date} The current timestamp
+ * @method
*/
now: Date.now || function() {
return +new Date();
@@ -3087,7 +3475,6 @@ Ext.Date = {
* default behaviour of javascript Date objects.
* (see {@link #parse} for more information)
* Defaults to false.
- * @static
* @type Boolean
*/
useStrict: false,
@@ -3130,7 +3517,6 @@ Ext.Date.parseFunctions['x-date-format'] = myDateParser;
* 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: {
@@ -3159,7 +3545,6 @@ Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
*
To 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: {
@@ -3173,48 +3558,41 @@ Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
/**
* 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",
@@ -3245,12 +3623,12 @@ Ext.Date.defaults.d = 1;
Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
* @property defaults
- * @static
* @type Object
*/
defaults: {},
/**
+ * @property {String[]} dayNames
* An array of textual day names.
* Override these values for international dates.
* Example:
@@ -3261,8 +3639,6 @@ Ext.Date.dayNames = [
...
];
- * @type Array
- * @static
*/
dayNames : [
"Sunday",
@@ -3275,6 +3651,7 @@ Ext.Date.dayNames = [
],
/**
+ * @property {String[]} monthNames
* An array of textual month names.
* Override these values for international dates.
* Example:
@@ -3285,8 +3662,6 @@ Ext.Date.monthNames = [
...
];
- * @type Array
- * @static
*/
monthNames : [
"January",
@@ -3304,6 +3679,7 @@ Ext.Date.monthNames = [
],
/**
+ * @property {Object} monthNumbers
* 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:
@@ -3314,8 +3690,6 @@ Ext.Date.monthNumbers = {
...
};
- * @type Object
- * @static
*/
monthNumbers : {
Jan:0,
@@ -3332,12 +3706,10 @@ Ext.Date.monthNumbers = {
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.
- * @property defaultFormat
- * @static
- * @type String
+ * @property {String} defaultFormat
+ * The date format string that the {@link Ext.util.Format#dateRenderer}
+ * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.
+ * This may be overridden in a locale file.
*/
defaultFormat : "m/d/Y",
/**
@@ -3345,7 +3717,6 @@ Ext.Date.monthNumbers = {
* Override this function for international dates.
* @param {Number} month A zero-based javascript month number.
* @return {String} The short month name.
- * @static
*/
getShortMonthName : function(month) {
return utilDate.monthNames[month].substring(0, 3);
@@ -3356,7 +3727,6 @@ Ext.Date.monthNumbers = {
* Override this function for international dates.
* @param {Number} day A zero-based javascript day number.
* @return {String} The short day name.
- * @static
*/
getShortDayName : function(day) {
return utilDate.dayNames[day].substring(0, 3);
@@ -3367,7 +3737,6 @@ Ext.Date.monthNumbers = {
* Override this function for international dates.
* @param {String} name The short/full month name.
* @return {Number} The zero-based javascript month number.
- * @static
*/
getMonthNumber : function(name) {
// handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
@@ -3378,7 +3747,7 @@ Ext.Date.monthNumbers = {
* Checks if the specified format contains hour information
* @param {String} format The format to check
* @return {Boolean} True if the format contains hour information
- * @static
+ * @method
*/
formatContainsHourInfo : (function(){
var stripEscapeRe = /(\\.)/g,
@@ -3394,7 +3763,7 @@ Ext.Date.monthNumbers = {
* @param {String} format The format to check
* @return {Boolean} True if the format contains information about
* date/day information.
- * @static
+ * @method
*/
formatContainsDateInfo : (function(){
var stripEscapeRe = /(\\.)/g,
@@ -3418,7 +3787,6 @@ 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')",
@@ -3488,7 +3856,6 @@ console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the
* @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
@@ -3538,7 +3905,6 @@ dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
* @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;
@@ -3785,7 +4151,7 @@ dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
+ "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.
@@ -4005,6 +4371,7 @@ dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
* (equivalent to the format specifier 'W', but without a leading zero).
* @param {Date} date The date
* @return {Number} 1 to 53
+ * @method
*/
getWeekOfYear : (function() {
// adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
@@ -4088,6 +4455,7 @@ console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
* 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.
+ * @method
*/
getDaysInMonth: (function() {
var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
@@ -4297,9 +4665,10 @@ var utilDate = Ext.Date;
* @docauthor Jacky Nguyen
* @class Ext.Base
*
- * The root of all classes created with {@link Ext#define}
- * All prototype and static members of this class are inherited by any other class
+ * The root of all classes created with {@link Ext#define}.
*
+ * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
+ * All prototype and static members of this class are inherited by all other classes.
*/
(function(flexSetter) {
@@ -4313,81 +4682,74 @@ var Base = Ext.Base = function() {};
* Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
* `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
* for a detailed comparison
-
- Ext.define('My.Cat', {
- statics: {
- speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
- },
-
- constructor: function() {
- alert(this.self.speciesName); / dependent on 'this'
-
- return this;
- },
-
- clone: function() {
- return new this.self();
- }
- });
-
-
- Ext.define('My.SnowLeopard', {
- extend: 'My.Cat',
- statics: {
- speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
- }
- });
-
- var cat = new My.Cat(); // alerts 'Cat'
- var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
-
- var clone = snowLeopard.clone();
- alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
-
- * @type Class
+ *
+ * Ext.define('My.Cat', {
+ * statics: {
+ * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
+ * },
+ *
+ * constructor: function() {
+ * alert(this.self.speciesName); / dependent on 'this'
+ *
+ * return this;
+ * },
+ *
+ * clone: function() {
+ * return new this.self();
+ * }
+ * });
+ *
+ *
+ * Ext.define('My.SnowLeopard', {
+ * extend: 'My.Cat',
+ * statics: {
+ * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
+ * }
+ * });
+ *
+ * var cat = new My.Cat(); // alerts 'Cat'
+ * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
+ *
+ * var clone = snowLeopard.clone();
+ * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
+ *
+ * @type Ext.Class
* @protected
- * @markdown
*/
self: Base,
- /**
- * Default constructor, simply returns `this`
- *
- * @constructor
- * @protected
- * @return {Object} this
- */
+ // Default constructor, simply returns `this`
constructor: function() {
return this;
},
+ //
/**
* Initialize configuration for this class. a typical example:
-
- Ext.define('My.awesome.Class', {
- // The default config
- config: {
- name: 'Awesome',
- isAwesome: true
- },
-
- constructor: function(config) {
- this.initConfig(config);
-
- return this;
- }
- });
-
- var awesome = new My.awesome.Class({
- name: 'Super Awesome'
- });
-
- alert(awesome.getName()); // 'Super Awesome'
-
+ *
+ * Ext.define('My.awesome.Class', {
+ * // The default config
+ * config: {
+ * name: 'Awesome',
+ * isAwesome: true
+ * },
+ *
+ * constructor: function(config) {
+ * this.initConfig(config);
+ *
+ * return this;
+ * }
+ * });
+ *
+ * var awesome = new My.awesome.Class({
+ * name: 'Super Awesome'
+ * });
+ *
+ * alert(awesome.getName()); // 'Super Awesome'
+ *
* @protected
* @param {Object} config
* @return {Object} mixins The mixin prototypes as key - value pairs
- * @markdown
*/
initConfig: function(config) {
if (!this.$configInited) {
@@ -4422,46 +4784,46 @@ var Base = Ext.Base = function() {};
return this;
}),
+ //
/**
* Call the parent's overridden method. For example:
-
- Ext.define('My.own.A', {
- constructor: function(test) {
- alert(test);
- }
- });
-
- Ext.define('My.own.B', {
- extend: 'My.own.A',
-
- constructor: function(test) {
- alert(test);
-
- this.callParent([test + 1]);
- }
- });
-
- Ext.define('My.own.C', {
- extend: 'My.own.B',
-
- constructor: function() {
- alert("Going to call parent's overriden constructor...");
-
- this.callParent(arguments);
- }
- });
-
- var a = new My.own.A(1); // alerts '1'
- var b = new My.own.B(1); // alerts '1', then alerts '2'
- var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
- // alerts '2', then alerts '3'
-
+ *
+ * Ext.define('My.own.A', {
+ * constructor: function(test) {
+ * alert(test);
+ * }
+ * });
+ *
+ * Ext.define('My.own.B', {
+ * extend: 'My.own.A',
+ *
+ * constructor: function(test) {
+ * alert(test);
+ *
+ * this.callParent([test + 1]);
+ * }
+ * });
+ *
+ * Ext.define('My.own.C', {
+ * extend: 'My.own.B',
+ *
+ * constructor: function() {
+ * alert("Going to call parent's overriden constructor...");
+ *
+ * this.callParent(arguments);
+ * }
+ * });
+ *
+ * var a = new My.own.A(1); // alerts '1'
+ * var b = new My.own.B(1); // alerts '1', then alerts '2'
+ * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
+ * // alerts '2', then alerts '3'
+ *
* @protected
* @param {Array/Arguments} args The arguments, either an array or the `arguments` object
* from the current method, for example: `this.callParent(arguments)`
- * @return {Mixed} Returns the result from the superclass' method
- * @markdown
+ * @return {Object} Returns the result from the superclass' method
*/
callParent: function(args) {
var method = this.callParent.caller,
@@ -4503,61 +4865,60 @@ var Base = Ext.Base = function() {};
* Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
* `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
* `this` points to during run-time
-
- Ext.define('My.Cat', {
- statics: {
- totalCreated: 0,
- speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
- },
-
- constructor: function() {
- var statics = this.statics();
-
- alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
- // equivalent to: My.Cat.speciesName
-
- alert(this.self.speciesName); // dependent on 'this'
-
- statics.totalCreated++;
-
- return this;
- },
-
- clone: function() {
- var cloned = new this.self; // dependent on 'this'
-
- cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
-
- return cloned;
- }
- });
-
-
- Ext.define('My.SnowLeopard', {
- extend: 'My.Cat',
-
- statics: {
- speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
- },
-
- constructor: function() {
- this.callParent();
- }
- });
-
- var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
-
- var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
-
- var clone = snowLeopard.clone();
- alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
- alert(clone.groupName); // alerts 'Cat'
-
- alert(My.Cat.totalCreated); // alerts 3
-
+ *
+ * Ext.define('My.Cat', {
+ * statics: {
+ * totalCreated: 0,
+ * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
+ * },
+ *
+ * constructor: function() {
+ * var statics = this.statics();
+ *
+ * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
+ * // equivalent to: My.Cat.speciesName
+ *
+ * alert(this.self.speciesName); // dependent on 'this'
+ *
+ * statics.totalCreated++;
+ *
+ * return this;
+ * },
+ *
+ * clone: function() {
+ * var cloned = new this.self; // dependent on 'this'
+ *
+ * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
+ *
+ * return cloned;
+ * }
+ * });
+ *
+ *
+ * Ext.define('My.SnowLeopard', {
+ * extend: 'My.Cat',
+ *
+ * statics: {
+ * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
+ * },
+ *
+ * constructor: function() {
+ * this.callParent();
+ * }
+ * });
+ *
+ * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
+ *
+ * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
+ *
+ * var clone = snowLeopard.clone();
+ * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
+ * alert(clone.groupName); // alerts 'Cat'
+ *
+ * alert(My.Cat.totalCreated); // alerts 3
+ *
* @protected
- * @return {Class}
- * @markdown
+ * @return {Ext.Class}
*/
statics: function() {
var method = this.statics.caller,
@@ -4572,34 +4933,34 @@ var Base = Ext.Base = function() {};
/**
* Call the original method that was previously overridden with {@link Ext.Base#override}
-
- Ext.define('My.Cat', {
- constructor: function() {
- alert("I'm a cat!");
-
- return this;
- }
- });
-
- My.Cat.override({
- constructor: function() {
- alert("I'm going to be a cat!");
-
- var instance = this.callOverridden();
-
- alert("Meeeeoooowwww");
-
- return instance;
- }
- });
-
- var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
- // alerts "I'm a cat!"
- // alerts "Meeeeoooowwww"
-
+ *
+ * Ext.define('My.Cat', {
+ * constructor: function() {
+ * alert("I'm a cat!");
+ *
+ * return this;
+ * }
+ * });
+ *
+ * My.Cat.override({
+ * constructor: function() {
+ * alert("I'm going to be a cat!");
+ *
+ * var instance = this.callOverridden();
+ *
+ * alert("Meeeeoooowwww");
+ *
+ * return instance;
+ * }
+ * });
+ *
+ * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
+ * // alerts "I'm a cat!"
+ * // alerts "Meeeeoooowwww"
+ *
* @param {Array/Arguments} args The arguments, either an array or the `arguments` object
- * @return {Mixed} Returns the result after calling the overridden method
- * @markdown
+ * @return {Object} Returns the result after calling the overridden method
+ * @protected
*/
callOverridden: function(args) {
var method = this.callOverridden.caller;
@@ -4633,17 +4994,20 @@ var Base = Ext.Base = function() {};
Ext.apply(Ext.Base, {
/**
* Create a new instance of this Class.
-Ext.define('My.cool.Class', {
- ...
-});
-
-My.cool.Class.create({
- someConfig: true
-});
- * @property create
+ *
+ * Ext.define('My.cool.Class', {
+ * ...
+ * });
+ *
+ * My.cool.Class.create({
+ * someConfig: true
+ * });
+ *
+ * All parameters are passed to the constructor of the class.
+ *
+ * @return {Object} the created instance.
* @static
- * @type Function
- * @markdown
+ * @inheritable
*/
create: function() {
return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
@@ -4651,23 +5015,25 @@ My.cool.Class.create({
/**
* @private
+ * @inheritable
*/
- own: flexSetter(function(name, value) {
- if (typeof value === 'function') {
+ own: function(name, value) {
+ if (typeof value == 'function') {
this.ownMethod(name, value);
}
else {
this.prototype[name] = value;
}
- }),
+ },
/**
* @private
+ * @inheritable
*/
ownMethod: function(name, fn) {
var originalFn;
- if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
+ if (typeof fn.$owner !== 'undefined' && fn !== Ext.emptyFn) {
originalFn = fn;
fn = function() {
@@ -4690,22 +5056,21 @@ My.cool.Class.create({
/**
* Add / override static properties of this class.
-
- Ext.define('My.cool.Class', {
- ...
- });
-
- My.cool.Class.addStatics({
- someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
- method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
- method2: function() { ... } // My.cool.Class.method2 = function() { ... };
- });
-
- * @property addStatics
- * @static
- * @type Function
+ *
+ * Ext.define('My.cool.Class', {
+ * ...
+ * });
+ *
+ * My.cool.Class.addStatics({
+ * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
+ * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
+ * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
+ * });
+ *
* @param {Object} members
- * @markdown
+ * @return {Ext.Base} this
+ * @static
+ * @inheritable
*/
addStatics: function(members) {
for (var name in members) {
@@ -4718,45 +5083,86 @@ My.cool.Class.create({
},
/**
- * Add methods / properties to the prototype of this class.
-
- Ext.define('My.awesome.Cat', {
- constructor: function() {
- ...
- }
- });
+ * @private
+ * @param {Object} members
+ */
+ addInheritableStatics: function(members) {
+ var inheritableStatics,
+ hasInheritableStatics,
+ prototype = this.prototype,
+ name, member;
- My.awesome.Cat.implement({
- meow: function() {
- alert('Meowww...');
- }
- });
+ inheritableStatics = prototype.$inheritableStatics;
+ hasInheritableStatics = prototype.$hasInheritableStatics;
- var kitty = new My.awesome.Cat;
- kitty.meow();
+ if (!inheritableStatics) {
+ inheritableStatics = prototype.$inheritableStatics = [];
+ hasInheritableStatics = prototype.$hasInheritableStatics = {};
+ }
- * @property implement
- * @static
- * @type Function
- * @param {Object} members
- * @markdown
- */
- implement: function(members) {
- var prototype = this.prototype,
- name, i, member, previous;
//
var className = Ext.getClassName(this);
//
+
for (name in members) {
if (members.hasOwnProperty(name)) {
member = members[name];
+ //
+ if (typeof member == 'function') {
+ member.displayName = className + '.' + name;
+ }
+ //
+ this[name] = member;
- if (typeof member === 'function') {
- member.$owner = this;
- member.$name = name;
- //
- if (className) {
- member.displayName = className + '#' + name;
+ if (!hasInheritableStatics[name]) {
+ hasInheritableStatics[name] = true;
+ inheritableStatics.push(name);
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Add methods / properties to the prototype of this class.
+ *
+ * Ext.define('My.awesome.Cat', {
+ * constructor: function() {
+ * ...
+ * }
+ * });
+ *
+ * My.awesome.Cat.implement({
+ * meow: function() {
+ * alert('Meowww...');
+ * }
+ * });
+ *
+ * var kitty = new My.awesome.Cat;
+ * kitty.meow();
+ *
+ * @param {Object} members
+ * @static
+ * @inheritable
+ */
+ implement: function(members) {
+ var prototype = this.prototype,
+ enumerables = Ext.enumerables,
+ name, i, member;
+ //
+ var className = Ext.getClassName(this);
+ //
+ for (name in members) {
+ if (members.hasOwnProperty(name)) {
+ member = members[name];
+
+ if (typeof member === 'function') {
+ member.$owner = this;
+ member.$name = name;
+ //
+ if (className) {
+ member.displayName = className + '#' + name;
}
//
}
@@ -4765,9 +5171,7 @@ My.cool.Class.create({
}
}
- if (Ext.enumerables) {
- var enumerables = Ext.enumerables;
-
+ if (enumerables) {
for (i = enumerables.length; i--;) {
name = enumerables[i];
@@ -4783,32 +5187,30 @@ My.cool.Class.create({
/**
* Borrow another class' members to the prototype of this class.
-
-Ext.define('Bank', {
- money: '$$$',
- printMoney: function() {
- alert('$$$$$$$');
- }
-});
-
-Ext.define('Thief', {
- ...
-});
-
-Thief.borrow(Bank, ['money', 'printMoney']);
-
-var steve = new Thief();
-
-alert(steve.money); // alerts '$$$'
-steve.printMoney(); // alerts '$$$$$$$'
-
- * @property borrow
- * @static
- * @type Function
+ *
+ * Ext.define('Bank', {
+ * money: '$$$',
+ * printMoney: function() {
+ * alert('$$$$$$$');
+ * }
+ * });
+ *
+ * Ext.define('Thief', {
+ * ...
+ * });
+ *
+ * Thief.borrow(Bank, ['money', 'printMoney']);
+ *
+ * var steve = new Thief();
+ *
+ * alert(steve.money); // alerts '$$$'
+ * steve.printMoney(); // alerts '$$$$$$$'
+ *
* @param {Ext.Base} fromClass The class to borrow members from
- * @param {Array/String} members The names of the members to borrow
+ * @param {String/String[]} members The names of the members to borrow
* @return {Ext.Base} this
- * @markdown
+ * @static
+ * @inheritable
*/
borrow: function(fromClass, members) {
var fromPrototype = fromClass.prototype,
@@ -4828,42 +5230,60 @@ steve.printMoney(); // alerts '$$$$$$$'
/**
* Override prototype members of this class. Overridden methods can be invoked via
* {@link Ext.Base#callOverridden}
-
- Ext.define('My.Cat', {
- constructor: function() {
- alert("I'm a cat!");
-
- return this;
- }
- });
-
- My.Cat.override({
- constructor: function() {
- alert("I'm going to be a cat!");
-
- var instance = this.callOverridden();
-
- alert("Meeeeoooowwww");
-
- return instance;
- }
- });
-
- var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
- // alerts "I'm a cat!"
- // alerts "Meeeeoooowwww"
-
- * @property override
- * @static
- * @type Function
+ *
+ * Ext.define('My.Cat', {
+ * constructor: function() {
+ * alert("I'm a cat!");
+ *
+ * return this;
+ * }
+ * });
+ *
+ * My.Cat.override({
+ * constructor: function() {
+ * alert("I'm going to be a cat!");
+ *
+ * var instance = this.callOverridden();
+ *
+ * alert("Meeeeoooowwww");
+ *
+ * return instance;
+ * }
+ * });
+ *
+ * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
+ * // alerts "I'm a cat!"
+ * // alerts "Meeeeoooowwww"
+ *
* @param {Object} members
* @return {Ext.Base} this
- * @markdown
+ * @static
+ * @inheritable
*/
override: function(members) {
var prototype = this.prototype,
+ enumerables = Ext.enumerables,
name, i, member, previous;
+ if (arguments.length === 2) {
+ name = members;
+ member = arguments[1];
+
+ if (typeof member == 'function') {
+ if (typeof prototype[name] == 'function') {
+ previous = prototype[name];
+ member.$previous = previous;
+ }
+
+ this.ownMethod(name, member);
+ }
+ else {
+ prototype[name] = member;
+ }
+
+ return this;
+ }
+
for (name in members) {
if (members.hasOwnProperty(name)) {
member = members[name];
@@ -4882,14 +5302,12 @@ steve.printMoney(); // alerts '$$$$$$$'
}
}
- if (Ext.enumerables) {
- var enumerables = Ext.enumerables;
-
+ if (enumerables) {
for (i = enumerables.length; i--;) {
name = enumerables[i];
if (members.hasOwnProperty(name)) {
- if (prototype[name] !== undefined) {
+ if (typeof prototype[name] !== 'undefined') {
previous = prototype[name];
members[name].$previous = previous;
}
@@ -4902,58 +5320,73 @@ steve.printMoney(); // alerts '$$$$$$$'
return this;
},
+ //
/**
* Used internally by the mixins pre-processor
* @private
+ * @inheritable
*/
- mixin: flexSetter(function(name, cls) {
+ mixin: function(name, cls) {
var mixin = cls.prototype,
my = this.prototype,
- i, fn;
+ key, fn;
- for (i in mixin) {
- if (mixin.hasOwnProperty(i)) {
- if (my[i] === undefined) {
- if (typeof mixin[i] === 'function') {
- fn = mixin[i];
+ for (key in mixin) {
+ if (mixin.hasOwnProperty(key)) {
+ if (typeof my[key] === 'undefined' && key !== 'mixins' && key !== 'mixinId') {
+ if (typeof mixin[key] === 'function') {
+ fn = mixin[key];
- if (fn.$owner === undefined) {
- this.ownMethod(i, fn);
+ if (typeof fn.$owner === 'undefined') {
+ this.ownMethod(key, fn);
}
else {
- my[i] = fn;
+ my[key] = fn;
}
}
else {
- my[i] = mixin[i];
+ my[key] = mixin[key];
}
}
- else if (i === 'config' && my.config && mixin.config) {
+ //
+ else if (key === 'config' && my.config && mixin.config) {
Ext.Object.merge(my.config, mixin.config);
}
+ //
}
}
- if (my.mixins === undefined) {
- my.mixins = {};
+ if (typeof mixin.onClassMixedIn !== 'undefined') {
+ mixin.onClassMixedIn.call(cls, this);
+ }
+
+ if (!my.hasOwnProperty('mixins')) {
+ if ('mixins' in my) {
+ my.mixins = Ext.Object.merge({}, my.mixins);
+ }
+ else {
+ my.mixins = {};
+ }
}
my.mixins[name] = mixin;
- }),
+ },
+ //
/**
* Get the current class' name in string format.
-
- Ext.define('My.cool.Class', {
- constructor: function() {
- alert(this.self.getName()); // alerts 'My.cool.Class'
- }
- });
-
- My.cool.Class.getName(); // 'My.cool.Class'
-
+ *
+ * Ext.define('My.cool.Class', {
+ * constructor: function() {
+ * alert(this.self.getName()); // alerts 'My.cool.Class'
+ * }
+ * });
+ *
+ * My.cool.Class.getName(); // 'My.cool.Class'
+ *
* @return {String} className
- * @markdown
+ * @static
+ * @inheritable
*/
getName: function() {
return Ext.getClassName(this);
@@ -4961,35 +5394,36 @@ steve.printMoney(); // alerts '$$$$$$$'
/**
* Create aliases for existing prototype methods. Example:
-
- Ext.define('My.cool.Class', {
- method1: function() { ... },
- method2: function() { ... }
- });
-
- var test = new My.cool.Class();
-
- My.cool.Class.createAlias({
- method3: 'method1',
- method4: 'method2'
- });
-
- test.method3(); // test.method1()
-
- My.cool.Class.createAlias('method5', 'method3');
-
- test.method5(); // test.method3() -> test.method1()
-
- * @property createAlias
- * @static
- * @type Function
+ *
+ * Ext.define('My.cool.Class', {
+ * method1: function() { ... },
+ * method2: function() { ... }
+ * });
+ *
+ * var test = new My.cool.Class();
+ *
+ * My.cool.Class.createAlias({
+ * method3: 'method1',
+ * method4: 'method2'
+ * });
+ *
+ * test.method3(); // test.method1()
+ *
+ * My.cool.Class.createAlias('method5', 'method3');
+ *
+ * test.method5(); // test.method3() -> test.method1()
+ *
* @param {String/Object} alias The new method name, or an object to set multiple aliases. See
* {@link Ext.Function#flexSetter flexSetter}
* @param {String/Object} origin The original method name
- * @markdown
+ * @static
+ * @inheritable
+ * @method
*/
createAlias: flexSetter(function(alias, origin) {
- this.prototype[alias] = this.prototype[origin];
+ this.prototype[alias] = function() {
+ return this[origin].apply(this, arguments);
+ }
})
});
@@ -4999,194 +5433,16 @@ steve.printMoney(); // alerts '$$$$$$$'
* @author Jacky Nguyen
* @docauthor Jacky Nguyen
* @class Ext.Class
- *
- * Handles class creation throughout the whole framework. Note that most of the time {@link Ext#define Ext.define} should
- * be used instead, since it's a higher level wrapper that aliases to {@link Ext.ClassManager#create}
- * to enable namespacing and dynamic dependency resolution.
- *
- * # Basic syntax: #
- *
- * Ext.define(className, properties);
- *
- * in which `properties` is an object represent a collection of properties that apply to the class. See
- * {@link Ext.ClassManager#create} for more detailed instructions.
- *
- * Ext.define('Person', {
- * name: 'Unknown',
- *
- * constructor: function(name) {
- * if (name) {
- * this.name = name;
- * }
- *
- * return this;
- * },
- *
- * eat: function(foodType) {
- * alert("I'm eating: " + foodType);
- *
- * return this;
- * }
- * });
- *
- * var aaron = new Person("Aaron");
- * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
- *
- * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
- * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
- *
- * # Inheritance: #
- *
- * Ext.define('Developer', {
- * extend: 'Person',
- *
- * constructor: function(name, isGeek) {
- * this.isGeek = isGeek;
- *
- * // Apply a method from the parent class' prototype
- * this.callParent([name]);
- *
- * return this;
- *
- * },
- *
- * code: function(language) {
- * alert("I'm coding in: " + language);
- *
- * this.eat("Bugs");
- *
- * return this;
- * }
- * });
- *
- * var jacky = new Developer("Jacky", true);
- * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
- * // alert("I'm eating: Bugs");
- *
- * See {@link Ext.Base#callParent} for more details on calling superclass' methods
- *
- * # Mixins: #
- *
- * Ext.define('CanPlayGuitar', {
- * playGuitar: function() {
- * alert("F#...G...D...A");
- * }
- * });
- *
- * Ext.define('CanComposeSongs', {
- * composeSongs: function() { ... }
- * });
- *
- * Ext.define('CanSing', {
- * sing: function() {
- * alert("I'm on the highway to hell...")
- * }
- * });
- *
- * Ext.define('Musician', {
- * extend: 'Person',
- *
- * mixins: {
- * canPlayGuitar: 'CanPlayGuitar',
- * canComposeSongs: 'CanComposeSongs',
- * canSing: 'CanSing'
- * }
- * })
- *
- * Ext.define('CoolPerson', {
- * extend: 'Person',
- *
- * mixins: {
- * canPlayGuitar: 'CanPlayGuitar',
- * canSing: 'CanSing'
- * },
- *
- * sing: function() {
- * alert("Ahem....");
- *
- * this.mixins.canSing.sing.call(this);
- *
- * alert("[Playing guitar at the same time...]");
- *
- * this.playGuitar();
- * }
- * });
- *
- * var me = new CoolPerson("Jacky");
- *
- * me.sing(); // alert("Ahem...");
- * // alert("I'm on the highway to hell...");
- * // alert("[Playing guitar at the same time...]");
- * // alert("F#...G...D...A");
- *
- * # Config: #
- *
- * Ext.define('SmartPhone', {
- * config: {
- * hasTouchScreen: false,
- * operatingSystem: 'Other',
- * price: 500
- * },
- *
- * isExpensive: false,
- *
- * constructor: function(config) {
- * this.initConfig(config);
- *
- * return this;
- * },
- *
- * applyPrice: function(price) {
- * this.isExpensive = (price > 500);
- *
- * return price;
- * },
- *
- * applyOperatingSystem: function(operatingSystem) {
- * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
- * return 'Other';
- * }
- *
- * return operatingSystem;
- * }
- * });
- *
- * var iPhone = new SmartPhone({
- * hasTouchScreen: true,
- * operatingSystem: 'iOS'
- * });
- *
- * iPhone.getPrice(); // 500;
- * iPhone.getOperatingSystem(); // 'iOS'
- * iPhone.getHasTouchScreen(); // true;
- * iPhone.hasTouchScreen(); // true
- *
- * iPhone.isExpensive; // false;
- * iPhone.setPrice(600);
- * iPhone.getPrice(); // 600
- * iPhone.isExpensive; // true;
- *
- * iPhone.setOperatingSystem('AlienOS');
- * iPhone.getOperatingSystem(); // 'Other'
- *
- * # Statics: #
- *
- * Ext.define('Computer', {
- * statics: {
- * factory: function(brand) {
- * // 'this' in static methods refer to the class itself
- * return new this(brand);
- * }
- * },
- *
- * constructor: function() { ... }
- * });
- *
- * var dellComputer = Computer.factory('Dell');
- *
- * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
- * static properties within class methods
*
+ * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
+ * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
+ * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
+ *
+ * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
+ * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
+ *
+ * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
+ * from, see {@link Ext.Base}.
*/
(function() {
@@ -5202,14 +5458,15 @@ steve.printMoney(); // alerts '$$$$$$$'
}
/**
- * @constructor
+ * @method constructor
+ * Creates new class.
* @param {Object} classData An object represent the properties of this class
- * @param {Function} createdFn Optional, the callback function to be executed when this class is fully created.
+ * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
* Note that the creation process can be asynchronous depending on the pre-processors used.
* @return {Ext.Base} The newly created class
*/
Ext.Class = Class = function(newClass, classData, onClassCreated) {
- if (typeof newClass !== 'function') {
+ if (typeof newClass != 'function') {
onClassCreated = classData;
classData = newClass;
newClass = function() {
@@ -5225,7 +5482,7 @@ steve.printMoney(); // alerts '$$$$$$$'
registeredPreprocessors = Class.getPreprocessors(),
index = 0,
preprocessors = [],
- preprocessor, preprocessors, staticPropertyName, process, i, j, ln;
+ preprocessor, staticPropertyName, process, i, j, ln;
for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
staticPropertyName = baseStaticProperties[i];
@@ -5237,7 +5494,7 @@ steve.printMoney(); // alerts '$$$$$$$'
for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
preprocessor = preprocessorStack[j];
- if (typeof preprocessor === 'string') {
+ if (typeof preprocessor == 'string') {
preprocessor = registeredPreprocessors[preprocessor];
if (!preprocessor.always) {
@@ -5254,7 +5511,7 @@ steve.printMoney(); // alerts '$$$$$$$'
}
}
- classData.onClassCreated = onClassCreated;
+ classData.onClassCreated = onClassCreated || Ext.emptyFn;
classData.onBeforeClassCreated = function(cls, data) {
onClassCreated = data.onClassCreated;
@@ -5264,9 +5521,7 @@ steve.printMoney(); // alerts '$$$$$$$'
cls.implement(data);
- if (onClassCreated) {
- onClassCreated.call(cls, cls);
- }
+ onClassCreated.call(cls, cls);
};
process = function(cls, data) {
@@ -5295,29 +5550,27 @@ steve.printMoney(); // alerts '$$$$$$$'
/**
* Register a new pre-processor to be used during the class creation process
*
- * @member Ext.Class registerPreprocessor
+ * @member Ext.Class
* @param {String} name The pre-processor's name
* @param {Function} fn The callback function to be executed. Typical format:
-
- function(cls, data, fn) {
- // Your code here
-
- // Execute this when the processing is finished.
- // Asynchronous processing is perfectly ok
- if (fn) {
- fn.call(this, cls, data);
- }
- });
-
- * Passed arguments for this function are:
*
- * - `{Function} cls`: The created class
- * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
- * - `{Function} fn`: The callback function that must to be executed when this pre-processor finishes,
+ * function(cls, data, fn) {
+ * // Your code here
+ *
+ * // Execute this when the processing is finished.
+ * // Asynchronous processing is perfectly ok
+ * if (fn) {
+ * fn.call(this, cls, data);
+ * }
+ * });
+ *
+ * @param {Function} fn.cls The created class
+ * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
+ * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
* regardless of whether the processing is synchronous or aynchronous
*
* @return {Ext.Class} this
- * @markdown
+ * @static
*/
registerPreprocessor: function(name, fn, always) {
this.preprocessors[name] = {
@@ -5334,6 +5587,7 @@ steve.printMoney(); // alerts '$$$$$$$'
*
* @param {String} name
* @return {Function} preprocessor
+ * @static
*/
getPreprocessor: function(name) {
return this.preprocessors[name];
@@ -5346,7 +5600,8 @@ steve.printMoney(); // alerts '$$$$$$$'
/**
* Retrieve the array stack of default pre-processors
*
- * @return {Function} defaultPreprocessors
+ * @return {Function[]} defaultPreprocessors
+ * @static
*/
getDefaultPreprocessors: function() {
return this.defaultPreprocessors || [];
@@ -5355,8 +5610,9 @@ steve.printMoney(); // alerts '$$$$$$$'
/**
* Set the default array stack of default pre-processors
*
- * @param {Array} preprocessors
+ * @param {Function/Function[]} preprocessors
* @return {Ext.Class} this
+ * @static
*/
setDefaultPreprocessors: function(preprocessors) {
this.defaultPreprocessors = Ext.Array.from(preprocessors);
@@ -5365,30 +5621,30 @@ steve.printMoney(); // alerts '$$$$$$$'
},
/**
- * Insert this pre-processor at a specific position in the stack, optionally relative to
+ * Inserts this pre-processor at a specific position in the stack, optionally relative to
* any existing pre-processor. For example:
-
- Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
- // Your code here
-
- if (fn) {
- fn.call(this, cls, data);
- }
- }).insertDefaultPreprocessor('debug', 'last');
-
+ *
+ * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
+ * // Your code here
+ *
+ * if (fn) {
+ * fn.call(this, cls, data);
+ * }
+ * }).setDefaultPreprocessorPosition('debug', 'last');
+ *
* @param {String} name The pre-processor name. Note that it needs to be registered with
- * {@link Ext#registerPreprocessor registerPreprocessor} before this
+ * {@link #registerPreprocessor registerPreprocessor} before this
* @param {String} offset The insertion position. Four possible values are:
* 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
* @param {String} relativeName
* @return {Ext.Class} this
- * @markdown
+ * @static
*/
setDefaultPreprocessorPosition: function(name, offset, relativeName) {
var defaultPreprocessors = this.defaultPreprocessors,
index;
- if (typeof offset === 'string') {
+ if (typeof offset == 'string') {
if (offset === 'first') {
defaultPreprocessors.unshift(name);
@@ -5406,13 +5662,26 @@ steve.printMoney(); // alerts '$$$$$$$'
index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
if (index !== -1) {
- defaultPreprocessors.splice(Math.max(0, index + offset), 0, name);
+ Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
}
return this;
}
});
+ /**
+ * @cfg {String} extend
+ * The parent class that this class extends. For example:
+ *
+ * Ext.define('Person', {
+ * say: function(text) { alert(text); }
+ * });
+ *
+ * Ext.define('Developer', {
+ * extend: 'Person',
+ * say: function(text) { this.callParent(["print "+text]); }
+ * });
+ */
Class.registerPreprocessor('extend', function(cls, data) {
var extend = data.extend,
base = Ext.Base,
@@ -5447,6 +5716,7 @@ steve.printMoney(); // alerts '$$$$$$$'
delete data.extend;
+ //
// Statics inheritance
parentStatics = parentPrototype.$inheritableStatics;
@@ -5459,7 +5729,9 @@ steve.printMoney(); // alerts '$$$$$$$'
}
}
}
+ //
+ //
// Merge the parent class' config object without referencing it
if (parentPrototype.config) {
clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
@@ -5467,7 +5739,9 @@ steve.printMoney(); // alerts '$$$$$$$'
else {
clsPrototype.config = {};
}
+ //
+ //
if (clsPrototype.$onExtended) {
clsPrototype.$onExtended.call(cls, cls, data);
}
@@ -5476,50 +5750,75 @@ steve.printMoney(); // alerts '$$$$$$$'
clsPrototype.$onExtended = data.onClassExtended;
delete data.onClassExtended;
}
+ //
}, true);
+ //
+ /**
+ * @cfg {Object} statics
+ * List of static methods for this class. For example:
+ *
+ * Ext.define('Computer', {
+ * statics: {
+ * factory: function(brand) {
+ * // 'this' in static methods refer to the class itself
+ * return new this(brand);
+ * }
+ * },
+ *
+ * constructor: function() { ... }
+ * });
+ *
+ * var dellComputer = Computer.factory('Dell');
+ */
Class.registerPreprocessor('statics', function(cls, data) {
- var statics = data.statics,
- name;
-
- for (name in statics) {
- if (statics.hasOwnProperty(name)) {
- cls[name] = statics[name];
- }
- }
+ cls.addStatics(data.statics);
delete data.statics;
});
+ //
+ //
+ /**
+ * @cfg {Object} inheritableStatics
+ * List of inheritable static methods for this class.
+ * Otherwise just like {@link #statics} but subclasses inherit these methods.
+ */
Class.registerPreprocessor('inheritableStatics', function(cls, data) {
- var statics = data.inheritableStatics,
- inheritableStatics,
- prototype = cls.prototype,
- name;
-
- inheritableStatics = prototype.$inheritableStatics;
-
- if (!inheritableStatics) {
- inheritableStatics = prototype.$inheritableStatics = [];
- }
-
- for (name in statics) {
- if (statics.hasOwnProperty(name)) {
- cls[name] = statics[name];
- inheritableStatics.push(name);
- }
- }
+ cls.addInheritableStatics(data.inheritableStatics);
delete data.inheritableStatics;
});
+ //
- Class.registerPreprocessor('mixins', function(cls, data) {
- cls.mixin(data.mixins);
-
- delete data.mixins;
- });
-
+ //
+ /**
+ * @cfg {Object} config
+ * List of configuration options with their default values, for which automatically
+ * accessor methods are generated. For example:
+ *
+ * Ext.define('SmartPhone', {
+ * config: {
+ * hasTouchScreen: false,
+ * operatingSystem: 'Other',
+ * price: 500
+ * },
+ * constructor: function(cfg) {
+ * this.initConfig(cfg);
+ * }
+ * });
+ *
+ * var iPhone = new SmartPhone({
+ * hasTouchScreen: true,
+ * operatingSystem: 'iOS'
+ * });
+ *
+ * iPhone.getPrice(); // 500;
+ * iPhone.getOperatingSystem(); // 'iOS'
+ * iPhone.getHasTouchScreen(); // true;
+ * iPhone.hasTouchScreen(); // true
+ */
Class.registerPreprocessor('config', function(cls, data) {
var prototype = cls.prototype;
@@ -5540,7 +5839,7 @@ steve.printMoney(); // alerts '$$$$$$$'
data[setter] = function(val) {
var ret = this[apply].call(this, val, this[pName]);
- if (ret !== undefined) {
+ if (typeof ret != 'undefined') {
this[pName] = ret;
}
@@ -5558,9 +5857,71 @@ steve.printMoney(); // alerts '$$$$$$$'
Ext.Object.merge(prototype.config, data.config);
delete data.config;
});
+ //
- Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);
+ //
+ /**
+ * @cfg {Object} mixins
+ * List of classes to mix into this class. For example:
+ *
+ * Ext.define('CanSing', {
+ * sing: function() {
+ * alert("I'm on the highway to hell...")
+ * }
+ * });
+ *
+ * Ext.define('Musician', {
+ * extend: 'Person',
+ *
+ * mixins: {
+ * canSing: 'CanSing'
+ * }
+ * })
+ */
+ Class.registerPreprocessor('mixins', function(cls, data) {
+ var mixins = data.mixins,
+ name, mixin, i, ln;
+ delete data.mixins;
+
+ Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
+ if (mixins instanceof Array) {
+ for (i = 0,ln = mixins.length; i < ln; i++) {
+ mixin = mixins[i];
+ name = mixin.prototype.mixinId || mixin.$className;
+
+ cls.mixin(name, mixin);
+ }
+ }
+ else {
+ for (name in mixins) {
+ if (mixins.hasOwnProperty(name)) {
+ cls.mixin(name, mixins[name]);
+ }
+ }
+ }
+ });
+ });
+
+ //
+
+ Class.setDefaultPreprocessors([
+ 'extend'
+ //
+ ,'statics'
+ //
+ //
+ ,'inheritableStatics'
+ //
+ //
+ ,'config'
+ //
+ //
+ ,'mixins'
+ //
+ ]);
+
+ //
// Backwards compatible
Ext.extend = function(subclass, superclass, members) {
if (arguments.length === 2 && Ext.isObject(superclass)) {
@@ -5576,7 +5937,21 @@ steve.printMoney(); // alerts '$$$$$$$'
}
members.extend = superclass;
- members.preprocessors = ['extend', 'mixins', 'config', 'statics'];
+ members.preprocessors = [
+ 'extend'
+ //
+ ,'statics'
+ //
+ //
+ ,'inheritableStatics'
+ //
+ //
+ ,'mixins'
+ //
+ //
+ ,'config'
+ //
+ ];
if (subclass) {
cls = new Class(subclass, members);
@@ -5595,6 +5970,7 @@ steve.printMoney(); // alerts '$$$$$$$'
return cls;
};
+ //
})();
@@ -5602,19 +5978,201 @@ steve.printMoney(); // alerts '$$$$$$$'
* @author Jacky Nguyen
* @docauthor Jacky Nguyen
* @class Ext.ClassManager
-
-Ext.ClassManager manages all classes and handles mapping from string class name to
-actual class objects throughout the whole framework. It is not generally accessed directly, rather through
-these convenient shorthands:
-
-- {@link Ext#define Ext.define}
-- {@link Ext#create Ext.create}
-- {@link Ext#widget Ext.widget}
-- {@link Ext#getClass Ext.getClass}
-- {@link Ext#getClassName Ext.getClassName}
-
+ *
+ * Ext.ClassManager manages all classes and handles mapping from string class name to
+ * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
+ * these convenient shorthands:
+ *
+ * - {@link Ext#define Ext.define}
+ * - {@link Ext#create Ext.create}
+ * - {@link Ext#widget Ext.widget}
+ * - {@link Ext#getClass Ext.getClass}
+ * - {@link Ext#getClassName Ext.getClassName}
+ *
+ * # Basic syntax:
+ *
+ * Ext.define(className, properties);
+ *
+ * in which `properties` is an object represent a collection of properties that apply to the class. See
+ * {@link Ext.ClassManager#create} for more detailed instructions.
+ *
+ * Ext.define('Person', {
+ * name: 'Unknown',
+ *
+ * constructor: function(name) {
+ * if (name) {
+ * this.name = name;
+ * }
+ *
+ * return this;
+ * },
+ *
+ * eat: function(foodType) {
+ * alert("I'm eating: " + foodType);
+ *
+ * return this;
+ * }
+ * });
+ *
+ * var aaron = new Person("Aaron");
+ * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
+ *
+ * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
+ * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
+ *
+ * # Inheritance:
+ *
+ * Ext.define('Developer', {
+ * extend: 'Person',
+ *
+ * constructor: function(name, isGeek) {
+ * this.isGeek = isGeek;
+ *
+ * // Apply a method from the parent class' prototype
+ * this.callParent([name]);
+ *
+ * return this;
+ *
+ * },
+ *
+ * code: function(language) {
+ * alert("I'm coding in: " + language);
+ *
+ * this.eat("Bugs");
+ *
+ * return this;
+ * }
+ * });
+ *
+ * var jacky = new Developer("Jacky", true);
+ * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
+ * // alert("I'm eating: Bugs");
+ *
+ * See {@link Ext.Base#callParent} for more details on calling superclass' methods
+ *
+ * # Mixins:
+ *
+ * Ext.define('CanPlayGuitar', {
+ * playGuitar: function() {
+ * alert("F#...G...D...A");
+ * }
+ * });
+ *
+ * Ext.define('CanComposeSongs', {
+ * composeSongs: function() { ... }
+ * });
+ *
+ * Ext.define('CanSing', {
+ * sing: function() {
+ * alert("I'm on the highway to hell...")
+ * }
+ * });
+ *
+ * Ext.define('Musician', {
+ * extend: 'Person',
+ *
+ * mixins: {
+ * canPlayGuitar: 'CanPlayGuitar',
+ * canComposeSongs: 'CanComposeSongs',
+ * canSing: 'CanSing'
+ * }
+ * })
+ *
+ * Ext.define('CoolPerson', {
+ * extend: 'Person',
+ *
+ * mixins: {
+ * canPlayGuitar: 'CanPlayGuitar',
+ * canSing: 'CanSing'
+ * },
+ *
+ * sing: function() {
+ * alert("Ahem....");
+ *
+ * this.mixins.canSing.sing.call(this);
+ *
+ * alert("[Playing guitar at the same time...]");
+ *
+ * this.playGuitar();
+ * }
+ * });
+ *
+ * var me = new CoolPerson("Jacky");
+ *
+ * me.sing(); // alert("Ahem...");
+ * // alert("I'm on the highway to hell...");
+ * // alert("[Playing guitar at the same time...]");
+ * // alert("F#...G...D...A");
+ *
+ * # Config:
+ *
+ * Ext.define('SmartPhone', {
+ * config: {
+ * hasTouchScreen: false,
+ * operatingSystem: 'Other',
+ * price: 500
+ * },
+ *
+ * isExpensive: false,
+ *
+ * constructor: function(config) {
+ * this.initConfig(config);
+ *
+ * return this;
+ * },
+ *
+ * applyPrice: function(price) {
+ * this.isExpensive = (price > 500);
+ *
+ * return price;
+ * },
+ *
+ * applyOperatingSystem: function(operatingSystem) {
+ * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
+ * return 'Other';
+ * }
+ *
+ * return operatingSystem;
+ * }
+ * });
+ *
+ * var iPhone = new SmartPhone({
+ * hasTouchScreen: true,
+ * operatingSystem: 'iOS'
+ * });
+ *
+ * iPhone.getPrice(); // 500;
+ * iPhone.getOperatingSystem(); // 'iOS'
+ * iPhone.getHasTouchScreen(); // true;
+ * iPhone.hasTouchScreen(); // true
+ *
+ * iPhone.isExpensive; // false;
+ * iPhone.setPrice(600);
+ * iPhone.getPrice(); // 600
+ * iPhone.isExpensive; // true;
+ *
+ * iPhone.setOperatingSystem('AlienOS');
+ * iPhone.getOperatingSystem(); // 'Other'
+ *
+ * # Statics:
+ *
+ * Ext.define('Computer', {
+ * statics: {
+ * factory: function(brand) {
+ * // 'this' in static methods refer to the class itself
+ * return new this(brand);
+ * }
+ * },
+ *
+ * constructor: function() { ... }
+ * });
+ *
+ * var dellComputer = Computer.factory('Dell');
+ *
+ * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
+ * static properties within class methods
+ *
* @singleton
- * @markdown
*/
(function(Class, alias) {
@@ -5623,8 +6181,7 @@ these convenient shorthands:
var Manager = Ext.ClassManager = {
/**
- * @property classes
- * @type Object
+ * @property {Object} classes
* All classes which were defined through the ClassManager. Keys are the
* name of the classes and the values are references to the classes.
* @private
@@ -5772,22 +6329,22 @@ these convenient shorthands:
/**
* Creates a namespace and assign the `value` to the created object
-
- Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
-
- alert(MyCompany.pkg.Example === someObject); // alerts true
-
+ *
+ * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
+ *
+ * alert(MyCompany.pkg.Example === someObject); // alerts true
+ *
* @param {String} name
- * @param {Mixed} value
- * @markdown
+ * @param {Object} value
*/
setNamespace: function(name, value) {
var root = Ext.global,
parts = this.parseNamespace(name),
- leaf = parts.pop(),
- i, ln, part;
+ ln = parts.length - 1,
+ leaf = parts[ln],
+ i, part;
- for (i = 0, ln = parts.length; i < ln; i++) {
+ for (i = 0; i < ln; i++) {
part = parts[i];
if (typeof part !== 'string') {
@@ -5858,7 +6415,7 @@ these convenient shorthands:
* Retrieve a class by its name.
*
* @param {String} name
- * @return {Class} class
+ * @return {Ext.Class} class
*/
get: function(name) {
if (this.classes.hasOwnProperty(name)) {
@@ -5889,7 +6446,7 @@ these convenient shorthands:
/**
* Register the alias for a class.
*
- * @param {Class/String} cls a reference to a class or a className
+ * @param {Ext.Class/String} cls a reference to a class or a className
* @param {String} alias Alias to use when referring to this class
*/
setAlias: function(cls, alias) {
@@ -5929,7 +6486,7 @@ these convenient shorthands:
* Get a reference to the class by its alias.
*
* @param {String} alias
- * @return {Class} class
+ * @return {Ext.Class} class
*/
getByAlias: function(alias) {
return this.get(this.getNameByAlias(alias));
@@ -5959,21 +6516,21 @@ these convenient shorthands:
* Get the aliases of a class by the class name
*
* @param {String} name
- * @return {Array} aliases
+ * @return {String[]} aliases
*/
getAliasesByName: function(name) {
return this.maps.nameToAliases[name] || [];
},
/**
- * Get the name of the class by its reference or its instance;
- * usually invoked by the shorthand {@link Ext#getClassName Ext.getClassName}
-
- Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
-
- * @param {Class/Object} object
+ * Get the name of the class by its reference or its instance.
+ *
+ * Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
+ *
+ * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
+ *
+ * @param {Ext.Class/Object} object
* @return {String} className
- * @markdown
*/
getName: function(object) {
return object && object.$className || '';
@@ -5981,57 +6538,66 @@ these convenient shorthands:
/**
* Get the class of the provided object; returns null if it's not an instance
- * of any class created with Ext.define. This is usually invoked by the shorthand {@link Ext#getClass Ext.getClass}
+ * of any class created with Ext.define.
+ *
+ * var component = new Ext.Component();
+ *
+ * Ext.ClassManager.getClass(component); // returns Ext.Component
+ *
+ * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
*
- var component = new Ext.Component();
-
- Ext.ClassManager.getClass(component); // returns Ext.Component
- *
* @param {Object} object
- * @return {Class} class
- * @markdown
+ * @return {Ext.Class} class
*/
getClass: function(object) {
return object && object.self || null;
},
/**
- * Defines a class. This is usually invoked via the alias {@link Ext#define Ext.define}
-
- Ext.ClassManager.create('My.awesome.Class', {
- someProperty: 'something',
- someMethod: function() { ... }
- ...
-
- }, function() {
- alert('Created!');
- alert(this === My.awesome.Class); // alerts true
-
- var myInstance = new this();
- });
-
+ * Defines a class.
+ *
+ * {@link Ext#define Ext.define} and {@link Ext.ClassManager#create Ext.ClassManager.create} are almost aliases
+ * of each other, with the only exception that Ext.define allows definition of {@link Ext.Class#override overrides}.
+ * To avoid trouble, always use Ext.define.
+ *
+ * Ext.define('My.awesome.Class', {
+ * someProperty: 'something',
+ * someMethod: function() { ... }
+ * ...
+ *
+ * }, function() {
+ * alert('Created!');
+ * alert(this === My.awesome.Class); // alerts true
+ *
+ * var myInstance = new this();
+ * });
+ *
* @param {String} className The class name to create in string dot-namespaced format, for example:
- * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
- * It is highly recommended to follow this simple convention:
-
-- The root and the class name are 'CamelCased'
-- Everything else is lower-cased
-
- * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid
- * strings, except those in the reserved listed below:
-
-- `mixins`
-- `statics`
-- `config`
-- `alias`
-- `self`
-- `singleton`
-- `alternateClassName`
+ * `My.very.awesome.Class`, `FeedViewer.plugin.CoolPager`. It is highly recommended to follow this simple convention:
+ *
+ * - The root and the class name are 'CamelCased'
+ * - Everything else is lower-cased
+ *
+ * @param {Object} data The key-value pairs of properties to apply to this class. Property names can be of any valid
+ * strings, except those in the reserved list below:
*
- * @param {Function} createdFn Optional callback to execute after the class is created, the execution scope of which
+ * - {@link Ext.Base#self self}
+ * - {@link Ext.Class#alias alias}
+ * - {@link Ext.Class#alternateClassName alternateClassName}
+ * - {@link Ext.Class#config config}
+ * - {@link Ext.Class#extend extend}
+ * - {@link Ext.Class#inheritableStatics inheritableStatics}
+ * - {@link Ext.Class#mixins mixins}
+ * - {@link Ext.Class#override override} (only when using {@link Ext#define Ext.define})
+ * - {@link Ext.Class#requires requires}
+ * - {@link Ext.Class#singleton singleton}
+ * - {@link Ext.Class#statics statics}
+ * - {@link Ext.Class#uses uses}
+ *
+ * @param {Function} [createdFn] callback to execute after the class is created, the execution scope of which
* (`this`) will be the newly created class itself.
+ *
* @return {Ext.Base}
- * @markdown
*/
create: function(className, data, createdFn) {
var manager = this;
@@ -6053,7 +6619,7 @@ these convenient shorthands:
registeredPostprocessors = manager.postprocessors,
index = 0,
postprocessors = [],
- postprocessor, postprocessors, process, i, ln;
+ postprocessor, process, i, ln;
delete data.postprocessors;
@@ -6102,17 +6668,19 @@ these convenient shorthands:
},
/**
- * Instantiate a class by its alias; usually invoked by the convenient shorthand {@link Ext#createByAlias Ext.createByAlias}
+ * Instantiate a class by its alias.
+ *
* If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
* attempt to load the class via synchronous loading.
-
- var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
-
+ *
+ * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
+ *
+ * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
+ *
* @param {String} alias
- * @param {Mixed} args,... Additional arguments after the alias will be passed to the
+ * @param {Object...} args Additional arguments after the alias will be passed to the
* class constructor.
* @return {Object} instance
- * @markdown
*/
instantiateByAlias: function() {
var alias = arguments[0],
@@ -6148,27 +6716,27 @@ these convenient shorthands:
},
/**
- * Instantiate a class by either full name, alias or alternate name; usually invoked by the convenient
- * shorthand {@link Ext#create Ext.create}
+ * Instantiate a class by either full name, alias or alternate name.
*
* If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
* attempt to load the class via synchronous loading.
*
* For example, all these three lines return the same result:
-
- // alias
- var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
-
- // alternate name
- var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
-
- // full class name
- var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
-
+ *
+ * // alias
+ * var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
+ *
+ * // alternate name
+ * var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
+ *
+ * // full class name
+ * var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
+ *
+ * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
+ *
* @param {String} name
- * @param {Mixed} args,... Additional arguments after the name will be passed to the class' constructor.
+ * @param {Object...} args Additional arguments after the name will be passed to the class' constructor.
* @return {Object} instance
- * @markdown
*/
instantiate: function() {
var name = arguments[0],
@@ -6318,7 +6886,7 @@ these convenient shorthands:
/**
* Set the default post processors array stack which are applied to every class.
*
- * @param {String/Array} The name of a registered post processor or an array of registered names.
+ * @param {String/String[]} The name of a registered post processor or an array of registered names.
* @return {Ext.ClassManager} this
*/
setDefaultPostprocessors: function(postprocessors) {
@@ -6360,7 +6928,7 @@ these convenient shorthands:
index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
if (index !== -1) {
- defaultPostprocessors.splice(Math.max(0, index + offset), 0, name);
+ Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
}
return this;
@@ -6369,19 +6937,18 @@ these convenient shorthands:
/**
* Converts a string expression to an array of matching class names. An expression can either refers to class aliases
* or class names. Expressions support wildcards:
-
- // returns ['Ext.window.Window']
- var window = Ext.ClassManager.getNamesByExpression('widget.window');
-
- // returns ['widget.panel', 'widget.window', ...]
- var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
-
- // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
- var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
-
+ *
+ * // returns ['Ext.window.Window']
+ * var window = Ext.ClassManager.getNamesByExpression('widget.window');
+ *
+ * // returns ['widget.panel', 'widget.window', ...]
+ * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
+ *
+ * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
+ * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
+ *
* @param {String} expression
- * @return {Array} classNames
- * @markdown
+ * @return {String[]} classNames
*/
getNamesByExpression: function(expression) {
var nameToAliasesMap = this.maps.nameToAliases,
@@ -6442,48 +7009,80 @@ these convenient shorthands:
}
};
+ var defaultPostprocessors = Manager.defaultPostprocessors;
+ //
+
+ /**
+ * @cfg {String[]} alias
+ * @member Ext.Class
+ * List of short aliases for class names. Most useful for defining xtypes for widgets:
+ *
+ * Ext.define('MyApp.CoolPanel', {
+ * extend: 'Ext.panel.Panel',
+ * alias: ['widget.coolpanel'],
+ * title: 'Yeah!'
+ * });
+ *
+ * // Using Ext.create
+ * Ext.widget('widget.coolpanel');
+ * // Using the shorthand for widgets and in xtypes
+ * Ext.widget('panel', {
+ * items: [
+ * {xtype: 'coolpanel', html: 'Foo'},
+ * {xtype: 'coolpanel', html: 'Bar'}
+ * ]
+ * });
+ */
Manager.registerPostprocessor('alias', function(name, cls, data) {
var aliases = data.alias,
- widgetPrefix = 'widget.',
- i, ln, alias;
+ i, ln;
- if (!(aliases instanceof Array)) {
- aliases = [aliases];
- }
+ delete data.alias;
for (i = 0, ln = aliases.length; i < ln; i++) {
alias = aliases[i];
- //
- if (typeof alias !== 'string') {
- Ext.Error.raise({
- sourceClass: "Ext",
- sourceMethod: "define",
- msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"
- });
- }
- //
-
this.setAlias(cls, alias);
}
-
- // This is ugly, will change to make use of parseNamespace for alias later on
- for (i = 0, ln = aliases.length; i < ln; i++) {
- alias = aliases[i];
-
- if (alias.substring(0, widgetPrefix.length) === widgetPrefix) {
- // Only the first alias with 'widget.' prefix will be used for xtype
- cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length);
- break;
- }
- }
});
+ /**
+ * @cfg {Boolean} singleton
+ * @member Ext.Class
+ * When set to true, the class will be instantiated as singleton. For example:
+ *
+ * Ext.define('Logger', {
+ * singleton: true,
+ * log: function(msg) {
+ * console.log(msg);
+ * }
+ * });
+ *
+ * Logger.log('Hello');
+ */
Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
fn.call(this, name, new cls(), data);
return false;
});
+ /**
+ * @cfg {String/String[]} alternateClassName
+ * @member Ext.Class
+ * Defines alternate names for this class. For example:
+ *
+ * Ext.define('Developer', {
+ * alternateClassName: ['Coder', 'Hacker'],
+ * code: function(msg) {
+ * alert('Typing... ' + msg);
+ * }
+ * });
+ *
+ * var joe = Ext.create('Developer');
+ * joe.code('stackoverflow');
+ *
+ * var rms = Ext.create('Hacker');
+ * rms.code('hack hack');
+ */
Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
var alternates = data.alternateClassName,
i, ln, alternate;
@@ -6513,9 +7112,9 @@ these convenient shorthands:
Ext.apply(Ext, {
/**
- * Convenient shorthand, see {@link Ext.ClassManager#instantiate}
+ * @method
* @member Ext
- * @method create
+ * @alias Ext.ClassManager#instantiate
*/
create: alias(Manager, 'instantiate'),
@@ -6523,7 +7122,7 @@ these convenient shorthands:
* @private
* API to be stablized
*
- * @param {Mixed} item
+ * @param {Object} item
* @param {String} namespace
*/
factory: function(item, namespace) {
@@ -6567,13 +7166,15 @@ these convenient shorthands:
/**
* Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
-
- var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
- var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
-
+ *
+ * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
+ * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
+ *
+ * @method
* @member Ext
- * @method widget
- * @markdown
+ * @param {String} name xtype of the widget to create.
+ * @param {Object...} args arguments for the widget constructor.
+ * @return {Object} widget instance
*/
widget: function(name) {
var args = slice.call(arguments);
@@ -6583,29 +7184,179 @@ these convenient shorthands:
},
/**
- * Convenient shorthand, see {@link Ext.ClassManager#instantiateByAlias}
+ * @method
* @member Ext
- * @method createByAlias
+ * @alias Ext.ClassManager#instantiateByAlias
*/
createByAlias: alias(Manager, 'instantiateByAlias'),
/**
- * Convenient shorthand for {@link Ext.ClassManager#create}, see detailed {@link Ext.Class explanation}
+ * @cfg {String} override
+ * @member Ext.Class
+ *
+ * Defines an override applied to a class. Note that **overrides can only be created using
+ * {@link Ext#define}.** {@link Ext.ClassManager#create} only creates classes.
+ *
+ * To define an override, include the override property. The content of an override is
+ * aggregated with the specified class in order to extend or modify that class. This can be
+ * as simple as setting default property values or it can extend and/or replace methods.
+ * This can also extend the statics of the class.
+ *
+ * One use for an override is to break a large class into manageable pieces.
+ *
+ * // File: /src/app/Panel.js
+ *
+ * Ext.define('My.app.Panel', {
+ * extend: 'Ext.panel.Panel',
+ * requires: [
+ * 'My.app.PanelPart2',
+ * 'My.app.PanelPart3'
+ * ]
+ *
+ * constructor: function (config) {
+ * this.callSuper(arguments); // calls Ext.panel.Panel's constructor
+ * //...
+ * },
+ *
+ * statics: {
+ * method: function () {
+ * return 'abc';
+ * }
+ * }
+ * });
+ *
+ * // File: /src/app/PanelPart2.js
+ * Ext.define('My.app.PanelPart2', {
+ * override: 'My.app.Panel',
+ *
+ * constructor: function (config) {
+ * this.callSuper(arguments); // calls My.app.Panel's constructor
+ * //...
+ * }
+ * });
+ *
+ * Another use of overrides is to provide optional parts of classes that can be
+ * independently required. In this case, the class may even be unaware of the
+ * override altogether.
+ *
+ * Ext.define('My.ux.CoolTip', {
+ * override: 'Ext.tip.ToolTip',
+ *
+ * constructor: function (config) {
+ * this.callSuper(arguments); // calls Ext.tip.ToolTip's constructor
+ * //...
+ * }
+ * });
+ *
+ * The above override can now be required as normal.
+ *
+ * Ext.define('My.app.App', {
+ * requires: [
+ * 'My.ux.CoolTip'
+ * ]
+ * });
+ *
+ * Overrides can also contain statics:
+ *
+ * Ext.define('My.app.BarMod', {
+ * override: 'Ext.foo.Bar',
+ *
+ * statics: {
+ * method: function (x) {
+ * return this.callSuper([x * 2]); // call Ext.foo.Bar.method
+ * }
+ * }
+ * });
+ *
+ * IMPORTANT: An override is only included in a build if the class it overrides is
+ * required. Otherwise, the override, like the target class, is not included.
+ */
+
+ /**
+ * @method
+ *
* @member Ext
- * @method define
+ * @alias Ext.ClassManager#create
*/
- define: alias(Manager, 'create'),
+ define: function (className, data, createdFn) {
+ if (!data.override) {
+ return Manager.create.apply(Manager, arguments);
+ }
+
+ var requires = data.requires,
+ uses = data.uses,
+ overrideName = className;
+
+ className = data.override;
+
+ // hoist any 'requires' or 'uses' from the body onto the faux class:
+ data = Ext.apply({}, data);
+ delete data.requires;
+ delete data.uses;
+ delete data.override;
+
+ // make sure className is in the requires list:
+ if (typeof requires == 'string') {
+ requires = [ className, requires ];
+ } else if (requires) {
+ requires = requires.slice(0);
+ requires.unshift(className);
+ } else {
+ requires = [ className ];
+ }
+
+// TODO - we need to rework this to allow the override to not require the target class
+// and rather 'wait' for it in such a way that if the target class is not in the build,
+// neither are any of its overrides.
+//
+// Also, this should process the overrides for a class ASAP (ideally before any derived
+// classes) if the target class 'requires' the overrides. Without some special handling, the
+// overrides so required will be processed before the class and have to be bufferred even
+// in a build.
+//
+// TODO - we should probably support the "config" processor on an override (to config new
+// functionaliy like Aria) and maybe inheritableStatics (although static is now supported
+// by callSuper). If inheritableStatics causes those statics to be included on derived class
+// constructors, that probably means "no" to this since an override can come after other
+// classes extend the target.
+ return Manager.create(overrideName, {
+ requires: requires,
+ uses: uses,
+ isPartial: true,
+ constructor: function () {
+ //
+ throw new Error("Cannot create override '" + overrideName + "'");
+ //
+ }
+ }, function () {
+ var cls = Manager.get(className);
+ if (cls.override) { // if (normal class)
+ cls.override(data);
+ } else { // else (singleton)
+ cls.self.override(data);
+ }
+
+ if (createdFn) {
+ // called once the override is applied and with the context of the
+ // overridden class (the override itself is a meaningless, name-only
+ // thing).
+ createdFn.call(cls);
+ }
+ });
+ },
/**
- * Convenient shorthand, see {@link Ext.ClassManager#getName}
+ * @method
* @member Ext
- * @method getClassName
+ * @alias Ext.ClassManager#getName
*/
getClassName: alias(Manager, 'getName'),
/**
- *
- * @param {Mixed} object
+ * Returns the displayName property or className or object.
+ * When all else fails, returns "Anonymous".
+ * @param {Object} object
+ * @return {String}
*/
getDisplayName: function(object) {
if (object.displayName) {
@@ -6624,195 +7375,258 @@ these convenient shorthands:
},
/**
- * Convenient shorthand, see {@link Ext.ClassManager#getClass}
+ * @method
* @member Ext
- * @method getClassName
+ * @alias Ext.ClassManager#getClass
*/
getClass: alias(Manager, 'getClass'),
/**
* Creates namespaces to be used for scoping variables and classes so that they are not global.
* Specifying the last node of a namespace implicitly creates all other nodes. Usage:
-
- Ext.namespace('Company', 'Company.data');
-
- // equivalent and preferable to the above syntax
- Ext.namespace('Company.data');
-
- Company.Widget = function() { ... };
-
- Company.data.CustomStore = function(config) { ... };
-
- * @param {String} namespace1
- * @param {String} namespace2
- * @param {String} etc
- * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
- * @function
+ *
+ * Ext.namespace('Company', 'Company.data');
+ *
+ * // equivalent and preferable to the above syntax
+ * Ext.namespace('Company.data');
+ *
+ * Company.Widget = function() { ... };
+ *
+ * Company.data.CustomStore = function(config) { ... };
+ *
+ * @method
* @member Ext
- * @method namespace
- * @markdown
- */
- namespace: alias(Manager, 'createNamespaces')
- });
-
- Ext.createWidget = Ext.widget;
-
- /**
- * Convenient alias for {@link Ext#namespace Ext.namespace}
- * @member Ext
- * @method ns
- */
- Ext.ns = Ext.namespace;
-
- Class.registerPreprocessor('className', function(cls, data) {
- if (data.$className) {
- cls.$className = data.$className;
- //
- cls.displayName = cls.$className;
- //
- }
- }, true);
-
- Class.setDefaultPreprocessorPosition('className', 'first');
-
-})(Ext.Class, Ext.Function.alias);
-
-/**
- * @author Jacky Nguyen
- * @docauthor Jacky Nguyen
- * @class Ext.Loader
- *
-
-Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
-via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
-approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons of each approach:
-
-# Asynchronous Loading #
-
-- Advantages:
- + Cross-domain
- + No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index
- .html`)
- + Best possible debugging experience: error messages come with the exact file name and line number
-
-- Disadvantages:
- + Dependencies need to be specified before-hand
-
-### Method 1: Explicitly include what you need: ###
-
- // Syntax
- Ext.require({String/Array} expressions);
-
- // Example: Single alias
- Ext.require('widget.window');
-
- // Example: Single class name
- Ext.require('Ext.window.Window');
-
- // Example: Multiple aliases / class names mix
- Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
-
- // Wildcards
- Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
-
-### Method 2: Explicitly exclude what you don't need: ###
-
- // Syntax: Note that it must be in this chaining format.
- Ext.exclude({String/Array} expressions)
- .require({String/Array} expressions);
-
- // Include everything except Ext.data.*
- Ext.exclude('Ext.data.*').require('*');Â
-
- // Include all widgets except widget.checkbox*,
- // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
- Ext.exclude('widget.checkbox*').require('widget.*');
-
-# Synchronous Loading on Demand #
-
-- *Advantages:*
- + There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js
- before
+ * @param {String} namespace1
+ * @param {String} namespace2
+ * @param {String} etc
+ * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
+ */
+ namespace: alias(Manager, 'createNamespaces')
+ });
-- *Disadvantages:*
- + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
- + Must be from the same domain due to XHR restriction
- + Need a web server, same reason as above
+ /**
+ * Old name for {@link Ext#widget}.
+ * @deprecated 4.0.0 Use {@link Ext#widget} instead.
+ * @method
+ * @member Ext
+ * @alias Ext#widget
+ */
+ Ext.createWidget = Ext.widget;
-There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
+ /**
+ * Convenient alias for {@link Ext#namespace Ext.namespace}
+ * @method
+ * @member Ext
+ * @alias Ext#namespace
+ */
+ Ext.ns = Ext.namespace;
- Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
+ Class.registerPreprocessor('className', function(cls, data) {
+ if (data.$className) {
+ cls.$className = data.$className;
+ //
+ cls.displayName = cls.$className;
+ //
+ }
+ }, true);
- Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
+ Class.setDefaultPreprocessorPosition('className', 'first');
- Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
+ Class.registerPreprocessor('xtype', function(cls, data) {
+ var xtypes = Ext.Array.from(data.xtype),
+ widgetPrefix = 'widget.',
+ aliases = Ext.Array.from(data.alias),
+ i, ln, xtype;
-Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
- existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load the given
- class and all its dependencies.
+ data.xtype = xtypes[0];
+ data.xtypes = xtypes;
-# Hybrid Loading - The Best of Both Worlds #
+ aliases = data.alias = Ext.Array.from(data.alias);
-It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
+ for (i = 0,ln = xtypes.length; i < ln; i++) {
+ xtype = xtypes[i];
-### Step 1: Start writing your application using synchronous approach. Ext.Loader will automatically fetch all
- dependencies on demand as they're needed during run-time. For example: ###
+ //
+ if (typeof xtype != 'string' || xtype.length < 1) {
+ throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
+ }
+ //
- Ext.onReady(function(){
- var window = Ext.createWidget('window', {
- width: 500,
- height: 300,
- layout: {
- type: 'border',
- padding: 5
- },
- title: 'Hello Dialog',
- items: [{
- title: 'Navigation',
- collapsible: true,
- region: 'west',
- width: 200,
- html: 'Hello',
- split: true
- }, {
- title: 'TabPanel',
- region: 'center'
- }]
- });
+ aliases.push(widgetPrefix + xtype);
+ }
- window.show();
- })
+ data.alias = aliases;
+ });
-### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ###
+ Class.setDefaultPreprocessorPosition('xtype', 'last');
- [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code
- ClassManager.js:432
- [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
+ Class.registerPreprocessor('alias', function(cls, data) {
+ var aliases = Ext.Array.from(data.alias),
+ xtypes = Ext.Array.from(data.xtypes),
+ widgetPrefix = 'widget.',
+ widgetPrefixLength = widgetPrefix.length,
+ i, ln, alias, xtype;
-Simply copy and paste the suggested code above `Ext.onReady`, i.e:
+ for (i = 0, ln = aliases.length; i < ln; i++) {
+ alias = aliases[i];
- Ext.require('Ext.window.Window');
- Ext.require('Ext.layout.container.Border');
+ //
+ if (typeof alias != 'string') {
+ throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
+ }
+ //
- Ext.onReady(...);
+ if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
+ xtype = alias.substring(widgetPrefixLength);
+ Ext.Array.include(xtypes, xtype);
-Everything should now load via asynchronous mode.
+ if (!cls.xtype) {
+ cls.xtype = data.xtype = xtype;
+ }
+ }
+ }
-# Deployment #
+ data.alias = aliases;
+ data.xtypes = xtypes;
+ });
-It's important to note that dynamic loading should only be used during development on your local machines.
-During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
-the whole process of transitioning from / to between development / maintenance and production as easy as
-possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies your application
-needs in the exact loading sequence. It's as simple as concatenating all files in this array into one,
-then include it on top of your application.
+ Class.setDefaultPreprocessorPosition('alias', 'last');
-This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
+})(Ext.Class, Ext.Function.alias);
+/**
+ * @class Ext.Loader
* @singleton
- * @markdown
+ * @author Jacky Nguyen
+ * @docauthor Jacky Nguyen
+ *
+ * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
+ * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
+ * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
+ * of each approach:
+ *
+ * # Asynchronous Loading
+ *
+ * - Advantages:
+ * + Cross-domain
+ * + No web server needed: you can run the application via the file system protocol
+ * (i.e: `file://path/to/your/index.html`)
+ * + Best possible debugging experience: error messages come with the exact file name and line number
+ *
+ * - Disadvantages:
+ * + Dependencies need to be specified before-hand
+ *
+ * ### Method 1: Explicitly include what you need:
+ *
+ * // Syntax
+ * Ext.require({String/Array} expressions);
+ *
+ * // Example: Single alias
+ * Ext.require('widget.window');
+ *
+ * // Example: Single class name
+ * Ext.require('Ext.window.Window');
+ *
+ * // Example: Multiple aliases / class names mix
+ * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
+ *
+ * // Wildcards
+ * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
+ *
+ * ### Method 2: Explicitly exclude what you don't need:
+ *
+ * // Syntax: Note that it must be in this chaining format.
+ * Ext.exclude({String/Array} expressions)
+ * .require({String/Array} expressions);
+ *
+ * // Include everything except Ext.data.*
+ * Ext.exclude('Ext.data.*').require('*');Â
+ *
+ * // Include all widgets except widget.checkbox*,
+ * // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
+ * Ext.exclude('widget.checkbox*').require('widget.*');
+ *
+ * # Synchronous Loading on Demand
+ *
+ * - Advantages:
+ * + There's no need to specify dependencies before-hand, which is always the convenience of including
+ * ext-all.js before
+ *
+ * - Disadvantages:
+ * + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
+ * + Must be from the same domain due to XHR restriction
+ * + Need a web server, same reason as above
+ *
+ * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
+ *
+ * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
+ *
+ * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
+ *
+ * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
+ *
+ * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
+ * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
+ * the given class and all its dependencies.
+ *
+ * # Hybrid Loading - The Best of Both Worlds
+ *
+ * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
+ *
+ * ### Step 1: Start writing your application using synchronous approach.
+ *
+ * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
+ *
+ * Ext.onReady(function(){
+ * var window = Ext.createWidget('window', {
+ * width: 500,
+ * height: 300,
+ * layout: {
+ * type: 'border',
+ * padding: 5
+ * },
+ * title: 'Hello Dialog',
+ * items: [{
+ * title: 'Navigation',
+ * collapsible: true,
+ * region: 'west',
+ * width: 200,
+ * html: 'Hello',
+ * split: true
+ * }, {
+ * title: 'TabPanel',
+ * region: 'center'
+ * }]
+ * });
+ *
+ * window.show();
+ * })
+ *
+ * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
+ *
+ * [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
+ * [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
+ *
+ * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
+ *
+ * Ext.require('Ext.window.Window');
+ * Ext.require('Ext.layout.container.Border');
+ *
+ * Ext.onReady(...);
+ *
+ * Everything should now load via asynchronous mode.
+ *
+ * # Deployment
+ *
+ * It's important to note that dynamic loading should only be used during development on your local machines.
+ * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
+ * the whole process of transitioning from / to between development / maintenance and production as easy as
+ * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
+ * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
+ * array into one, then include it on top of your application.
+ *
+ * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
*/
-
(function(Manager, Class, flexSetter, alias) {
var
@@ -6889,12 +7703,9 @@ This process will be automated with Sencha Command, to be released and documente
classNameToFilePathMap: {},
/**
+ * @property {String[]} history
* An array of class names to keep track of the dependency loading order.
- * This is not guaranteed to be the same everytime due to the asynchronous
- * nature of the Loader.
- *
- * @property history
- * @type Array
+ * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
*/
history: [],
@@ -6904,39 +7715,38 @@ This process will be automated with Sencha Command, to be released and documente
*/
config: {
/**
- * Whether or not to enable the dynamic dependency loading feature
- * Defaults to false
* @cfg {Boolean} enabled
+ * Whether or not to enable the dynamic dependency loading feature.
*/
enabled: false,
/**
* @cfg {Boolean} disableCaching
- * Appends current timestamp to script files to prevent caching
- * Defaults to true
+ * Appends current timestamp to script files to prevent caching.
*/
disableCaching: true,
/**
* @cfg {String} disableCachingParam
* The get parameter name for the cache buster's timestamp.
- * Defaults to '_dc'
*/
disableCachingParam: '_dc',
/**
* @cfg {Object} paths
* The mapping from namespaces to file paths
- {
- 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
- // loaded from ./layout/Container.js
-
- 'My': './src/my_own_folder' // My.layout.Container will be loaded from
- // ./src/my_own_folder/layout/Container.js
- }
+ *
+ * {
+ * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
+ * // loaded from ./layout/Container.js
+ *
+ * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
+ * // ./src/my_own_folder/layout/Container.js
+ * }
+ *
* Note that all relative paths are relative to the current HTML document.
- * If not being specified, for example, Other.awesome.Class
- * will simply be loaded from ./Other/awesome/Class.js
+ * If not being specified, for example, `Other.awesome.Class`
+ * will simply be loaded from `./Other/awesome/Class.js`
*/
paths: {
'Ext': '.'
@@ -6945,30 +7755,30 @@ This process will be automated with Sencha Command, to be released and documente
/**
* 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}
+ * (or ext-core-debug.js) is included in the page, e.g.:
+ *
+ *
+ *
+ *
+ * Refer to config options of {@link Ext.Loader} for the list of possible properties.
+ *
+ * @param {String/Object} name Name of the value to override, or a config object to override multiple values.
+ * @param {Object} value (optional) The new value to set, needed if first parameter is String.
* @return {Ext.Loader} this
- * @markdown
*/
setConfig: function(name, value) {
if (Ext.isObject(name) && arguments.length === 1) {
@@ -6982,9 +7792,10 @@ This process will be automated with Sencha Command, to be released and documente
},
/**
- * Get the config value corresponding to the specified name. If no name is given, will return the config object
+ * 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}
+ * @return {Object}
*/
getConfig: function(name) {
if (name) {
@@ -6995,15 +7806,14 @@ This process will be automated with Sencha Command, to be released and documente
},
/**
- * Sets the path of a namespace.
- * For Example:
-
- Ext.Loader.setPath('Ext', '.');
-
+ * 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
+ * @method
*/
setPath: flexSetter(function(name, path) {
//
@@ -7019,32 +7829,31 @@ This process will be automated with Sencha Command, to be released and documente
}),
/**
- * 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'
-
+ * 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'
-
+ *
+ * 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 = '',
@@ -7122,7 +7931,7 @@ This process will be automated with Sencha Command, to be released and documente
do {
if (Manager.isCreated(requires[j])) {
// Take out from the queue
- requires.splice(j, 1);
+ Ext.Array.erase(requires, j, 1);
}
else {
j++;
@@ -7130,7 +7939,7 @@ This process will be automated with Sencha Command, to be released and documente
} while (j < requires.length);
if (item.requires.length === 0) {
- this.queue.splice(i, 1);
+ Ext.Array.erase(this.queue, i, 1);
item.callback.call(item.scope);
this.refreshQueue();
break;
@@ -7188,7 +7997,7 @@ This process will be automated with Sencha Command, to be released and documente
*
* @param {String} url
* @param {Function} onLoad
- * @param {Scope} scope
+ * @param {Object} scope
* @param {Boolean} synchronous
* @private
*/
@@ -7270,15 +8079,16 @@ This process will be automated with Sencha Command, to be released and documente
/**
* 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
+ * Can be chained with more `require` and `exclude` methods, e.g.:
+ *
+ * Ext.exclude('Ext.data.*').require('*');
+ *
+ * Ext.exclude('widget.button*').require('widget.*');
+ *
+ * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
+ *
+ * @param {String/String[]} excludes
* @return {Object} object contains `require` method for chaining
- * @markdown
*/
exclude: function(excludes) {
var me = this;
@@ -7295,12 +8105,15 @@ This process will be automated with Sencha Command, to be released and documente
},
/**
- * 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
+ * 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.
+ *
+ * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
+ *
+ * @param {String/String[]} 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
+ * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
*/
syncRequire: function() {
this.syncModeEnabled = true;
@@ -7310,13 +8123,15 @@ This process will be automated with Sencha Command, to be released and documente
},
/**
- * 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
+ * Loads all classes by the given names and all their direct dependencies;
+ * optionally executes the given callback function when finishes, within the optional scope.
+ *
+ * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
+ *
+ * @param {String/String[]} 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
+ * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
*/
require: function(expressions, fn, scope, excludes) {
var filePath, expression, exclude, className, excluded = {},
@@ -7578,10 +8393,10 @@ This process will be automated with Sencha Command, to be released and documente
},
/**
- * Add a new listener to be executed when all required scripts are fully loaded
+ * Adds 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 {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) {
@@ -7620,36 +8435,49 @@ This process will be automated with Sencha Command, to be released and documente
};
/**
- * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of
- * {@link Ext.Loader} for examples.
* @member Ext
* @method require
+ * @alias Ext.Loader#require
*/
Ext.require = alias(Loader, 'require');
/**
- * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
- *
* @member Ext
* @method syncRequire
+ * @alias Ext.Loader#syncRequire
*/
Ext.syncRequire = alias(Loader, 'syncRequire');
/**
- * Convenient shortcut to {@link Ext.Loader#exclude}
* @member Ext
* @method exclude
+ * @alias Ext.Loader#exclude
*/
Ext.exclude = alias(Loader, 'exclude');
/**
* @member Ext
* @method onReady
+ * @alias Ext.Loader#onReady
*/
Ext.onReady = function(fn, scope, options) {
Loader.onReady(fn, scope, true, options);
};
+ /**
+ * @cfg {String[]} requires
+ * @member Ext.Class
+ * List of classes that have to be loaded before instantiating this class.
+ * For example:
+ *
+ * Ext.define('Mother', {
+ * requires: ['Child'],
+ * giveBirth: function() {
+ * // we can be sure that child class is available.
+ * return new Child();
+ * }
+ * });
+ */
Class.registerPreprocessor('loader', function(cls, data, continueFn) {
var me = this,
dependencies = [],
@@ -7694,7 +8522,7 @@ This process will be automated with Sencha Command, to be released and documente
}
}
}
- else {
+ else if (typeof propertyValue != 'function') {
for (j in propertyValue) {
if (propertyValue.hasOwnProperty(j)) {
value = propertyValue[j];
@@ -7775,7 +8603,7 @@ This process will be automated with Sencha Command, to be released and documente
}
}
}
- else {
+ else if (typeof propertyValue != 'function') {
for (var k in propertyValue) {
if (propertyValue.hasOwnProperty(k)) {
value = propertyValue[k];
@@ -7797,6 +8625,23 @@ This process will be automated with Sencha Command, to be released and documente
Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
+ /**
+ * @cfg {String[]} uses
+ * @member Ext.Class
+ * List of classes to load together with this class. These aren't neccessarily loaded before
+ * this class is instantiated. For example:
+ *
+ * Ext.define('Mother', {
+ * uses: ['Child'],
+ * giveBirth: function() {
+ * // This code might, or might not work:
+ * // return new Child();
+ *
+ * // Instead use Ext.create() to load the class at the spot if not loaded already:
+ * return Ext.create('Child');
+ * }
+ * });
+ */
Manager.registerPostprocessor('uses', function(name, cls, data) {
var uses = Ext.Array.from(data.uses),
items = [],
@@ -7818,132 +8663,144 @@ This process will be automated with Sencha Command, to be released and documente
})(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
* @docauthor Brian Moeskau
+ *
+ * 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
+ * }
+ *
*/
Ext.Error = Ext.extend(Error, {
statics: {
/**
- * @property ignore
-Static flag that can be used to globally disable error reporting to the browser if set to true
-(defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
-and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
-be preferable to supply a custom error {@link #handle handling} function instead.
-
-#Example usage:#
-
- Ext.Error.ignore = true;
-
- * @markdown
+ * @property {Boolean} ignore
+ * Static flag that can be used to globally disable error reporting to the browser if set to true
+ * (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
+ * and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
+ * be preferable to supply a custom error {@link #handle handling} function instead.
+ *
+ * Example usage:
+ *
+ * Ext.Error.ignore = true;
+ *
* @static
*/
ignore: false,
/**
-Raise an error that can include additional data and supports automatic console logging if available.
-You can pass a string error message or an object with the `msg` attribute which will be used as the
-error message. The object can contain any other name-value attributes (or objects) to be logged
-along with the error.
-
-Note that after displaying the error message a JavaScript error will ultimately be thrown so that
-execution will halt.
-
-#Example usage:#
-
- Ext.Error.raise('A simple string error message');
-
- // or...
+ * @property {Boolean} notify
+ * Static flag that can be used to globally control error notification to the user. Unlike
+ * Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
+ * set to false to disable the alert notification (default is true for IE6 and IE7).
+ *
+ * Only the first error will generate an alert. Internally this flag is set to false when the
+ * first error occurs prior to displaying the alert.
+ *
+ * This flag is not used in a release build.
+ *
+ * Example usage:
+ *
+ * Ext.Error.notify = false;
+ *
+ * @static
+ */
+ //notify: Ext.isIE6 || Ext.isIE7,
- 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
- });
- }
- }
- });
- * @param {String/Object} err The error message string, or an object containing the
- * attribute "msg" that will be used as the error message. Any other data included in
- * the object will also be logged to the browser console, if available.
+ /**
+ * Raise an error that can include additional data and supports automatic console logging if available.
+ * You can pass a string error message or an object with the `msg` attribute which will be used as the
+ * error message. The object can contain any other name-value attributes (or objects) to be logged
+ * along with the error.
+ *
+ * Note that after displaying the error message a JavaScript error will ultimately be thrown so that
+ * execution will halt.
+ *
+ * Example usage:
+ *
+ * Ext.Error.raise('A simple string error message');
+ *
+ * // or...
+ *
+ * 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
+ * });
+ * }
+ * }
+ * });
+ *
+ * @param {String/Object} err The error message string, or an object containing the attribute "msg" that will be
+ * used as the error message. Any other data included in the object will also be logged to the browser console,
+ * if available.
* @static
- * @markdown
*/
raise: function(err){
err = err || {};
@@ -7963,61 +8820,50 @@ execution will halt.
}
if (Ext.Error.handle(err) !== true) {
- var global = Ext.global,
- con = global.console,
- msg = Ext.Error.prototype.toString.call(err),
- noConsoleMsg = 'An uncaught error was raised: "' + msg +
- '". Use Firebug or Webkit console for additional details.';
-
- if (con) {
- if (con.dir) {
- con.warn('An uncaught error was raised with the following data:');
- con.dir(err);
- }
- else {
- con.warn(noConsoleMsg);
- }
- if (con.error) {
- con.error(msg);
- }
- }
- else if (global.alert){
- global.alert(noConsoleMsg);
- }
-
+ var msg = Ext.Error.prototype.toString.call(err);
+
+ Ext.log({
+ msg: msg,
+ level: 'error',
+ dump: err,
+ stack: true
+ });
+
throw new Ext.Error(err);
}
},
/**
-Globally handle any Ext errors that may be raised, optionally providing custom logic to
-handle different errors individually. Return true from the function to bypass throwing the
-error to the browser, otherwise the error will be thrown and execution will halt.
-
-#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
- }
-
- * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes
- * that were originally raised with it, plus properties about the method and class from which
- * the error originated (if raised from a class that uses the Ext 4 class system).
+ * Globally handle any Ext errors that may be raised, optionally providing custom logic to
+ * handle different errors individually. Return true from the function to bypass throwing the
+ * error to the browser, otherwise the error will be thrown and execution will halt.
+ *
+ * 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
+ * }
+ *
+ * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes that were originally
+ * raised with it, plus properties about the method and class from which the error originated (if raised from a
+ * class that uses the Ext 4 class system).
* @static
- * @markdown
*/
handle: function(){
return Ext.Error.ignore;
}
},
+ // This is the standard property that is the name of the constructor.
+ name: 'Ext.Error',
+
/**
- * @constructor
- * @param {String/Object} config The error message string, or an object containing the
+ * Creates new Error object.
+ * @param {String/Object} config The error message string, or an object containing the
* attribute "msg" that will be used as the error message. Any other data included in
* the object will be applied to the error instance and logged to the browser console, if available.
*/
@@ -8025,20 +8871,25 @@ error to the browser, otherwise the error will be thrown and execution will halt
if (Ext.isString(config)) {
config = { msg: config };
}
- Ext.apply(this, config);
+
+ var me = this;
+
+ Ext.apply(me, config);
+
+ me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
+ // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
},
/**
-Provides a custom string representation of the error object. This is an override of the base JavaScript
-`Object.toString` method, which is useful so that when logged to the browser console, an error object will
-be displayed with a useful message instead of `[object Object]`, the default `toString` result.
-
-The default implementation will include the error message along with the raising class and method, if available,
-but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
-a particular error instance, if you want to provide a custom description that will show up in the console.
- * @markdown
- * @return {String} The error message. If raised from within the Ext 4 class system, the error message
- * will also include the raising class and method names, if available.
+ * Provides a custom string representation of the error object. This is an override of the base JavaScript
+ * `Object.toString` method, which is useful so that when logged to the browser console, an error object will
+ * be displayed with a useful message instead of `[object Object]`, the default `toString` result.
+ *
+ * The default implementation will include the error message along with the raising class and method, if available,
+ * but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
+ * a particular error instance, if you want to provide a custom description that will show up in the console.
+ * @return {String} The error message. If raised from within the Ext 4 class system, the error message will also
+ * include the raising class and method names, if available.
*/
toString: function(){
var me = this,
@@ -8050,3 +8901,68 @@ a particular error instance, if you want to provide a custom description that wi
}
});
+/*
+ * This mechanism is used to notify the user of the first error encountered on the page. This
+ * was previously internal to Ext.Error.raise and is a desirable feature since errors often
+ * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
+ * where exceptions are handled in a try/catch.
+ */
+//
+(function () {
+ var prevOnError, timer, errors = 0,
+ extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
+ win = Ext.global;
+
+ if (typeof window === 'undefined') {
+ return; // build system or some such environment...
+ }
+
+ // This method is called to notify the user of the current error status.
+ function notify () {
+ var counters = Ext.log.counters,
+ supports = Ext.supports,
+ hasOnError = supports && supports.WindowOnError; // TODO - timing
+
+ // Put log counters to the status bar (for most browsers):
+ if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
+ var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
+ 'Info:',counters.info, 'Log:',counters.log].join(' ');
+ if (errors) {
+ msg = '*** Errors: ' + errors + ' - ' + msg;
+ } else if (counters.error) {
+ msg = '*** ' + msg;
+ }
+ win.status = msg;
+ }
+
+ // Display an alert on the first error:
+ if (!Ext.isDefined(Ext.Error.notify)) {
+ Ext.Error.notify = Ext.isIE6 || Ext.isIE7; // TODO - timing
+ }
+ if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
+ Ext.Error.notify = false;
+
+ if (timer) {
+ win.clearInterval(timer); // ticks can queue up so stop...
+ timer = null;
+ }
+
+ alert('Unhandled error on page: See console or log');
+ poll();
+ }
+ }
+
+ // Sets up polling loop. This is the only way to know about errors in some browsers
+ // (Opera/Safari) and is the only way to update the status bar for warnings and other
+ // non-errors.
+ function poll () {
+ timer = win.setInterval(notify, 1000);
+ }
+
+ // window.onerror sounds ideal but it prevents the built-in error dialog from doing
+ // its (better) thing.
+ poll();
+})();
+//
+
+