X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..f562e4c6e5fac7bcb445985b99acbea4d706e6f0:/ext-all-dev.js diff --git a/ext-all-dev.js b/ext-all-dev.js index 2dc6de6a..d8128e00 100644 --- a/ext-all-dev.js +++ b/ext-all-dev.js @@ -41,7 +41,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * An array containing extra enumerables for old browsers - * @type Array + * @property {String[]} */ Ext.enumerables = enumerables; @@ -258,10 +258,10 @@ If you are unsure which license is appropriate for your use, please contact the * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default * value (second argument) otherwise. * - * @param {Mixed} value The value to test - * @param {Mixed} defaultValue The value to return if the original value is empty + * @param {Object} value The value to test + * @param {Object} defaultValue The value to return if the original value is empty * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false) - * @return {Mixed} value, if non-empty, else defaultValue + * @return {Object} value, if non-empty, else defaultValue */ valueFrom: function(value, defaultValue, allowBlank){ return Ext.isEmpty(value, allowBlank) ? defaultValue : value; @@ -284,7 +284,7 @@ If you are unsure which license is appropriate for your use, please contact the * - `textnode`: If the given value is a DOM text node and contains something other than whitespace * - `whitespace`: If the given value is a DOM text node and contains only whitespace * - * @param {Mixed} value + * @param {Object} value * @return {String} * @markdown */ @@ -346,7 +346,7 @@ If you are unsure which license is appropriate for your use, please contact the * - a zero-length array * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`) * - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false) * @return {Boolean} * @markdown @@ -358,7 +358,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a JavaScript Array, false otherwise. * - * @param {Mixed} target The target to test + * @param {Object} target The target to test * @return {Boolean} * @method */ @@ -377,7 +377,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a JavaScript Object, false otherwise. - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} * @method */ @@ -392,7 +392,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean. - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isPrimitive: function(value) { @@ -403,7 +403,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a JavaScript Function, false otherwise. - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} * @method */ @@ -418,7 +418,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a number. Returns false for non-finite numbers. - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isNumber: function(value) { @@ -427,7 +427,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Validates that a value is numeric. - * @param {Mixed} value Examples: 1, '1', '2.34' + * @param {Object} value Examples: 1, '1', '2.34' * @return {Boolean} True if numeric, false otherwise */ isNumeric: function(value) { @@ -436,7 +436,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a string. - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isString: function(value) { @@ -446,7 +446,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a boolean. * - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isBoolean: function(value) { @@ -455,7 +455,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is an HTMLElement - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isElement: function(value) { @@ -464,7 +464,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is a TextNode - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isTextNode: function(value) { @@ -473,7 +473,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is defined. - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isDefined: function(value) { @@ -482,7 +482,7 @@ If you are unsure which license is appropriate for your use, please contact the /** * Returns true if the passed value is iterable, false otherwise - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @return {Boolean} */ isIterable: function(value) { @@ -494,8 +494,8 @@ If you are unsure which license is appropriate for your use, please contact the /** * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference - * @param {Mixed} item The variable to clone - * @return {Mixed} clone + * @param {Object} item The variable to clone + * @return {Object} clone */ clone: function(item) { if (item === null || item === undefined) { @@ -624,7 +624,7 @@ If you are unsure which license is appropriate for your use, please contact the (function() { // Current core version -var version = '4.0.2', Version; +var version = '4.0.7', Version; Ext.Version = Version = Ext.extend(Object, { /** @@ -764,7 +764,7 @@ var version = '4.0.2', Version; /** * Returns this format: [major, minor, patch, build, release]. Useful for comparison - * @return {Array} + * @return {Number[]} */ toArray: function() { return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()]; @@ -797,8 +797,8 @@ var version = '4.0.2', Version; * Converts a version component to a comparable value * * @static - * @param {Mixed} value The value to convert - * @return {Mixed} + * @param {Object} value The value to convert + * @return {Object} */ getComponentValue: function(value) { return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10)); @@ -918,7 +918,7 @@ Ext.String = { escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g, /** - * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages. + * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages. * @param {String} value The string to encode * @return {String} The encoded text * @method @@ -945,7 +945,7 @@ Ext.String = { })(), /** - * Convert certain characters (&, <, >, and ') from their HTML character equivalents. + * Convert certain characters (&, <, >, and ") from their HTML character equivalents. * @param {String} value The string to decode * @return {String} The decoded text * @method @@ -1113,6 +1113,24 @@ var s = Ext.String.format('<div class="{0}">{1}</div>', cls, text); return format.replace(Ext.String.formatRe, function(m, i) { return args[i]; }); + }, + + /** + * Returns a string with a specified number of repititions a given string pattern. + * The pattern be separated by a different string. + * + * var s = Ext.String.repeat('---', 4); // = '------------' + * var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--' + * + * @param {String} pattern The pattern to repeat. + * @param {Number} count The number of times to repeat the pattern (may be 0). + * @param {String} sep An option string to separate each pattern. + */ + repeat: function(pattern, count, sep) { + for (var buf = [], i = count; i--; ) { + buf.push(pattern); + } + return buf.join(sep || ''); } }; @@ -1198,7 +1216,7 @@ Ext.Number = { Ext.Number.from('1.23', 1); // returns 1.23 Ext.Number.from('abc', 1); // returns 1 - * @param {Mixed} value + * @param {Object} value * @param {Number} defaultValue The value to return if the original value is non-numeric * @return {Number} value, if numeric, defaultValue otherwise */ @@ -1214,24 +1232,21 @@ Ext.Number.from('abc', 1); // returns 1 })(); /** - * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead - * - * @deprecated 4.0.0 Replaced by Ext.Number.from + * @deprecated 4.0.0 Please use {@link Ext.Number#from} instead. * @member Ext * @method num + * @alias Ext.Number#from */ Ext.num = function() { return Ext.Number.from.apply(this, arguments); }; /** + * @class Ext.Array + * @singleton * @author Jacky Nguyen * @docauthor Jacky Nguyen - * @class Ext.Array * * A set of useful static methods to deal with arrays; provide missing methods for older browsers. - - * @singleton - * @markdown */ (function() { @@ -1441,15 +1456,14 @@ Ext.num = function() { * * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each} * - * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this + * @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`. Arguments passed to this callback function are: - * - * - `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` - * + * 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 @@ -1481,18 +1495,15 @@ 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. */ forEach: function(array, fn, scope) { @@ -1513,7 +1524,7 @@ 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) */ @@ -1537,7 +1548,7 @@ 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 */ contains: function(array, item) { @@ -1576,7 +1587,7 @@ Ext.num = function() { * * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray} * - * @param {Mixed} iterable the iterable object to be turned into a true Array. + * @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 @@ -1613,7 +1624,7 @@ Ext.num = function() { * * 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 {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. */ @@ -1796,8 +1807,8 @@ Ext.num = function() { * - 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 */ @@ -1821,7 +1832,7 @@ Ext.num = function() { * 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) { @@ -1838,7 +1849,7 @@ Ext.num = function() { * 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 + * @param {Object} item The item to include */ include: function(array, item) { if (!ExtArray.contains(array, item)) { @@ -1970,9 +1981,24 @@ Ext.num = function() { * all items up to the end of the array are copied. * @return {Array} The copied piece of the array. */ - slice: function(array, begin, end) { - return slice.call(array, begin, end); - }, + // 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. @@ -2021,6 +2047,8 @@ Ext.num = function() { /** * Recursively flattens into 1-d Array. Injects Arrays inline. * + * @param {Array} array The array to flatten + * @return {Array} The 1-d array. */ flatten: function(array) { var worker = []; @@ -2047,10 +2075,10 @@ Ext.num = function() { /** * 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 + * @return {Object} minValue The minimum value */ min: function(array, comparisonFn) { var min = array[0], @@ -2077,10 +2105,10 @@ Ext.num = function() { /** * Returns the maximum value in the Array. * - * @param {Array|NodeList} array The Array from which to select the maximum value. + * @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 + * @return {Object} maxValue The maximum value */ max: function(array, comparisonFn) { var max = array[0], @@ -2151,7 +2179,7 @@ Ext.num = function() { /** * 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. @@ -2166,11 +2194,11 @@ Ext.num = function() { * 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 An optional array of items to insert at index. + * @param {Array} insert (optional) An array of items to insert at index. * @return {Array} The array passed. * @method */ @@ -2369,6 +2397,12 @@ Ext.Function = { * @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, slice = Array.prototype.slice; @@ -2379,7 +2413,7 @@ Ext.Function = { callArgs = slice.call(arguments, 0); callArgs = callArgs.concat(args); } - else if (Ext.isNumber(appendArgs)) { + else if (typeof appendArgs == 'number') { callArgs = slice.call(arguments, 0); // copy arguments first Ext.Array.insert(callArgs, appendArgs, args); } @@ -2459,7 +2493,7 @@ Ext.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} 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) { @@ -2599,7 +2633,7 @@ Ext.Function = { return function() { var me = this; if (timerId) { - clearInterval(timerId); + clearTimeout(timerId); timerId = null; } timerId = setTimeout(function(){ @@ -2640,6 +2674,80 @@ Ext.Function = { 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); + }; } }; @@ -2669,7 +2777,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 */ @@ -2679,41 +2787,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, @@ -2759,37 +2867,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 = [], @@ -2822,31 +2928,30 @@ 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']] - } - + * 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 + * @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) { @@ -2937,32 +3042,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) { @@ -2976,44 +3078,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') { @@ -3052,17 +3153,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) { @@ -3076,15 +3176,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 = [], @@ -3101,14 +3200,14 @@ 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) { @@ -3126,15 +3225,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, @@ -3152,19 +3250,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), @@ -3180,11 +3280,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); @@ -3362,7 +3463,6 @@ Ext.Date = { * default behaviour of javascript Date objects. * (see {@link #parse} for more information) * Defaults to false. - * @static * @type Boolean */ useStrict: false, @@ -3405,7 +3505,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: { @@ -3434,7 +3533,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: { @@ -3448,48 +3546,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", @@ -3520,12 +3611,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: @@ -3536,8 +3627,6 @@ Ext.Date.dayNames = [ ... ]; - * @type Array - * @static */ dayNames : [ "Sunday", @@ -3550,6 +3639,7 @@ Ext.Date.dayNames = [ ], /** + * @property {String[]} monthNames * An array of textual month names. * Override these values for international dates. * Example: @@ -3560,8 +3650,6 @@ Ext.Date.monthNames = [ ... ]; - * @type Array - * @static */ monthNames : [ "January", @@ -3579,6 +3667,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: @@ -3589,8 +3678,6 @@ Ext.Date.monthNumbers = { ... }; - * @type Object - * @static */ monthNumbers : { Jan:0, @@ -3607,12 +3694,10 @@ Ext.Date.monthNumbers = { Dec:11 }, /** + * @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 defaults to m/d/Y, but may be overridden in a locale file.

- * @property defaultFormat - * @static - * @type String + *

This may be overridden in a locale file.

*/ defaultFormat : "m/d/Y", /** @@ -3620,7 +3705,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); @@ -3631,7 +3715,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); @@ -3642,7 +3725,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) @@ -3653,7 +3735,6 @@ 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(){ @@ -3670,7 +3751,6 @@ 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(){ @@ -3695,7 +3775,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')", @@ -3765,7 +3844,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 @@ -3815,7 +3893,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; @@ -4576,9 +4653,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) { @@ -4623,7 +4701,7 @@ var Base = Ext.Base = function() {}; * var clone = snowLeopard.clone(); * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard' * - * @type Class + * @type Ext.Class * @protected */ self: Base, @@ -4633,6 +4711,7 @@ var Base = Ext.Base = function() {}; return this; }, + // /** * Initialize configuration for this class. a typical example: * @@ -4693,6 +4772,7 @@ var Base = Ext.Base = function() {}; return this; }), + // /** * Call the parent's overridden method. For example: @@ -4731,7 +4811,7 @@ var Base = Ext.Base = function() {}; * @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 + * @return {Object} Returns the result from the superclass' method */ callParent: function(args) { var method = this.callParent.caller, @@ -4775,25 +4855,25 @@ var Base = Ext.Base = function() {}; * 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; * } * }); @@ -4801,11 +4881,11 @@ var Base = Ext.Base = function() {}; * * Ext.define('My.SnowLeopard', { * extend: 'My.Cat', - * + * * statics: { * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard' * }, - * + * * constructor: function() { * this.callParent(); * } @@ -4822,7 +4902,7 @@ var Base = Ext.Base = function() {}; * alert(My.Cat.totalCreated); // alerts 3 * * @protected - * @return {Class} + * @return {Ext.Class} */ statics: function() { var method = this.statics.caller, @@ -4841,7 +4921,7 @@ var Base = Ext.Base = function() {}; * Ext.define('My.Cat', { * constructor: function() { * alert("I'm a cat!"); - * + * * return this; * } * }); @@ -4849,11 +4929,11 @@ var Base = Ext.Base = function() {}; * My.Cat.override({ * constructor: function() { * alert("I'm going to be a cat!"); - * + * * var instance = this.callOverridden(); - * + * * alert("Meeeeoooowwww"); - * + * * return instance; * } * }); @@ -4863,7 +4943,8 @@ var Base = Ext.Base = function() {}; * // 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 + * @return {Object} Returns the result after calling the overridden method + * @protected */ callOverridden: function(args) { var method = this.callOverridden.caller; @@ -4899,7 +4980,7 @@ var Base = Ext.Base = function() {}; * Ext.define('My.cool.Class', { * ... * }); - * + * * My.cool.Class.create({ * someConfig: true * }); @@ -4908,6 +4989,7 @@ var Base = Ext.Base = function() {}; * * @return {Object} the created instance. * @static + * @inheritable */ create: function() { return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0))); @@ -4915,23 +4997,25 @@ var Base = Ext.Base = function() {}; /** * @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() { @@ -4966,6 +5050,7 @@ var Base = Ext.Base = function() {}; * @param {Object} members * @return {Ext.Base} this * @static + * @inheritable */ addStatics: function(members) { for (var name in members) { @@ -4977,6 +5062,44 @@ var Base = Ext.Base = function() {}; return this; }, + /** + * @private + * @param {Object} members + */ + addInheritableStatics: function(members) { + var inheritableStatics, + hasInheritableStatics, + prototype = this.prototype, + name, member; + + inheritableStatics = prototype.$inheritableStatics; + hasInheritableStatics = prototype.$hasInheritableStatics; + + if (!inheritableStatics) { + inheritableStatics = prototype.$inheritableStatics = []; + hasInheritableStatics = prototype.$hasInheritableStatics = {}; + } + + 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 (!hasInheritableStatics[name]) { + hasInheritableStatics[name] = true; + inheritableStatics.push(name); + } + } + } + + return this; + }, + /** * Add methods / properties to the prototype of this class. * @@ -4997,10 +5120,12 @@ var Base = Ext.Base = function() {}; * * @param {Object} members * @static + * @inheritable */ implement: function(members) { var prototype = this.prototype, - name, i, member, previous; + enumerables = Ext.enumerables, + name, i, member; var className = Ext.getClassName(this); for (name in members) { if (members.hasOwnProperty(name)) { @@ -5018,9 +5143,7 @@ var Base = Ext.Base = function() {}; } } - if (Ext.enumerables) { - var enumerables = Ext.enumerables; - + if (enumerables) { for (i = enumerables.length; i--;) { name = enumerables[i]; @@ -5056,10 +5179,10 @@ var Base = Ext.Base = function() {}; * 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 * @static - * @private + * @inheritable */ borrow: function(fromClass, members) { var fromPrototype = fromClass.prototype, @@ -5107,11 +5230,32 @@ var Base = Ext.Base = function() {}; * @param {Object} members * @return {Ext.Base} this * @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]; @@ -5130,14 +5274,12 @@ var Base = Ext.Base = function() {}; } } - 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; } @@ -5150,44 +5292,58 @@ var Base = Ext.Base = function() {}; 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. @@ -5201,6 +5357,8 @@ var Base = Ext.Base = function() {}; * My.cool.Class.getName(); // 'My.cool.Class' * * @return {String} className + * @static + * @inheritable */ getName: function() { return Ext.getClassName(this); @@ -5231,10 +5389,13 @@ var Base = Ext.Base = function() {}; * {@link Ext.Function#flexSetter flexSetter} * @param {String/Object} origin The original method name * @static + * @inheritable * @method */ createAlias: flexSetter(function(alias, origin) { - this.prototype[alias] = this.prototype[origin]; + this.prototype[alias] = function() { + return this[origin].apply(this, arguments); + } }) }); @@ -5244,194 +5405,16 @@ var Base = Ext.Base = function() {}; * @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() { @@ -5450,12 +5433,12 @@ var Base = Ext.Base = function() {}; * @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() { @@ -5471,7 +5454,7 @@ var Base = Ext.Base = function() {}; 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]; @@ -5483,7 +5466,7 @@ var Base = Ext.Base = function() {}; 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) { @@ -5500,7 +5483,7 @@ var Base = Ext.Base = function() {}; } } - classData.onClassCreated = onClassCreated; + classData.onClassCreated = onClassCreated || Ext.emptyFn; classData.onBeforeClassCreated = function(cls, data) { onClassCreated = data.onClassCreated; @@ -5510,9 +5493,7 @@ var Base = Ext.Base = function() {}; cls.implement(data); - if (onClassCreated) { - onClassCreated.call(cls, cls); - } + onClassCreated.call(cls, cls); }; process = function(cls, data) { @@ -5541,29 +5522,27 @@ var Base = Ext.Base = function() {}; /** * 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] = { @@ -5580,6 +5559,7 @@ var Base = Ext.Base = function() {}; * * @param {String} name * @return {Function} preprocessor + * @static */ getPreprocessor: function(name) { return this.preprocessors[name]; @@ -5592,7 +5572,8 @@ var Base = Ext.Base = function() {}; /** * Retrieve the array stack of default pre-processors * - * @return {Function} defaultPreprocessors + * @return {Function[]} defaultPreprocessors + * @static */ getDefaultPreprocessors: function() { return this.defaultPreprocessors || []; @@ -5601,8 +5582,9 @@ var Base = Ext.Base = function() {}; /** * 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); @@ -5611,30 +5593,30 @@ var Base = Ext.Base = function() {}; }, /** - * 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); @@ -5706,6 +5688,7 @@ var Base = Ext.Base = function() {}; delete data.extend; + // // Statics inheritance parentStatics = parentPrototype.$inheritableStatics; @@ -5718,7 +5701,9 @@ var Base = Ext.Base = function() {}; } } } + // + // // Merge the parent class' config object without referencing it if (parentPrototype.config) { clsPrototype.config = Ext.Object.merge({}, parentPrototype.config); @@ -5726,7 +5711,9 @@ var Base = Ext.Base = function() {}; else { clsPrototype.config = {}; } + // + // if (clsPrototype.$onExtended) { clsPrototype.$onExtended.call(cls, cls, data); } @@ -5735,9 +5722,11 @@ var Base = Ext.Base = function() {}; clsPrototype.$onExtended = data.onClassExtended; delete data.onClassExtended; } + // }, true); + // /** * @cfg {Object} statics * List of static methods for this class. For example: @@ -5756,69 +5745,26 @@ var Base = Ext.Base = 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; }); + // - /** - * @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) { - cls.mixin(data.mixins); - - delete data.mixins; - }); - + // /** * @cfg {Object} config * List of configuration options with their default values, for which automatically @@ -5865,7 +5811,7 @@ var Base = Ext.Base = function() {}; data[setter] = function(val) { var ret = this[apply].call(this, val, this[pName]); - if (ret !== undefined) { + if (typeof ret != 'undefined') { this[pName] = ret; } @@ -5883,9 +5829,71 @@ var Base = Ext.Base = function() {}; Ext.Object.merge(prototype.config, data.config); delete data.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', 'mixins', 'config']); + // + + Class.setDefaultPreprocessors([ + 'extend' + // + ,'statics' + // + // + ,'inheritableStatics' + // + // + ,'config' + // + // + ,'mixins' + // + ]); + // // Backwards compatible Ext.extend = function(subclass, superclass, members) { if (arguments.length === 2 && Ext.isObject(superclass)) { @@ -5901,7 +5909,21 @@ var Base = Ext.Base = function() {}; } members.extend = superclass; - members.preprocessors = ['extend', 'mixins', 'config', 'statics']; + members.preprocessors = [ + 'extend' + // + ,'statics' + // + // + ,'inheritableStatics' + // + // + ,'mixins' + // + // + ,'config' + // + ]; if (subclass) { cls = new Class(subclass, members); @@ -5920,6 +5942,7 @@ var Base = Ext.Base = function() {}; return cls; }; + // })(); @@ -5938,6 +5961,189 @@ var Base = Ext.Base = function() {}; * - {@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 */ (function(Class, alias) { @@ -6095,15 +6301,16 @@ var Base = Ext.Base = function() {}; * alert(MyCompany.pkg.Example === someObject); // alerts true * * @param {String} name - * @param {Mixed} value + * @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') { @@ -6174,7 +6381,7 @@ var Base = Ext.Base = function() {}; * Retrieve a class by its name. * * @param {String} name - * @return {Class} class + * @return {Ext.Class} class */ get: function(name) { if (this.classes.hasOwnProperty(name)) { @@ -6205,7 +6412,7 @@ var Base = Ext.Base = function() {}; /** * 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) { @@ -6243,7 +6450,7 @@ var Base = Ext.Base = function() {}; * 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)); @@ -6273,7 +6480,7 @@ var Base = Ext.Base = function() {}; * 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] || []; @@ -6286,7 +6493,7 @@ var Base = Ext.Base = function() {}; * * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}. * - * @param {Class/Object} object + * @param {Ext.Class/Object} object * @return {String} className */ getName: function(object) { @@ -6304,7 +6511,7 @@ var Base = Ext.Base = function() {}; * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}. * * @param {Object} object - * @return {Class} class + * @return {Ext.Class} class */ getClass: function(object) { return object && object.self || null; @@ -6313,7 +6520,11 @@ var Base = Ext.Base = function() {}; /** * Defines a class. * - * Ext.ClassManager.create('My.awesome.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() { ... } * ... @@ -6325,16 +6536,13 @@ var Base = Ext.Base = function() {}; * var myInstance = new this(); * }); * - * {@link Ext#define Ext.define} is alias for {@link Ext.ClassManager#create Ext.ClassManager.create}. - * * @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: + * `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 + * @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: * * - {@link Ext.Base#self self} @@ -6344,13 +6552,15 @@ var Base = Ext.Base = function() {}; * - {@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 Optional callback to execute after the class is created, the execution scope of which + * @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} */ create: function(className, data, createdFn) { @@ -6371,7 +6581,7 @@ var Base = Ext.Base = function() {}; registeredPostprocessors = manager.postprocessors, index = 0, postprocessors = [], - postprocessor, postprocessors, process, i, ln; + postprocessor, process, i, ln; delete data.postprocessors; @@ -6430,7 +6640,7 @@ var Base = Ext.Base = function() {}; * {@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 */ @@ -6483,7 +6693,7 @@ var Base = Ext.Base = function() {}; * {@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 */ instantiate: function() { @@ -6626,7 +6836,7 @@ var Base = Ext.Base = function() {}; /** * 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) { @@ -6688,8 +6898,7 @@ var Base = Ext.Base = function() {}; * 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, @@ -6748,8 +6957,11 @@ var Base = Ext.Base = function() {}; } }; + var defaultPostprocessors = Manager.defaultPostprocessors; + // + /** - * @cfg {[String]} alias + * @cfg {String[]} alias * @member Ext.Class * List of short aliases for class names. Most useful for defining xtypes for widgets: * @@ -6771,43 +6983,21 @@ var Base = Ext.Base = function() {}; */ 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 instanciated as singleton. For example: + * When set to true, the class will be instantiated as singleton. For example: * * Ext.define('Logger', { * singleton: true, @@ -6824,7 +7014,7 @@ var Base = Ext.Base = function() {}; }); /** - * @cfg {String/[String]} alternateClassName + * @cfg {String/String[]} alternateClassName * @member Ext.Class * Defines alternate names for this class. For example: * @@ -6878,7 +7068,7 @@ var Base = Ext.Base = function() {}; * @private * API to be stablized * - * @param {Mixed} item + * @param {Object} item * @param {String} namespace */ factory: function(item, namespace) { @@ -6929,6 +7119,7 @@ var Base = Ext.Base = function() {}; * @method * @member Ext * @param {String} name xtype of the widget to create. + * @param {Object...} args arguments for the widget constructor. * @return {Object} widget instance */ widget: function(name) { @@ -6945,12 +7136,158 @@ var Base = Ext.Base = function() {}; */ createByAlias: alias(Manager, 'instantiateByAlias'), + /** + * @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 * @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); + } + }); + }, /** * @method @@ -6960,8 +7297,10 @@ var Base = Ext.Base = function() {}; 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) { @@ -7035,6 +7374,62 @@ var Base = Ext.Base = function() {}; Class.setDefaultPreprocessorPosition('className', 'first'); + Class.registerPreprocessor('xtype', function(cls, data) { + var xtypes = Ext.Array.from(data.xtype), + widgetPrefix = 'widget.', + aliases = Ext.Array.from(data.alias), + i, ln, xtype; + + data.xtype = xtypes[0]; + data.xtypes = xtypes; + + aliases = data.alias = Ext.Array.from(data.alias); + + for (i = 0,ln = xtypes.length; i < ln; i++) { + xtype = xtypes[i]; + + 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"); + } + + aliases.push(widgetPrefix + xtype); + } + + data.alias = aliases; + }); + + Class.setDefaultPreprocessorPosition('xtype', 'last'); + + 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; + + for (i = 0, ln = aliases.length; i < ln; i++) { + alias = aliases[i]; + + if (typeof alias != 'string') { + throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"); + } + + if (alias.substring(0, widgetPrefixLength) === widgetPrefix) { + xtype = alias.substring(widgetPrefixLength); + Ext.Array.include(xtypes, xtype); + + if (!cls.xtype) { + cls.xtype = data.xtype = xtype; + } + } + } + + data.alias = aliases; + data.xtypes = xtypes; + }); + + Class.setDefaultPreprocessorPosition('alias', 'last'); + })(Ext.Class, Ext.Function.alias); /** @@ -7050,13 +7445,13 @@ var Base = Ext.Base = function() {}; * * # Asynchronous Loading * - * - *Advantages:* + * - 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:* + * - Disadvantages: * + Dependencies need to be specified before-hand * * ### Method 1: Explicitly include what you need: @@ -7091,11 +7486,11 @@ var Base = Ext.Base = function() {}; * * # Synchronous Loading on Demand * - * - *Advantages:* + * - Advantages: * + There's no need to specify dependencies before-hand, which is always the convenience of including * ext-all.js before * - * - *Disadvantages:* + * - 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 @@ -7117,7 +7512,7 @@ var Base = Ext.Base = function() {}; * 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(){ @@ -7241,7 +7636,7 @@ var Base = Ext.Base = function() {}; classNameToFilePathMap: {}, /** - * @property {[String]} history + * @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. */ @@ -7254,19 +7649,19 @@ var Base = Ext.Base = function() {}; config: { /** * @cfg {Boolean} enabled - * Whether or not to enable the dynamic dependency loading feature Defaults to false + * 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' + * The get parameter name for the cache buster's timestamp. */ disableCachingParam: '_dc', @@ -7333,7 +7728,7 @@ var Base = Ext.Base = function() {}; * 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) { @@ -7528,7 +7923,7 @@ var Base = Ext.Base = function() {}; * * @param {String} url * @param {Function} onLoad - * @param {Scope} scope + * @param {Object} scope * @param {Boolean} synchronous * @private */ @@ -7612,7 +8007,7 @@ var Base = Ext.Base = function() {}; * * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience. * - * @param {String/[String]} excludes + * @param {String/String[]} excludes * @return {Object} object contains `require` method for chaining */ exclude: function(excludes) { @@ -7635,10 +8030,10 @@ var Base = Ext.Base = function() {}; * * {@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 {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/[String]} excludes (Optional) Classes to be excluded, useful when being used with expressions + * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions */ syncRequire: function() { this.syncModeEnabled = true; @@ -7653,10 +8048,10 @@ var Base = Ext.Base = function() {}; * * {@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 {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/[String]} excludes (Optional) Classes to be excluded, useful when being used with expressions + * @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 = {}, @@ -7952,9 +8347,9 @@ var Base = Ext.Base = function() {}; }; /** - * @cfg {[String]} requires + * @cfg {String[]} requires * @member Ext.Class - * List of classes that have to be loaded before instanciating this class. + * List of classes that have to be loaded before instantiating this class. * For example: * * Ext.define('Mother', { @@ -8009,7 +8404,7 @@ var Base = Ext.Base = function() {}; } } } - else { + else if (typeof propertyValue != 'function') { for (j in propertyValue) { if (propertyValue.hasOwnProperty(j)) { value = propertyValue[j]; @@ -8088,7 +8483,7 @@ var Base = Ext.Base = function() {}; } } } - else { + else if (typeof propertyValue != 'function') { for (var k in propertyValue) { if (propertyValue.hasOwnProperty(k)) { value = propertyValue[k]; @@ -8111,10 +8506,10 @@ var Base = Ext.Base = function() {}; Class.setDefaultPreprocessorPosition('loader', 'after', 'className'); /** - * @cfg {[String]} uses + * @cfg {String[]} uses * @member Ext.Class * List of classes to load together with this class. These aren't neccessarily loaded before - * this class is instanciated. For example: + * this class is instantiated. For example: * * Ext.define('Mother', { * uses: ['Child'], @@ -8148,152 +8543,144 @@ var Base = Ext.Base = function() {}; })(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, /** - * @property 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; - - * @markdown + * @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, /** -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. + * 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 || {}; @@ -8327,25 +8714,24 @@ execution will halt. }, /** -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; @@ -8356,6 +8742,7 @@ error to the browser, otherwise the error will be thrown and execution will halt name: 'Ext.Error', /** + * 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. @@ -8374,16 +8761,15 @@ error to the browser, otherwise the error will be thrown and execution will halt }, /** -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, @@ -8452,20 +8838,8 @@ a particular error instance, if you want to provide a custom description that wi timer = win.setInterval(notify, 1000); } - // window.onerror is ideal (esp in IE) because you get full context. This is harmless - // otherwise (never called) which is good because you cannot feature detect it. - prevOnError = win.onerror || Ext.emptyFn; - win.onerror = function (message) { - ++errors; - - if (!extraordinarilyBad.test(message)) { - // too much recursion + our alert right now = crash IE - // our polling loop will pick it up even if we don't alert now - notify(); - } - - return prevOnError.apply(this, arguments); - }; + // window.onerror sounds ideal but it prevents the built-in error dialog from doing + // its (better) thing. poll(); })(); @@ -8487,7 +8861,7 @@ If you are unsure which license is appropriate for your use, please contact the */ /** * @class Ext.JSON - * Modified version of Douglas Crockford"s json.js that doesn"t + * Modified version of Douglas Crockford's JSON.js that doesn't * mess with the Object prototype * http://www.json.org/js.html * @singleton @@ -8580,15 +8954,15 @@ Ext.JSON = new(function() { * The returned value includes enclosing double quotation marks.

*

The default return format is "yyyy-mm-ddThh:mm:ss".

*

To override this:


-     Ext.JSON.encodeDate = function(d) {
-     return d.format('"Y-m-d"');
-     };
+Ext.JSON.encodeDate = function(d) {
+    return Ext.Date.format(d, '"Y-m-d"');
+};
      
* @param {Date} d The Date to encode * @return {String} The string literal to use in a JSON string. */ this.encodeDate = function(o) { - return '"' + o.getFullYear() + "-" + return '"' + o.getFullYear() + "-" + pad(o.getMonth() + 1) + "-" + pad(o.getDate()) + "T" + pad(o.getHours()) + ":" @@ -8598,7 +8972,7 @@ Ext.JSON = new(function() { /** * Encodes an Object, Array or other value - * @param {Mixed} o The variable to encode + * @param {Object} o The variable to encode * @return {String} The JSON string */ this.encode = function() { @@ -8644,19 +9018,16 @@ Ext.JSON = new(function() { })(); /** * Shorthand for {@link Ext.JSON#encode} - * @param {Mixed} o The variable to encode - * @return {String} The JSON string * @member Ext * @method encode + * @alias Ext.JSON#encode */ Ext.encode = Ext.JSON.encode; /** * Shorthand for {@link Ext.JSON#decode} - * @param {String} json The JSON string - * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid. - * @return {Object} The resulting object * @member Ext * @method decode + * @alias Ext.JSON#decode */ Ext.decode = Ext.JSON.decode; @@ -8694,8 +9065,6 @@ Ext.apply(Ext, { userAgent: navigator.userAgent.toLowerCase(), cache: {}, idSeed: 1000, - BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', - isStrict: document.compatMode == "CSS1Compat", windowId: 'ext-window', documentId: 'ext-document', @@ -8706,20 +9075,20 @@ Ext.apply(Ext, { isReady: false, /** - * True to automatically uncache orphaned Ext.core.Elements periodically (defaults to true) + * True to automatically uncache orphaned Ext.Elements periodically * @type Boolean */ enableGarbageCollector: true, /** - * True to automatically purge event listeners during garbageCollection (defaults to true). + * True to automatically purge event listeners during garbageCollection. * @type Boolean */ enableListenerCollection: true, /** * Generates unique ids. If the element already has an id, it is unchanged - * @param {Mixed} el (optional) The element to generate an id for + * @param {HTMLElement/Ext.Element} el (optional) The element to generate an id for * @param {String} prefix (optional) Id prefix (defaults "ext-gen") * @return {String} The generated Id. */ @@ -8746,16 +9115,16 @@ Ext.apply(Ext, { }, /** - * Returns the current document body as an {@link Ext.core.Element}. - * @return Ext.core.Element The document body + * Returns the current document body as an {@link Ext.Element}. + * @return Ext.Element The document body */ getBody: function() { return Ext.get(document.body || false); }, /** - * Returns the current document head as an {@link Ext.core.Element}. - * @return Ext.core.Element The document head + * Returns the current document head as an {@link Ext.Element}. + * @return Ext.Element The document head * @method */ getHead: function() { @@ -8771,8 +9140,8 @@ Ext.apply(Ext, { }(), /** - * Returns the current HTML document object as an {@link Ext.core.Element}. - * @return Ext.core.Element The document + * Returns the current HTML document object as an {@link Ext.Element}. + * @return Ext.Element The document */ getDoc: function() { return Ext.get(document); @@ -8800,12 +9169,11 @@ Ext.apply(Ext, { /** * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the * DOM (if applicable) and calling their destroy functions (if available). This method is primarily - * intended for arguments of type {@link Ext.core.Element} and {@link Ext.Component}, but any subclass of + * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be * passed into this function in a single call as separate arguments. - * @param {Mixed} arg1 An {@link Ext.core.Element}, {@link Ext.Component}, or an Array of either of these to destroy - * @param {Mixed} arg2 (optional) - * @param {Mixed} etc... (optional) + * @param {Ext.Element/Ext.Component/Ext.Element[]/Ext.Component[]...} arg1 + * An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy */ destroy: function() { var ln = arguments.length, @@ -8829,12 +9197,12 @@ Ext.apply(Ext, { /** * Execute a callback function in a particular scope. If no function is passed the call is ignored. - * + * * For example, these lines are equivalent: - * + * * Ext.callback(myFunc, this, [arg1, arg2]); * Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]); - * + * * @param {Function} callback The callback to execute * @param {Object} scope (optional) The scope to execute in * @param {Array} args (optional) The arguments to pass to the function @@ -8897,9 +9265,30 @@ window.undefined = window.undefined; * @singleton */ (function(){ +/* +FF 3.6 - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17 +FF 4.0.1 - Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 +FF 5.0 - Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0 + +IE6 - Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;) +IE7 - Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1;) +IE8 - Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0) +IE9 - Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E) + +Chrome 11 - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.60 Safari/534.24 + +Safari 5 - Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1 + +Opera 11.11 - Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11 +*/ var check = function(regex){ return regex.test(Ext.userAgent); }, + isStrict = document.compatMode == "CSS1Compat", + version = function (is, regex) { + var m; + return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0; + }, docMode = document.documentMode, isOpera = check(/opera/), isOpera10_5 = isOpera && check(/version\/10\.5/), @@ -8909,6 +9298,7 @@ window.undefined = window.undefined; isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2 isSafari3 = isSafari && check(/version\/3/), isSafari4 = isSafari && check(/version\/4/), + isSafari5 = isSafari && check(/version\/5/), isIE = !isOpera && check(/msie/), isIE7 = isIE && (check(/msie 7/) || docMode == 7), isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8), @@ -8917,6 +9307,7 @@ window.undefined = window.undefined; isGecko = !isWebKit && check(/gecko/), isGecko3 = isGecko && check(/rv:1\.9/), isGecko4 = isGecko && check(/rv:2\.0/), + isGecko5 = isGecko && check(/rv:5\./), isFF3_0 = isGecko3 && check(/rv:1\.9\.0/), isFF3_5 = isGecko3 && check(/rv:1\.9\.1/), isFF3_6 = isGecko3 && check(/rv:1\.9\.2/), @@ -8924,21 +9315,160 @@ window.undefined = window.undefined; isMac = check(/macintosh|mac os x/), isLinux = check(/linux/), scrollbarSize = null, - webKitVersion = isWebKit && (/webkit\/(\d+\.\d+)/.exec(Ext.userAgent)); + chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/), + firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/), + ieVersion = version(isIE, /msie (\d+\.\d+)/), + operaVersion = version(isOpera, /version\/(\d+\.\d+)/), + safariVersion = version(isSafari, /version\/(\d+\.\d+)/), + webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/), + isSecure = /^https/i.test(window.location.protocol); // remove css image flicker try { document.execCommand("BackgroundImageCache", false, true); } catch(e) {} - Ext.setVersion('extjs', '4.0.2'); + function dumpObject (object) { + var member, members = []; + + // Cannot use Ext.encode since it can recurse endlessly (if we're lucky) + // ...and the data could be prettier! + Ext.Object.each(object, function (name, value) { + if (typeof(value) === "function") { + return; + } + + if (!Ext.isDefined(value) || value === null || + Ext.isDate(value) || + Ext.isString(value) || (typeof(value) == "number") || + Ext.isBoolean(value)) { + member = Ext.encode(value); + } else if (Ext.isArray(value)) { + member = '[ ]'; + } else if (Ext.isObject(value)) { + member = '{ }'; + } else { + member = 'undefined'; + } + members.push(Ext.encode(name) + ': ' + member); + }); + + if (members.length) { + return ' \nData: {\n ' + members.join(',\n ') + '\n}'; + } + return ''; + } + + function log (message) { + var options, dump, + con = Ext.global.console, + level = 'log', + indent = log.indent || 0, + stack; + + log.indent = indent; + + if (!Ext.isString(message)) { + options = message; + message = options.msg || ''; + level = options.level || level; + dump = options.dump; + stack = options.stack; + + if (options.indent) { + ++log.indent; + } else if (options.outdent) { + log.indent = indent = Math.max(indent - 1, 0); + } + + if (dump && !(con && con.dir)) { + message += dumpObject(dump); + dump = null; + } + } + + if (arguments.length > 1) { + message += Array.prototype.slice.call(arguments, 1).join(''); + } + + message = indent ? Ext.String.repeat(' ', indent) + message : message; + // w/o console, all messages are equal, so munge the level into the message: + if (level != 'log') { + message = '[' + level.charAt(0).toUpperCase() + '] ' + message; + } + + // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so + // an early test may fail either direction if Firebug is toggled. + // + if (con) { // if (Firebug-like console) + if (con[level]) { + con[level](message); + } else { + con.log(message); + } + + if (dump) { + con.dir(dump); + } + + if (stack && con.trace) { + // Firebug's console.error() includes a trace already... + if (!con.firebug || level != 'error') { + con.trace(); + } + } + } else { + if (Ext.isOpera) { + opera.postError(message); + } else { + var out = log.out, + max = log.max; + + if (out.length >= max) { + // this formula allows out.max to change (via debugger), where the + // more obvious "max/4" would not quite be the same + Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75% + } + + out.push(message); + } + } + + // Mostly informational, but the Ext.Error notifier uses them: + ++log.count; + ++log.counters[level]; + } + + log.count = 0; + log.counters = { error: 0, warn: 0, info: 0, log: 0 }; + log.out = []; + log.max = 250; + log.show = function () { + window.open('','extlog').document.write([ + ''].join('')); + }; + + Ext.setVersion('extjs', '4.0.7'); Ext.apply(Ext, { /** * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent * the IE insecure content warning ('about:blank', except for IE in secure mode, which is 'javascript:""'). * @type String */ - SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank', + SSL_SECURE_URL : isSecure && isIE ? 'javascript:""' : 'about:blank', /** * True if the {@link Ext.fx.Anim} Class is available @@ -8971,7 +9501,7 @@ window.undefined = window.undefined; USE_NATIVE_JSON : false, /** - * Return the dom node for the passed String (id), dom node, or Ext.core.Element. + * Return the dom node for the passed String (id), dom node, or Ext.Element. * Optional 'strict' flag is needed for IE since it can return 'name' and * 'id' elements by using getElementById. * Here are some examples: @@ -8982,7 +9512,7 @@ var elDom = Ext.getDom('elId'); var elDom1 = Ext.getDom(elDom); // If we don't know if we are working with an -// Ext.core.Element or a dom node use Ext.getDom +// Ext.Element or a dom node use Ext.getDom function(el){ var dom = Ext.getDom(el); // do something with the dom node @@ -8990,7 +9520,7 @@ function(el){ * * Note: the dom node to be found actually needs to exist (be rendered, etc) * when this method is called to be successful. - * @param {Mixed} el + * @param {String/HTMLElement/Ext.Element} el * @return HTMLElement */ getDom : function(el, strict) { @@ -9046,6 +9576,10 @@ function(el){ } }, + isStrict: isStrict, + + isIEQuirks: isIE && !isStrict, + /** * True if the detected browser is Opera. * @type Boolean @@ -9088,6 +9622,12 @@ function(el){ */ isSafari4 : isSafari4, + /** + * True if the detected browser is Safari 5.x. + * @type Boolean + */ + isSafari5 : isSafari5, + /** * True if the detected browser is Safari 2.x. * @type Boolean @@ -9143,23 +9683,41 @@ function(el){ isGecko4 : isGecko4, /** - * True if the detected browser uses FireFox 3.0 + * True if the detected browser uses a Gecko 5.0+ layout engine (e.g. Firefox 5.x). * @type Boolean */ + isGecko5 : isGecko5, + /** + * True if the detected browser uses FireFox 3.0 + * @type Boolean + */ isFF3_0 : isFF3_0, + /** * True if the detected browser uses FireFox 3.5 * @type Boolean */ - isFF3_5 : isFF3_5, + /** * True if the detected browser uses FireFox 3.6 * @type Boolean */ isFF3_6 : isFF3_6, + /** + * True if the detected browser uses FireFox 4 + * @type Boolean + */ + isFF4 : 4 <= firefoxVersion && firefoxVersion < 5, + + /** + * True if the detected browser uses FireFox 5 + * @type Boolean + */ + isFF5 : 5 <= firefoxVersion && firefoxVersion < 6, + /** * True if the detected platform is Linux. * @type Boolean @@ -9179,10 +9737,52 @@ function(el){ isMac : isMac, /** - * The current version of WebKit (-1 if the browser does not use WebKit). - * @type Float + * The current version of Chrome (0 if the browser is not Chrome). + * @type Number + */ + chromeVersion: chromeVersion, + + /** + * The current version of Firefox (0 if the browser is not Firefox). + * @type Number + */ + firefoxVersion: firefoxVersion, + + /** + * The current version of IE (0 if the browser is not IE). This does not account + * for the documentMode of the current page, which is factored into {@link #isIE7}, + * {@link #isIE8} and {@link #isIE9}. Thus this is not always true: + * + * Ext.isIE8 == (Ext.ieVersion == 8) + * + * @type Number + * @markdown + */ + ieVersion: ieVersion, + + /** + * The current version of Opera (0 if the browser is not Opera). + * @type Number + */ + operaVersion: operaVersion, + + /** + * The current version of Safari (0 if the browser is not Safari). + * @type Number + */ + safariVersion: safariVersion, + + /** + * The current version of WebKit (0 if the browser does not use WebKit). + * @type Number */ - webKitVersion: webKitVersion ? parseFloat(webKitVersion[1]) : -1, + webKitVersion: webKitVersion, + + /** + * True if the page is running over SSL + * @type Boolean + */ + isSecure: isSecure, /** * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images. @@ -9190,7 +9790,7 @@ function(el){ * For other browsers it uses an inline data URL. * @type String */ - BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', + BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', /** *

Utility method for returning a default value if the passed value is empty.

@@ -9200,10 +9800,10 @@ function(el){ *
  • an empty array
  • *
  • a zero length string (Unless the allowBlank parameter is true)
  • * - * @param {Mixed} value The value to test - * @param {Mixed} defaultValue The value to return if the original value is empty + * @param {Object} value The value to test + * @param {Object} defaultValue The value to return if the original value is empty * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false) - * @return {Mixed} value, if non-empty, else defaultValue + * @return {Object} value, if non-empty, else defaultValue * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead */ value : function(v, defaultValue, allowBlank){ @@ -9327,7 +9927,7 @@ Ext.addBehaviors({ * * @param {Object} dest The destination object. * @param {Object} source The source object. - * @param {Array/String} names Either an Array of property names, or a comma-delimited list + * @param {String/String[]} names Either an Array of property names, or a comma-delimited list * of property names to copy. * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance. * @return {Object} The modified object. @@ -9347,8 +9947,7 @@ Ext.addBehaviors({ /** * Attempts to destroy and then remove a set of named properties of the passed object. * @param {Object} o The object (most likely a Component) who's properties you wish to destroy. - * @param {Mixed} arg1 The name of the property to destroy and remove from the object. - * @param {Mixed} etc... More property names to destroy and remove. + * @param {String...} args One or more names of the properties to destroy and remove from the object. */ destroyMembers : function(o){ for (var i = 1, a = arguments, len = a.length; i < len; i++) { @@ -9361,121 +9960,40 @@ Ext.addBehaviors({ * Logs a message. If a console is present it will be used. On Opera, the method * "opera.postError" is called. In other cases, the message is logged to an array * "Ext.log.out". An attached debugger can watch this array and view the log. The - * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 100). + * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250). + * The `Ext.log.out` array can also be written to a popup window by entering the + * following in the URL bar (a "bookmarklet"): + * + * javascript:void(Ext.log.show()); * * If additional parameters are passed, they are joined and appended to the message. - * + * A technique for tracing entry and exit of a function is this: + * + * function foo () { + * Ext.log({ indent: 1 }, '>> foo'); + * + * // log statements in here or methods called from here will be indented + * // by one step + * + * Ext.log({ outdent: 1 }, '<< foo'); + * } + * * This method does nothing in a release build. * - * @param {String|Object} message The message to log or an options object with any + * @param {String/Object} message The message to log or an options object with any * of the following properties: * * - `msg`: The message to log (required). * - `level`: One of: "error", "warn", "info" or "log" (the default is "log"). * - `dump`: An object to dump to the log as part of the message. * - `stack`: True to include a stack trace in the log. + * - `indent`: Cause subsequent log statements to be indented one step. + * - `outdent`: Cause this and following statements to be one step less indented. * @markdown */ - log : function (message) { - var options, dump, - con = Ext.global.console, - log = Ext.log, - level = 'log', - stack, - members, - member; - - if (!Ext.isString(message)) { - options = message; - message = options.msg || ''; - level = options.level || level; - dump = options.dump; - stack = options.stack; - - if (dump && !(con && con.dir)) { - members = []; - - // Cannot use Ext.encode since it can recurse endlessly (if we're lucky) - // ...and the data could be prettier! - Ext.Object.each(dump, function (name, value) { - if (typeof(value) === "function") { - return; - } - - if (!Ext.isDefined(value) || value === null || - Ext.isDate(value) || - Ext.isString(value) || (typeof(value) == "number") || - Ext.isBoolean(value)) { - member = Ext.encode(value); - } else if (Ext.isArray(value)) { - member = '[ ]'; - } else if (Ext.isObject(value)) { - member = '{ }'; - } else { - member = 'undefined'; - } - members.push(Ext.encode(name) + ': ' + member); - }); - - if (members.length) { - message += ' \nData: {\n ' + members.join(',\n ') + '\n}'; - } - dump = null; - } - } - - if (arguments.length > 1) { - message += Array.prototype.slice.call(arguments, 1).join(''); - } - - // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so - // an early test may fail either direction if Firebug is toggled. - // - if (con) { // if (Firebug-like console) - if (con[level]) { - con[level](message); - } else { - con.log(message); - } - - if (dump) { - con.dir(dump); - } - - if (stack && con.trace) { - // Firebug's console.error() includes a trace already... - if (!con.firebug || level != 'error') { - con.trace(); - } - } - } else { - // w/o console, all messages are equal, so munge the level into the message: - if (level != 'log') { - message = level.toUpperCase() + ': ' + message; - } - - if (Ext.isOpera) { - opera.postError(message); - } else { - var out = log.out || (log.out = []), - max = log.max || (log.max = 100); - - if (out.length >= max) { - // this formula allows out.max to change (via debugger), where the - // more obvious "max/4" would not quite be the same - Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75% - } - - out.push(message); - } - } - - // Mostly informational, but the Ext.Error notifier uses them: - var counters = log.counters || - (log.counters = { error: 0, warn: 0, info: 0, log: 0 }); - - ++counters[level]; - }, + log : + log || + Ext.emptyFn, /** * Partitions the set into two sets: a true set and a false set. @@ -9495,7 +10013,7 @@ Ext.partition( // true are those paragraph elements with a className of "class1", // false set are those that do not have that className. * - * @param {Array|NodeList} arr The array to partition + * @param {Array/NodeList} arr The array to partition * @param {Function} truth (optional) a function to determine truth. If this is omitted the element * itself must be able to be evaluated for its truthfulness. * @return {Array} [array of truish values, array of falsy values] @@ -9516,9 +10034,9 @@ Ext.partition( Ext.invoke(Ext.query("p"), "getAttribute", "id"); // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")] * - * @param {Array|NodeList} arr The Array of items to invoke the method on. + * @param {Array/NodeList} arr The Array of items to invoke the method on. * @param {String} methodName The method name to invoke. - * @param {...*} args Arguments to send into the method invocation. + * @param {Object...} args Arguments to send into the method invocation. * @return {Array} The results of invoking the method on each item in the array. * @deprecated 4.0.0 Will be removed in the next major version */ @@ -9550,7 +10068,7 @@ Ext.zip( } ); // ["$+12.43", "$-10.15", "$+22.96"] * - * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values. + * @param {Array/NodeList...} arr This argument may be repeated. Array(s) to contribute values. * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together. * @return {Array} The zipped set. * @deprecated 4.0.0 Will be removed in the next major version @@ -9579,7 +10097,7 @@ Ext.zip( * Turns an array into a sentence, joined by a specified connector - e.g.: * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin' * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin' - * @param {Array} items The array to create a sentence from + * @param {String[]} items The array to create a sentence from * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'. * @return {String} The sentence string * @deprecated 4.0.0 Will be removed in the next major version @@ -9624,7 +10142,7 @@ Ext.application = function(config) { /** * @class Ext.util.Format -This class is a centralized place for formatting functions inside the library. It includes +This class is a centralized place for formatting functions. It includes functions to format various different types of data, such as text, dates and numeric values. __Localization__ @@ -9639,7 +10157,7 @@ Options include: This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}. __Using with renderers__ -There are two helper functions that return a new function that can be used in conjunction with +There are two helper functions that return a new function that can be used in conjunction with grid renderers: columns: [{ @@ -9649,7 +10167,7 @@ grid renderers: dataIndex: 'time', renderer: Ext.util.Format.numberRenderer('0.000') }] - + Functions that only take a single argument can also be passed directly: columns: [{ dataIndex: 'cost', @@ -9658,7 +10176,7 @@ Functions that only take a single argument can also be passed directly: dataIndex: 'productCode', renderer: Ext.util.Format.uppercase }] - + __Using with XTemplates__ XTemplates can also directly use Ext.util.Format functions: @@ -9688,50 +10206,45 @@ XTemplates can also directly use Ext.util.Format functions: Ext.apply(UtilFormat, { /** - * @type String - * @property thousandSeparator + * @property {String} thousandSeparator *

    The character that the {@link #number} function uses as a thousand separator.

    - *

    This defaults to ,, but may be overridden in a locale file.

    + *

    This may be overridden in a locale file.

    */ thousandSeparator: ',', /** - * @type String - * @property decimalSeparator + * @property {String} decimalSeparator *

    The character that the {@link #number} function uses as a decimal point.

    - *

    This defaults to ., but may be overridden in a locale file.

    + *

    This may be overridden in a locale file.

    */ decimalSeparator: '.', /** - * @type Number - * @property currencyPrecision + * @property {Number} currencyPrecision *

    The number of decimal places that the {@link #currency} function displays.

    - *

    This defaults to 2, but may be overridden in a locale file.

    + *

    This may be overridden in a locale file.

    */ currencyPrecision: 2, /** - * @type String - * @property currencySign + * @property {String} currencySign *

    The currency sign that the {@link #currency} function displays.

    - *

    This defaults to $, but may be overridden in a locale file.

    + *

    This may be overridden in a locale file.

    */ currencySign: '$', /** - * @type Boolean - * @property currencyAtEnd + * @property {Boolean} currencyAtEnd *

    This may be set to true to make the {@link #currency} function * append the currency sign to the formatted value.

    - *

    This defaults to false, but may be overridden in a locale file.

    + *

    This may be overridden in a locale file.

    */ currencyAtEnd: false, /** * Checks a reference and converts it to empty string if it is undefined - * @param {Mixed} value Reference to check - * @return {Mixed} Empty string if converted, otherwise the original value + * @param {Object} value Reference to check + * @return {Object} Empty string if converted, otherwise the original value */ undef : function(value) { return value !== undefined ? value : ""; @@ -9739,7 +10252,7 @@ XTemplates can also directly use Ext.util.Format functions: /** * Checks a reference and converts it to the default value if it's empty - * @param {Mixed} value Reference to check + * @param {Object} value Reference to check * @param {String} defaultValue The value to insert of it's undefined (defaults to "") * @return {String} */ @@ -9807,7 +10320,7 @@ XTemplates can also directly use Ext.util.Format functions: for (; i < decimals; i++) { format += '0'; } - v = UtilFormat.number(v, format); + v = UtilFormat.number(v, format); if ((end || UtilFormat.currencyAtEnd) === true) { return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign); } else { @@ -9845,7 +10358,7 @@ XTemplates can also directly use Ext.util.Format functions: /** * Strips all HTML tags - * @param {Mixed} value The text from which to strip tags + * @param {Object} value The text from which to strip tags * @return {String} The stripped text */ stripTags : function(v) { @@ -9854,7 +10367,7 @@ XTemplates can also directly use Ext.util.Format functions: /** * Strips all script tags - * @param {Mixed} value The text from which to strip script tags + * @param {Object} value The text from which to strip script tags * @return {String} The stripped text */ stripScripts : function(v) { @@ -9916,7 +10429,7 @@ XTemplates can also directly use Ext.util.Format functions: *

    The presence of a thousand separator character in the format string specifies that * the locale-specific thousand separator (if any) is inserted separating thousand groups.

    *

    By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.

    - *

    New to Ext4

    + *

    New to Ext JS 4

    *

    Locale-specific characters are always used in the formatted output when inserting * thousand and decimal separators.

    *

    The format string must specify separator characters according to US/UK conventions ("," as the @@ -10015,7 +10528,7 @@ XTemplates can also directly use Ext.util.Format functions: fnum = psplit[0] + dec + psplit[1]; } } - + if (neg) { /* * Edge case. If we have a very small negative number it will get rounded to 0, @@ -10061,54 +10574,58 @@ XTemplates can also directly use Ext.util.Format functions: }, /** - * Capitalize the given string. See {@link Ext.String#capitalize}. + * Alias for {@link Ext.String#capitalize}. * @method + * @alias Ext.String#capitalize */ capitalize: Ext.String.capitalize, /** - * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length. - * See {@link Ext.String#ellipsis}. + * Alias for {@link Ext.String#ellipsis}. * @method + * @alias Ext.String#ellipsis */ ellipsis: Ext.String.ellipsis, /** - * Formats to a string. See {@link Ext.String#format} + * Alias for {@link Ext.String#format}. * @method + * @alias Ext.String#format */ format: Ext.String.format, /** - * Convert certain characters (&, <, >, and ') from their HTML character equivalents. - * See {@link Ext.String#htmlDecode}. + * Alias for {@link Ext.String#htmlDecode}. * @method + * @alias Ext.String#htmlDecode */ htmlDecode: Ext.String.htmlDecode, /** - * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages. - * See {@link Ext.String#htmlEncode}. + * Alias for {@link Ext.String#htmlEncode}. * @method + * @alias Ext.String#htmlEncode */ htmlEncode: Ext.String.htmlEncode, /** - * Adds left padding to a string. See {@link Ext.String#leftPad} + * Alias for {@link Ext.String#leftPad}. * @method + * @alias Ext.String#leftPad */ leftPad: Ext.String.leftPad, /** - * Trims any whitespace from either side of a string. See {@link Ext.String#trim}. + * Alias for {@link Ext.String#trim}. * @method + * @alias Ext.String#trim */ trim : Ext.String.trim, /** * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result) - * @param {Number|String} v The encoded margins + * @param {Number/String} v The encoded margins * @return {Object} An object with margin sizes for top, right, bottom and left */ parseBox : function(box) { @@ -10178,8 +10695,7 @@ Ext.TaskManager.start({ * Also see {@link Ext.util.DelayedTask}. * * @constructor - * @param {Number} interval (optional) The minimum precision in milliseconds supported by this TaskRunner instance - * (defaults to 10) + * @param {Number} [interval=10] The minimum precision in milliseconds supported by this TaskRunner instance */ Ext.ns('Ext.util'); @@ -10930,7 +11446,9 @@ If you are unsure which license is appropriate for your use, please contact the */ /** - * @class Ext.core.DomHelper + * @class Ext.DomHelper + * @alternateClassName Ext.core.DomHelper + * *

    The DomHelper class provides a layer of abstraction from DOM and transparently supports creating * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates * from your DOM building code.

    @@ -10957,19 +11475,19 @@ If you are unsure which license is appropriate for your use, please contact the *

    Insertion methods

    *

    Commonly used insertion methods: *

      - *
    • {@link #append} :
    • - *
    • {@link #insertBefore} :
    • - *
    • {@link #insertAfter} :
    • - *
    • {@link #overwrite} :
    • - *
    • {@link #createTemplate} :
    • - *
    • {@link #insertHtml} :
    • + *
    • {@link #append} :
    • + *
    • {@link #insertBefore} :
    • + *
    • {@link #insertAfter} :
    • + *
    • {@link #overwrite} :
    • + *
    • {@link #createTemplate} :
    • + *
    • {@link #insertHtml} :
    • *

    * *

    Example

    *

    This is an example, where an unordered list with 3 children items is appended to an existing * element with id 'my-div':

    
    -var dh = Ext.core.DomHelper; // create shorthand alias
    +var dh = Ext.DomHelper; // create shorthand alias
     // specification object
     var spec = {
         id: 'my-ul',
    @@ -11013,7 +11531,7 @@ for(var i = 0; i < 5, i++){
      * 

    An example using a template:

    
     var html = '{2}';
     
    -var tpl = new Ext.core.DomHelper.createTemplate(html);
    +var tpl = new Ext.DomHelper.createTemplate(html);
     tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
     tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
      * 

    @@ -11021,7 +11539,7 @@ tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Si *

    The same example using named parameters:

    
     var html = '{text}';
     
    -var tpl = new Ext.core.DomHelper.createTemplate(html);
    +var tpl = new Ext.DomHelper.createTemplate(html);
     tpl.append('blog-roll', {
         id: 'link1',
         url: 'http://www.edspencer.net/',
    @@ -11045,7 +11563,7 @@ tpl.append('blog-roll', {
      * 
    
     var html = '{text}';
     
    -var tpl = new Ext.core.DomHelper.createTemplate(html);
    +var tpl = new Ext.DomHelper.createTemplate(html);
     tpl.compile();
     
     //... use template like normal
    @@ -11058,12 +11576,12 @@ tpl.compile();
      * then the string is used as innerHTML. If {@link #useDom} is true, a string specification
      * results in the creation of a text node. Usage:

    *
    
    -Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
    +Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
      * 
    * @singleton */ Ext.ns('Ext.core'); -Ext.core.DomHelper = function(){ +Ext.core.DomHelper = Ext.DomHelper = function(){ var tempTableEl = null, emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i, tableRe = /^table|tbody|tr|td$/i, @@ -11095,11 +11613,11 @@ Ext.core.DomHelper = function(){ (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el); } } else { - newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o)); + newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o)); } return returnElement ? Ext.get(newNode, true) : newNode; } - + function createDom(o, parentNode){ var el, doc = document, @@ -11132,7 +11650,7 @@ Ext.core.DomHelper = function(){ } } } - Ext.core.DomHelper.applyStyles(el, o.style); + Ext.DomHelper.applyStyles(el, o.style); if ((cn = o.children || o.cn)) { createDom(cn, el); @@ -11250,17 +11768,17 @@ Ext.core.DomHelper = function(){ el.insertBefore(node, before); return node; } - + /** * @ignore * Fix for IE9 createContextualFragment missing method - */ + */ function createContextualFragment(html){ var div = document.createElement("div"), fragment = document.createDocumentFragment(), i = 0, length, childNodes; - + div.innerHTML = html; childNodes = div.childNodes; length = childNodes.length; @@ -11271,7 +11789,7 @@ Ext.core.DomHelper = function(){ return fragment; } - + pub = { /** * Returns the markup for the passed Element(s) config. @@ -11295,7 +11813,7 @@ Ext.core.DomHelper = function(){ styles = styles.call(); } if (typeof styles == "string") { - styles = Ext.core.Element.parseStyles(styles); + styles = Ext.Element.parseStyles(styles); } if (typeof styles == "object") { el.setStyle(styles); @@ -11306,6 +11824,16 @@ Ext.core.DomHelper = function(){ /** * Inserts an HTML fragment into the DOM. * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd. + * + * For example take the following HTML: `
    Contents
    ` + * + * Using different `where` values inserts element to the following places: + * + * - beforeBegin: `
    Contents
    ` + * - afterBegin: `
    Contents
    ` + * - beforeEnd: `
    Contents
    ` + * - afterEnd: `
    Contents
    ` + * * @param {HTMLElement/TextNode} el The context element * @param {String} html The HTML fragment * @return {HTMLElement} The new node @@ -11323,13 +11851,13 @@ Ext.core.DomHelper = function(){ // add these here because they are used in both branches of the condition. hash[beforebegin] = ['BeforeBegin', 'previousSibling']; hash[afterend] = ['AfterEnd', 'nextSibling']; - + // if IE and context element is an HTMLElement if (el.insertAdjacentHTML) { if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){ return rs; } - + // add these two to the hash. hash[afterbegin] = ['AfterBegin', 'firstChild']; hash[beforeend] = ['BeforeEnd', 'lastChild']; @@ -11341,7 +11869,7 @@ Ext.core.DomHelper = function(){ } else { // we cannot insert anything inside a textnode so... if (Ext.isTextNode(el)) { - where = where === 'afterbegin' ? 'beforebegin' : where; + where = where === 'afterbegin' ? 'beforebegin' : where; where = where === 'beforeend' ? 'afterend' : where; } range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined; @@ -11364,7 +11892,7 @@ Ext.core.DomHelper = function(){ } else { frag = createContextualFragment(html); } - + if(where == afterbegin){ el.insertBefore(frag, el.firstChild); }else{ @@ -11377,7 +11905,7 @@ Ext.core.DomHelper = function(){ } } Ext.Error.raise({ - sourceClass: 'Ext.core.DomHelper', + sourceClass: 'Ext.DomHelper', sourceMethod: 'insertHtml', htmlToInsert: html, targetElement: el, @@ -11387,10 +11915,10 @@ Ext.core.DomHelper = function(){ /** * Creates new DOM element(s) and inserts them before el. - * @param {Mixed} el The context element + * @param {String/HTMLElement/Ext.Element} el The context element * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element - * @return {HTMLElement/Ext.core.Element} The new node + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node */ insertBefore : function(el, o, returnElement){ return doInsert(el, o, returnElement, beforebegin); @@ -11398,10 +11926,10 @@ Ext.core.DomHelper = function(){ /** * Creates new DOM element(s) and inserts them after el. - * @param {Mixed} el The context element + * @param {String/HTMLElement/Ext.Element} el The context element * @param {Object} o The DOM object spec (and children) - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element - * @return {HTMLElement/Ext.core.Element} The new node + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node */ insertAfter : function(el, o, returnElement){ return doInsert(el, o, returnElement, afterend, 'nextSibling'); @@ -11409,10 +11937,10 @@ Ext.core.DomHelper = function(){ /** * Creates new DOM element(s) and inserts them as the first child of el. - * @param {Mixed} el The context element + * @param {String/HTMLElement/Ext.Element} el The context element * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element - * @return {HTMLElement/Ext.core.Element} The new node + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node */ insertFirst : function(el, o, returnElement){ return doInsert(el, o, returnElement, afterbegin, 'firstChild'); @@ -11420,10 +11948,10 @@ Ext.core.DomHelper = function(){ /** * Creates new DOM element(s) and appends them to el. - * @param {Mixed} el The context element + * @param {String/HTMLElement/Ext.Element} el The context element * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element - * @return {HTMLElement/Ext.core.Element} The new node + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node */ append : function(el, o, returnElement){ return doInsert(el, o, returnElement, beforeend, '', true); @@ -11431,10 +11959,10 @@ Ext.core.DomHelper = function(){ /** * Creates new DOM element(s) and overwrites the contents of el with them. - * @param {Mixed} el The context element + * @param {String/HTMLElement/Ext.Element} el The context element * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element - * @return {HTMLElement/Ext.core.Element} The new node + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node */ overwrite : function(el, o, returnElement){ el = Ext.getDom(el); @@ -11443,7 +11971,7 @@ Ext.core.DomHelper = function(){ }, createHtml : createHtml, - + /** * Creates new DOM element(s) without inserting them to the document. * @param {Object/String} o The DOM object spec (and children) or raw HTML blob @@ -11451,17 +11979,17 @@ Ext.core.DomHelper = function(){ * @method */ createDom: createDom, - + /** True to force the use of DOM instead of html fragments @type Boolean */ useDom : false, - + /** * Creates a new Ext.Template from the DOM object spec. * @param {Object} o The DOM object spec (and children) * @return {Ext.Template} The new template */ createTemplate : function(o){ - var html = Ext.core.DomHelper.createHtml(o); + var html = Ext.DomHelper.createHtml(o); return Ext.create('Ext.Template', html); } }; @@ -11547,6 +12075,7 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ tagTokenRe = /^(#)?([\w-\*]+)/, nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/, + startIdRe = /^\s*\#/, // This is for IE MSXML which does not support expandos. // IE runs the same speed using setAttribute, however FF slows way down // and Safari completely fails so they need to continue to use expandos. @@ -11996,8 +12525,8 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ * Use {@link #select} to take advantage of browsers built-in support for CSS selectors. * * @param {String} selector The selector/xpath query (can be a comma separated list of selectors) - * @param {Node/String} root (optional) The start of the query (defaults to document). - * @return {Array} An Array of DOM elements which match the selector. If there are + * @param {HTMLElement/String} root (optional) The start of the query (defaults to document). + * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are * no matches, and empty Array is returned. */ jsSelect: function(path, root, type){ @@ -12047,26 +12576,46 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ * Selects an array of DOM nodes by CSS/XPath selector. * * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to - * {@link #jsSelect} to do the work. - * + * {@link Ext.DomQuery#jsSelect} to do the work. + * * Aliased as {@link Ext#query}. - * + * * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll * * @param {String} path The selector/xpath query - * @param {Node} root (optional) The start of the query (defaults to document). - * @return {Array} An array of DOM elements (not a NodeList as returned by `querySelectorAll`). + * @param {HTMLElement} root (optional) The start of the query (defaults to document). + * @return {HTMLElement[]} An array of DOM elements (not a NodeList as returned by `querySelectorAll`). * Empty array when no matches. * @method */ select : document.querySelectorAll ? function(path, root, type) { root = root || document; - if (!Ext.DomQuery.isXml(root)) { - try { - var cs = root.querySelectorAll(path); - return Ext.Array.toArray(cs); - } - catch (ex) {} + /* + * Safari 3.x can't handle uppercase or unicode characters when in quirks mode. + */ + if (!Ext.DomQuery.isXml(root) && !(Ext.isSafari3 && !Ext.isStrict)) { + try { + /* + * This checking here is to "fix" the behaviour of querySelectorAll + * for non root document queries. The way qsa works is intentional, + * however it's definitely not the expected way it should work. + * More info: http://ejohn.org/blog/thoughts-on-queryselectorall/ + * + * We only modify the path for single selectors (ie, no multiples), + * without a full parser it makes it difficult to do this correctly. + */ + var isDocumentRoot = root.nodeType === 9, + _path = path, + _root = root; + + if (!isDocumentRoot && path.indexOf(',') === -1 && !startIdRe.test(path)) { + _path = '#' + Ext.id(root) + ' ' + path; + _root = root.parentNode; + } + return Ext.Array.toArray(_root.querySelectorAll(_path)); + } + catch (e) { + } } return Ext.DomQuery.jsSelect.call(this, path, root, type); } : function(path, root, type) { @@ -12076,8 +12625,8 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ /** * Selects a single element. * @param {String} selector The selector/xpath query - * @param {Node} root (optional) The start of the query (defaults to document). - * @return {Element} The DOM element which matched the selector. + * @param {HTMLElement} root (optional) The start of the query (defaults to document). + * @return {HTMLElement} The DOM element which matched the selector. */ selectNode : function(path, root){ return Ext.DomQuery.select(path, root)[0]; @@ -12086,8 +12635,8 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ /** * Selects the value of a node, optionally replacing null with the defaultValue. * @param {String} selector The selector/xpath query - * @param {Node} root (optional) The start of the query (defaults to document). - * @param {String} defaultValue + * @param {HTMLElement} root (optional) The start of the query (defaults to document). + * @param {String} defaultValue (optional) When specified, this is return as empty value. * @return {String} */ selectValue : function(path, root, defaultValue){ @@ -12111,8 +12660,8 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ /** * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified. * @param {String} selector The selector/xpath query - * @param {Node} root (optional) The start of the query (defaults to document). - * @param {Number} defaultValue + * @param {HTMLElement} root (optional) The start of the query (defaults to document). + * @param {Number} defaultValue (optional) When specified, this is return as empty value. * @return {Number} */ selectNumber : function(path, root, defaultValue){ @@ -12122,7 +12671,7 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ /** * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child) - * @param {String/HTMLElement/Array} el An element id, element or array of elements + * @param {String/HTMLElement/HTMLElement[]} el An element id, element or array of elements * @param {String} selector The simple selector to test * @return {Boolean} */ @@ -12137,11 +12686,11 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ /** * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child) - * @param {Array} el An array of elements to filter + * @param {HTMLElement[]} el An array of elements to filter * @param {String} selector The simple selector to test * @param {Boolean} nonMatches If true, it returns the elements that DON'T match * the selector instead of the ones that match - * @return {Array} An Array of DOM elements which match the selector. If there are + * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are * no matches, and empty Array is returned. */ filter : function(els, ss, nonMatches){ @@ -12208,12 +12757,12 @@ Ext.core.DomQuery = Ext.DomQuery = function(){ }, /** -Object hash of "pseudo class" filter functions which are used when filtering selections. +Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed two parameters: - **c** : Array An Array of DOM elements to filter. - + - **v** : String The argument (if any) supplied in the selector. @@ -12425,83 +12974,91 @@ Then external links could be gathered with the following statement: }(); /** - * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select} - * @param {String} path The selector/xpath query - * @param {Node} root (optional) The start of the query (defaults to document). - * @return {Array} + * Shorthand of {@link Ext.DomQuery#select} * @member Ext * @method query + * @alias Ext.DomQuery#select */ Ext.query = Ext.DomQuery.select; /** - * @class Ext.core.Element - *

    Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.

    - *

    All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.

    - *

    Note that the events documented in this class are not Ext events, they encapsulate browser events. To - * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older - * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.

    - * Usage:
    -
    
    -// by id
    -var el = Ext.get("my-div");
    -
    -// by DOM element reference
    -var el = Ext.get(myDivElement);
    -
    - * Animations
    - *

    When an element is manipulated, by default there is no animation.

    - *
    
    -var el = Ext.get("my-div");
    -
    -// no animation
    -el.setWidth(100);
    - * 
    - *

    Many of the functions for manipulating an element have an optional "animate" parameter. This - * parameter can be specified as boolean (true) for default animation effects.

    - *
    
    -// default animation
    -el.setWidth(100, true);
    - * 
    + * @class Ext.Element + * @alternateClassName Ext.core.Element * - *

    To configure the effects, an object literal with animation options to use as the Element animation - * configuration object can also be specified. Note that the supported Element animation configuration - * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects. The supported - * Element animation configuration options are:

    -
    -Option    Default   Description
    ---------- --------  ---------------------------------------------
    -{@link Ext.fx.Anim#duration duration}  .35       The duration of the animation in seconds
    -{@link Ext.fx.Anim#easing easing}    easeOut   The easing method
    -{@link Ext.fx.Anim#callback callback}  none      A function to execute when the anim completes
    -{@link Ext.fx.Anim#scope scope}     this      The scope (this) of the callback function
    -
    + * Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences. * - *
    
    -// Element animation options object
    -var opt = {
    -    {@link Ext.fx.Anim#duration duration}: 1,
    -    {@link Ext.fx.Anim#easing easing}: 'elasticIn',
    -    {@link Ext.fx.Anim#callback callback}: this.foo,
    -    {@link Ext.fx.Anim#scope scope}: this
    -};
    -// animation with some options set
    -el.setWidth(100, opt);
    - * 
    - *

    The Element animation object being used for the animation will be set on the options - * object as "anim", which allows you to stop or manipulate the animation. Here is an example:

    - *
    
    -// using the "anim" property to get the Anim object
    -if(opt.anim.isAnimated()){
    -    opt.anim.stop();
    -}
    - * 
    - *

    Also see the {@link #animate} method for another animation technique.

    - *

    Composite (Collections of) Elements

    - *

    For working with collections of Elements, see {@link Ext.CompositeElement}

    - * @constructor Create a new Element directly. + * All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all + * DOM elements. + * + * Note that the events documented in this class are not Ext events, they encapsulate browser events. Some older browsers + * may not support the full range of events. Which events are supported is beyond the control of Ext JS. + * + * Usage: + * + * // by id + * var el = Ext.get("my-div"); + * + * // by DOM element reference + * var el = Ext.get(myDivElement); + * + * # Animations + * + * When an element is manipulated, by default there is no animation. + * + * var el = Ext.get("my-div"); + * + * // no animation + * el.setWidth(100); + * + * Many of the functions for manipulating an element have an optional "animate" parameter. This parameter can be + * specified as boolean (true) for default animation effects. + * + * // default animation + * el.setWidth(100, true); + * + * To configure the effects, an object literal with animation options to use as the Element animation configuration + * object can also be specified. Note that the supported Element animation configuration options are a subset of the + * {@link Ext.fx.Anim} animation options specific to Fx effects. The supported Element animation configuration options + * are: + * + * Option Default Description + * --------- -------- --------------------------------------------- + * {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds + * {@link Ext.fx.Anim#easing easing} easeOut The easing method + * {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes + * {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function + * + * Usage: + * + * // Element animation options object + * var opt = { + * {@link Ext.fx.Anim#duration duration}: 1, + * {@link Ext.fx.Anim#easing easing}: 'elasticIn', + * {@link Ext.fx.Anim#callback callback}: this.foo, + * {@link Ext.fx.Anim#scope scope}: this + * }; + * // animation with some options set + * el.setWidth(100, opt); + * + * The Element animation object being used for the animation will be set on the options object as "anim", which allows + * you to stop or manipulate the animation. Here is an example: + * + * // using the "anim" property to get the Anim object + * if(opt.anim.isAnimated()){ + * opt.anim.stop(); + * } + * + * # Composite (Collections of) Elements + * + * For working with collections of Elements, see {@link Ext.CompositeElement} + * + * @constructor + * Creates new Element directly. * @param {String/HTMLElement} element - * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class). + * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this + * element in the cache and if there is it returns the same instance. This will skip that check (useful for extending + * this class). + * @return {Object} */ (function() { var DOC = document, @@ -12523,29 +13080,29 @@ if(opt.anim.isAnimated()){ } /** - * The DOM element - * @type HTMLElement - */ + * @property {HTMLElement} dom + * The DOM element + */ this.dom = dom; /** - * The DOM element ID - * @type String - */ + * @property {String} id + * The DOM element ID + */ this.id = id || Ext.id(dom); }; - var DH = Ext.core.DomHelper, - El = Ext.core.Element; + var DH = Ext.DomHelper, + El = Ext.Element; El.prototype = { /** - * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function) - * @param {Object} o The object with the attributes - * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos. - * @return {Ext.core.Element} this - */ + * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function) + * @param {Object} o The object with the attributes + * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos. + * @return {Ext.Element} this + */ set: function(o, useSet) { var el = this.dom, attr, @@ -12571,282 +13128,249 @@ if(opt.anim.isAnimated()){ // Mouse events /** - * @event click - * Fires when a mouse click is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event click + * Fires when a mouse click is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event contextmenu - * Fires when a right click is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event contextmenu + * Fires when a right click is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event dblclick - * Fires when a mouse double click is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event dblclick + * Fires when a mouse double click is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mousedown - * Fires when a mousedown is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mousedown + * Fires when a mousedown is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mouseup - * Fires when a mouseup is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mouseup + * Fires when a mouseup is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mouseover - * Fires when a mouseover is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mouseover + * Fires when a mouseover is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mousemove - * Fires when a mousemove is detected with the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mousemove + * Fires when a mousemove is detected with the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mouseout - * Fires when a mouseout is detected with the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mouseout + * Fires when a mouseout is detected with the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mouseenter - * Fires when the mouse enters the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mouseenter + * Fires when the mouse enters the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event mouseleave - * Fires when the mouse leaves the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event mouseleave + * Fires when the mouse leaves the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ // Keyboard events /** - * @event keypress - * Fires when a keypress is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event keypress + * Fires when a keypress is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event keydown - * Fires when a keydown is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event keydown + * Fires when a keydown is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event keyup - * Fires when a keyup is detected within the element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event keyup + * Fires when a keyup is detected within the element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ // HTML frame/object events /** - * @event load - * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event load + * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, + * objects and images. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event unload - * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event unload + * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target + * element or any of its content has been removed. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event abort - * Fires when an object/image is stopped from loading before completely loaded. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event abort + * Fires when an object/image is stopped from loading before completely loaded. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event error - * Fires when an object/image/frame cannot be loaded properly. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event error + * Fires when an object/image/frame cannot be loaded properly. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event resize - * Fires when a document view is resized. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event resize + * Fires when a document view is resized. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event scroll - * Fires when a document view is scrolled. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event scroll + * Fires when a document view is scrolled. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ // Form events /** - * @event select - * Fires when a user selects some text in a text field, including input and textarea. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event select + * Fires when a user selects some text in a text field, including input and textarea. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event change - * Fires when a control loses the input focus and its value has been modified since gaining focus. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event change + * Fires when a control loses the input focus and its value has been modified since gaining focus. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event submit - * Fires when a form is submitted. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event submit + * Fires when a form is submitted. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event reset - * Fires when a form is reset. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event reset + * Fires when a form is reset. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event focus - * Fires when an element receives focus either via the pointing device or by tab navigation. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event focus + * Fires when an element receives focus either via the pointing device or by tab navigation. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event blur - * Fires when an element loses focus either via the pointing device or by tabbing navigation. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event blur + * Fires when an element loses focus either via the pointing device or by tabbing navigation. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ // User Interface events /** - * @event DOMFocusIn - * Where supported. Similar to HTML focus event, but can be applied to any focusable element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMFocusIn + * Where supported. Similar to HTML focus event, but can be applied to any focusable element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMFocusOut - * Where supported. Similar to HTML blur event, but can be applied to any focusable element. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMFocusOut + * Where supported. Similar to HTML blur event, but can be applied to any focusable element. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMActivate - * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMActivate + * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ // DOM Mutation events /** - * @event DOMSubtreeModified - * Where supported. Fires when the subtree is modified. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMSubtreeModified + * Where supported. Fires when the subtree is modified. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMNodeInserted - * Where supported. Fires when a node has been added as a child of another node. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMNodeInserted + * Where supported. Fires when a node has been added as a child of another node. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMNodeRemoved - * Where supported. Fires when a descendant node of the element is removed. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMNodeRemoved + * Where supported. Fires when a descendant node of the element is removed. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMNodeRemovedFromDocument - * Where supported. Fires when a node is being removed from a document. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMNodeRemovedFromDocument + * Where supported. Fires when a node is being removed from a document. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMNodeInsertedIntoDocument - * Where supported. Fires when a node is being inserted into a document. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMNodeInsertedIntoDocument + * Where supported. Fires when a node is being inserted into a document. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMAttrModified - * Where supported. Fires when an attribute has been modified. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMAttrModified + * Where supported. Fires when an attribute has been modified. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * @event DOMCharacterDataModified - * Where supported. Fires when the character data has been modified. - * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. - * @param {HtmlElement} t The target of the event. - * @param {Object} o The options configuration passed to the {@link #addListener} call. - */ + * @event DOMCharacterDataModified + * Where supported. Fires when the character data has been modified. + * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event. + * @param {HTMLElement} t The target of the event. + */ /** - * The default unit to append to CSS values where a unit isn't provided (defaults to px). - * @type String - */ + * @property {String} defaultUnit + * The default unit to append to CSS values where a unit isn't provided. + */ defaultUnit: "px", /** - * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child) - * @param {String} selector The simple selector to test - * @return {Boolean} True if this element matches the selector, else false - */ + * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child) + * @param {String} selector The simple selector to test + * @return {Boolean} True if this element matches the selector, else false + */ is: function(simpleSelector) { return Ext.DomQuery.is(this.dom, simpleSelector); }, /** - * Tries to focus the element. Any exceptions are caught and ignored. - * @param {Number} defer (optional) Milliseconds to defer the focus - * @return {Ext.core.Element} this - */ + * Tries to focus the element. Any exceptions are caught and ignored. + * @param {Number} defer (optional) Milliseconds to defer the focus + * @return {Ext.Element} this + */ focus: function(defer, /* private */ dom) { @@ -12863,9 +13387,9 @@ if(opt.anim.isAnimated()){ }, /** - * Tries to blur the element. Any exceptions are caught and ignored. - * @return {Ext.core.Element} this - */ + * Tries to blur the element. Any exceptions are caught and ignored. + * @return {Ext.Element} this + */ blur: function() { try { this.dom.blur(); @@ -12874,150 +13398,200 @@ if(opt.anim.isAnimated()){ }, /** - * Returns the value of the "value" attribute - * @param {Boolean} asNumber true to parse the value as a number - * @return {String/Number} - */ + * Returns the value of the "value" attribute + * @param {Boolean} asNumber true to parse the value as a number + * @return {String/Number} + */ getValue: function(asNumber) { var val = this.dom.value; return asNumber ? parseInt(val, 10) : val; }, /** - * Appends an event handler to this element. The shorthand version {@link #on} is equivalent. - * @param {String} eventName The name of event to handle. - * @param {Function} fn The handler function the event invokes. This function is passed - * the following parameters:
      - *
    • evt : EventObject
      The {@link Ext.EventObject EventObject} describing the event.
    • - *
    • el : HtmlElement
      The DOM element which was the target of the event. - * Note that this may be filtered by using the delegate option.
    • - *
    • o : Object
      The options object from the addListener call.
    • - *
    - * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. - * If omitted, defaults to this Element.. - * @param {Object} options (optional) An object containing handler configuration properties. - * This may contain any of the following properties:
      - *
    • scope Object :
      The scope (this reference) in which the handler function is executed. - * If omitted, defaults to this Element.
    • - *
    • delegate String:
      A simple selector to filter the target or look for a descendant of the target. See below for additional details.
    • - *
    • stopEvent Boolean:
      True to stop the event. That is stop propagation, and prevent the default action.
    • - *
    • preventDefault Boolean:
      True to prevent the default action
    • - *
    • stopPropagation Boolean:
      True to prevent event propagation
    • - *
    • normalized Boolean:
      False to pass a browser event to the handler function instead of an Ext.EventObject
    • - *
    • target Ext.core.Element:
      Only call the handler if the event was fired on the target Element, not if the event was bubbled up from a child node.
    • - *
    • delay Number:
      The number of milliseconds to delay the invocation of the handler after the event fires.
    • - *
    • single Boolean:
      True to add a handler to handle just the next firing of the event, and then remove itself.
    • - *
    • buffer Number:
      Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed - * by the specified number of milliseconds. If the event fires again within that time, the original - * handler is not invoked, but the new handler is scheduled in its place.
    • - *

    - *

    - * Combining Options
    - * In the following examples, the shorthand form {@link #on} is used rather than the more verbose - * addListener. The two are equivalent. Using the options argument, it is possible to combine different - * types of listeners:
    - *
    - * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the - * options object. The options object is available as the third parameter in the handler function.

    - * Code:
    
    -el.on('click', this.onClick, this, {
    -    single: true,
    -    delay: 100,
    -    stopEvent : true,
    -    forumId: 4
    -});

    - *

    - * Attaching multiple handlers in 1 call
    - * The method also allows for a single argument to be passed which is a config object containing properties - * which specify multiple handlers.

    - *

    - * Code:

    
    -el.on({
    -    'click' : {
    -        fn: this.onClick,
    -        scope: this,
    -        delay: 100
    -    },
    -    'mouseover' : {
    -        fn: this.onMouseOver,
    -        scope: this
    -    },
    -    'mouseout' : {
    -        fn: this.onMouseOut,
    -        scope: this
    -    }
    -});
    - *

    - * Or a shorthand syntax:
    - * Code:

    -el.on({ - 'click' : this.onClick, - 'mouseover' : this.onMouseOver, - 'mouseout' : this.onMouseOut, - scope: this -}); - *

    - *

    delegate

    - *

    This is a configuration option that you can pass along when registering a handler for - * an event to assist with event delegation. Event delegation is a technique that is used to - * reduce memory consumption and prevent exposure to memory-leaks. By registering an event - * for a container element as opposed to each element within a container. By setting this - * configuration option to a simple selector, the target element will be filtered to look for - * a descendant of the target. - * For example:

    
    -// using this markup:
    -<div id='elId'>
    -    <p id='p1'>paragraph one</p>
    -    <p id='p2' class='clickable'>paragraph two</p>
    -    <p id='p3'>paragraph three</p>
    -</div>
    -// utilize event delegation to registering just one handler on the container element:
    -el = Ext.get('elId');
    -el.on(
    -    'click',
    -    function(e,t) {
    -        // handle click
    -        console.info(t.id); // 'p2'
    -    },
    -    this,
    -    {
    -        // filter the target element to be a descendant with the class 'clickable'
    -        delegate: '.clickable'
    -    }
    -);
    -     * 

    - * @return {Ext.core.Element} this - */ + * Appends an event handler to this element. + * + * @param {String} eventName The name of event to handle. + * + * @param {Function} fn The handler function the event invokes. This function is passed the following parameters: + * + * - **evt** : EventObject + * + * The {@link Ext.EventObject EventObject} describing the event. + * + * - **el** : HtmlElement + * + * The DOM element which was the target of the event. Note that this may be filtered by using the delegate option. + * + * - **o** : Object + * + * The options object from the addListener call. + * + * @param {Object} scope (optional) The scope (**this** reference) in which the handler function is executed. **If + * omitted, defaults to this Element.** + * + * @param {Object} options (optional) An object containing handler configuration properties. This may contain any of + * the following properties: + * + * - **scope** Object : + * + * The scope (**this** reference) in which the handler function is executed. **If omitted, defaults to this + * Element.** + * + * - **delegate** String: + * + * A simple selector to filter the target or look for a descendant of the target. See below for additional details. + * + * - **stopEvent** Boolean: + * + * True to stop the event. That is stop propagation, and prevent the default action. + * + * - **preventDefault** Boolean: + * + * True to prevent the default action + * + * - **stopPropagation** Boolean: + * + * True to prevent event propagation + * + * - **normalized** Boolean: + * + * False to pass a browser event to the handler function instead of an Ext.EventObject + * + * - **target** Ext.Element: + * + * Only call the handler if the event was fired on the target Element, _not_ if the event was bubbled up from a + * child node. + * + * - **delay** Number: + * + * The number of milliseconds to delay the invocation of the handler after the event fires. + * + * - **single** Boolean: + * + * True to add a handler to handle just the next firing of the event, and then remove itself. + * + * - **buffer** Number: + * + * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of + * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new + * handler is scheduled in its place. + * + * **Combining Options** + * + * In the following examples, the shorthand form {@link #on} is used rather than the more verbose addListener. The + * two are equivalent. Using the options argument, it is possible to combine different types of listeners: + * + * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the options + * object. The options object is available as the third parameter in the handler function. + * + * Code: + * + * el.on('click', this.onClick, this, { + * single: true, + * delay: 100, + * stopEvent : true, + * forumId: 4 + * }); + * + * **Attaching multiple handlers in 1 call** + * + * The method also allows for a single argument to be passed which is a config object containing properties which + * specify multiple handlers. + * + * Code: + * + * el.on({ + * 'click' : { + * fn: this.onClick, + * scope: this, + * delay: 100 + * }, + * 'mouseover' : { + * fn: this.onMouseOver, + * scope: this + * }, + * 'mouseout' : { + * fn: this.onMouseOut, + * scope: this + * } + * }); + * + * Or a shorthand syntax: + * + * Code: + * + * el.on({ + * 'click' : this.onClick, + * 'mouseover' : this.onMouseOver, + * 'mouseout' : this.onMouseOut, + * scope: this + * }); + * + * **delegate** + * + * This is a configuration option that you can pass along when registering a handler for an event to assist with + * event delegation. Event delegation is a technique that is used to reduce memory consumption and prevent exposure + * to memory-leaks. By registering an event for a container element as opposed to each element within a container. + * By setting this configuration option to a simple selector, the target element will be filtered to look for a + * descendant of the target. For example: + * + * // using this markup: + *
    + *

    paragraph one

    + *

    paragraph two

    + *

    paragraph three

    + *
    + * + * // utilize event delegation to registering just one handler on the container element: + * el = Ext.get('elId'); + * el.on( + * 'click', + * function(e,t) { + * // handle click + * console.info(t.id); // 'p2' + * }, + * this, + * { + * // filter the target element to be a descendant with the class 'clickable' + * delegate: '.clickable' + * } + * ); + * + * @return {Ext.Element} this + */ addListener: function(eventName, fn, scope, options) { Ext.EventManager.on(this.dom, eventName, fn, scope || this, options); return this; }, /** - * Removes an event handler from this element. The shorthand version {@link #un} is equivalent. - * Note: if a scope was explicitly specified when {@link #addListener adding} the - * listener, the same scope must be specified here. - * Example: - *
    
    -el.removeListener('click', this.handlerFn);
    -// or
    -el.un('click', this.handlerFn);
    -
    - * @param {String} eventName The name of the event from which to remove the handler. - * @param {Function} fn The handler function to remove. This must be a reference to the function passed into the {@link #addListener} call. - * @param {Object} scope If a scope (this reference) was specified when the listener was added, - * then this must refer to the same object. - * @return {Ext.core.Element} this - */ + * Removes an event handler from this element. + * + * **Note**: if a *scope* was explicitly specified when {@link #addListener adding} the listener, + * the same scope must be specified here. + * + * Example: + * + * el.removeListener('click', this.handlerFn); + * // or + * el.un('click', this.handlerFn); + * + * @param {String} eventName The name of the event from which to remove the handler. + * @param {Function} fn The handler function to remove. **This must be a reference to the function passed into the + * {@link #addListener} call.** + * @param {Object} scope If a scope (**this** reference) was specified when the listener was added, then this must + * refer to the same object. + * @return {Ext.Element} this + */ removeListener: function(eventName, fn, scope) { Ext.EventManager.un(this.dom, eventName, fn, scope || this); return this; }, /** - * Removes all previous added listeners from this element - * @return {Ext.core.Element} this - */ + * Removes all previous added listeners from this element + * @return {Ext.Element} this + */ removeAllListeners: function() { Ext.EventManager.removeAll(this.dom); return this; @@ -13025,7 +13599,7 @@ el.un('click', this.handlerFn); /** * Recursively removes all previous added listeners from this element and its children - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ purgeAllListeners: function() { Ext.EventManager.purgeElement(this); @@ -13033,9 +13607,10 @@ el.un('click', this.handlerFn); }, /** - * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element. + * Test if size has a unit, otherwise appends the passed unit string, or the default for this Element. * @param size {Mixed} The size to set * @param units {String} The units to append to a numeric size value + * @private */ addUnits: function(size, units) { @@ -13045,7 +13620,7 @@ el.un('click', this.handlerFn); } // Size set to a value which means "auto" - if (size === "" || size == "auto" || size === undefined || size === null) { + if (size === "" || size == "auto" || size == null) { return size || ''; } @@ -13068,7 +13643,8 @@ el.un('click', this.handlerFn); }, /** - *

    Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}

    + * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode + * Ext.removeNode} */ remove: function() { var me = this, @@ -13084,9 +13660,11 @@ el.un('click', this.handlerFn); * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element. * @param {Function} overFn The function to call when the mouse enters the Element. * @param {Function} outFn The function to call when the mouse leaves the Element. - * @param {Object} scope (optional) The scope (this reference) in which the functions are executed. Defaults to the Element's DOM element. - * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the options parameter}. - * @return {Ext.core.Element} this + * @param {Object} scope (optional) The scope (`this` reference) in which the functions are executed. Defaults + * to the Element's DOM element. + * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the + * options parameter}. + * @return {Ext.Element} this */ hover: function(overFn, outFn, scope, options) { var me = this; @@ -13101,7 +13679,7 @@ el.un('click', this.handlerFn); * @return {Boolean} True if this element is an ancestor of el, else false */ contains: function(el) { - return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el); + return ! el ? false: Ext.Element.isAncestor(this.dom, el.dom ? el.dom: el); }, /** @@ -13109,7 +13687,6 @@ el.un('click', this.handlerFn); * @param {String} namespace The namespace in which to look for the attribute * @param {String} name The attribute name * @return {String} The attribute value - * @deprecated */ getAttributeNS: function(ns, name) { return this.getAttribute(name, ns); @@ -13148,7 +13725,7 @@ el.un('click', this.handlerFn); /** * Update the innerHTML of this element * @param {String} html The new HTML - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ update: function(html) { if (this.dom) { @@ -13165,45 +13742,36 @@ el.un('click', this.handlerFn); }; /** - * Appends an event handler (shorthand for {@link #addListener}). - * @param {String} eventName The name of event to handle. - * @param {Function} fn The handler function the event invokes. - * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. - * @param {Object} options (optional) An object containing standard {@link #addListener} options - * @member Ext.core.Element - * @method on + * @method + * @alias Ext.Element#addListener + * Shorthand for {@link #addListener}. */ ep.on = ep.addListener; /** - * Removes an event handler from this element (see {@link #removeListener} for additional notes). - * @param {String} eventName The name of the event from which to remove the handler. - * @param {Function} fn The handler function to remove. This must be a reference to the function passed into the {@link #addListener} call. - * @param {Object} scope If a scope (this reference) was specified when the listener was added, - * then this must refer to the same object. - * @return {Ext.core.Element} this - * @member Ext.core.Element - * @method un + * @method + * @alias Ext.Element#removeListener + * Shorthand for {@link #removeListener}. */ ep.un = ep.removeListener; /** - * Removes all previous added listeners from this element - * @return {Ext.core.Element} this - * @member Ext.core.Element - * @method clearListeners + * @method + * @alias Ext.Element#removeAllListeners + * Alias for {@link #removeAllListeners}. */ ep.clearListeners = ep.removeAllListeners; /** - * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}. - * Alias to {@link #remove}. - * @member Ext.core.Element * @method destroy + * @member Ext.Element + * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode + * Ext.removeNode}. Alias to {@link #remove}. */ ep.destroy = ep.remove; /** + * @property {Boolean} autoBoxAdjust * true to automatically adjust width and height settings for box-model issues (default to true) */ ep.autoBoxAdjust = true; @@ -13213,17 +13781,17 @@ el.un('click', this.handlerFn); docEl; /** - * Retrieves Ext.core.Element objects. - *

    This method does not retrieve {@link Ext.Component Component}s. This method - * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by - * its ID, use {@link Ext.ComponentManager#get}.

    - *

    Uses simple caching to consistently return the same object. Automatically fixes if an - * object was recreated with the same id via AJAX or DOM.

    - * @param {Mixed} el The id of the node, a DOM Node or an existing Element. - * @return {Element} The Element object (or null if no matching element was found) + * Retrieves Ext.Element objects. {@link Ext#get} is an alias for {@link Ext.Element#get}. + * + * **This method does not retrieve {@link Ext.Component Component}s.** This method retrieves Ext.Element + * objects which encapsulate DOM elements. To retrieve a Component by its ID, use {@link Ext.ComponentManager#get}. + * + * Uses simple caching to consistently return the same object. Automatically fixes if an object was recreated with + * the same id via AJAX or DOM. + * + * @param {String/HTMLElement/Ext.Element} el The id of the node, a DOM Node or an existing Element. + * @return {Ext.Element} The Element object (or null if no matching element was found) * @static - * @member Ext.core.Element - * @method get */ El.get = function(el) { var ex, @@ -13285,6 +13853,46 @@ el.un('click', this.handlerFn); return null; }; + /** + * Retrieves Ext.Element objects like {@link Ext#get} but is optimized for sub-elements. + * This is helpful for performance, because in IE (prior to IE 9), `getElementById` uses + * an non-optimized search. In those browsers, starting the search for an element with a + * matching ID at a parent of that element will greatly speed up the process. + * + * Unlike {@link Ext#get}, this method only accepts ID's. If the ID is not a child of + * this element, it will still be found if it exists in the document, but will be slower + * than calling {@link Ext#get} directly. + * + * @param {String} id The id of the element to get. + * @return {Ext.Element} The Element object (or null if no matching element was found) + * @member Ext.Element + * @method getById + * @markdown + */ + ep.getById = (!Ext.isIE6 && !Ext.isIE7 && !Ext.isIE8) ? El.get : + function (id) { + var dom = this.dom, + cached, el, ret; + + if (dom) { + el = dom.all[id]; + if (el) { + // calling El.get here is a real hit (2x slower) because it has to + // redetermine that we are giving it a dom el. + cached = EC[id]; + if (cached && cached.el) { + ret = cached.el; + ret.dom = el; + } else { + ret = El.addToCache(new El(el)); + } + return ret; + } + } + + return El.get(id); + }; + El.addToCache = function(el, id) { if (el) { id = id || el.id; @@ -13385,17 +13993,20 @@ el.un('click', this.handlerFn); El._flyweights = {}; /** - *

    Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element - - * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}

    - *

    Use this to make one-time references to DOM elements which are not going to be accessed again either by - * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get} - * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.

    + * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference + * to this element - the dom node can be overwritten by other code. {@link Ext#fly} is alias for + * {@link Ext.Element#fly}. + * + * Use this to make one-time references to DOM elements which are not going to be accessed again either by + * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link + * Ext#get Ext.get} will be more appropriate to take advantage of the caching provided by the Ext.Element + * class. + * * @param {String/HTMLElement} el The dom node or id - * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts - * (e.g. internally Ext uses "_global") - * @return {Element} The shared Element object (or null if no matching element was found) - * @member Ext.core.Element - * @method fly + * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts (e.g. + * internally Ext uses "_global") + * @return {Ext.Element} The shared Element object (or null if no matching element was found) + * @static */ El.fly = function(el, named) { var ret = null; @@ -13409,32 +14020,16 @@ el.un('click', this.handlerFn); }; /** - * Retrieves Ext.core.Element objects. - *

    This method does not retrieve {@link Ext.Component Component}s. This method - * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by - * its ID, use {@link Ext.ComponentManager#get}.

    - *

    Uses simple caching to consistently return the same object. Automatically fixes if an - * object was recreated with the same id via AJAX or DOM.

    - * Shorthand of {@link Ext.core.Element#get} - * @param {Mixed} el The id of the node, a DOM Node or an existing Element. - * @return {Element} The Element object (or null if no matching element was found) * @member Ext * @method get + * @alias Ext.Element#get */ Ext.get = El.get; /** - *

    Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element - - * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}

    - *

    Use this to make one-time references to DOM elements which are not going to be accessed again either by - * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get} - * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.

    - * @param {String/HTMLElement} el The dom node or id - * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts - * (e.g. internally Ext uses "_global") - * @return {Element} The shared Element object (or null if no matching element was found) * @member Ext * @method fly + * @alias Ext.Element#fly */ Ext.fly = El.fly; @@ -13452,14 +14047,15 @@ el.un('click', this.handlerFn); })(); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.core.Element.addMethods({ +Ext.Element.addMethods({ /** * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child) * @param {String} selector The simple selector to test - * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body) - * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node + * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional) + * The max depth to search as a number or element (defaults to 50 || document.body) + * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node * @return {HTMLElement} The matching DOM node (or null if no match was found) */ findParent : function(simpleSelector, maxDepth, returnEl) { @@ -13482,13 +14078,13 @@ Ext.core.Element.addMethods({ } return null; }, - + /** * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child) * @param {String} selector The simple selector to test - * @param {Number/Mixed} maxDepth (optional) The max depth to - search as a number or element (defaults to 10 || document.body) - * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node + * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional) + * The max depth to search as a number or element (defaults to 10 || document.body) + * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node * @return {HTMLElement} The matching DOM node (or null if no match was found) */ findParentNode : function(simpleSelector, maxDepth, returnEl) { @@ -13498,11 +14094,11 @@ Ext.core.Element.addMethods({ /** * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child). - * This is a shortcut for findParentNode() that always returns an Ext.core.Element. + * This is a shortcut for findParentNode() that always returns an Ext.Element. * @param {String} selector The simple selector to test - * @param {Number/Mixed} maxDepth (optional) The max depth to - search as a number or element (defaults to 10 || document.body) - * @return {Ext.core.Element} The matching DOM node (or null if no match was found) + * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional) + * The max depth to search as a number or element (defaults to 10 || document.body) + * @return {Ext.Element} The matching DOM node (or null if no match was found) */ up : function(simpleSelector, maxDepth) { return this.findParentNode(simpleSelector, maxDepth, true); @@ -13511,16 +14107,16 @@ Ext.core.Element.addMethods({ /** * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id). * @param {String} selector The CSS selector - * @return {CompositeElement/CompositeElement} The composite element + * @return {Ext.CompositeElement/Ext.CompositeElement} The composite element */ select : function(selector) { - return Ext.core.Element.select(selector, false, this.dom); + return Ext.Element.select(selector, false, this.dom); }, /** * Selects child nodes based on the passed CSS selector (the selector should not contain an id). * @param {String} selector The CSS selector - * @return {Array} An array of the matched nodes + * @return {HTMLElement[]} An array of the matched nodes */ query : function(selector) { return Ext.DomQuery.select(selector, this.dom); @@ -13529,8 +14125,8 @@ Ext.core.Element.addMethods({ /** * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id). * @param {String} selector The CSS selector - * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false) - * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true) + * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false) + * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true) */ down : function(selector, returnDom) { var n = Ext.DomQuery.selectNode(selector, this.dom); @@ -13540,8 +14136,8 @@ Ext.core.Element.addMethods({ /** * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id). * @param {String} selector The CSS selector - * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false) - * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true) + * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false) + * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true) */ child : function(selector, returnDom) { var node, @@ -13557,8 +14153,8 @@ Ext.core.Element.addMethods({ /** * Gets the parent node for this element, optionally chaining up trying to match a selector * @param {String} selector (optional) Find a parent node that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element - * @return {Ext.core.Element/HTMLElement} The parent node or null + * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element + * @return {Ext.Element/HTMLElement} The parent node or null */ parent : function(selector, returnDom) { return this.matchNode('parentNode', 'parentNode', selector, returnDom); @@ -13567,8 +14163,8 @@ Ext.core.Element.addMethods({ /** * Gets the next sibling, skipping text nodes * @param {String} selector (optional) Find the next sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element - * @return {Ext.core.Element/HTMLElement} The next sibling or null + * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element + * @return {Ext.Element/HTMLElement} The next sibling or null */ next : function(selector, returnDom) { return this.matchNode('nextSibling', 'nextSibling', selector, returnDom); @@ -13577,8 +14173,8 @@ Ext.core.Element.addMethods({ /** * Gets the previous sibling, skipping text nodes * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element - * @return {Ext.core.Element/HTMLElement} The previous sibling or null + * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element + * @return {Ext.Element/HTMLElement} The previous sibling or null */ prev : function(selector, returnDom) { return this.matchNode('previousSibling', 'previousSibling', selector, returnDom); @@ -13588,8 +14184,8 @@ Ext.core.Element.addMethods({ /** * Gets the first child, skipping text nodes * @param {String} selector (optional) Find the next sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element - * @return {Ext.core.Element/HTMLElement} The first child or null + * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element + * @return {Ext.Element/HTMLElement} The first child or null */ first : function(selector, returnDom) { return this.matchNode('nextSibling', 'firstChild', selector, returnDom); @@ -13598,8 +14194,8 @@ Ext.core.Element.addMethods({ /** * Gets the last child, skipping text nodes * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element - * @return {Ext.core.Element/HTMLElement} The last child or null + * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element + * @return {Ext.Element/HTMLElement} The last child or null */ last : function(selector, returnDom) { return this.matchNode('previousSibling', 'lastChild', selector, returnDom); @@ -13609,7 +14205,7 @@ Ext.core.Element.addMethods({ if (!this.dom) { return null; } - + var n = this.dom[start]; while (n) { if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) { @@ -13622,13 +14218,14 @@ Ext.core.Element.addMethods({ }); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.core.Element.addMethods({ +Ext.Element.addMethods({ /** * Appends the passed element(s) to this element - * @param {String/HTMLElement/Array/Element/CompositeElement} el - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element} el + * The id of the node, a DOM Node or an existing Element. + * @return {Ext.Element} this */ appendChild : function(el) { return Ext.get(el).appendTo(this); @@ -13636,8 +14233,9 @@ Ext.core.Element.addMethods({ /** * Appends this element to the passed element - * @param {Mixed} el The new parent element - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element} el The new parent element. + * The id of the node, a DOM Node or an existing Element. + * @return {Ext.Element} this */ appendTo : function(el) { Ext.getDom(el).appendChild(this.dom); @@ -13646,8 +14244,9 @@ Ext.core.Element.addMethods({ /** * Inserts this element before the passed element in the DOM - * @param {Mixed} el The element before which this element will be inserted - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element} el The element before which this element will be inserted. + * The id of the node, a DOM Node or an existing Element. + * @return {Ext.Element} this */ insertBefore : function(el) { el = Ext.getDom(el); @@ -13657,8 +14256,9 @@ Ext.core.Element.addMethods({ /** * Inserts this element after the passed element in the DOM - * @param {Mixed} el The element to insert after - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element} el The element to insert after. + * The id of the node, a DOM Node or an existing Element. + * @return {Ext.Element} this */ insertAfter : function(el) { el = Ext.getDom(el); @@ -13668,8 +14268,9 @@ Ext.core.Element.addMethods({ /** * Inserts (or creates) an element (or DomHelper config) as the first child of this element - * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert - * @return {Ext.core.Element} The new child + * @param {String/HTMLElement/Ext.Element/Object} el The id or element to insert or a DomHelper config + * to create and insert + * @return {Ext.Element} The new child */ insertFirst : function(el, returnDom) { el = el || {}; @@ -13685,10 +14286,11 @@ Ext.core.Element.addMethods({ /** * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element - * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those. + * @param {String/HTMLElement/Ext.Element/Object/Array} el The id, element to insert or a DomHelper config + * to create and insert *or* an array of any of those. * @param {String} where (optional) 'before' or 'after' defaults to before - * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element - * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned. + * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.Element + * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned. */ insertSibling: function(el, where, returnDom){ var me = this, rt, @@ -13715,9 +14317,9 @@ Ext.core.Element.addMethods({ } }else{ if (isAfter && !me.dom.nextSibling) { - rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom); + rt = Ext.DomHelper.append(me.dom.parentNode, el, !returnDom); } else { - rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom); + rt = Ext.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom); } } return rt; @@ -13725,8 +14327,9 @@ Ext.core.Element.addMethods({ /** * Replaces the passed element with this element - * @param {Mixed} el The element to replace - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element} el The element to replace. + * The id of the node, a DOM Node or an existing Element. + * @return {Ext.Element} this */ replace : function(el) { el = Ext.get(el); @@ -13737,8 +14340,9 @@ Ext.core.Element.addMethods({ /** * Replaces this element with the passed element - * @param {Mixed/Object} el The new element or a DomHelper config of an element to create - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element/Object} el The new element (id of the node, a DOM Node + * or an existing Element) or a DomHelper config of an element to create + * @return {Ext.Element} this */ replaceWith: function(el){ var me = this; @@ -13747,13 +14351,13 @@ Ext.core.Element.addMethods({ el = Ext.get(el); me.dom.parentNode.insertBefore(el, me.dom); }else{ - el = Ext.core.DomHelper.insertBefore(me.dom, el); + el = Ext.DomHelper.insertBefore(me.dom, el); } delete Ext.cache[me.id]; Ext.removeNode(me.dom); me.id = Ext.id(me.dom = el); - Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me); + Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me); return me; }, @@ -13763,26 +14367,26 @@ Ext.core.Element.addMethods({ * automatically generated with the specified attributes. * @param {HTMLElement} insertBefore (optional) a child element of this element * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element - * @return {Ext.core.Element} The new child element + * @return {Ext.Element} The new child element */ createChild : function(config, insertBefore, returnDom) { config = config || {tag:'div'}; if (insertBefore) { - return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true); + return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true); } else { - return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true); + return Ext.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true); } }, /** * Creates and wraps this element with another element * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div - * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element - * @return {HTMLElement/Element} The newly created wrapper element + * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element + * @return {HTMLElement/Ext.Element} The newly created wrapper element */ wrap : function(config, returnDom) { - var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom), + var newEl = Ext.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom), d = newEl.dom || newEl; d.appendChild(this.dom); @@ -13792,23 +14396,24 @@ Ext.core.Element.addMethods({ /** * Inserts an html fragment into this element * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd. + * See {@link Ext.DomHelper#insertHtml} for details. * @param {String} html The HTML fragment - * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false) - * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted) + * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false) + * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted) */ insertHtml : function(where, html, returnEl) { - var el = Ext.core.DomHelper.insertHtml(where, this.dom, html); + var el = Ext.DomHelper.insertHtml(where, this.dom, html); return returnEl ? Ext.get(el) : el; } }); /** - * @class Ext.core.Element + * @class Ext.Element */ (function(){ - Ext.core.Element.boxMarkup = '
    '; // local style camelizing for speed - var supports = Ext.supports, + var ELEMENT = Ext.Element, + supports = Ext.supports, view = document.defaultView, opacityRe = /alpha\(opacity=(.*)\)/i, trimRe = /^\s+|\s+$/g, @@ -13835,10 +14440,20 @@ Ext.core.Element.addMethods({ borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH}, paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM}, margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM}, - data = Ext.core.Element.data; + data = ELEMENT.data; + + ELEMENT.boxMarkup = '
    '; + + // These property values are read from the parentNode if they cannot be read + // from the child: + ELEMENT.inheritedProps = { + fontSize: 1, + fontStyle: 1, + opacity: 1 + }; + + Ext.override(ELEMENT, { - Ext.override(Ext.core.Element, { - /** * TODO: Look at this */ @@ -13846,7 +14461,7 @@ Ext.core.Element.addMethods({ adjustWidth : function(width) { var me = this, isNum = (typeof width == 'number'); - + if(isNum && me.autoBoxAdjust && !me.isBorderBox()){ width -= (me.getBorderWidth("lr") + me.getPadding("lr")); } @@ -13857,7 +14472,7 @@ Ext.core.Element.addMethods({ adjustHeight : function(height) { var me = this, isNum = (typeof height == "number"); - + if(isNum && me.autoBoxAdjust && !me.isBorderBox()){ height -= (me.getBorderWidth("tb") + me.getPadding("tb")); } @@ -13867,8 +14482,8 @@ Ext.core.Element.addMethods({ /** * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out. - * @param {String/Array} className The CSS classes to add separated by space, or an array of classes - * @return {Ext.core.Element} this + * @param {String/String[]} className The CSS classes to add separated by space, or an array of classes + * @return {Ext.Element} this */ addCls : function(className){ var me = this, @@ -13907,8 +14522,8 @@ Ext.core.Element.addMethods({ /** * Removes one or more CSS classes from the element. - * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes - * @return {Ext.core.Element} this + * @param {String/String[]} className The CSS classes to remove separated by space, or an array of classes + * @return {Ext.Element} this */ removeCls : function(className){ var me = this, @@ -13938,8 +14553,8 @@ Ext.core.Element.addMethods({ /** * Adds one or more CSS classes to this element and removes the same class(es) from all siblings. - * @param {String/Array} className The CSS class to add, or an array of classes - * @return {Ext.core.Element} this + * @param {String/String[]} className The CSS class to add, or an array of classes + * @return {Ext.Element} this */ radioCls : function(className){ var cn = this.dom.parentNode.childNodes, @@ -13957,7 +14572,7 @@ Ext.core.Element.addMethods({ /** * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it). * @param {String} className The CSS class to toggle - * @return {Ext.core.Element} this + * @return {Ext.Element} this * @method */ toggleCls : Ext.supports.ClassList ? @@ -13998,7 +14613,7 @@ Ext.core.Element.addMethods({ * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added. * @param {String} oldClassName The CSS class to replace * @param {String} newClassName The replacement CSS class - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ replaceCls : function(oldClassName, newClassName){ return this.removeCls(oldClassName).addCls(newClassName); @@ -14014,7 +14629,7 @@ Ext.core.Element.addMethods({ * @return {String} The current value of the style property for this element. * @method */ - getStyle : function(){ + getStyle : function() { return view && view.getComputedStyle ? function(prop){ var el = this.dom, @@ -14023,49 +14638,78 @@ Ext.core.Element.addMethods({ if(el == document){ return null; } - prop = Ext.core.Element.normalize(prop); + prop = ELEMENT.normalize(prop); out = (v = el.style[prop]) ? v : (cs = view.getComputedStyle(el, "")) ? cs[prop] : null; - + // Ignore cases when the margin is correctly reported as 0, the bug only shows // numbers larger. if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){ - cleaner = Ext.core.Element.getRightMarginFixCleaner(el); + cleaner = ELEMENT.getRightMarginFixCleaner(el); display = this.getStyle('display'); el.style.display = 'inline-block'; out = view.getComputedStyle(el, '').marginRight; el.style.display = display; cleaner(); } - + if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){ out = 'transparent'; } return out; } : - function(prop){ + function (prop) { var el = this.dom, m, cs; if (el == document) { return null; } - - if (prop == 'opacity') { - if (el.style.filter.match) { - m = el.style.filter.match(opacityRe); - if(m){ - var fv = parseFloat(m[1]); - if(!isNaN(fv)){ - return fv ? fv / 100 : 0; + prop = ELEMENT.normalize(prop); + + do { + if (prop == 'opacity') { + if (el.style.filter.match) { + m = el.style.filter.match(opacityRe); + if(m){ + var fv = parseFloat(m[1]); + if(!isNaN(fv)){ + return fv ? fv / 100 : 0; + } } } + return 1; } - return 1; - } - prop = Ext.core.Element.normalize(prop); - return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null); - }; + + // the try statement does have a cost, so we avoid it unless we are + // on IE6 + if (!Ext.isIE6) { + return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null); + } + + try { + return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null); + } catch (e) { + // in some cases, IE6 will throw Invalid Argument for properties + // like fontSize (see in /examples/tabs/tabs.html). + } + + if (!ELEMENT.inheritedProps[prop]) { + break; + } + + el = el.parentNode; + // this is _not_ perfect, but we can only hope that the style we + // need is inherited from a parentNode. If not and since IE won't + // give us the info we need, we are never going to be 100% right. + } while (el); + + Ext.log({ + level: 'warn', + msg: 'Failed to get ' + this.dom.id + '.currentStyle.' + prop + }); + return null; + } }(), /** @@ -14100,7 +14744,7 @@ Ext.core.Element.addMethods({ * Wrapper for setting style properties, also takes single object parameter of multiple styles. * @param {String/Object} property The style property to be set, or an object of multiple styles. * @param {String} value (optional) The value to apply to the given property, or null if an object was passed. - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setStyle : function(prop, value){ var me = this, @@ -14121,7 +14765,7 @@ Ext.core.Element.addMethods({ me.setOpacity(value); } else { - me.dom.style[Ext.core.Element.normalize(style)] = value; + me.dom.style[ELEMENT.normalize(style)] = value; } } } @@ -14130,10 +14774,10 @@ Ext.core.Element.addMethods({ /** * Set the opacity of the element - * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc + * @param {Number} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc * @param {Boolean/Object} animate (optional) a standard Element animation config object or true for * the default animation ({duration: .35, easing: 'easeIn'}) - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setOpacity: function(opacity, animate) { var me = this, @@ -14179,7 +14823,7 @@ Ext.core.Element.addMethods({ /** * Clears any opacity settings from this element. Required in some cases for IE. - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ clearOpacity : function(){ var style = this.dom.style; @@ -14192,11 +14836,11 @@ Ext.core.Element.addMethods({ } return this; }, - + /** * @private * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel. - * @return {Number} 0 or 1 + * @return {Number} 0 or 1 */ adjustDirect2DDimension: function(dimension) { var me = this, @@ -14206,7 +14850,7 @@ Ext.core.Element.addMethods({ inlinePosition = dom.style['position'], originIndex = dimension === 'width' ? 0 : 1, floating; - + if (display === 'inline') { dom.style['display'] = 'inline-block'; } @@ -14216,16 +14860,16 @@ Ext.core.Element.addMethods({ // floating will contain digits that appears after the decimal point // if height or width are set to auto we fallback to msTransformOrigin calculation floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1; - + dom.style['position'] = inlinePosition; - + if (display === 'inline') { dom.style['display'] = inlineDisplay; } return floating; }, - + /** * Returns the offset height of the element * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding @@ -14273,7 +14917,7 @@ Ext.core.Element.addMethods({ } return height; }, - + /** * Returns the offset width of the element * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding @@ -14292,8 +14936,8 @@ Ext.core.Element.addMethods({ overflow = style.overflow; me.setStyle({overflow: 'hidden'}); } - - // Fix Opera 10.5x width calculation issues + + // Fix Opera 10.5x width calculation issues if (Ext.isOpera10_5) { if (dom.parentNode.currentStyle.position === 'relative') { parentPosition = dom.parentNode.style.position; @@ -14302,7 +14946,7 @@ Ext.core.Element.addMethods({ dom.parentNode.style.position = parentPosition; } width = Math.max(width || 0, dom.offsetWidth); - + // Gecko will in some cases report an offsetWidth that is actually less than the width of the // text contents, because it measures fonts with sub-pixel precision but rounds the calculated // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise @@ -14328,11 +14972,11 @@ Ext.core.Element.addMethods({ width++; } } - + if (contentWidth) { width -= (me.getBorderWidth("lr") + me.getPadding("lr")); } - + if (Ext.isIEQuirks) { me.setStyle({ overflow: overflow}); } @@ -14345,12 +14989,12 @@ Ext.core.Element.addMethods({ /** * Set the width of this Element. - * @param {Mixed} width The new width. This may be one of:
      + * @param {Number/String} width The new width. This may be one of:
        *
      • A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).
      • *
      • A String used to set the CSS width style. Animation may not be used. *
      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setWidth : function(width, animate){ var me = this; @@ -14384,12 +15028,12 @@ Ext.fly('elId').setHeight(150, { callback: function(){ this.{@link #update}("finished"); } }); *
    - * @param {Mixed} height The new height. This may be one of:
      + * @param {Number/String} height The new height. This may be one of:
        *
      • A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)
      • *
      • A String used to set the CSS height style. Animation may not be used.
      • *
      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setHeight : function(height, animate){ var me = this; @@ -14432,7 +15076,7 @@ Ext.fly('elId').setHeight(150, { /** * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ clip : function(){ var me = this, @@ -14454,7 +15098,7 @@ Ext.fly('elId').setHeight(150, { /** * Return clipping (overflow) to original clipping before {@link #clip} was called - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ unclip : function(){ var me = this, @@ -14464,14 +15108,14 @@ Ext.fly('elId').setHeight(150, { if(data(dom, ISCLIPPED)){ data(dom, ISCLIPPED, false); clip = data(dom, ORIGINALCLIP); - if(o.o){ - me.setStyle(OVERFLOW, o.o); + if(clip.o){ + me.setStyle(OVERFLOW, clip.o); } - if(o.x){ - me.setStyle(OVERFLOWX, o.x); + if(clip.x){ + me.setStyle(OVERFLOWX, clip.x); } - if(o.y){ - me.setStyle(OVERFLOWY, o.y); + if(clip.y){ + me.setStyle(OVERFLOWY, clip.y); } } return me; @@ -14495,15 +15139,15 @@ Ext.fly('elId').setHeight(150, { }, margins : margins, - + /** * More flexible version of {@link #setStyle} for setting style properties. * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or * a function which returns such a specification. - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ applyStyles : function(style){ - Ext.core.DomHelper.applyStyles(this.dom, style); + Ext.DomHelper.applyStyles(this.dom, style); return this; }, @@ -14520,7 +15164,7 @@ Ext.fly('elId').setHeight(150, { var styles = {}, len = arguments.length, i = 0, style; - + for(; i < len; ++i) { style = arguments[i]; styles[style] = this.getStyle(style); @@ -14535,7 +15179,7 @@ Ext.fly('elId').setHeight(150, { * {@link Ext.panel.Panel} when {@link Ext.panel.Panel#frame frame=true}, {@link Ext.window.Window}). The markup * is of this form:

      *
      
      -    Ext.core.Element.boxMarkup =
      +    Ext.Element.boxMarkup =
           '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
            <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
            <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
      @@ -14554,28 +15198,28 @@ Ext.fly('elId').setHeight(150, {
               * (defaults to 'x-box'). Note that there are a number of CSS rules that are dependent on
               * this name to make the overall effect work, so if you supply an alternate base class, make sure you
               * also supply all of the necessary rules.
      -        * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
      +        * @return {Ext.Element} The outermost wrapping element of the created box structure.
               */
               boxWrap : function(cls){
                   cls = cls || Ext.baseCSSPrefix + 'box';
      -            var el = Ext.get(this.insertHtml("beforeBegin", "
      " + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "
      ")); + var el = Ext.get(this.insertHtml("beforeBegin", "
      " + Ext.String.format(ELEMENT.boxMarkup, cls) + "
      ")); Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom); return el; }, /** * Set the size of this Element. If animation is true, both width and height will be animated concurrently. - * @param {Mixed} width The new width. This may be one of:
        + * @param {Number/String} width The new width. This may be one of:
          *
        • A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).
        • *
        • A String used to set the CSS width style. Animation may not be used. *
        • A size object in the format {width: widthValue, height: heightValue}.
        • *
        - * @param {Mixed} height The new height. This may be one of:
          + * @param {Number/String} height The new height. This may be one of:
            *
          • A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).
          • *
          • A String used to set the CSS height style. Animation may not be used.
          • *
          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setSize : function(width, height, animate){ var me = this; @@ -14587,6 +15231,11 @@ Ext.fly('elId').setHeight(150, { width = me.adjustWidth(width); height = me.adjustHeight(height); if(!animate || !me.anim){ + // Must touch some property before setting style.width/height on non-quirk IE6,7, or the + // properties will not reflect the changes on the style immediately + if (!Ext.isIEQuirks && (Ext.isIE6 || Ext.isIE7)) { + me.dom.offsetTop; + } me.dom.style.width = me.addUnits(width); me.dom.style.height = me.addUnits(height); } @@ -14631,7 +15280,7 @@ Ext.fly('elId').setHeight(150, { getComputedWidth : function(){ var me = this, w = Math.max(me.dom.offsetWidth, me.dom.clientWidth); - + if(!w){ w = parseFloat(me.getStyle('width')) || 0; if(!me.isBorderBox()){ @@ -14654,7 +15303,7 @@ Ext.fly('elId').setHeight(150, { /** * Sets up event handlers to add and remove a css class when the mouse is over this element * @param {String} className - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ addClsOnOver : function(className){ var dom = this.dom; @@ -14672,7 +15321,7 @@ Ext.fly('elId').setHeight(150, { /** * Sets up event handlers to add and remove a css class when this element has the focus * @param {String} className - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ addClsOnFocus : function(className){ var me = this, @@ -14689,7 +15338,7 @@ Ext.fly('elId').setHeight(150, { /** * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect) * @param {String} className - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ addClsOnClick : function(className){ var dom = this.dom; @@ -14734,8 +15383,8 @@ Ext.fly('elId').setHeight(150, { // If the body, use static methods if (isDoc) { ret = { - width : Ext.core.Element.getViewWidth(), - height : Ext.core.Element.getViewHeight() + width : ELEMENT.getViewWidth(), + height : ELEMENT.getViewHeight() }; // Else use clientHeight/clientWidth @@ -14779,8 +15428,8 @@ Ext.fly('elId').setHeight(150, { // If the body, use static methods if (isDoc) { return { - width : Ext.core.Element.getViewWidth(), - height : Ext.core.Element.getViewHeight() + width : ELEMENT.getViewWidth(), + height : ELEMENT.getViewHeight() }; } // Use Styles if they are set @@ -14812,7 +15461,7 @@ Ext.fly('elId').setHeight(150, { /** * Forces the browser to repaint this element - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ repaint : function(){ var dom = this.dom; @@ -14823,18 +15472,35 @@ Ext.fly('elId').setHeight(150, { return this; }, + /** + * Enable text selection for this element (normalized across browsers) + * @return {Ext.Element} this + */ + selectable : function() { + var me = this; + me.dom.unselectable = "off"; + // Prevent it from bubles up and enables it to be selectable + me.on('selectstart', function (e) { + e.stopPropagation(); + return true; + }); + me.applyStyles("-moz-user-select: text; -khtml-user-select: text;"); + me.removeCls(Ext.baseCSSPrefix + 'unselectable'); + return me; + }, + /** * Disables text selection for this element (normalized across browsers) - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ unselectable : function(){ var me = this; me.dom.unselectable = "on"; me.swallowEvent("selectstart", true); - me.applyStyles("-moz-user-select:none;-khtml-user-select:none;"); + me.applyStyles("-moz-user-select:-moz-none;-khtml-user-select:none;"); me.addCls(Ext.baseCSSPrefix + 'unselectable'); - + return me; }, @@ -14862,20 +15528,20 @@ Ext.fly('elId').setHeight(150, { }); })(); /** - * @class Ext.core.Element + * @class Ext.Element */ /** * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element * @static * @type Number */ -Ext.core.Element.VISIBILITY = 1; +Ext.Element.VISIBILITY = 1; /** * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element * @static * @type Number */ -Ext.core.Element.DISPLAY = 2; +Ext.Element.DISPLAY = 2; /** * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen) @@ -14883,20 +15549,20 @@ Ext.core.Element.DISPLAY = 2; * @static * @type Number */ -Ext.core.Element.OFFSETS = 3; +Ext.Element.OFFSETS = 3; -Ext.core.Element.ASCLASS = 4; +Ext.Element.ASCLASS = 4; /** * Defaults to 'x-hide-nosize' * @static * @type String */ -Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize'; +Ext.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize'; -Ext.core.Element.addMethods(function(){ - var El = Ext.core.Element, +Ext.Element.addMethods(function(){ + var El = Ext.Element, OPACITY = "opacity", VISIBILITY = "visibility", DISPLAY = "display", @@ -14926,8 +15592,8 @@ Ext.core.Element.addMethods(function(){ return { /** - * The element's default display mode (defaults to "") - * @type String + * @property {String} originalDisplay + * The element's default display mode */ originalDisplay : "", visibilityMode : 1, @@ -14935,8 +15601,8 @@ Ext.core.Element.addMethods(function(){ /** * Sets the element's visibility mode. When setVisible() is called it * will use this to determine whether to set the visibility or the display property. - * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY - * @return {Ext.core.Element} this + * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY + * @return {Ext.Element} this */ setVisibilityMode : function(visMode){ data(this.dom, VISMODE, visMode); @@ -14969,7 +15635,7 @@ Ext.core.Element.addMethods(function(){ * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property. * @param {Boolean} visible Whether the element is visible * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setVisible : function(visible, animate){ var me = this, isDisplay, isVisibility, isOffsets, isNosize, @@ -15072,7 +15738,7 @@ Ext.core.Element.addMethods(function(){ /** * Toggles the element's visibility or display, depending on visibility mode. * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ toggle : function(animate){ var me = this; @@ -15082,8 +15748,8 @@ Ext.core.Element.addMethods(function(){ /** * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true. - * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly. - * @return {Ext.core.Element} this + * @param {Boolean/String} value Boolean value to display the element using its default display, or a string to set the display directly. + * @return {Ext.Element} this */ setDisplayed : function(value) { if(typeof value == "boolean"){ @@ -15108,7 +15774,7 @@ Ext.core.Element.addMethods(function(){ /** * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}. * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ hide : function(animate){ // hideMode override @@ -15123,7 +15789,7 @@ Ext.core.Element.addMethods(function(){ /** * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}. * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ show : function(animate){ // hideMode override @@ -15137,9 +15803,9 @@ Ext.core.Element.addMethods(function(){ }; }()); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.applyIf(Ext.core.Element.prototype, { +Ext.applyIf(Ext.Element.prototype, { // @private override base Ext.util.Animate mixin for animate for backwards compatibility animate: function(config) { var me = this; @@ -15217,28 +15883,27 @@ Ext.applyIf(Ext.core.Element.prototype, { }, /** - * Slides the element into view. An anchor point can be optionally passed to set the point of - * origin for the slide effect. This function automatically handles wrapping the element with - * a fixed-size container if needed. See the Fx class overview for valid anchor point options. - * Usage: - *
          
          -// default: slide the element in from the top
          -el.slideIn();
          -
          -// custom: slide the element in from the right with a 2-second duration
          -el.slideIn('r', { duration: 2 });
          -
          -// common config options shown with default values
          -el.slideIn('t', {
          -    easing: 'easeOut',
          -    duration: 500
          -});
          -
          - * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element + * Slides the element into view. An anchor point can be optionally passed to set the point of origin for the slide + * effect. This function automatically handles wrapping the element with a fixed-size container if needed. See the + * Fx class overview for valid anchor point options. Usage: + * + * // default: slide the element in from the top + * el.slideIn(); + * + * // custom: slide the element in from the right with a 2-second duration + * el.slideIn('r', { duration: 2000 }); + * + * // common config options shown with default values + * el.slideIn('t', { + * easing: 'easeOut', + * duration: 500 + * }); + * + * @param {String} [anchor='t'] One of the valid Fx anchor positions + * @param {Object} [options] Object literal with any of the Fx config options + * @return {Ext.Element} The Element */ - slideIn: function(anchor, obj, slideOut) { + slideIn: function(anchor, obj, slideOut) { var me = this, elStyle = me.dom.style, beforeAnim, wrapAnim; @@ -15256,13 +15921,13 @@ el.slideIn('t', { } box = me.getBox(); - if ((anchor == 't' || anchor == 'b') && box.height == 0) { + if ((anchor == 't' || anchor == 'b') && box.height === 0) { box.height = me.dom.scrollHeight; } - else if ((anchor == 'l' || anchor == 'r') && box.width == 0) { + else if ((anchor == 'l' || anchor == 'r') && box.width === 0) { box.width = me.dom.scrollWidth; } - + position = me.getPositioning(); me.setSize(box.width, box.height); @@ -15426,7 +16091,7 @@ el.slideIn('t', { if (obj.useDisplay) { me.setDisplayed(false); } else { - me.hide(); + me.hide(); } } else { @@ -15434,7 +16099,7 @@ el.slideIn('t', { me.setPositioning(position); } if (wrap.dom) { - wrap.dom.parentNode.insertBefore(me.dom, wrap.dom); + wrap.dom.parentNode.insertBefore(me.dom, wrap.dom); wrap.remove(); } me.setSize(box.width, box.height); @@ -15464,56 +16129,53 @@ el.slideIn('t', { return me; }, - + /** - * Slides the element out of view. An anchor point can be optionally passed to set the end point - * for the slide effect. When the effect is completed, the element will be hidden (visibility = - * 'hidden') but block elements will still take up space in the document. The element must be removed - * from the DOM using the 'remove' config option if desired. This function automatically handles - * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options. - * Usage: - *
          
          -// default: slide the element out to the top
          -el.slideOut();
          -
          -// custom: slide the element out to the right with a 2-second duration
          -el.slideOut('r', { duration: 2 });
          -
          -// common config options shown with default values
          -el.slideOut('t', {
          -    easing: 'easeOut',
          -    duration: 500,
          -    remove: false,
          -    useDisplay: false
          -});
          -
          - * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element + * Slides the element out of view. An anchor point can be optionally passed to set the end point for the slide + * effect. When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will + * still take up space in the document. The element must be removed from the DOM using the 'remove' config option if + * desired. This function automatically handles wrapping the element with a fixed-size container if needed. See the + * Fx class overview for valid anchor point options. Usage: + * + * // default: slide the element out to the top + * el.slideOut(); + * + * // custom: slide the element out to the right with a 2-second duration + * el.slideOut('r', { duration: 2000 }); + * + * // common config options shown with default values + * el.slideOut('t', { + * easing: 'easeOut', + * duration: 500, + * remove: false, + * useDisplay: false + * }); + * + * @param {String} [anchor='t'] One of the valid Fx anchor positions + * @param {Object} [options] Object literal with any of the Fx config options + * @return {Ext.Element} The Element */ slideOut: function(anchor, o) { return this.slideIn(anchor, o, true); }, /** - * Fades the element out while slowly expanding it in all directions. When the effect is completed, the - * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. - * Usage: - *
          
          -// default
          -el.puff();
          -
          -// common config options shown with default values
          -el.puff({
          -    easing: 'easeOut',
          -    duration: 500,
          -    useDisplay: false
          -});
          -
          + * Fades the element out while slowly expanding it in all directions. When the effect is completed, the element will + * be hidden (visibility = 'hidden') but block elements will still take up space in the document. Usage: + * + * // default + * el.puff(); + * + * // common config options shown with default values + * el.puff({ + * easing: 'easeOut', + * duration: 500, + * useDisplay: false + * }); + * * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element + * @return {Ext.Element} The Element */ - puff: function(obj) { var me = this, beforeAnim; @@ -15545,7 +16207,7 @@ el.puff({ } else { me.hide(); } - me.clearOpacity(); + me.clearOpacity(); me.setPositioning(position); me.setStyle({fontSize: fontSize}); } @@ -15566,28 +16228,28 @@ el.puff({ /** * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television). - * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still - * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired. - * Usage: - *
          
          -// default
          -el.switchOff();
          -
          -// all config options shown with default values
          -el.switchOff({
          -    easing: 'easeIn',
          -    duration: .3,
          -    remove: false,
          -    useDisplay: false
          -});
          -
          + * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still + * take up space in the document. The element must be removed from the DOM using the 'remove' config option if + * desired. Usage: + * + * // default + * el.switchOff(); + * + * // all config options shown with default values + * el.switchOff({ + * easing: 'easeIn', + * duration: .3, + * remove: false, + * useDisplay: false + * }); + * * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element + * @return {Ext.Element} The Element */ switchOff: function(obj) { var me = this, beforeAnim; - + obj = Ext.applyIf(obj || {}, { easing: 'ease-in', duration: 500, @@ -15627,7 +16289,7 @@ el.switchOff({ me.setDisplayed(false); } else { me.hide(); - } + } me.clearOpacity(); me.setPositioning(position); me.setSize(size); @@ -15645,27 +16307,27 @@ el.switchOff({ return me; }, - /** - * Shows a ripple of exploding, attenuating borders to draw attention to an Element. - * Usage: -
          
          -// default: a single light blue ripple
          -el.frame();
          -
          -// custom: 3 red ripples lasting 3 seconds total
          -el.frame("#ff0000", 3, { duration: 3 });
          -
          -// common config options shown with default values
          -el.frame("#C3DAF9", 1, {
          -    duration: 1 //duration of each individual ripple.
          -    // Note: Easing is not configurable and will be ignored if included
          -});
          -
          - * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9'). - * @param {Number} count (optional) The number of ripples to display (defaults to 1) - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element - */ + /** + * Shows a ripple of exploding, attenuating borders to draw attention to an Element. Usage: + * + * // default: a single light blue ripple + * el.frame(); + * + * // custom: 3 red ripples lasting 3 seconds total + * el.frame("#ff0000", 3, { duration: 3 }); + * + * // common config options shown with default values + * el.frame("#C3DAF9", 1, { + * duration: 1 //duration of each individual ripple. + * // Note: Easing is not configurable and will be ignored if included + * }); + * + * @param {String} [color='C3DAF9'] The color of the border. Should be a 6 char hex color without the leading # + * (defaults to light blue). + * @param {Number} [count=1] The number of ripples to display + * @param {Object} [options] Object literal with any of the Fx config options + * @return {Ext.Element} The Element + */ frame : function(color, count, obj){ var me = this, beforeAnim; @@ -15726,25 +16388,24 @@ el.frame("#C3DAF9", 1, { }, /** - * Slides the element while fading it out of view. An anchor point can be optionally passed to set the - * ending point of the effect. - * Usage: - *
          
          -// default: slide the element downward while fading out
          -el.ghost();
          -
          -// custom: slide the element out to the right with a 2-second duration
          -el.ghost('r', { duration: 2 });
          -
          -// common config options shown with default values
          -el.ghost('b', {
          -    easing: 'easeOut',
          -    duration: 500
          -});
          -
          - * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element + * Slides the element while fading it out of view. An anchor point can be optionally passed to set the ending point + * of the effect. Usage: + * + * // default: slide the element downward while fading out + * el.ghost(); + * + * // custom: slide the element out to the right with a 2-second duration + * el.ghost('r', { duration: 2000 }); + * + * // common config options shown with default values + * el.ghost('b', { + * easing: 'easeOut', + * duration: 500 + * }); + * + * @param {String} [anchor='b'] One of the valid Fx anchor positions + * @param {Object} [options] Object literal with any of the Fx config options + * @return {Ext.Element} The Element */ ghost: function(anchor, obj) { var me = this, @@ -15812,29 +16473,28 @@ el.ghost('b', { }, /** - * Highlights the Element by setting a color (applies to the background-color by default, but can be - * changed using the "attr" config option) and then fading back to the original color. If no original - * color is available, you should provide the "endColor" config option which will be cleared after the animation. - * Usage: -
          
          -// default: highlight background to yellow
          -el.highlight();
          -
          -// custom: highlight foreground text to blue for 2 seconds
          -el.highlight("0000ff", { attr: 'color', duration: 2 });
          -
          -// common config options shown with default values
          -el.highlight("ffff9c", {
          -    attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
          -    endColor: (current color) or "ffffff",
          -    easing: 'easeIn',
          -    duration: 1000
          -});
          -
          - * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.core.Element} The Element - */ + * Highlights the Element by setting a color (applies to the background-color by default, but can be changed using + * the "attr" config option) and then fading back to the original color. If no original color is available, you + * should provide the "endColor" config option which will be cleared after the animation. Usage: + * + * // default: highlight background to yellow + * el.highlight(); + * + * // custom: highlight foreground text to blue for 2 seconds + * el.highlight("0000ff", { attr: 'color', duration: 2000 }); + * + * // common config options shown with default values + * el.highlight("ffff9c", { + * attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value + * endColor: (current color) or "ffffff", + * easing: 'easeIn', + * duration: 1000 + * }); + * + * @param {String} [color='ffff9c'] The highlight color. Should be a 6 char hex color without the leading # + * @param {Object} [options] Object literal with any of the Fx config options + * @return {Ext.Element} The Element + */ highlight: function(color, o) { var me = this, dom = me.dom, @@ -15845,7 +16505,7 @@ el.highlight("ffff9c", { lns = o.listeners || {}; attr = o.attr || 'backgroundColor'; from[attr] = color || 'ffff9c'; - + if (!o.to) { to = {}; to[attr] = o.endColor || me.getColor(attr, 'ffffff', ''); @@ -15853,14 +16513,14 @@ el.highlight("ffff9c", { else { to = o.to; } - + // Don't apply directly on lns, since we reference it in our own callbacks below o.listeners = Ext.apply(Ext.apply({}, lns), { beforeanimate: function() { restore = dom.style[attr]; me.clearOpacity(); me.show(); - + event = lns.beforeanimate; if (event) { fn = event.fn || event; @@ -15871,7 +16531,7 @@ el.highlight("ffff9c", { if (dom) { dom.style[attr] = restore; } - + event = lns.afteranimate; if (event) { fn = event.fn || event; @@ -15891,12 +16551,11 @@ el.highlight("ffff9c", { /** * @deprecated 4.0 - * Creates a pause before any subsequent queued effects begin. If there are - * no effects queued after the pause it will have no effect. - * Usage: -
          
          -el.pause(1);
          -
          + * Creates a pause before any subsequent queued effects begin. If there are no effects queued after the pause it will + * have no effect. Usage: + * + * el.pause(1); + * * @param {Number} seconds The length of time to pause (in seconds) * @return {Ext.Element} The Element */ @@ -15908,27 +16567,26 @@ el.pause(1); return me; }, - /** - * Fade an element in (from transparent to opaque). The ending opacity can be specified - * using the {@link #endOpacity} config option. - * Usage: -
          
          -// default: fade in from opacity 0 to 100%
          -el.fadeIn();
          -
          -// custom: fade in from opacity 0 to 75% over 2 seconds
          -el.fadeIn({ endOpacity: .75, duration: 2});
          -
          -// common config options shown with default values
          -el.fadeIn({
          -    endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
          -    easing: 'easeOut',
          -    duration: 500
          -});
          -
          - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ + /** + * Fade an element in (from transparent to opaque). The ending opacity can be specified using the `opacity` + * config option. Usage: + * + * // default: fade in from opacity 0 to 100% + * el.fadeIn(); + * + * // custom: fade in from opacity 0 to 75% over 2 seconds + * el.fadeIn({ opacity: .75, duration: 2000}); + * + * // common config options shown with default values + * el.fadeIn({ + * opacity: 1, //can be any value between 0 and 1 (e.g. .5) + * easing: 'easeOut', + * duration: 500 + * }); + * + * @param {Object} options (optional) Object literal with any of the Fx config options + * @return {Ext.Element} The Element + */ fadeIn: function(o) { this.animate(Ext.apply({}, o, { opacity: 1 @@ -15936,30 +16594,29 @@ el.fadeIn({ return this; }, - /** - * Fade an element out (from opaque to transparent). The ending opacity can be specified - * using the {@link #endOpacity} config option. Note that IE may require - * {@link #useDisplay}:true in order to redisplay correctly. - * Usage: -
          
          -// default: fade out from the element's current opacity to 0
          -el.fadeOut();
          -
          -// custom: fade out from the element's current opacity to 25% over 2 seconds
          -el.fadeOut({ endOpacity: .25, duration: 2});
          -
          -// common config options shown with default values
          -el.fadeOut({
          -    endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
          -    easing: 'easeOut',
          -    duration: 500,
          -    remove: false,
          -    useDisplay: false
          -});
          -
          - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ + /** + * Fade an element out (from opaque to transparent). The ending opacity can be specified using the `opacity` + * config option. Note that IE may require `useDisplay:true` in order to redisplay correctly. + * Usage: + * + * // default: fade out from the element's current opacity to 0 + * el.fadeOut(); + * + * // custom: fade out from the element's current opacity to 25% over 2 seconds + * el.fadeOut({ opacity: .25, duration: 2000}); + * + * // common config options shown with default values + * el.fadeOut({ + * opacity: 0, //can be any value between 0 and 1 (e.g. .5) + * easing: 'easeOut', + * duration: 500, + * remove: false, + * useDisplay: false + * }); + * + * @param {Object} options (optional) Object literal with any of the Fx config options + * @return {Ext.Element} The Element + */ fadeOut: function(o) { this.animate(Ext.apply({}, o, { opacity: 0 @@ -15967,30 +16624,29 @@ el.fadeOut({ return this; }, - /** - * @deprecated 4.0 - * Animates the transition of an element's dimensions from a starting height/width - * to an ending height/width. This method is a convenience implementation of {@link #shift}. - * Usage: -
          
          -// change height and width to 100x100 pixels
          -el.scale(100, 100);
          -
          -// common config options shown with default values.  The height and width will default to
          -// the element's existing values if passed as null.
          -el.scale(
          -    [element's width],
          -    [element's height], {
          -        easing: 'easeOut',
          -        duration: .35
          -    }
          -);
          -
          - * @param {Number} width The new width (pass undefined to keep the original width) - * @param {Number} height The new height (pass undefined to keep the original height) - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ + /** + * @deprecated 4.0 + * Animates the transition of an element's dimensions from a starting height/width to an ending height/width. This + * method is a convenience implementation of {@link #shift}. Usage: + * + * // change height and width to 100x100 pixels + * el.scale(100, 100); + * + * // common config options shown with default values. The height and width will default to + * // the element's existing values if passed as null. + * el.scale( + * [element's width], + * [element's height], { + * easing: 'easeOut', + * duration: .35 + * } + * ); + * + * @param {Number} width The new width (pass undefined to keep the original width) + * @param {Number} height The new height (pass undefined to keep the original height) + * @param {Object} options (optional) Object literal with any of the Fx config options + * @return {Ext.Element} The Element + */ scale: function(w, h, o) { this.animate(Ext.apply({}, o, { width: w, @@ -15999,31 +16655,30 @@ el.scale( return this; }, - /** - * @deprecated 4.0 - * Animates the transition of any combination of an element's dimensions, xy position and/or opacity. - * Any of these properties not specified in the config object will not be changed. This effect - * requires that at least one new dimension, position or opacity setting must be passed in on - * the config object in order for the function to have any effect. - * Usage: -
          
          -// slide the element horizontally to x position 200 while changing the height and opacity
          -el.shift({ x: 200, height: 50, opacity: .8 });
          -
          -// common config options shown with default values.
          -el.shift({
          -    width: [element's width],
          -    height: [element's height],
          -    x: [element's x position],
          -    y: [element's y position],
          -    opacity: [element's opacity],
          -    easing: 'easeOut',
          -    duration: .35
          -});
          -
          - * @param {Object} options Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ + /** + * @deprecated 4.0 + * Animates the transition of any combination of an element's dimensions, xy position and/or opacity. Any of these + * properties not specified in the config object will not be changed. This effect requires that at least one new + * dimension, position or opacity setting must be passed in on the config object in order for the function to have + * any effect. Usage: + * + * // slide the element horizontally to x position 200 while changing the height and opacity + * el.shift({ x: 200, height: 50, opacity: .8 }); + * + * // common config options shown with default values. + * el.shift({ + * width: [element's width], + * height: [element's height], + * x: [element's x position], + * y: [element's y position], + * opacity: [element's opacity], + * easing: 'easeOut', + * duration: .35 + * }); + * + * @param {Object} options Object literal with any of the Fx config options + * @return {Ext.Element} The Element + */ shift: function(config) { this.animate(config); return this; @@ -16031,9 +16686,9 @@ el.shift({ }); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.applyIf(Ext.core.Element, { +Ext.applyIf(Ext.Element, { unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i, camelRe: /(-[a-z])/gi, opacityRe: /alpha\(opacity=(.*)\)/i, @@ -16045,13 +16700,13 @@ Ext.applyIf(Ext.core.Element, { margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'}, // Reference the prototype's version of the method. Signatures are identical. - addUnits : Ext.core.Element.prototype.addUnits, + addUnits : Ext.Element.prototype.addUnits, /** * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result) * @static - * @param {Number|String} box The encoded margins + * @param {Number/String} box The encoded margins * @return {Object} An object with margin sizes for top, right, bottom and left */ parseBox : function(box) { @@ -16094,7 +16749,7 @@ Ext.applyIf(Ext.core.Element, { * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result) * @static - * @param {Number|String} box The encoded margins + * @param {Number/String} box The encoded margins * @param {String} units The type of units to add * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left */ @@ -16198,7 +16853,7 @@ Ext.applyIf(Ext.core.Element, { * Returns the top Element that is located at the passed coordinates * @static * @param {Number} x The x coordinate - * @param {Number} x The y coordinate + * @param {Number} y The y coordinate * @return {String} The found Element */ fromPoint: function(x, y) { @@ -16212,7 +16867,7 @@ Ext.applyIf(Ext.core.Element, { * for background-color and one for color.

          *
          
           var css = 'background-color: red;color: blue; ';
          -console.log(Ext.core.Element.parseStyles(css));
          +console.log(Ext.Element.parseStyles(css));
                * 
          * @static * @param {String} styles A CSS string @@ -16241,7 +16896,7 @@ console.log(Ext.core.Element.parseStyles(css)); * @class Ext.CompositeElementLite *

          This class encapsulates a collection of DOM elements, providing methods to filter * members, or to perform collective actions upon the whole set.

          - *

          Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and + *

          Although they are not listed, this class supports all of the methods of {@link Ext.Element} and * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.

          * Example:
          
           var els = Ext.select("#some-el div.some-class");
          @@ -16279,12 +16934,11 @@ Ext.override(Ext.CompositeElementLite, {
                   return this.add(r);
               }
           });
          - * @type Array - * @property elements + * @property {HTMLElement} elements */ this.elements = []; this.add(els, root); - this.el = new Ext.core.Element.Flyweight(); + this.el = new Ext.Element.Flyweight(); }; Ext.CompositeElementLite.prototype = { @@ -16313,8 +16967,8 @@ Ext.CompositeElementLite.prototype = { }, /** * Adds elements to this Composite object. - * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added. - * @return {CompositeElement} This Composite object. + * @param {HTMLElement[]/Ext.CompositeElement} els Either an Array of DOM elements to add, or another Composite object who's elements should be added. + * @return {Ext.CompositeElement} This Composite object. */ add : function(els, root){ var me = this, @@ -16323,7 +16977,7 @@ Ext.CompositeElementLite.prototype = { return this; } if(typeof els == "string"){ - els = Ext.core.Element.selectorFunction(els, root); + els = Ext.Element.selectorFunction(els, root); }else if(els.isComposite){ els = els.elements; }else if(!Ext.isIterable(els)){ @@ -16346,7 +17000,7 @@ Ext.CompositeElementLite.prototype = { for(i = 0; i < len; i++) { e = els[i]; if(e){ - Ext.core.Element.prototype[fn].apply(me.getElement(e), args); + Ext.Element.prototype[fn].apply(me.getElement(e), args); } } return me; @@ -16354,7 +17008,7 @@ Ext.CompositeElementLite.prototype = { /** * Returns a flyweight Element of the dom element object at the specified index * @param {Number} index - * @return {Ext.core.Element} + * @return {Ext.Element} */ item : function(index){ var me = this, @@ -16385,13 +17039,13 @@ Ext.CompositeElementLite.prototype = { *

          Calls the passed function for each element in this composite.

          * @param {Function} fn The function to call. The function is passed the following parameters:
            *
          • el : Element
            The current Element in the iteration. - * This is the flyweight (shared) Ext.core.Element instance, so if you require a + * This is the flyweight (shared) Ext.Element instance, so if you require a * a reference to the dom node, use el.dom.
          • *
          • c : Composite
            This Composite object.
          • *
          • idx : Number
            The zero-based index in the iteration.
          • *
          - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. (defaults to the Element) - * @return {CompositeElement} this + * @param {Object} [scope] The scope (this reference) in which the function is executed. (defaults to the Element) + * @return {Ext.CompositeElement} this */ each : function(fn, scope){ var me = this, @@ -16413,8 +17067,8 @@ Ext.CompositeElementLite.prototype = { /** * Clears this Composite and adds the elements passed. - * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite. - * @return {CompositeElement} this + * @param {HTMLElement[]/Ext.CompositeElement} els Either an array of DOM elements, or another Composite from which to fill this Composite. + * @return {Ext.CompositeElement} this */ fill : function(els){ var me = this; @@ -16427,10 +17081,10 @@ Ext.CompositeElementLite.prototype = { * Filters this composite to only elements that match the passed selector. * @param {String/Function} selector A string CSS selector or a comparison function. * The comparison function will be called with the following arguments:
            - *
          • el : Ext.core.Element
            The current DOM element.
          • + *
          • el : Ext.Element
            The current DOM element.
          • *
          • index : Number
            The current index within the collection.
          • *
          - * @return {CompositeElement} this + * @return {Ext.CompositeElement} this */ filter : function(selector){ var els = [], @@ -16445,15 +17099,15 @@ Ext.CompositeElementLite.prototype = { els[els.length] = me.transformElement(el); } }); - + me.elements = els; return me; }, /** * Find the index of the passed element within the composite collection. - * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection. - * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found. + * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection. + * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found. */ indexOf : function(el){ return Ext.Array.indexOf(this.elements, this.transformElement(el)); @@ -16461,11 +17115,11 @@ Ext.CompositeElementLite.prototype = { /** * Replaces the specified element with the passed element. - * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite + * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite * to replace. - * @param {Mixed} replacement The id of an element or the Element itself. + * @param {String/Ext.Element} replacement The id of an element or the Element itself. * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too. - * @return {CompositeElement} this + * @return {Ext.CompositeElement} this */ replaceElement : function(el, replacement, domReplace){ var index = !isNaN(el) ? el : this.indexOf(el), @@ -16494,13 +17148,13 @@ Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addLi /** * @private - * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype. - * This is called twice - once immediately below, and once again after additional Ext.core.Element + * Copies all of the functions from Ext.Element's prototype onto CompositeElementLite's prototype. + * This is called twice - once immediately below, and once again after additional Ext.Element * are added in Ext JS */ Ext.CompositeElementLite.importElementMethods = function() { var fnName, - ElProto = Ext.core.Element.prototype, + ElProto = Ext.Element.prototype, CelProto = Ext.CompositeElementLite.prototype; for (fnName in ElProto) { @@ -16518,28 +17172,28 @@ Ext.CompositeElementLite.importElementMethods = function() { Ext.CompositeElementLite.importElementMethods(); if(Ext.DomQuery){ - Ext.core.Element.selectorFunction = Ext.DomQuery.select; + Ext.Element.selectorFunction = Ext.DomQuery.select; } /** - * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements + * @param {String/HTMLElement[]} selector The CSS selector or an array of elements * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} - * @member Ext.core.Element + * @return {Ext.CompositeElementLite/Ext.CompositeElement} + * @member Ext.Element * @method select */ -Ext.core.Element.select = function(selector, root){ +Ext.Element.select = function(selector, root){ var els; if(typeof selector == "string"){ - els = Ext.core.Element.selectorFunction(selector, root); + els = Ext.Element.selectorFunction(selector, root); }else if(selector.length !== undefined){ els = selector; }else{ Ext.Error.raise({ - sourceClass: "Ext.core.Element", + sourceClass: "Ext.Element", sourceMethod: "select", selector: selector, root: root, @@ -16549,16 +17203,16 @@ Ext.core.Element.select = function(selector, root){ return new Ext.CompositeElementLite(els); }; /** - * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements + * @param {String/HTMLElement[]} selector The CSS selector or an array of elements * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} + * @return {Ext.CompositeElementLite/Ext.CompositeElement} * @member Ext * @method select */ -Ext.select = Ext.core.Element.select; +Ext.select = Ext.Element.select; /** * @class Ext.util.DelayedTask @@ -16591,7 +17245,7 @@ Ext.select = Ext.core.Element.select; * also setup a delayed task for you to buffer events. * * @constructor The parameters to this constructor serve as defaults and are not required. - * @param {Function} fn (optional) The default function to call. + * @param {Function} fn (optional) The default function to call. If not specified here, it must be specified during the {@link #delay} call. * @param {Object} scope (optional) The default scope (The this reference) in which the * function is called. If not specified, this will refer to the browser window. * @param {Array} args (optional) The default Array of arguments. @@ -16867,6 +17521,7 @@ Ext.EventManager = { if(window.attachEvent){ // See here for reference: http://javascript.nwbox.com/IEContentLoaded/ + // licensed courtesy of http://developer.yahoo.com/yui/license.html if (window != top) { return false; } @@ -16949,7 +17604,7 @@ Ext.EventManager = { * accessed shorthanded as Ext.onReady(). * @param {Function} fn The method the event invokes. * @param {Object} scope (optional) The scope (this reference) in which the handler function executes. Defaults to the browser window. - * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}. + * @param {Boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. */ onDocumentReady: function(fn, scope, options){ options = options || {}; @@ -16987,15 +17642,15 @@ Ext.EventManager = { /** * Get the id of the element. If one has not been assigned, automatically assign it. - * @param {Mixed} element The element to get the id for. + * @param {HTMLElement/Ext.Element} element The element to get the id for. * @return {String} id */ getId : function(element) { var skipGarbageCollection = false, id; - + element = Ext.getDom(element); - + if (element === document || element === window) { id = element === document ? Ext.documentId : Ext.windowId; } @@ -17006,9 +17661,9 @@ Ext.EventManager = { if (element && (element.getElementById || element.navigator)) { skipGarbageCollection = true; } - + if (!Ext.cache[id]){ - Ext.core.Element.addToCache(new Ext.core.Element(element), id); + Ext.Element.addToCache(new Ext.Element(element), id); if (skipGarbageCollection) { Ext.cache[id].skipGarbageCollection = true; } @@ -17101,13 +17756,13 @@ Ext.EventManager = { /** * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will - * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version. + * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The html element or id to assign the event handler to. * @param {String} eventName The name of the event to listen for. * @param {Function} handler The handler function the event invokes. This function is passed * the following parameters:
            *
          • evt : EventObject
            The {@link Ext.EventObject EventObject} describing the event.
          • - *
          • t : Element
            The {@link Ext.core.Element Element} which was the target of the event. + *
          • t : Element
            The {@link Ext.Element Element} which was the target of the event. * Note that this may be filtered by using the delegate option.
          • *
          • o : Object
            The options object from the addListener call.
          • *
          @@ -17127,7 +17782,7 @@ Ext.EventManager = { * handler is not invoked, but the new handler is scheduled in its place.
        *
      • target : Element
        Only call the handler if the event was fired on the target Element, not if the event was bubbled up from a child node.
      • *

      - *

      See {@link Ext.core.Element#addListener} for examples of how to use these options.

      + *

      See {@link Ext.Element#addListener} for examples of how to use these options.

      */ addListener: function(element, eventName, fn, scope, options){ // Check if we've been passed a "config style" event. @@ -17186,7 +17841,7 @@ Ext.EventManager = { /** * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically - * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version. + * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The id or html element from which to remove the listener. * @param {String} eventName The name of the event. * @param {Function} fn The handler function to remove. This must be a reference to the function passed into the {@link #addListener} call. @@ -17245,7 +17900,7 @@ Ext.EventManager = { }, /** - * Removes all event handers from an element. Typically you will use {@link Ext.core.Element#removeAllListeners} + * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners} * directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The id or html element from which to remove all event handlers. */ @@ -17266,7 +17921,7 @@ Ext.EventManager = { }, /** - * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners} + * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.Element#purgeAllListeners} * directly on an Element in favor of calling this version. * @param {String/HTMLElement} el The id or html element from which to remove all event handlers. * @param {String} eventName (optional) The name of the event. @@ -17384,7 +18039,7 @@ Ext.EventManager = { if (!element) { return []; } - + var eventCache = this.getElementEventCache(element); return eventCache[eventName] || (eventCache[eventName] = []); }, @@ -17488,9 +18143,9 @@ Ext.EventManager = { }, /** - * Gets the x & ycoordinate from the event + * Gets the x & y coordinate from the event * @param {Object} event The event - * @return {Array} The x/y coordinate + * @return {Number[]} The x/y coordinate */ getPageXY: function(event) { event = event.browserEvent || event; @@ -17550,7 +18205,7 @@ Ext.EventManager = { * passes new viewport width and height to handlers. * @param {Function} fn The handler function the window resize event invokes. * @param {Object} scope The scope (this reference) in which the handler function executes. Defaults to the browser window. - * @param {boolean} options Options object as passed to {@link Ext.core.Element#addListener} + * @param {Boolean} options Options object as passed to {@link Ext.Element#addListener} */ onWindowResize: function(fn, scope, options){ var resize = this.resizeEvent; @@ -17567,8 +18222,8 @@ Ext.EventManager = { */ fireResize: function(){ var me = this, - w = Ext.core.Element.getViewWidth(), - h = Ext.core.Element.getViewHeight(); + w = Ext.Element.getViewWidth(), + h = Ext.Element.getViewHeight(); //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same. if(me.curHeight != h || me.curWidth != w){ @@ -17640,7 +18295,7 @@ Ext.EventManager = { /** * note 1: IE fires ONLY the keydown event on specialkey autorepeat * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat - * (research done by @Jan Wolter at http://unixpapa.com/js/key.html) + * (research done by Jan Wolter at http://unixpapa.com/js/key.html) * @private */ useKeyDown: Ext.isWebKit ? @@ -17701,66 +18356,107 @@ Ext.EventManager.un = Ext.EventManager.removeListener; html = bd.parentNode; + function add (c) { + cls.push(baseCSSPrefix + c); + } + //Let's keep this human readable! if (Ext.isIE) { - cls.push(baseCSSPrefix + 'ie'); - } - if (Ext.isIE6) { - cls.push(baseCSSPrefix + 'ie6'); - } - if (Ext.isIE7) { - cls.push(baseCSSPrefix + 'ie7'); - } - if (Ext.isIE8) { - cls.push(baseCSSPrefix + 'ie8'); - } - if (Ext.isIE9) { - cls.push(baseCSSPrefix + 'ie9'); + add('ie'); + + // very often CSS needs to do checks like "IE7+" or "IE6 or 7". To help + // reduce the clutter (since CSS/SCSS cannot do these tests), we add some + // additional classes: + // + // x-ie7p : IE7+ : 7 <= ieVer + // x-ie7m : IE7- : ieVer <= 7 + // x-ie8p : IE8+ : 8 <= ieVer + // x-ie8m : IE8- : ieVer <= 8 + // x-ie9p : IE9+ : 9 <= ieVer + // x-ie78 : IE7 or 8 : 7 <= ieVer <= 8 + // + if (Ext.isIE6) { + add('ie6'); + } else { // ignore pre-IE6 :) + add('ie7p'); + + if (Ext.isIE7) { + add('ie7'); + } else { + add('ie8p'); + + if (Ext.isIE8) { + add('ie8'); + } else { + add('ie9p'); + + if (Ext.isIE9) { + add('ie9'); + } + } + } + } + + if (Ext.isIE6 || Ext.isIE7) { + add('ie7m'); + } + if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) { + add('ie8m'); + } + if (Ext.isIE7 || Ext.isIE8) { + add('ie78'); + } } if (Ext.isGecko) { - cls.push(baseCSSPrefix + 'gecko'); - } - if (Ext.isGecko3) { - cls.push(baseCSSPrefix + 'gecko3'); - } - if (Ext.isGecko4) { - cls.push(baseCSSPrefix + 'gecko4'); + add('gecko'); + if (Ext.isGecko3) { + add('gecko3'); + } + if (Ext.isGecko4) { + add('gecko4'); + } + if (Ext.isGecko5) { + add('gecko5'); + } } if (Ext.isOpera) { - cls.push(baseCSSPrefix + 'opera'); + add('opera'); } if (Ext.isWebKit) { - cls.push(baseCSSPrefix + 'webkit'); + add('webkit'); } if (Ext.isSafari) { - cls.push(baseCSSPrefix + 'safari'); - } - if (Ext.isSafari2) { - cls.push(baseCSSPrefix + 'safari2'); - } - if (Ext.isSafari3) { - cls.push(baseCSSPrefix + 'safari3'); - } - if (Ext.isSafari4) { - cls.push(baseCSSPrefix + 'safari4'); + add('safari'); + if (Ext.isSafari2) { + add('safari2'); + } + if (Ext.isSafari3) { + add('safari3'); + } + if (Ext.isSafari4) { + add('safari4'); + } + if (Ext.isSafari5) { + add('safari5'); + } } if (Ext.isChrome) { - cls.push(baseCSSPrefix + 'chrome'); + add('chrome'); } if (Ext.isMac) { - cls.push(baseCSSPrefix + 'mac'); + add('mac'); } if (Ext.isLinux) { - cls.push(baseCSSPrefix + 'linux'); + add('linux'); } if (!Ext.supports.CSS3BorderRadius) { - cls.push(baseCSSPrefix + 'nbr'); + add('nbr'); } if (!Ext.supports.CSS3LinearGradient) { - cls.push(baseCSSPrefix + 'nlg'); + add('nlg'); } if (!Ext.scopeResetCSS) { - cls.push(baseCSSPrefix + 'reset'); + add('reset'); } // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly @@ -17775,9 +18471,6 @@ Ext.EventManager.un = Ext.EventManager.removeListener; htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict')); if (!Ext.isStrict) { htmlCls.push(baseCSSPrefix + 'quirks'); - if (Ext.isIE && !Ext.isStrict) { - Ext.isIEQuirks = true; - } } Ext.fly(html, '_internal').addCls(htmlCls); } @@ -17792,7 +18485,7 @@ Ext.EventManager.un = Ext.EventManager.removeListener; /** * @class Ext.EventObject -Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject +Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject wraps the browser's native event-object normalizing cross-browser differences, such as which mouse button is clicked, keys pressed, mechanisms to stop event-propagation along with a method to prevent default actions from taking place. @@ -17805,7 +18498,7 @@ For example: ... } - var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.core.Element} + var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element} myDiv.on( // 'on' is shorthand for addListener "click", // perform an action on click of myDiv handleClick // reference to the action handler @@ -17998,11 +18691,11 @@ Ext.define('Ext.EventObjectImpl', { /** * The mouse wheel delta scaling factor. This value depends on browser version and OS and * attempts to produce a similar scrolling experience across all platforms and browsers. - * + * * To change this value: - * + * * Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72; - * + * * @type Number * @markdown */ @@ -18199,15 +18892,15 @@ Ext.define('Ext.EventObjectImpl', { getPageY: function(){ return this.getY(); }, - + /** * Gets the x coordinate of the event. * @return {Number} */ getX: function() { return this.getXY()[0]; - }, - + }, + /** * Gets the y coordinate of the event. * @return {Number} @@ -18215,10 +18908,10 @@ Ext.define('Ext.EventObjectImpl', { getY: function() { return this.getXY()[1]; }, - + /** * Gets the page coordinates of the event. - * @return {Array} The xy values like [x, y] + * @return {Number[]} The xy values like [x, y] */ getXY: function() { if (!this.xy) { @@ -18231,9 +18924,9 @@ Ext.define('Ext.EventObjectImpl', { /** * Gets the target for the event. * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target - * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body) - * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node - * @return {HTMLelement} + * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body) + * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node + * @return {HTMLElement} */ getTarget : function(selector, maxDepth, returnEl){ if (selector) { @@ -18245,8 +18938,8 @@ Ext.define('Ext.EventObjectImpl', { /** * Gets the related target. * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target - * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body) - * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node + * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body) + * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node * @return {HTMLElement} */ getRelatedTarget : function(selector, maxDepth, returnEl){ @@ -18262,7 +18955,7 @@ Ext.define('Ext.EventObjectImpl', { */ correctWheelDelta : function (delta) { var scale = this.WHEEL_SCALE, - ret = Math.round(delta / scale + 0.5); + ret = Math.round(delta / scale); if (!ret && delta) { ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero! @@ -18322,8 +19015,8 @@ Ext.define('Ext.EventObjectImpl', { }, /** - * Returns true if the target of this event is a child of el. Unless the allowEl parameter is set, it will return false if if the target is el. - * Example usage:
      
      +     * Returns true if the target of this event is a child of el.  Unless the allowEl parameter is set, it will return false if if the target is el.
      +     * Example usage:
      
       // Handle click on any child of an element
       Ext.getBody().on('click', function(e){
           if(e.within('some-el')){
      @@ -18338,9 +19031,9 @@ Ext.getBody().on('click', function(e,t){
           }
       });
       
      - * @param {Mixed} el The id, DOM element or Ext.core.Element to check + * @param {String/HTMLElement/Ext.Element} el The id, DOM element or Ext.Element to check * @param {Boolean} related (optional) true to test if the related target is within el instead of the target - * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target + * @param {Boolean} allowEl (optional) true to also check if the passed element is the target or related target * @return {Boolean} */ within : function(el, related, allowEl){ @@ -18436,7 +19129,7 @@ Ext.getBody().on('click', function(e,t){ *
    • focus
    • *
    • blur
    • *
    - * @param {Element/HTMLElement} target If specified, the target for the event. This + * @param {Ext.Element/HTMLElement} target (optional) If specified, the target for the event. This * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget} * is used to determine the target. */ @@ -18657,16 +19350,16 @@ Ext.EventObject = new Ext.EventObjectImpl(); /** - * @class Ext.core.Element + * @class Ext.Element */ (function(){ var doc = document, activeElement = null, isCSS1 = doc.compatMode == "CSS1Compat", - ELEMENT = Ext.core.Element, + ELEMENT = Ext.Element, fly = function(el){ if (!_fly) { - _fly = new Ext.core.Element.Flyweight(); + _fly = new Ext.Element.Flyweight(); } _fly.dom = el; return _fly; @@ -18802,6 +19495,17 @@ Ext.EventObject = new Ext.EventObjectImpl(); return ELEMENT.getXY(el)[0]; }, + getOffsetParent: function (el) { + el = Ext.getDom(el); + try { + // accessing offsetParent can throw "Unspecified Error" in IE6-8 (not 9) + return el.offsetParent; + } catch (e) { + var body = document.body; // safe bet, unless... + return (el == body) ? null : body; + } + }, + getXY : function(el) { var p, pe, @@ -18814,7 +19518,7 @@ Ext.EventObject = new Ext.EventObjectImpl(); scroll, hasAbsolute, bd = (doc.body || doc.documentElement), - ret = [0,0]; + ret; el = Ext.getDom(el); @@ -18822,13 +19526,17 @@ Ext.EventObject = new Ext.EventObjectImpl(); hasAbsolute = fly(el).isStyle("position", "absolute"); if (el.getBoundingClientRect) { - b = el.getBoundingClientRect(); - scroll = fly(document).getScroll(); - ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)]; - } else { - p = el; + try { + b = el.getBoundingClientRect(); + scroll = fly(document).getScroll(); + ret = [ Math.round(b.left + scroll.left), Math.round(b.top + scroll.top) ]; + } catch (e) { + // IE6-8 can also throw from getBoundingClientRect... + } + } - while (p) { + if (!ret) { + for (p = el; p; p = ELEMENT.getOffsetParent(p)) { pe = fly(p); x += p.offsetLeft; y += p.offsetTop; @@ -18844,7 +19552,6 @@ Ext.EventObject = new Ext.EventObjectImpl(); y += bt; } } - p = p.offsetParent; } if (Ext.isSafari && hasAbsolute) { @@ -18869,7 +19576,7 @@ Ext.EventObject = new Ext.EventObjectImpl(); ret = [x,y]; } } - return ret; + return ret || [0,0]; }, setXY : function(el, xy) { @@ -18934,272 +19641,304 @@ Ext.EventObject = new Ext.EventObjectImpl(); })(); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.core.Element.addMethods({ - - /** - * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if - * the mouse was not moved back into the Element within the delay. If the mouse was moved - * back in, the function is not called. - * @param {Number} delay The delay in milliseconds to wait for possible mouse re-entry before calling the handler function. - * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time. - * @param {Object} scope The scope (this reference) in which the handler function executes. Defaults to this Element. - * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:
    +Ext.Element.addMethods((function(){ + var focusRe = /button|input|textarea|select|object/; + return { + /** + * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if + * the mouse was not moved back into the Element within the delay. If the mouse was moved + * back in, the function is not called. + * @param {Number} delay The delay in milliseconds to wait for possible mouse re-entry before calling the handler function. + * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time. + * @param {Object} scope The scope (this reference) in which the handler function executes. Defaults to this Element. + * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:
    
     // Hide the menu if the mouse moves out for 250ms or more
     this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
     
     ...
     // Remove mouseleave monitor on menu destroy
     this.menuEl.un(this.mouseLeaveMonitor);
    -
    - */ - monitorMouseLeave: function(delay, handler, scope) { - var me = this, - timer, - listeners = { - mouseleave: function(e) { - timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay); - }, - mouseenter: function() { - clearTimeout(timer); - }, - freezeEvent: true - }; +
    + */ + monitorMouseLeave: function(delay, handler, scope) { + var me = this, + timer, + listeners = { + mouseleave: function(e) { + timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay); + }, + mouseenter: function() { + clearTimeout(timer); + }, + freezeEvent: true + }; - me.on(listeners); - return listeners; - }, + me.on(listeners); + return listeners; + }, - /** - * Stops the specified event(s) from bubbling and optionally prevents the default action - * @param {String/Array} eventName an event / array of events to stop from bubbling - * @param {Boolean} preventDefault (optional) true to prevent the default action too - * @return {Ext.core.Element} this - */ - swallowEvent : function(eventName, preventDefault) { - var me = this; - function fn(e) { - e.stopPropagation(); - if (preventDefault) { - e.preventDefault(); + /** + * Stops the specified event(s) from bubbling and optionally prevents the default action + * @param {String/String[]} eventName an event / array of events to stop from bubbling + * @param {Boolean} preventDefault (optional) true to prevent the default action too + * @return {Ext.Element} this + */ + swallowEvent : function(eventName, preventDefault) { + var me = this; + function fn(e) { + e.stopPropagation(); + if (preventDefault) { + e.preventDefault(); + } } - } - - if (Ext.isArray(eventName)) { - Ext.each(eventName, function(e) { - me.on(e, fn); - }); - return me; - } - me.on(eventName, fn); - return me; - }, - /** - * Create an event handler on this element such that when the event fires and is handled by this element, - * it will be relayed to another object (i.e., fired again as if it originated from that object instead). - * @param {String} eventName The type of event to relay - * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context - * for firing the relayed event - */ - relayEvent : function(eventName, observable) { - this.on(eventName, function(e) { - observable.fireEvent(eventName, e); - }); - }, + if (Ext.isArray(eventName)) { + Ext.each(eventName, function(e) { + me.on(e, fn); + }); + return me; + } + me.on(eventName, fn); + return me; + }, - /** - * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes. - * @param {Boolean} forceReclean (optional) By default the element - * keeps track if it has been cleaned already so - * you can call this over and over. However, if you update the element and - * need to force a reclean, you can pass true. - */ - clean : function(forceReclean) { - var me = this, - dom = me.dom, - n = dom.firstChild, - nx, - ni = -1; + /** + * Create an event handler on this element such that when the event fires and is handled by this element, + * it will be relayed to another object (i.e., fired again as if it originated from that object instead). + * @param {String} eventName The type of event to relay + * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context + * for firing the relayed event + */ + relayEvent : function(eventName, observable) { + this.on(eventName, function(e) { + observable.fireEvent(eventName, e); + }); + }, - if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) { - return me; - } + /** + * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes. + * @param {Boolean} forceReclean (optional) By default the element + * keeps track if it has been cleaned already so + * you can call this over and over. However, if you update the element and + * need to force a reclean, you can pass true. + */ + clean : function(forceReclean) { + var me = this, + dom = me.dom, + n = dom.firstChild, + nx, + ni = -1; + + if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) { + return me; + } - while (n) { - nx = n.nextSibling; - if (n.nodeType == 3) { - // Remove empty/whitespace text nodes - if (!(/\S/.test(n.nodeValue))) { - dom.removeChild(n); - // Combine adjacent text nodes - } else if (nx && nx.nodeType == 3) { - n.appendData(Ext.String.trim(nx.data)); - dom.removeChild(nx); - nx = n.nextSibling; + while (n) { + nx = n.nextSibling; + if (n.nodeType == 3) { + // Remove empty/whitespace text nodes + if (!(/\S/.test(n.nodeValue))) { + dom.removeChild(n); + // Combine adjacent text nodes + } else if (nx && nx.nodeType == 3) { + n.appendData(Ext.String.trim(nx.data)); + dom.removeChild(nx); + nx = n.nextSibling; + n.nodeIndex = ++ni; + } + } else { + // Recursively clean + Ext.fly(n).clean(); n.nodeIndex = ++ni; } - } else { - // Recursively clean - Ext.fly(n).clean(); - n.nodeIndex = ++ni; + n = nx; } - n = nx; - } - Ext.core.Element.data(dom, 'isCleaned', true); - return me; - }, + Ext.Element.data(dom, 'isCleaned', true); + return me; + }, - /** - * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object - * parameter as {@link Ext.ElementLoader#load} - * @return {Ext.core.Element} this - */ - load : function(options) { - this.getLoader().load(options); - return this; - }, + /** + * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object + * parameter as {@link Ext.ElementLoader#load} + * @return {Ext.Element} this + */ + load : function(options) { + this.getLoader().load(options); + return this; + }, - /** - * Gets this element's {@link Ext.ElementLoader ElementLoader} - * @return {Ext.ElementLoader} The loader - */ - getLoader : function() { - var dom = this.dom, - data = Ext.core.Element.data, - loader = data(dom, 'loader'); - - if (!loader) { - loader = Ext.create('Ext.ElementLoader', { - target: this - }); - data(dom, 'loader', loader); - } - return loader; - }, + /** + * Gets this element's {@link Ext.ElementLoader ElementLoader} + * @return {Ext.ElementLoader} The loader + */ + getLoader : function() { + var dom = this.dom, + data = Ext.Element.data, + loader = data(dom, 'loader'); + + if (!loader) { + loader = Ext.create('Ext.ElementLoader', { + target: this + }); + data(dom, 'loader', loader); + } + return loader; + }, - /** - * Update the innerHTML of this element, optionally searching for and processing scripts - * @param {String} html The new HTML - * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false) - * @param {Function} callback (optional) For async script loading you can be notified when the update completes - * @return {Ext.core.Element} this - */ - update : function(html, loadScripts, callback) { - var me = this, - id, - dom, - interval; - - if (!me.dom) { - return me; - } - html = html || ''; - dom = me.dom; + /** + * Update the innerHTML of this element, optionally searching for and processing scripts + * @param {String} html The new HTML + * @param {Boolean} [loadScripts=false] True to look for and process scripts + * @param {Function} [callback] For async script loading you can be notified when the update completes + * @return {Ext.Element} this + */ + update : function(html, loadScripts, callback) { + var me = this, + id, + dom, + interval; - if (loadScripts !== true) { - dom.innerHTML = html; - Ext.callback(callback, me); - return me; - } + if (!me.dom) { + return me; + } + html = html || ''; + dom = me.dom; - id = Ext.id(); - html += ''; - - interval = setInterval(function(){ - if (!document.getElementById(id)) { - return false; - } - clearInterval(interval); - var DOC = document, - hd = DOC.getElementsByTagName("head")[0], - re = /(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig, - srcRe = /\ssrc=([\'\"])(.*?)\1/i, - typeRe = /\stype=([\'\"])(.*?)\1/i, - match, - attrs, - srcMatch, - typeMatch, - el, - s; + if (loadScripts !== true) { + dom.innerHTML = html; + Ext.callback(callback, me); + return me; + } - while ((match = re.exec(html))) { - attrs = match[1]; - srcMatch = attrs ? attrs.match(srcRe) : false; - if (srcMatch && srcMatch[2]) { - s = DOC.createElement("script"); - s.src = srcMatch[2]; - typeMatch = attrs.match(typeRe); - if (typeMatch && typeMatch[2]) { - s.type = typeMatch[2]; - } - hd.appendChild(s); - } else if (match[2] && match[2].length > 0) { - if (window.execScript) { - window.execScript(match[2]); - } else { - window.eval(match[2]); + id = Ext.id(); + html += ''; + + interval = setInterval(function(){ + if (!document.getElementById(id)) { + return false; + } + clearInterval(interval); + var DOC = document, + hd = DOC.getElementsByTagName("head")[0], + re = /(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig, + srcRe = /\ssrc=([\'\"])(.*?)\1/i, + typeRe = /\stype=([\'\"])(.*?)\1/i, + match, + attrs, + srcMatch, + typeMatch, + el, + s; + + while ((match = re.exec(html))) { + attrs = match[1]; + srcMatch = attrs ? attrs.match(srcRe) : false; + if (srcMatch && srcMatch[2]) { + s = DOC.createElement("script"); + s.src = srcMatch[2]; + typeMatch = attrs.match(typeRe); + if (typeMatch && typeMatch[2]) { + s.type = typeMatch[2]; + } + hd.appendChild(s); + } else if (match[2] && match[2].length > 0) { + if (window.execScript) { + window.execScript(match[2]); + } else { + window.eval(match[2]); + } } } - } - - el = DOC.getElementById(id); - if (el) { - Ext.removeNode(el); - } - Ext.callback(callback, me); - }, 20); - dom.innerHTML = html.replace(/(?:)((\n|\r|.)*?)(?:<\/script>)/ig, ''); - return me; - }, - // inherit docs, overridden so we can add removeAnchor - removeAllListeners : function() { - this.removeAnchor(); - Ext.EventManager.removeAll(this.dom); - return this; - }, + el = DOC.getElementById(id); + if (el) { + Ext.removeNode(el); + } + Ext.callback(callback, me); + }, 20); + dom.innerHTML = html.replace(/(?:)((\n|\r|.)*?)(?:<\/script>)/ig, ''); + return me; + }, - /** - * Creates a proxy element of this element - * @param {String/Object} config The class name of the proxy element or a DomHelper config object - * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body) - * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false) - * @return {Ext.core.Element} The new proxy element - */ - createProxy : function(config, renderTo, matchBox) { - config = (typeof config == 'object') ? config : {tag : "div", cls: config}; + // inherit docs, overridden so we can add removeAnchor + removeAllListeners : function() { + this.removeAnchor(); + Ext.EventManager.removeAll(this.dom); + return this; + }, + + /** + * Gets the parent node of the current element taking into account Ext.scopeResetCSS + * @protected + * @return {HTMLElement} The parent element + */ + getScopeParent: function(){ + var parent = this.dom.parentNode; + return Ext.scopeResetCSS ? parent.parentNode : parent; + }, - var me = this, - proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) : - Ext.core.DomHelper.insertBefore(me.dom, config, true); + /** + * Creates a proxy element of this element + * @param {String/Object} config The class name of the proxy element or a DomHelper config object + * @param {String/HTMLElement} [renderTo] The element or element id to render the proxy to (defaults to document.body) + * @param {Boolean} [matchBox=false] True to align and size the proxy to this element now. + * @return {Ext.Element} The new proxy element + */ + createProxy : function(config, renderTo, matchBox) { + config = (typeof config == 'object') ? config : {tag : "div", cls: config}; - proxy.setVisibilityMode(Ext.core.Element.DISPLAY); - proxy.hide(); - if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded - proxy.setBox(me.getBox()); - } - return proxy; - } -}); -Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners; + var me = this, + proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) : + Ext.DomHelper.insertBefore(me.dom, config, true); + + proxy.setVisibilityMode(Ext.Element.DISPLAY); + proxy.hide(); + if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded + proxy.setBox(me.getBox()); + } + return proxy; + }, + + /** + * Checks whether this element can be focused. + * @return {Boolean} True if the element is focusable + */ + focusable: function(){ + var dom = this.dom, + nodeName = dom.nodeName.toLowerCase(), + canFocus = false, + hasTabIndex = !isNaN(dom.tabIndex); + + if (!dom.disabled) { + if (focusRe.test(nodeName)) { + canFocus = true; + } else { + canFocus = nodeName == 'a' ? dom.href || hasTabIndex : hasTabIndex; + } + } + return canFocus && this.isVisible(true); + } + }; +})()); +Ext.Element.prototype.clearListeners = Ext.Element.prototype.removeAllListeners; /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.core.Element.addMethods({ +Ext.Element.addMethods({ /** * Gets the x,y coordinates specified by the anchor position on the element. - * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo} + * @param {String} [anchor='c'] The specified anchor position. See {@link #alignTo} * for details on supported anchor positions. - * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead + * @param {Boolean} [local] True to get the local (element top/left-relative) anchor position instead * of page coordinates - * @param {Object} size (optional) An object containing the size to use for calculating anchor position + * @param {Object} [size] An object containing the size to use for calculating anchor position * {width: (target width), height: (target height)} (defaults to the element's current size) - * @return {Array} [x, y] An array containing the element's x and y coordinates + * @return {Number[]} [x, y] An array containing the element's x and y coordinates */ getAnchorXY : function(anchor, local, s){ //Passing a different size is useful for pre-calculating anchors, @@ -19209,8 +19948,8 @@ Ext.core.Element.addMethods({ var me = this, vp = me.dom == document.body || me.dom == document, - w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(), - h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(), + w = s.width || vp ? Ext.Element.getViewWidth() : me.getWidth(), + h = s.height || vp ? Ext.Element.getViewHeight() : me.getHeight(), xy, r = Math.round, o = me.getXY(), @@ -19235,14 +19974,14 @@ Ext.core.Element.addMethods({ /** * Anchors an element to another element and realigns it when the window is resized. - * @param {Mixed} element The element to align to. + * @param {String/HTMLElement/Ext.Element} element The element to align to. * @param {String} position The position to align to. - * @param {Array} offsets (optional) Offset the positioning by [x, y] - * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object - * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter + * @param {Number[]} [offsets] Offset the positioning by [x, y] + * @param {Boolean/Object} [animate] True for the default animation or a standard Element animation config object + * @param {Boolean/Number} [monitorScroll] True to monitor body scroll and reposition. If this parameter * is a number, it is used as the buffer delay (defaults to 50ms). - * @param {Function} callback The function to call after the animation finishes - * @return {Ext.core.Element} this + * @param {Function} [callback] The function to call after the animation finishes + * @return {Ext.Element} this */ anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){ var me = this, @@ -19273,7 +20012,7 @@ Ext.core.Element.addMethods({ /** * Remove any anchor to this element. See {@link #anchorTo}. - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ removeAnchor : function(){ var me = this, @@ -19291,7 +20030,7 @@ Ext.core.Element.addMethods({ // private getAnchor : function(){ - var data = Ext.core.Element.data, + var data = Ext.Element.data, dom = this.dom; if (!dom) { return; @@ -19313,7 +20052,7 @@ Ext.core.Element.addMethods({ el = Ext.get(el); if(!el || !el.dom){ Ext.Error.raise({ - sourceClass: 'Ext.core.Element', + sourceClass: 'Ext.Element', sourceMethod: 'getAlignVector', msg: 'Attempted to align an element that doesn\'t exist' }); @@ -19325,17 +20064,17 @@ Ext.core.Element.addMethods({ /** * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the * supported position values. - * @param {Mixed} element The element to align to. - * @param {String} position (optional, defaults to "tl-bl?") The position to align to. - * @param {Array} offsets (optional) Offset the positioning by [x, y] - * @return {Array} [x, y] + * @param {String/HTMLElement/Ext.Element} element The element to align to. + * @param {String} [position="tl-bl?"] The position to align to (defaults to ) + * @param {Number[]} [offsets] Offset the positioning by [x, y] + * @return {Number[]} [x, y] */ getAlignToXY : function(el, p, o){ el = Ext.get(el); if(!el || !el.dom){ Ext.Error.raise({ - sourceClass: 'Ext.core.Element', + sourceClass: 'Ext.Element', sourceMethod: 'getAlignToXY', msg: 'Attempted to align an element that doesn\'t exist' }); @@ -19354,8 +20093,8 @@ Ext.core.Element.addMethods({ w, h, r, - dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie - dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie + dw = Ext.Element.getViewWidth() -10, // 10px of margin for ie + dh = Ext.Element.getViewHeight()-10, // 10px of margin for ie p1y, p1x, p2y, @@ -19374,7 +20113,7 @@ Ext.core.Element.addMethods({ if(!m){ Ext.Error.raise({ - sourceClass: 'Ext.core.Element', + sourceClass: 'Ext.Element', sourceMethod: 'getAlignToXY', el: el, position: p, @@ -19471,11 +20210,11 @@ el.alignTo("other-el", "br-l?"); // adjust the x position by -6 pixels (and the y position by 0) el.alignTo("other-el", "c-bl", [-6, 0]); - * @param {Mixed} element The element to align to. - * @param {String} position (optional, defaults to "tl-bl?") The position to align to. - * @param {Array} offsets (optional) Offset the positioning by [x, y] - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @param {String/HTMLElement/Ext.Element} element The element to align to. + * @param {String} [position="tl-bl?"] The position to align to + * @param {Number[]} [offsets] Offset the positioning by [x, y] + * @param {Boolean/Object} [animate] true for the default animation or a standard Element animation config object + * @return {Ext.Element} this */ alignTo : function(element, position, offsets, animate){ var me = this; @@ -19502,7 +20241,7 @@ el.alignTo("other-el", "c-bl", [-6, 0]); * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained. * @param proposedPosition {Array} A proposed [X, Y] position to test for validity and to produce a vector for instead * of using this Element's current position; - * @returns {Array} If this element needs to be translated, an [X, Y] + * @returns {Number[]/Boolean} If this element needs to be translated, an [X, Y] * vector by which this element must be translated. Otherwise, false. */ getConstrainVector: function(constrainTo, proposedPosition) { @@ -19549,7 +20288,7 @@ el.alignTo("other-el", "c-bl", [-6, 0]); /** * Calculates the x, y to center this element on the screen - * @return {Array} The x, y values [x, y] + * @return {Number[]} The x, y values [x, y] */ getCenterXY : function(){ return this.getAlignToXY(document, 'c-c'); @@ -19557,7 +20296,7 @@ el.alignTo("other-el", "c-bl", [-6, 0]); /** * Centers the Element in either the viewport, or another Element. - * @param {Mixed} centerIn (optional) The element in which to center the element. + * @param {String/HTMLElement/Ext.Element} centerIn (optional) The element in which to center the element. */ center : function(centerIn){ return this.alignTo(centerIn || document, 'c-c'); @@ -19565,11 +20304,11 @@ el.alignTo("other-el", "c-bl", [-6, 0]); }); /** - * @class Ext.core.Element + * @class Ext.Element */ (function(){ -var ELEMENT = Ext.core.Element, +var ELEMENT = Ext.Element, LEFT = "left", RIGHT = "right", TOP = "top", @@ -19580,7 +20319,7 @@ var ELEMENT = Ext.core.Element, AUTO = "auto", ZINDEX = "z-index"; -Ext.override(Ext.core.Element, { +Ext.override(Ext.Element, { /** * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). * @return {Number} The X position of the element @@ -19599,7 +20338,7 @@ Ext.override(Ext.core.Element, { /** * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @return {Array} The XY position of the element + * @return {Number[]} The XY position of the element */ getXY : function(){ return ELEMENT.getXY(this.dom); @@ -19607,8 +20346,8 @@ Ext.override(Ext.core.Element, { /** * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates. - * @param {Mixed} element The element to get the offsets from. - * @return {Array} The XY page offsets (e.g. [100, -200]) + * @param {String/HTMLElement/Ext.Element} element The element to get the offsets from. + * @return {Number[]} The XY page offsets (e.g. [100, -200]) */ getOffsetsTo : function(el){ var o = this.getXY(), @@ -19620,7 +20359,7 @@ Ext.override(Ext.core.Element, { * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). * @param {Number} The X position of the element * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setX : function(x, animate){ return this.setXY([x, this.getY()], animate); @@ -19630,7 +20369,7 @@ Ext.override(Ext.core.Element, { * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). * @param {Number} The Y position of the element * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setY : function(y, animate){ return this.setXY([this.getX(), y], animate); @@ -19639,7 +20378,7 @@ Ext.override(Ext.core.Element, { /** * Sets the element's left position directly using CSS style (instead of {@link #setX}). * @param {String} left The left CSS property value - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setLeft : function(left){ this.setStyle(LEFT, this.addUnits(left)); @@ -19649,7 +20388,7 @@ Ext.override(Ext.core.Element, { /** * Sets the element's top position directly using CSS style (instead of {@link #setY}). * @param {String} top The top CSS property value - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setTop : function(top){ this.setStyle(TOP, this.addUnits(top)); @@ -19659,7 +20398,7 @@ Ext.override(Ext.core.Element, { /** * Sets the element's CSS right style. * @param {String} right The right CSS property value - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setRight : function(right){ this.setStyle(RIGHT, this.addUnits(right)); @@ -19669,7 +20408,7 @@ Ext.override(Ext.core.Element, { /** * Sets the element's CSS bottom style. * @param {String} bottom The bottom CSS property value - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setBottom : function(bottom){ this.setStyle(BOTTOM, this.addUnits(bottom)); @@ -19679,9 +20418,9 @@ Ext.override(Ext.core.Element, { /** * Sets the position of the element in page coordinates, regardless of how the element is positioned. * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based) + * @param {Number[]} pos Contains X & Y [x, y] values for new position (coordinates are page-based) * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setXY: function(pos, animate) { var me = this; @@ -19703,7 +20442,7 @@ Ext.override(Ext.core.Element, { * @param {Number} x X value for new position (coordinates are page-based) * @param {Number} y Y value for new position (coordinates are page-based) * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setLocation : function(x, y, animate){ return this.setXY([x, y], animate); @@ -19715,7 +20454,7 @@ Ext.override(Ext.core.Element, { * @param {Number} x X value for new position (coordinates are page-based) * @param {Number} y Y value for new position (coordinates are page-based) * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ moveTo : function(x, y, animate){ return this.setXY([x, y], animate); @@ -19786,7 +20525,7 @@ Ext.override(Ext.core.Element, { /** * Clear positioning back to the default when the document was loaded * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'. - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ clearPositioning : function(value){ value = value || ''; @@ -19822,7 +20561,7 @@ Ext.override(Ext.core.Element, { /** * Set positioning with an object returned by getPositioning(). * @param {Object} posCfg - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setPositioning : function(pc){ var me = this, @@ -19842,7 +20581,7 @@ Ext.override(Ext.core.Element, { /** * Translates the passed page coordinates into left/top css values for this element - * @param {Number/Array} x The page x or an array containing [x, y] + * @param {Number/Number[]} x The page x or an array containing [x, y] * @param {Number} y (optional) The page y, required if x is not an array * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)} */ @@ -19876,7 +20615,7 @@ Ext.override(Ext.core.Element, { * @param {Object} box The box to fill {x, y, width, height} * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setBox: function(box, adjust, animate) { var me = this, @@ -19958,7 +20697,6 @@ Ext.override(Ext.core.Element, { * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down"). * @param {Number} distance How far to move the element in pixels * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this */ move: function(direction, distance, animate) { var me = this, @@ -19990,7 +20728,7 @@ Ext.override(Ext.core.Element, { * Quick set left and top adding default units * @param {String} left The left CSS property value * @param {String} top The top CSS property value - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setLeftTop: function(left, top) { var me = this, @@ -20003,7 +20741,7 @@ Ext.override(Ext.core.Element, { /** * Returns the region of this element. * The element must be part of the DOM tree to have a region (display:none or elements not appended return false). - * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data. + * @return {Ext.util.Region} A Region containing "top, left, bottom, right" member data. */ getRegion: function() { return this.getPageBox(true); @@ -20011,20 +20749,20 @@ Ext.override(Ext.core.Element, { /** * Returns the content region of this element. That is the region within the borders and padding. - * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data. + * @return {Ext.util.Region} A Region containing "top, left, bottom, right" member data. */ getViewRegion: function() { var me = this, isBody = me.dom === document.body, scroll, pos, top, left, width, height; - + // For the body we want to do some special logic if (isBody) { scroll = me.getScroll(); left = scroll.left; top = scroll.top; - width = Ext.core.Element.getViewportWidth(); - height = Ext.core.Element.getViewportHeight(); + width = Ext.Element.getViewportWidth(); + height = Ext.Element.getViewportHeight(); } else { pos = me.getXY(); @@ -20058,8 +20796,8 @@ Ext.override(Ext.core.Element, { var me = this, el = me.dom, isDoc = el === document.body, - w = isDoc ? Ext.core.Element.getViewWidth() : el.offsetWidth, - h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight, + w = isDoc ? Ext.Element.getViewWidth() : el.offsetWidth, + h = isDoc ? Ext.Element.getViewHeight() : el.offsetHeight, xy = me.getXY(), t = xy[1], r = xy[0] + w, @@ -20085,16 +20823,16 @@ Ext.override(Ext.core.Element, { * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently. * @param {Number} x X value for new position (coordinates are page-based) * @param {Number} y Y value for new position (coordinates are page-based) - * @param {Mixed} width The new width. This may be one of:
      + * @param {Number/String} width The new width. This may be one of:
        *
      • A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)
      • *
      • A String used to set the CSS width style. Animation may not be used. *
      - * @param {Mixed} height The new height. This may be one of:
        + * @param {Number/String} height The new height. This may be one of:
          *
        • A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)
        • *
        • A String used to set the CSS height style. Animation may not be used.
        • *
        * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setBounds: function(x, y, width, height, animate) { var me = this; @@ -20121,7 +20859,7 @@ Ext.override(Ext.core.Element, { * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently. * @param {Ext.util.Region} region The region to fill * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ setRegion: function(region, animate) { return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate); @@ -20130,9 +20868,9 @@ Ext.override(Ext.core.Element, { })(); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.override(Ext.core.Element, { +Ext.override(Ext.Element, { /** * Returns true if this element is scrollable. * @return {Boolean} @@ -20182,7 +20920,7 @@ Ext.override(Ext.core.Element, { * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values. * @param {Number} value The new scroll value * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Element} this + * @return {Ext.Element} this */ scrollTo : function(side, value, animate) { //check if we're scrolling top or left @@ -20210,10 +20948,10 @@ Ext.override(Ext.core.Element, { /** * Scrolls this element into view within the passed container. - * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a - * string (id), dom node, or Ext.core.Element. + * @param {String/HTMLElement/Ext.Element} container (optional) The container element to scroll (defaults to document.body). Should be a + * string (id), dom node, or Ext.Element. * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true) - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ scrollIntoView : function(container, hscroll) { container = Ext.getDom(container) || Ext.getBody().dom; @@ -20292,9 +21030,9 @@ Ext.override(Ext.core.Element, { } }); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.core.Element.addMethods( +Ext.Element.addMethods( function() { var VISIBILITY = "visibility", DISPLAY = "display", @@ -20302,12 +21040,12 @@ Ext.core.Element.addMethods( NONE = "none", XMASKED = Ext.baseCSSPrefix + "masked", XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative", - data = Ext.core.Element.data; + data = Ext.Element.data; return { /** * Checks whether the element is currently visible using both visibility and display properties. - * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false) + * @param {Boolean} [deep=false] True to walk the dom and see if parent elements are hidden * @return {Boolean} True if the element is currently visible, else false */ isVisible : function(deep) { @@ -20338,10 +21076,10 @@ Ext.core.Element.addMethods( /** * Convenience method for setVisibilityMode(Element.DISPLAY) * @param {String} display (optional) What to set display to when visible - * @return {Ext.core.Element} this + * @return {Ext.Element} this */ enableDisplayMode : function(display) { - this.setVisibilityMode(Ext.core.Element.DISPLAY); + this.setVisibilityMode(Ext.Element.DISPLAY); if (!Ext.isEmpty(display)) { data(this.dom, 'originalDisplay', display); @@ -20355,13 +21093,13 @@ Ext.core.Element.addMethods( * This method can only be applied to elements which accept child nodes. * @param {String} msg (optional) A message to display in the mask * @param {String} msgCls (optional) A css class to apply to the msg element - * @return {Element} The mask element + * @return {Ext.Element} The mask element */ mask : function(msg, msgCls) { var me = this, dom = me.dom, setExpression = dom.style.setExpression, - dh = Ext.core.DomHelper, + dh = Ext.DomHelper, EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg", el, mask; @@ -20459,7 +21197,7 @@ Ext.core.Element.addMethods( /** * Creates an iframe shim for this element to keep selects and other windowed objects from * showing through. - * @return {Ext.core.Element} The new shim element + * @return {Ext.Element} The new shim element */ createShim : function() { var el = document.createElement('iframe'), @@ -20476,12 +21214,12 @@ Ext.core.Element.addMethods( }() ); /** - * @class Ext.core.Element + * @class Ext.Element */ -Ext.core.Element.addMethods({ +Ext.Element.addMethods({ /** * Convenience method for constructing a KeyMap - * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options: + * @param {String/Number/Number[]/Object} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options: * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)} * @param {Function} fn The function to call * @param {Object} scope (optional) The scope (this reference) in which the specified function is executed. Defaults to this Element. @@ -20518,8 +21256,8 @@ Ext.core.Element.addMethods({ } }); -//Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because -//Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build +//Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because +//Element.keys.js is the last extra Ext.Element include in the ext-all.js build Ext.CompositeElementLite.importElementMethods(); /** @@ -20531,7 +21269,7 @@ Ext.apply(Ext.CompositeElementLite.prototype, { return this; } if(typeof els == "string"){ - els = Ext.core.Element.selectorFunction(els, root); + els = Ext.Element.selectorFunction(els, root); } var yels = this.elements; Ext.each(els, function(e) { @@ -20542,7 +21280,7 @@ Ext.apply(Ext.CompositeElementLite.prototype, { /** * Returns the first Element - * @return {Ext.core.Element} + * @return {Ext.Element} */ first : function(){ return this.item(0); @@ -20550,7 +21288,7 @@ Ext.apply(Ext.CompositeElementLite.prototype, { /** * Returns the last Element - * @return {Ext.core.Element} + * @return {Ext.Element} */ last : function(){ return this.item(this.getCount()-1); @@ -20558,7 +21296,7 @@ Ext.apply(Ext.CompositeElementLite.prototype, { /** * Returns true if this composite contains the passed element - * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection. + * @param el {String/HTMLElement/Ext.Element/Number} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection. * @return Boolean */ contains : function(el){ @@ -20567,10 +21305,10 @@ Ext.apply(Ext.CompositeElementLite.prototype, { /** * Removes the specified element(s). - * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite + * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite * or an array of any of those. * @param {Boolean} removeDom (optional) True to also remove the element from the document - * @return {CompositeElement} this + * @return {Ext.CompositeElement} this */ removeElement : function(keys, removeDom){ var me = this, @@ -20597,7 +21335,7 @@ Ext.apply(Ext.CompositeElementLite.prototype, { * @extends Ext.CompositeElementLite *

        This class encapsulates a collection of DOM elements, providing methods to filter * members, or to perform collective actions upon the whole set.

        - *

        Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and + *

        Although they are not listed, this class supports all of the methods of {@link Ext.Element} and * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.

        *

        All methods return this and can be chained.

        * Usage: @@ -20614,80 +21352,44 @@ els.setWidth(100).hide(true); */ Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, { - + constructor : function(els, root){ this.elements = []; this.add(els, root); }, - + // private getElement : function(el){ // In this case just return it, since we already have a reference to it return el; }, - + // private transformElement : function(el){ return Ext.get(el); } - - /** - * Adds elements to this composite. - * @param {String/Array} els A string CSS selector, an array of elements or an element - * @return {CompositeElement} this - */ - - /** - * Returns the Element object at the specified index - * @param {Number} index - * @return {Ext.core.Element} - */ - - /** - * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}. - * @param {Function} fn - -The function to be called with each -`element`. If the supplied function returns false, -iteration stops. This function is called with the following arguments: - -- `element` : __Ext.core.Element++ - The element at the current `index` in the `composite` - -- `composite` : __Object__ - This composite. - -- `index` : __Number__ - The current index within the `composite` - - * @param {Object} scope (optional) The scope (this reference) in which the specified function is executed. - * Defaults to the element at the current index - * within the composite. - * @return {CompositeElement} this - * @markdown - */ }); /** - * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements - * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object) - * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} - * @member Ext.core.Element + * @param {String/HTMLElement[]} selector The CSS selector or an array of elements + * @param {Boolean} [unique] true to create a unique Ext.Element for each element (defaults to a shared flyweight object) + * @param {HTMLElement/String} [root] The root element of the query or id of the root + * @return {Ext.CompositeElementLite/Ext.CompositeElement} + * @member Ext.Element * @method select */ -Ext.core.Element.select = function(selector, unique, root){ +Ext.Element.select = function(selector, unique, root){ var els; if(typeof selector == "string"){ - els = Ext.core.Element.selectorFunction(selector, root); + els = Ext.Element.selectorFunction(selector, root); }else if(selector.length !== undefined){ els = selector; }else{ Ext.Error.raise({ - sourceClass: "Ext.core.Element", + sourceClass: "Ext.Element", sourceMethod: "select", selector: selector, unique: unique, @@ -20699,17 +21401,12 @@ Ext.core.Element.select = function(selector, unique, root){ }; /** - * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods - * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or - * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements - * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object) - * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} + * Shorthand of {@link Ext.Element#select}. * @member Ext * @method select + * @alias Ext.Element#select */ -Ext.select = Ext.core.Element.select; +Ext.select = Ext.Element.select; /* @@ -20746,7 +21443,7 @@ If you are unsure which license is appropriate for your use, please contact the * this.listeners = config.listeners; * * // Call our superclass constructor to complete construction process. - * Employee.superclass.constructor.call(this, config) + * this.callParent(arguments) * } * }); * @@ -20772,7 +21469,7 @@ Ext.define('Ext.util.Observable', { /** * Removes **all** added captures from the Observable. * - * @param {Observable} o The Observable to release + * @param {Ext.util.Observable} o The Observable to release * @static */ releaseCapture: function(o) { @@ -20784,7 +21481,7 @@ Ext.define('Ext.util.Observable', { * name + standard signature of the event **before** the event is fired. If the supplied function returns false, * the event will not fire. * - * @param {Observable} o The Observable to capture events from. + * @param {Ext.util.Observable} o The Observable to capture events from. * @param {Function} fn The function to call when an event is fired. * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to * the Observable firing the event. @@ -20834,9 +21531,9 @@ Ext.define('Ext.util.Observable', { * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple * handlers at once. * - * **DOM events from ExtJS {@link Ext.Component Components}** + * **DOM events from Ext JS {@link Ext.Component Components}** * - * While _some_ ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually + * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a * child element of a Component, we need to specify the `element` option to identify the Component property to add a @@ -20879,13 +21576,13 @@ Ext.define('Ext.util.Observable', { }, // @private - eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/, + eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/, /** - * Adds listeners to any Observable object (or Element) which are automatically removed when this Component is + * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is * destroyed. * - * @param {Observable/Element} item The item to which to add a listener/listeners. + * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners. * @param {Object/String} ename The event name, or an object containing event name properties. * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function. * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference) @@ -20925,10 +21622,10 @@ Ext.define('Ext.util.Observable', { /** * Removes listeners that were added by the {@link #mon} method. * - * @param {Observable|Element} item The item from which to remove a listener/listeners. - * @param {Object|String} ename The event name, or an object containing event name properties. - * @param {Function} fn Optional. If the `ename` parameter was an event name, this is the handler function. - * @param {Object} scope Optional. If the `ename` parameter was an event name, this is the scope (`this` reference) + * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners. + * @param {Object/String} ename The event name, or an object containing event name properties. + * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function. + * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference) * in which the handler function is executed. */ removeManagedListener : function(item, ename, fn, scope) { @@ -20969,51 +21666,72 @@ Ext.define('Ext.util.Observable', { * @param {Object...} args Variable number of parameters are passed to handlers. * @return {Boolean} returns false if any of the handlers return false otherwise it returns true. */ - fireEvent: function() { - var me = this, - args = Ext.Array.toArray(arguments), - ename = args[0].toLowerCase(), - ret = true, - event = me.events[ename], - queue = me.eventQueue, - parent; + fireEvent: function(eventName) { + var name = eventName.toLowerCase(), + events = this.events, + event = events && events[name], + bubbles = event && event.bubble; - if (me.eventsSuspended === true) { - if (queue) { - queue.push(args); - } - } else if (event && event !== true) { - if (event.bubble) { - if (event.fire.apply(event, args.slice(1)) === false) { - return false; + return this.continueFireEvent(name, Ext.Array.slice(arguments, 1), bubbles); + }, + + /** + * Continue to fire event. + * @private + * + * @param {String} eventName + * @param {Array} args + * @param {Boolean} bubbles + */ + continueFireEvent: function(eventName, args, bubbles) { + var target = this, + queue, event, + ret = true; + + do { + if (target.eventsSuspended === true) { + if ((queue = target.eventQueue)) { + queue.push([eventName, args, bubbles]); } - parent = me.getBubbleTarget && me.getBubbleTarget(); - if (parent && parent.isObservable) { - if (!parent.events[ename] || parent.events[ename] === true || !parent.events[ename].bubble) { - parent.enableBubble(ename); + return ret; + } else { + event = target.events[eventName]; + // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it + // configure to bubble. + if (event && event != true) { + if ((ret = event.fire.apply(event, args)) === false) { + break; } - return parent.fireEvent.apply(parent, args); - } - } - else { - args.shift(); - ret = event.fire.apply(event, args); + } } - } + } while (bubbles && (target = target.getBubbleParent())); return ret; }, + /** + * Gets the bubbling parent for an Observable + * @private + * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists + */ + getBubbleParent: function(){ + var me = this, parent = me.getBubbleTarget && me.getBubbleTarget(); + if (parent && parent.isObservable) { + return parent; + } + return null; + }, + /** * Appends an event handler to this object. * * @param {String} eventName The name of the event to listen for. May also be an object who's property names are * event names. - * @param {Function} handler The method the event invokes. Will be called with arguments given to + * @param {Function} fn The method the event invokes. Will be called with arguments given to * {@link #fireEvent} plus the `options` parameter described below. - * @param {Object} scope (optional) The scope (`this` reference) in which the handler function is executed. **If + * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If * omitted, defaults to the object which fired the event.** - * @param {Object} options (optional) An object containing handler configuration. - * + * @param {Object} [options] An object containing handler configuration. + * * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler. * * This object may contain any of the following properties: @@ -21121,9 +21839,10 @@ Ext.define('Ext.util.Observable', { * Removes an event handler. * * @param {String} eventName The type of event the handler was associated with. - * @param {Function} handler The handler to remove. **This must be a reference to the function passed into the + * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the * {@link #addListener} call.** - * @param {Object} scope (optional) The scope originally specified for the handler. + * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the + * scope argument specified in the original call to {@link #addListener} or the listener will not be removed. */ removeListener: function(ename, fn, scope) { var me = this, @@ -21191,7 +21910,7 @@ Ext.define('Ext.util.Observable', { this.managedListeners = []; }, - + /** * Remove a single managed listener item * @private @@ -21204,7 +21923,7 @@ Ext.define('Ext.util.Observable', { managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope); if (!isClear) { Ext.Array.remove(this.managedListeners, managedListener); - } + } } }, @@ -21226,7 +21945,7 @@ Ext.define('Ext.util.Observable', { * storecleared: true * }); * - * @param {String...} more Optional additional event names if multiple event names are being passed as separate + * @param {String...} more (optional) Additional event names if multiple event names are being passed as separate * parameters. Usage: * * this.addEvents('storeloaded', 'storecleared'); @@ -21237,12 +21956,12 @@ Ext.define('Ext.util.Observable', { args, len, i; - + me.events = me.events || {}; if (Ext.isString(o)) { args = arguments; i = args.length; - + while (i--) { me.events[args[i]] = me.events[args[i]] || true; } @@ -21277,29 +21996,30 @@ Ext.define('Ext.util.Observable', { /** * Resumes firing events (see {@link #suspendEvents}). - * - * If events were suspended using the `**queueSuspended**` parameter, then all events fired + * + * If events were suspended using the `queueSuspended` parameter, then all events fired * during event suspension will be sent to any listeners now. */ resumeEvents: function() { var me = this, - queued = me.eventQueue || []; + queued = me.eventQueue; me.eventsSuspended = false; delete me.eventQueue; - Ext.each(queued, - function(e) { - me.fireEvent.apply(me, e); - }); + if (queued) { + Ext.each(queued, function(e) { + me.continueFireEvent.apply(me, e); + }); + } }, /** * Relays selected events from the specified Observable as if the events were fired by `this`. * * @param {Object} origin The Observable whose events this object is to relay. - * @param {[String]} events Array of event names to relay. - * @param {Object} prefix + * @param {String[]} events Array of event names to relay. + * @param {String} prefix */ relayEvents : function(origin, events, prefix) { prefix = prefix || ''; @@ -21369,7 +22089,7 @@ Ext.define('Ext.util.Observable', { * } * }); * - * @param {String/[String]} events The event name to bubble, or an Array of event names. + * @param {String/String[]} events The event name to bubble, or an Array of event names. */ enableBubble: function(events) { var me = this; @@ -21540,7 +22260,7 @@ Ext.define('Ext.util.Observable', { * This animation class is a mixin. * * Ext.util.Animate provides an API for the creation of animated transitions of properties and styles. - * This class is used as a mixin and currently applied to {@link Ext.core.Element}, {@link Ext.CompositeElement}, + * This class is used as a mixin and currently applied to {@link Ext.Element}, {@link Ext.CompositeElement}, * {@link Ext.draw.Sprite}, {@link Ext.draw.CompositeSprite}, and {@link Ext.Component}. Note that Components * have a limited subset of what attributes can be animated such as top, left, x, y, height, width, and * opacity (color, paddings, and margins can not be animated). @@ -21658,7 +22378,7 @@ Ext.define('Ext.util.Observable', { * * ## Animation Keyframes * - * You can also set up complex animations with {@link Ext.fx.Anim#keyframe keyframe} which follows the + * You can also set up complex animations with {@link Ext.fx.Anim#keyframes keyframes} which follow the * CSS3 Animation configuration pattern. Note rotation, translation, and scaling can only be done for sprites. * The previous example can be written with the following syntax: * @@ -21682,7 +22402,7 @@ Ext.define('Ext.util.Observable', { * * ## Animation Events * - * Each animation you create has events for {@link Ext.fx.Anim#beforeanimation beforeanimation}, + * Each animation you create has events for {@link Ext.fx.Anim#beforeanimate beforeanimate}, * {@link Ext.fx.Anim#afteranimate afteranimate}, and {@link Ext.fx.Anim#lastframe lastframe}. * Keyframed animations adds an additional {@link Ext.fx.Animator#keyframe keyframe} event which * fires for each keyframe in your animation. @@ -21743,7 +22463,7 @@ Ext.define('Ext.util.Animate', { /** *

        Perform custom animation on this object.

        - *

        This method is applicable to both the {@link Ext.Component Component} class and the {@link Ext.core.Element Element} class. + *

        This method is applicable to both the {@link Ext.Component Component} class and the {@link Ext.Element Element} class. * It performs animated transitions of certain properties of this object over a specified timeline.

        *

        The sole parameter is an object which specifies start property values, end property values, and properties which * describe the timeline. Of the properties listed below, only to is mandatory.

        @@ -21770,7 +22490,7 @@ Ext.define('Ext.util.Animate', { *
      • listeners
        This is a standard {@link Ext.util.Observable#listeners listeners} configuration object which may be used * to inject behaviour at either the beforeanimate event or the afteranimate event.
      • *

      - *

      Animating an {@link Ext.core.Element Element}

      + *

      Animating an {@link Ext.Element Element}

      * When animating an Element, the following properties may be specified in from, to, and keyframe objects:
        *
      • x
        The page X position in pixels.
      • *
      • y
        The page Y position in pixels
      • @@ -21864,7 +22584,7 @@ myWindow.header.el.on('click', function() { * @deprecated 4.0 Replaced by {@link #stopAnimation} * Stops any running effects and clears this object's internal effects queue if it contains * any additional effects that haven't started yet. - * @return {Ext.core.Element} The Element + * @return {Ext.Element} The Element * @method */ stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'), @@ -21872,7 +22592,7 @@ myWindow.header.el.on('click', function() { /** * Stops any running effects and clears this object's internal effects queue if it contains * any additional effects that haven't started yet. - * @return {Ext.core.Element} The Element + * @return {Ext.Element} The Element */ stopAnimation: function() { Ext.fx.Manager.stopAnimation(this.id); @@ -21905,22 +22625,21 @@ myWindow.header.el.on('click', function() { /** * @deprecated 4.0 Replaced by {@link #getActiveAnimation} - * Returns thq current animation if this object has any effects actively running or queued, else returns false. - * @return {Mixed} anim if element has active effects, else false + * @alias Ext.util.Animate#getActiveAnimation * @method */ hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'), /** - * Returns thq current animation if this object has any effects actively running or queued, else returns false. - * @return {Mixed} anim if element has active effects, else false + * Returns the current animation if this object has any effects actively running or queued, else returns false. + * @return {Ext.fx.Anim/Boolean} Anim if element has active effects, else false */ getActiveAnimation: function() { return Ext.fx.Manager.getActiveAnimation(this.id); } }, function(){ // Apply Animate mixin manually until Element is defined in the proper 4.x way - Ext.applyIf(Ext.core.Element.prototype, this.prototype); + Ext.applyIf(Ext.Element.prototype, this.prototype); // We need to call this again so the animation methods get copied over to CE Ext.CompositeElementLite.importElementMethods(); }); @@ -21954,7 +22673,7 @@ Ext.define('Ext.state.Provider', { /** * @event statechange * Fires when a state change occurs. - * @param {Provider} this This state provider + * @param {Ext.state.Provider} this This state provider * @param {String} key The state key which was changed * @param {String} value The encoded value for the state */ @@ -21966,8 +22685,8 @@ Ext.define('Ext.state.Provider', { /** * Returns the current value for a key * @param {String} name The key name - * @param {Mixed} defaultValue A default value to return if the key's value is not found - * @return {Mixed} The state data + * @param {Object} defaultValue A default value to return if the key's value is not found + * @return {Object} The state data */ get : function(name, defaultValue){ return typeof this.state[name] == "undefined" ? @@ -21987,7 +22706,7 @@ Ext.define('Ext.state.Provider', { /** * Sets the value for a key * @param {String} name The key name - * @param {Mixed} value The value to set + * @param {Object} value The value to set */ set : function(name, value){ var me = this; @@ -21998,7 +22717,7 @@ Ext.define('Ext.state.Provider', { /** * Decodes a string previously encoded with {@link #encodeValue}. * @param {String} value The value to decode - * @return {Mixed} The decoded value + * @return {Object} The decoded value */ decodeValue : function(value){ @@ -22057,7 +22776,7 @@ Ext.define('Ext.state.Provider', { /** * Encodes a value including type information. Decode with {@link #decodeValue}. - * @param {Mixed} value The value to encode + * @param {Object} value The value to encode * @return {String} The encoded value */ encodeValue : function(value){ @@ -22097,236 +22816,749 @@ Ext.define('Ext.state.Provider', { } }); /** - * @class Ext.util.HashMap - *

        - * Represents a collection of a set of key and value pairs. Each key in the HashMap - * must be unique, the same key cannot exist twice. Access to items is provided via - * the key only. Sample usage: - *

        
        -var map = new Ext.util.HashMap();
        -map.add('key1', 1);
        -map.add('key2', 2);
        -map.add('key3', 3);
        -
        -map.each(function(key, value, length){
        -    console.log(key, value, length);
        -});
        - * 
        - *

        + * Provides searching of Components within Ext.ComponentManager (globally) or a specific + * Ext.container.Container on the document with a similar syntax to a CSS selector. * - *

        The HashMap is an unordered class, - * there is no guarantee when iterating over the items that they will be in any particular - * order. If this is required, then use a {@link Ext.util.MixedCollection}. - *

        + * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix + * + * - `component` or `.component` + * - `gridpanel` or `.gridpanel` + * + * An itemId or id must be prefixed with a # + * + * - `#myContainer` + * + * Attributes must be wrapped in brackets + * + * - `component[autoScroll]` + * - `panel[title="Test"]` + * + * Member expressions from candidate Components may be tested. If the expression returns a *truthy* value, + * the candidate Component will be included in the query: + * + * var disabledFields = myFormPanel.query("{isDisabled()}"); + * + * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}: + * + * // Function receives array and returns a filtered array. + * Ext.ComponentQuery.pseudos.invalid = function(items) { + * var i = 0, l = items.length, c, result = []; + * for (; i < l; i++) { + * if (!(c = items[i]).isValid()) { + * result.push(c); + * } + * } + * return result; + * }; + * + * var invalidFields = myFormPanel.query('field:invalid'); + * if (invalidFields.length) { + * invalidFields[0].getEl().scrollIntoView(myFormPanel.body); + * for (var i = 0, l = invalidFields.length; i < l; i++) { + * invalidFields[i].getEl().frame("red"); + * } + * } + * + * Default pseudos include: + * + * - not + * - last + * + * Queries return an array of components. + * Here are some example queries. + * + * // retrieve all Ext.Panels in the document by xtype + * var panelsArray = Ext.ComponentQuery.query('panel'); + * + * // retrieve all Ext.Panels within the container with an id myCt + * var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel'); + * + * // retrieve all direct children which are Ext.Panels within myCt + * var directChildPanel = Ext.ComponentQuery.query('#myCt > panel'); + * + * // retrieve all grids and trees + * var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel'); + * + * For easy access to queries based from a particular Container see the {@link Ext.container.Container#query}, + * {@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see + * {@link Ext.Component#up}. */ -Ext.define('Ext.util.HashMap', { +Ext.define('Ext.ComponentQuery', { + singleton: true, + uses: ['Ext.ComponentManager'] +}, function() { - /** - * @cfg {Function} keyFn A function that is used to retrieve a default key for a passed object. - * A default is provided that returns the id property on the object. This function is only used - * if the add method is called with a single argument. - */ + var cq = this, - mixins: { - observable: 'Ext.util.Observable' - }, + // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied + // as a member on each item in the passed array. + filterFnPattern = [ + 'var r = [],', + 'i = 0,', + 'it = items,', + 'l = it.length,', + 'c;', + 'for (; i < l; i++) {', + 'c = it[i];', + 'if (c.{0}) {', + 'r.push(c);', + '}', + '}', + 'return r;' + ].join(''), - /** - * Creates new HashMap. - * @param {Object} config (optional) Config object. - */ - constructor: function(config) { - config = config || {}; - - var me = this, - keyFn = config.keyFn; + filterItems = function(items, operation) { + // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...] + // The operation's method loops over each item in the candidate array and + // returns an array of items which match its criteria + return operation.method.apply(this, [ items ].concat(operation.args)); + }, - me.addEvents( - /** - * @event add - * Fires when a new item is added to the hash - * @param {Ext.util.HashMap} this. - * @param {String} key The key of the added item. - * @param {Object} value The value of the added item. - */ - 'add', - /** - * @event clear - * Fires when the hash is cleared. - * @param {Ext.util.HashMap} this. - */ - 'clear', - /** - * @event remove - * Fires when an item is removed from the hash. - * @param {Ext.util.HashMap} this. - * @param {String} key The key of the removed item. - * @param {Object} value The value of the removed item. - */ - 'remove', - /** - * @event replace - * Fires when an item is replaced in the hash. - * @param {Ext.util.HashMap} this. - * @param {String} key The key of the replaced item. - * @param {Object} value The new value for the item. - * @param {Object} old The old value for the item. - */ - 'replace' - ); + getItems = function(items, mode) { + var result = [], + i = 0, + length = items.length, + candidate, + deep = mode !== '>'; + + for (; i < length; i++) { + candidate = items[i]; + if (candidate.getRefItems) { + result = result.concat(candidate.getRefItems(deep)); + } + } + return result; + }, - me.mixins.observable.constructor.call(me, config); - me.clear(true); - - if (keyFn) { - me.getKey = keyFn; - } - }, + getAncestors = function(items) { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) { + result.push(candidate); + } + } + return result; + }, - /** - * Gets the number of items in the hash. - * @return {Number} The number of items in the hash. - */ - getCount: function() { - return this.length; - }, + // Filters the passed candidate array and returns only items which match the passed xtype + filterByXType = function(items, xtype, shallow) { + if (xtype === '*') { + return items.slice(); + } + else { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + if (candidate.isXType(xtype, shallow)) { + result.push(candidate); + } + } + return result; + } + }, - /** - * Implementation for being able to extract the key from an object if only - * a single argument is passed. - * @private - * @param {String} key The key - * @param {Object} value The value - * @return {Array} [key, value] - */ - getData: function(key, value) { - // if we have no value, it means we need to get the key from the object - if (value === undefined) { - value = key; - key = this.getKey(value); - } + // Filters the passed candidate array and returns only items which have the passed className + filterByClassName = function(items, className) { + var EA = Ext.Array, + result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) { + result.push(candidate); + } + } + return result; + }, - return [key, value]; - }, + // Filters the passed candidate array and returns only items which have the specified property match + filterByAttribute = function(items, property, operator, value) { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + if (!value ? !!candidate[property] : (String(candidate[property]) === value)) { + result.push(candidate); + } + } + return result; + }, - /** - * Extracts the key from an object. This is a default implementation, it may be overridden - * @param {Object} o The object to get the key from - * @return {String} The key to use. - */ - getKey: function(o) { - return o.id; - }, + // Filters the passed candidate array and returns only items which have the specified itemId or id + filterById = function(items, id) { + var result = [], + i = 0, + length = items.length, + candidate; + for (; i < length; i++) { + candidate = items[i]; + if (candidate.getItemId() === id) { + result.push(candidate); + } + } + return result; + }, - /** - * Adds an item to the collection. Fires the {@link #add} event when complete. - * @param {String} key

        The key to associate with the item, or the new item.

        - *

        If a {@link #getKey} implementation was specified for this HashMap, - * or if the key of the stored items is in a property called id, - * the HashMap will be able to derive the key for the new item. - * In this case just pass the new item in this parameter.

        - * @param {Object} o The item to add. - * @return {Object} The item added. - */ - add: function(key, value) { - var me = this, - data; + // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in + filterByPseudo = function(items, name, value) { + return cq.pseudos[name](items, value); + }, - if (arguments.length === 1) { - value = key; - key = me.getKey(value); - } + // Determines leading mode + // > for direct child, and ^ to switch to ownerCt axis + modeRe = /^(\s?([>\^])\s?|\s|$)/, - if (me.containsKey(key)) { - me.replace(key, value); - } + // Matches a token with possibly (true|false) appended for the "shallow" parameter + tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/, - data = me.getData(key, value); - key = data[0]; - value = data[1]; - me.map[key] = value; - ++me.length; - me.fireEvent('add', me, key, value); - return value; - }, + matchers = [{ + // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter + re: /^\.([\w\-]+)(?:\((true|false)\))?/, + method: filterByXType + },{ + // checks for [attribute=value] + re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/, + method: filterByAttribute + }, { + // checks for #cmpItemId + re: /^#([\w\-]+)/, + method: filterById + }, { + // checks for :() + re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/, + method: filterByPseudo + }, { + // checks for {} + re: /^(?:\{([^\}]+)\})/, + method: filterFnPattern + }]; - /** - * Replaces an item in the hash. If the key doesn't exist, the - * {@link #add} method will be used. - * @param {String} key The key of the item. - * @param {Object} value The new value for the item. - * @return {Object} The new value of the item. - */ - replace: function(key, value) { - var me = this, - map = me.map, - old; + // @class Ext.ComponentQuery.Query + // This internal class is completely hidden in documentation. + cq.Query = Ext.extend(Object, { + constructor: function(cfg) { + cfg = cfg || {}; + Ext.apply(this, cfg); + }, - if (!me.containsKey(key)) { - me.add(key, value); - } - old = map[key]; - map[key] = value; - me.fireEvent('replace', me, key, value, old); - return value; - }, + // Executes this Query upon the selected root. + // The root provides the initial source of candidate Component matches which are progressively + // filtered by iterating through this Query's operations cache. + // If no root is provided, all registered Components are searched via the ComponentManager. + // root may be a Container who's descendant Components are filtered + // root may be a Component with an implementation of getRefItems which provides some nested Components such as the + // docked items within a Panel. + // root may be an array of candidate Components to filter using this Query. + execute : function(root) { + var operations = this.operations, + i = 0, + length = operations.length, + operation, + workingItems; - /** - * Remove an item from the hash. - * @param {Object} o The value of the item to remove. - * @return {Boolean} True if the item was successfully removed. - */ - remove: function(o) { - var key = this.findKey(o); - if (key !== undefined) { - return this.removeAtKey(key); - } - return false; - }, + // no root, use all Components in the document + if (!root) { + workingItems = Ext.ComponentManager.all.getArray(); + } + // Root is a candidate Array + else if (Ext.isArray(root)) { + workingItems = root; + } - /** - * Remove an item from the hash. - * @param {String} key The key to remove. - * @return {Boolean} True if the item was successfully removed. - */ - removeAtKey: function(key) { - var me = this, - value; + // We are going to loop over our operations and take care of them + // one by one. + for (; i < length; i++) { + operation = operations[i]; - if (me.containsKey(key)) { - value = me.map[key]; - delete me.map[key]; - --me.length; - me.fireEvent('remove', me, key, value); - return true; - } - return false; - }, + // The mode operation requires some custom handling. + // All other operations essentially filter down our current + // working items, while mode replaces our current working + // items by getting children from each one of our current + // working items. The type of mode determines the type of + // children we get. (e.g. > only gets direct children) + if (operation.mode === '^') { + workingItems = getAncestors(workingItems || [root]); + } + else if (operation.mode) { + workingItems = getItems(workingItems || [root], operation.mode); + } + else { + workingItems = filterItems(workingItems || getItems([root]), operation); + } - /** - * Retrieves an item with a particular key. - * @param {String} key The key to lookup. - * @return {Object} The value at that key. If it doesn't exist, undefined is returned. - */ - get: function(key) { - return this.map[key]; - }, + // If this is the last operation, it means our current working + // items are the final matched items. Thus return them! + if (i === length -1) { + return workingItems; + } + } + return []; + }, - /** - * Removes all items from the hash. - * @return {Ext.util.HashMap} this - */ - clear: function(/* private */ initial) { - var me = this; - me.map = {}; - me.length = 0; - if (initial !== true) { - me.fireEvent('clear', me); - } - return me; - }, + is: function(component) { + var operations = this.operations, + components = Ext.isArray(component) ? component : [component], + originalLength = components.length, + lastOperation = operations[operations.length-1], + ln, i; - /** + components = filterItems(components, lastOperation); + if (components.length === originalLength) { + if (operations.length > 1) { + for (i = 0, ln = components.length; i < ln; i++) { + if (Ext.Array.indexOf(this.execute(), components[i]) === -1) { + return false; + } + } + } + return true; + } + return false; + } + }); + + Ext.apply(this, { + + // private cache of selectors and matching ComponentQuery.Query objects + cache: {}, + + // private cache of pseudo class filter functions + pseudos: { + not: function(components, selector){ + var CQ = Ext.ComponentQuery, + i = 0, + length = components.length, + results = [], + index = -1, + component; + + for(; i < length; ++i) { + component = components[i]; + if (!CQ.is(component, selector)) { + results[++index] = component; + } + } + return results; + }, + last: function(components) { + return components[components.length - 1]; + } + }, + + /** + * Returns an array of matched Components from within the passed root object. + * + * This method filters returned Components in a similar way to how CSS selector based DOM + * queries work using a textual selector string. + * + * See class summary for details. + * + * @param {String} selector The selector string to filter returned Components + * @param {Ext.container.Container} root The Container within which to perform the query. + * If omitted, all Components within the document are included in the search. + * + * This parameter may also be an array of Components to filter according to the selector.

        + * @returns {Ext.Component[]} The matched Components. + * + * @member Ext.ComponentQuery + */ + query: function(selector, root) { + var selectors = selector.split(','), + length = selectors.length, + i = 0, + results = [], + noDupResults = [], + dupMatcher = {}, + query, resultsLn, cmp; + + for (; i < length; i++) { + selector = Ext.String.trim(selectors[i]); + query = this.cache[selector]; + if (!query) { + this.cache[selector] = query = this.parse(selector); + } + results = results.concat(query.execute(root)); + } + + // multiple selectors, potential to find duplicates + // lets filter them out. + if (length > 1) { + resultsLn = results.length; + for (i = 0; i < resultsLn; i++) { + cmp = results[i]; + if (!dupMatcher[cmp.id]) { + noDupResults.push(cmp); + dupMatcher[cmp.id] = true; + } + } + results = noDupResults; + } + return results; + }, + + /** + * Tests whether the passed Component matches the selector string. + * @param {Ext.Component} component The Component to test + * @param {String} selector The selector string to test against. + * @return {Boolean} True if the Component matches the selector. + * @member Ext.ComponentQuery + */ + is: function(component, selector) { + if (!selector) { + return true; + } + var query = this.cache[selector]; + if (!query) { + this.cache[selector] = query = this.parse(selector); + } + return query.is(component); + }, + + parse: function(selector) { + var operations = [], + length = matchers.length, + lastSelector, + tokenMatch, + matchedChar, + modeMatch, + selectorMatch, + i, matcher, method; + + // We are going to parse the beginning of the selector over and + // over again, slicing off the selector any portions we converted into an + // operation, until it is an empty string. + while (selector && lastSelector !== selector) { + lastSelector = selector; + + // First we check if we are dealing with a token like #, * or an xtype + tokenMatch = selector.match(tokenRe); + + if (tokenMatch) { + matchedChar = tokenMatch[1]; + + // If the token is prefixed with a # we push a filterById operation to our stack + if (matchedChar === '#') { + operations.push({ + method: filterById, + args: [Ext.String.trim(tokenMatch[2])] + }); + } + // If the token is prefixed with a . we push a filterByClassName operation to our stack + // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix + else if (matchedChar === '.') { + operations.push({ + method: filterByClassName, + args: [Ext.String.trim(tokenMatch[2])] + }); + } + // If the token is a * or an xtype string, we push a filterByXType + // operation to the stack. + else { + operations.push({ + method: filterByXType, + args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])] + }); + } + + // Now we slice of the part we just converted into an operation + selector = selector.replace(tokenMatch[0], ''); + } + + // If the next part of the query is not a space or > or ^, it means we + // are going to check for more things that our current selection + // has to comply to. + while (!(modeMatch = selector.match(modeRe))) { + // Lets loop over each type of matcher and execute it + // on our current selector. + for (i = 0; selector && i < length; i++) { + matcher = matchers[i]; + selectorMatch = selector.match(matcher.re); + method = matcher.method; + + // If we have a match, add an operation with the method + // associated with this matcher, and pass the regular + // expression matches are arguments to the operation. + if (selectorMatch) { + operations.push({ + method: Ext.isString(matcher.method) + // Turn a string method into a function by formatting the string with our selector matche expression + // A new method is created for different match expressions, eg {id=='textfield-1024'} + // Every expression may be different in different selectors. + ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1)))) + : matcher.method, + args: selectorMatch.slice(1) + }); + selector = selector.replace(selectorMatch[0], ''); + break; // Break on match + } + // Exhausted all matches: It's an error + if (i === (length - 1)) { + Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"'); + } + } + } + + // Now we are going to check for a mode change. This means a space + // or a > to determine if we are going to select all the children + // of the currently matched items, or a ^ if we are going to use the + // ownerCt axis as the candidate source. + if (modeMatch[1]) { // Assignment, and test for truthiness! + operations.push({ + mode: modeMatch[2]||modeMatch[1] + }); + selector = selector.replace(modeMatch[0], ''); + } + } + + // Now that we have all our operations in an array, we are going + // to create a new Query using these operations. + return new cq.Query({ + operations: operations + }); + } + }); +}); +/** + * @class Ext.util.HashMap + *

        + * Represents a collection of a set of key and value pairs. Each key in the HashMap + * must be unique, the same key cannot exist twice. Access to items is provided via + * the key only. Sample usage: + *

        
        +var map = new Ext.util.HashMap();
        +map.add('key1', 1);
        +map.add('key2', 2);
        +map.add('key3', 3);
        +
        +map.each(function(key, value, length){
        +    console.log(key, value, length);
        +});
        + * 
        + *

        + * + *

        The HashMap is an unordered class, + * there is no guarantee when iterating over the items that they will be in any particular + * order. If this is required, then use a {@link Ext.util.MixedCollection}. + *

        + */ +Ext.define('Ext.util.HashMap', { + mixins: { + observable: 'Ext.util.Observable' + }, + + /** + * @cfg {Function} keyFn A function that is used to retrieve a default key for a passed object. + * A default is provided that returns the id property on the object. This function is only used + * if the add method is called with a single argument. + */ + + /** + * Creates new HashMap. + * @param {Object} config (optional) Config object. + */ + constructor: function(config) { + config = config || {}; + + var me = this, + keyFn = config.keyFn; + + me.addEvents( + /** + * @event add + * Fires when a new item is added to the hash + * @param {Ext.util.HashMap} this. + * @param {String} key The key of the added item. + * @param {Object} value The value of the added item. + */ + 'add', + /** + * @event clear + * Fires when the hash is cleared. + * @param {Ext.util.HashMap} this. + */ + 'clear', + /** + * @event remove + * Fires when an item is removed from the hash. + * @param {Ext.util.HashMap} this. + * @param {String} key The key of the removed item. + * @param {Object} value The value of the removed item. + */ + 'remove', + /** + * @event replace + * Fires when an item is replaced in the hash. + * @param {Ext.util.HashMap} this. + * @param {String} key The key of the replaced item. + * @param {Object} value The new value for the item. + * @param {Object} old The old value for the item. + */ + 'replace' + ); + + me.mixins.observable.constructor.call(me, config); + me.clear(true); + + if (keyFn) { + me.getKey = keyFn; + } + }, + + /** + * Gets the number of items in the hash. + * @return {Number} The number of items in the hash. + */ + getCount: function() { + return this.length; + }, + + /** + * Implementation for being able to extract the key from an object if only + * a single argument is passed. + * @private + * @param {String} key The key + * @param {Object} value The value + * @return {Array} [key, value] + */ + getData: function(key, value) { + // if we have no value, it means we need to get the key from the object + if (value === undefined) { + value = key; + key = this.getKey(value); + } + + return [key, value]; + }, + + /** + * Extracts the key from an object. This is a default implementation, it may be overridden + * @param {Object} o The object to get the key from + * @return {String} The key to use. + */ + getKey: function(o) { + return o.id; + }, + + /** + * Adds an item to the collection. Fires the {@link #add} event when complete. + * @param {String} key

        The key to associate with the item, or the new item.

        + *

        If a {@link #getKey} implementation was specified for this HashMap, + * or if the key of the stored items is in a property called id, + * the HashMap will be able to derive the key for the new item. + * In this case just pass the new item in this parameter.

        + * @param {Object} o The item to add. + * @return {Object} The item added. + */ + add: function(key, value) { + var me = this, + data; + + if (arguments.length === 1) { + value = key; + key = me.getKey(value); + } + + if (me.containsKey(key)) { + return me.replace(key, value); + } + + data = me.getData(key, value); + key = data[0]; + value = data[1]; + me.map[key] = value; + ++me.length; + me.fireEvent('add', me, key, value); + return value; + }, + + /** + * Replaces an item in the hash. If the key doesn't exist, the + * {@link #add} method will be used. + * @param {String} key The key of the item. + * @param {Object} value The new value for the item. + * @return {Object} The new value of the item. + */ + replace: function(key, value) { + var me = this, + map = me.map, + old; + + if (!me.containsKey(key)) { + me.add(key, value); + } + old = map[key]; + map[key] = value; + me.fireEvent('replace', me, key, value, old); + return value; + }, + + /** + * Remove an item from the hash. + * @param {Object} o The value of the item to remove. + * @return {Boolean} True if the item was successfully removed. + */ + remove: function(o) { + var key = this.findKey(o); + if (key !== undefined) { + return this.removeAtKey(key); + } + return false; + }, + + /** + * Remove an item from the hash. + * @param {String} key The key to remove. + * @return {Boolean} True if the item was successfully removed. + */ + removeAtKey: function(key) { + var me = this, + value; + + if (me.containsKey(key)) { + value = me.map[key]; + delete me.map[key]; + --me.length; + me.fireEvent('remove', me, key, value); + return true; + } + return false; + }, + + /** + * Retrieves an item with a particular key. + * @param {String} key The key to lookup. + * @return {Object} The value at that key. If it doesn't exist, undefined is returned. + */ + get: function(key) { + return this.map[key]; + }, + + /** + * Removes all items from the hash. + * @return {Ext.util.HashMap} this + */ + clear: function(/* private */ initial) { + var me = this; + me.map = {}; + me.length = 0; + if (initial !== true) { + me.fireEvent('clear', me); + } + return me; + }, + + /** * Checks whether a key exists in the hash. * @param {String} key The key to check for. * @return {Boolean} True if they key exists in the hash. @@ -22448,10629 +23680,10108 @@ Ext.define('Ext.util.HashMap', { }); /** - * @class Ext.Template - *

        Represents an HTML fragment template. Templates may be {@link #compile precompiled} - * for greater performance.

        - * An instance of this class may be created by passing to the constructor either - * a single argument, or multiple arguments: - *
          - *
        • single argument : String/Array - *
          - * The single argument may be either a String or an Array:
            - *
          • String :
          • 
            -var t = new Ext.Template("<div>Hello {0}.</div>");
            -t.{@link #append}('some-element', ['foo']);
            -   
            - *
          • Array :
          • - * An Array will be combined with join(''). -
            
            -var t = new Ext.Template([
            -    '<div name="{id}">',
            -        '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
            -    '</div>',
            -]);
            -t.{@link #compile}();
            -t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
            -   
            - *
        • - *
        • multiple arguments : String, Object, Array, ... - *
          - * Multiple arguments will be combined with join(''). - *
          
          -var t = new Ext.Template(
          -    '<div name="{id}">',
          -        '<span class="{cls}">{name} {value}</span>',
          -    '</div>',
          -    // a configuration object:
          -    {
          -        compiled: true,      // {@link #compile} immediately
          -    }
          -);
          -   
          - *

          Notes:

          - *
            - *
          • For a list of available format functions, see {@link Ext.util.Format}.
          • - *
          • disableFormats reduces {@link #apply} time - * when no formatting is required.
          • - *
          - *
        • - *
        - * @param {Mixed} config + * @class Ext.state.Manager + * This is the global state manager. By default all components that are "state aware" check this class + * for state information if you don't pass them a custom state provider. In order for this class + * to be useful, it must be initialized with a provider when your application initializes. Example usage: +
        
        +// in your initialization function
        +init : function(){
        +   Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
        +   var win = new Window(...);
        +   win.restoreState();
        +}
        + 
        + * This class passes on calls from components to the underlying {@link Ext.state.Provider} so that + * there is a common interface that can be used without needing to refer to a specific provider instance + * in every component. + * @singleton + * @docauthor Evan Trimboli */ +Ext.define('Ext.state.Manager', { + singleton: true, + requires: ['Ext.state.Provider'], + constructor: function() { + this.provider = Ext.create('Ext.state.Provider'); + }, + + + /** + * Configures the default state provider for your application + * @param {Ext.state.Provider} stateProvider The state provider to set + */ + setProvider : function(stateProvider){ + this.provider = stateProvider; + }, -Ext.define('Ext.Template', { - - /* Begin Definitions */ - - requires: ['Ext.core.DomHelper', 'Ext.util.Format'], - - statics: { - /** - * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. - * @param {String/HTMLElement} el A DOM element or its id - * @param {Object} config A configuration object - * @return {Ext.Template} The created template - * @static - */ - from: function(el, config) { - el = Ext.getDom(el); - return new this(el.value || el.innerHTML, config || ''); - } + /** + * Returns the current value for a key + * @param {String} name The key name + * @param {Object} defaultValue The default value to return if the key lookup does not match + * @return {Object} The state data + */ + get : function(key, defaultValue){ + return this.provider.get(key, defaultValue); }, - /* End Definitions */ + /** + * Sets the value for a key + * @param {String} name The key name + * @param {Object} value The state data + */ + set : function(key, value){ + this.provider.set(key, value); + }, - constructor: function(html) { - var me = this, - args = arguments, - buffer = [], - i = 0, - length = args.length, - value; + /** + * Clears a value from the state + * @param {String} name The key name + */ + clear : function(key){ + this.provider.clear(key); + }, - me.initialConfig = {}; + /** + * Gets the currently configured state provider + * @return {Ext.state.Provider} The state provider + */ + getProvider : function(){ + return this.provider; + } +}); +/** + * @class Ext.state.Stateful + * A mixin for being able to save the state of an object to an underlying + * {@link Ext.state.Provider}. + */ +Ext.define('Ext.state.Stateful', { - if (length > 1) { - for (; i < length; i++) { - value = args[i]; - if (typeof value == 'object') { - Ext.apply(me.initialConfig, value); - Ext.apply(me, value); - } else { - buffer.push(value); - } - } - html = buffer.join(''); - } else { - if (Ext.isArray(html)) { - buffer.push(html.join('')); - } else { - buffer.push(html); - } + /* Begin Definitions */ + + mixins: { + observable: 'Ext.util.Observable' + }, + + requires: ['Ext.state.Manager'], + + /* End Definitions */ + + /** + * @cfg {Boolean} stateful + *

        A flag which causes the object to attempt to restore the state of + * internal properties from a saved state on startup. The object must have + * a {@link #stateId} for state to be managed. + * Auto-generated ids are not guaranteed to be stable across page loads and + * cannot be relied upon to save and restore the same state for a object.

        + *

        For state saving to work, the state manager's provider must have been + * set to an implementation of {@link Ext.state.Provider} which overrides the + * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get} + * methods to save and recall name/value pairs. A built-in implementation, + * {@link Ext.state.CookieProvider} is available.

        + *

        To set the state provider for the current page:

        + *
        
        +Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
        +    expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
        +}));
        +     * 
        + *

        A stateful object attempts to save state when one of the events + * listed in the {@link #stateEvents} configuration fires.

        + *

        To save state, a stateful object first serializes its state by + * calling {@link #getState}. By default, this function does + * nothing. The developer must provide an implementation which returns an + * object hash which represents the restorable state of the object.

        + *

        The value yielded by getState is passed to {@link Ext.state.Manager#set} + * which uses the configured {@link Ext.state.Provider} to save the object + * keyed by the {@link #stateId}.

        + *

        During construction, a stateful object attempts to restore + * its state by calling {@link Ext.state.Manager#get} passing the + * {@link #stateId}

        + *

        The resulting object is passed to {@link #applyState}. + * The default implementation of {@link #applyState} simply copies + * properties into the object, but a developer may override this to support + * more behaviour.

        + *

        You can perform extra processing on state save and restore by attaching + * handlers to the {@link #beforestaterestore}, {@link #staterestore}, + * {@link #beforestatesave} and {@link #statesave} events.

        + */ + stateful: true, + + /** + * @cfg {String} stateId + * The unique id for this object to use for state management purposes. + *

        See {@link #stateful} for an explanation of saving and restoring state.

        + */ + + /** + * @cfg {String[]} stateEvents + *

        An array of events that, when fired, should trigger this object to + * save its state. Defaults to none. stateEvents may be any type + * of event supported by this object, including browser or custom events + * (e.g., ['click', 'customerchange']).

        + *

        See {@link #stateful} for an explanation of saving and + * restoring object state.

        + */ + + /** + * @cfg {Number} saveDelay + * A buffer to be applied if many state events are fired within a short period. + */ + saveDelay: 100, + + autoGenIdRe: /^((\w+-)|(ext-comp-))\d{4,}$/i, + + constructor: function(config) { + var me = this; + + config = config || {}; + if (Ext.isDefined(config.stateful)) { + me.stateful = config.stateful; } + if (Ext.isDefined(config.saveDelay)) { + me.saveDelay = config.saveDelay; + } + me.stateId = me.stateId || config.stateId; - // @private - me.html = buffer.join(''); + if (!me.stateEvents) { + me.stateEvents = []; + } + if (config.stateEvents) { + me.stateEvents.concat(config.stateEvents); + } + this.addEvents( + /** + * @event beforestaterestore + * Fires before the state of the object is restored. Return false from an event handler to stop the restore. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values returned from the StateProvider. If this + * event is not vetoed, then the state object is passed to applyState. By default, + * that simply copies property values into this object. The method maybe overriden to + * provide custom state restoration. + */ + 'beforestaterestore', - if (me.compiled) { - me.compile(); + /** + * @event staterestore + * Fires after the state of the object is restored. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values returned from the StateProvider. This is passed + * to applyState. By default, that simply copies property values into this + * object. The method maybe overriden to provide custom state restoration. + */ + 'staterestore', + + /** + * @event beforestatesave + * Fires before the state of the object is saved to the configured state provider. Return false to stop the save. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values. This is determined by calling + * getState() on the object. This method must be provided by the + * developer to return whetever representation of state is required, by default, Ext.state.Stateful + * has a null implementation. + */ + 'beforestatesave', + + /** + * @event statesave + * Fires after the state of the object is saved to the configured state provider. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values. This is determined by calling + * getState() on the object. This method must be provided by the + * developer to return whetever representation of state is required, by default, Ext.state.Stateful + * has a null implementation. + */ + 'statesave' + ); + me.mixins.observable.constructor.call(me); + if (me.stateful !== false) { + me.initStateEvents(); + me.initState(); } }, - isTemplate: true, + /** - * @cfg {Boolean} disableFormats true to disable format functions in the template. If the template doesn't contain format functions, setting - * disableFormats to true will reduce apply time (defaults to false) + * Initializes any state events for this object. + * @private */ - disableFormats: false, + initStateEvents: function() { + this.addStateEvents(this.stateEvents); + }, - re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g, /** - * Returns an HTML fragment of this template with the specified values applied. - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @return {String} The HTML fragment + * Add events that will trigger the state to be saved. + * @param {String/String[]} events The event name or an array of event names. */ - applyTemplate: function(values) { + addStateEvents: function(events){ + if (!Ext.isArray(events)) { + events = [events]; + } + var me = this, - useFormat = me.disableFormats !== true, - fm = Ext.util.Format, - tpl = me; + i = 0, + len = events.length; - if (me.compiled) { - return me.compiled(values); - } - function fn(m, name, format, args) { - if (format && useFormat) { - if (args) { - args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')()); - } else { - args = [values[name]]; - } - if (format.substr(0, 5) == "this.") { - return tpl[format.substr(5)].apply(tpl, args); - } - else { - return fm[format].apply(fm, args); - } - } - else { - return values[name] !== undefined ? values[name] : ""; - } + for (; i < len; ++i) { + me.on(events[i], me.onStateChange, me); } - return me.html.replace(me.re, fn); }, /** - * Sets the HTML used as the template and optionally compiles it. - * @param {String} html - * @param {Boolean} compile (optional) True to compile the template (defaults to undefined) - * @return {Ext.Template} this + * This method is called when any of the {@link #stateEvents} are fired. + * @private */ - set: function(html, compile) { - var me = this; - me.html = html; - me.compiled = null; - return compile ? me.compile() : me; + onStateChange: function(){ + var me = this, + delay = me.saveDelay; + + if (delay > 0) { + if (!me.stateTask) { + me.stateTask = Ext.create('Ext.util.DelayedTask', me.saveState, me); + } + me.stateTask.delay(me.saveDelay); + } else { + me.saveState(); + } }, - compileARe: /\\/g, - compileBRe: /(\r\n|\n)/g, - compileCRe: /'/g, /** - * Compiles the template into an internal function, eliminating the RegEx overhead. - * @return {Ext.Template} this + * Saves the state of the object to the persistence store. + * @private */ - compile: function() { + saveState: function() { var me = this, - fm = Ext.util.Format, - useFormat = me.disableFormats !== true, - body, bodyReturn; + id, + state; - function fn(m, name, format, args) { - if (format && useFormat) { - args = args ? ',' + args: ""; - if (format.substr(0, 5) != "this.") { - format = "fm." + format + '('; - } - else { - format = 'this.' + format.substr(5) + '('; + if (me.stateful !== false) { + id = me.getStateId(); + if (id) { + state = me.getState(); + if (me.fireEvent('beforestatesave', me, state) !== false) { + Ext.state.Manager.set(id, state); + me.fireEvent('statesave', me, state); } } - else { - args = ''; - format = "(values['" + name + "'] == undefined ? '' : "; - } - return "'," + format + "values['" + name + "']" + args + ") ,'"; } + }, - bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn); - body = "this.compiled = function(values){ return ['" + bodyReturn + "'].join('');};"; - eval(body); - return me; + /** + * Gets the current state of the object. By default this function returns null, + * it should be overridden in subclasses to implement methods for getting the state. + * @return {Object} The current state + */ + getState: function(){ + return null; }, /** - * Applies the supplied values to the template and inserts the new node(s) as the first child of el. - * @param {Mixed} el The context element - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined) - * @return {HTMLElement/Ext.core.Element} The new node or Element + * Applies the state to the object. This should be overridden in subclasses to do + * more complex state operations. By default it applies the state properties onto + * the current object. + * @param {Object} state The state */ - insertFirst: function(el, values, returnElement) { - return this.doInsert('afterBegin', el, values, returnElement); + applyState: function(state) { + if (state) { + Ext.apply(this, state); + } }, /** - * Applies the supplied values to the template and inserts the new node(s) before el. - * @param {Mixed} el The context element - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined) - * @return {HTMLElement/Ext.core.Element} The new node or Element + * Gets the state id for this object. + * @return {String} The state id, null if not found. */ - insertBefore: function(el, values, returnElement) { - return this.doInsert('beforeBegin', el, values, returnElement); + getStateId: function() { + var me = this, + id = me.stateId; + + if (!id) { + id = me.autoGenIdRe.test(String(me.id)) ? null : me.id; + } + return id; }, /** - * Applies the supplied values to the template and inserts the new node(s) after el. - * @param {Mixed} el The context element - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined) - * @return {HTMLElement/Ext.core.Element} The new node or Element + * Initializes the state of the object upon construction. + * @private */ - insertAfter: function(el, values, returnElement) { - return this.doInsert('afterEnd', el, values, returnElement); + initState: function(){ + var me = this, + id = me.getStateId(), + state; + + if (me.stateful !== false) { + if (id) { + state = Ext.state.Manager.get(id); + if (state) { + state = Ext.apply({}, state); + if (me.fireEvent('beforestaterestore', me, state) !== false) { + me.applyState(state); + me.fireEvent('staterestore', me, state); + } + } + } + } }, /** - * Applies the supplied values to the template and appends - * the new node(s) to the specified el. - *

        For example usage {@link #Template see the constructor}.

        - * @param {Mixed} el The context element - * @param {Object/Array} values - * The template values. Can be an array if the params are numeric (i.e. {0}) - * or an object (i.e. {foo: 'bar'}). - * @param {Boolean} returnElement (optional) true to return an Ext.core.Element (defaults to undefined) - * @return {HTMLElement/Ext.core.Element} The new node or Element + * Conditionally saves a single property from this object to the given state object. + * The idea is to only save state which has changed from the initial state so that + * current software settings do not override future software settings. Only those + * values that are user-changed state should be saved. + * + * @param {String} propName The name of the property to save. + * @param {Object} state The state object in to which to save the property. + * @param {String} stateName (optional) The name to use for the property in state. + * @return {Boolean} True if the property was saved, false if not. */ - append: function(el, values, returnElement) { - return this.doInsert('beforeEnd', el, values, returnElement); + savePropToState: function (propName, state, stateName) { + var me = this, + value = me[propName], + config = me.initialConfig; + + if (me.hasOwnProperty(propName)) { + if (!config || config[propName] !== value) { + if (state) { + state[stateName || propName] = value; + } + return true; + } + } + return false; }, - doInsert: function(where, el, values, returnEl) { - el = Ext.getDom(el); - var newNode = Ext.core.DomHelper.insertHtml(where, el, this.applyTemplate(values)); - return returnEl ? Ext.get(newNode, true) : newNode; + savePropsToState: function (propNames, state) { + var me = this; + Ext.each(propNames, function (propName) { + me.savePropToState(propName, state); + }); + return state; }, /** - * Applies the supplied values to the template and overwrites the content of el with the new node(s). - * @param {Mixed} el The context element - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined) - * @return {HTMLElement/Ext.core.Element} The new node or Element + * Destroys this stateful object. */ - overwrite: function(el, values, returnElement) { - el = Ext.getDom(el); - el.innerHTML = this.applyTemplate(values); - return returnElement ? Ext.get(el.firstChild, true) : el.firstChild; + destroy: function(){ + var task = this.stateTask; + if (task) { + task.cancel(); + } + this.clearListeners(); + } -}, function() { - /** - * Alias for {@link #applyTemplate} - * Returns an HTML fragment of this template with the specified values applied. - * @param {Object/Array} values - * The template values. Can be an array if the params are numeric (i.e. {0}) - * or an object (i.e. {foo: 'bar'}). - * @return {String} The HTML fragment - * @member Ext.Template - * @method apply - */ - this.createAlias('apply', 'applyTemplate'); }); /** - * @class Ext.ComponentQuery - * @extends Object - * @singleton - * - * Provides searching of Components within Ext.ComponentManager (globally) or a specific - * Ext.container.Container on the document with a similar syntax to a CSS selector. - * - * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix - * - * - `component` or `.component` - * - `gridpanel` or `.gridpanel` - * - * An itemId or id must be prefixed with a # - * - * - `#myContainer` - * - * Attributes must be wrapped in brackets - * - * - `component[autoScroll]` - * - `panel[title="Test"]` - * - * Member expressions from candidate Components may be tested. If the expression returns a *truthy* value, - * the candidate Component will be included in the query: - * - * var disabledFields = myFormPanel.query("{isDisabled()}"); - * - * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}: - * - * // Function receives array and returns a filtered array. - * Ext.ComponentQuery.pseudos.invalid = function(items) { - * var i = 0, l = items.length, c, result = []; - * for (; i < l; i++) { - * if (!(c = items[i]).isValid()) { - * result.push(c); - * } - * } - * return result; - * }; - * - * var invalidFields = myFormPanel.query('field:invalid'); - * if (invalidFields.length) { - * invalidFields[0].getEl().scrollIntoView(myFormPanel.body); - * for (var i = 0, l = invalidFields.length; i < l; i++) { - * invalidFields[i].getEl().frame("red"); - * } - * } - * - * Default pseudos include: - * - * - not - * - * Queries return an array of components. - * Here are some example queries. - * - * // retrieve all Ext.Panels in the document by xtype - * var panelsArray = Ext.ComponentQuery.query('panel'); - * - * // retrieve all Ext.Panels within the container with an id myCt - * var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel'); - * - * // retrieve all direct children which are Ext.Panels within myCt - * var directChildPanel = Ext.ComponentQuery.query('#myCt > panel'); - * - * // retrieve all grids and trees - * var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel'); - * - * For easy access to queries based from a particular Container see the {@link Ext.container.Container#query}, - * {@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see - * {@link Ext.Component#up}. + * Base Manager class */ -Ext.define('Ext.ComponentQuery', { - singleton: true, - uses: ['Ext.ComponentManager'] -}, function() { +Ext.define('Ext.AbstractManager', { - var cq = this, + /* Begin Definitions */ - // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied - // as a member on each item in the passed array. - filterFnPattern = [ - 'var r = [],', - 'i = 0,', - 'it = items,', - 'l = it.length,', - 'c;', - 'for (; i < l; i++) {', - 'c = it[i];', - 'if (c.{0}) {', - 'r.push(c);', - '}', - '}', - 'return r;' - ].join(''), + requires: ['Ext.util.HashMap'], - filterItems = function(items, operation) { - // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...] - // The operation's method loops over each item in the candidate array and - // returns an array of items which match its criteria - return operation.method.apply(this, [ items ].concat(operation.args)); - }, + /* End Definitions */ - getItems = function(items, mode) { - var result = [], - i = 0, - length = items.length, - candidate, - deep = mode !== '>'; - - for (; i < length; i++) { - candidate = items[i]; - if (candidate.getRefItems) { - result = result.concat(candidate.getRefItems(deep)); - } - } - return result; - }, + typeName: 'type', - getAncestors = function(items) { - var result = [], - i = 0, - length = items.length, - candidate; - for (; i < length; i++) { - candidate = items[i]; - while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) { - result.push(candidate); - } - } - return result; - }, - - // Filters the passed candidate array and returns only items which match the passed xtype - filterByXType = function(items, xtype, shallow) { - if (xtype === '*') { - return items.slice(); - } - else { - var result = [], - i = 0, - length = items.length, - candidate; - for (; i < length; i++) { - candidate = items[i]; - if (candidate.isXType(xtype, shallow)) { - result.push(candidate); - } - } - return result; - } - }, - - // Filters the passed candidate array and returns only items which have the passed className - filterByClassName = function(items, className) { - var EA = Ext.Array, - result = [], - i = 0, - length = items.length, - candidate; - for (; i < length; i++) { - candidate = items[i]; - if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) { - result.push(candidate); - } - } - return result; - }, - - // Filters the passed candidate array and returns only items which have the specified property match - filterByAttribute = function(items, property, operator, value) { - var result = [], - i = 0, - length = items.length, - candidate; - for (; i < length; i++) { - candidate = items[i]; - if (!value ? !!candidate[property] : (String(candidate[property]) === value)) { - result.push(candidate); - } - } - return result; - }, - - // Filters the passed candidate array and returns only items which have the specified itemId or id - filterById = function(items, id) { - var result = [], - i = 0, - length = items.length, - candidate; - for (; i < length; i++) { - candidate = items[i]; - if (candidate.getItemId() === id) { - result.push(candidate); - } - } - return result; - }, - - // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in - filterByPseudo = function(items, name, value) { - return cq.pseudos[name](items, value); - }, - - // Determines leading mode - // > for direct child, and ^ to switch to ownerCt axis - modeRe = /^(\s?([>\^])\s?|\s|$)/, - - // Matches a token with possibly (true|false) appended for the "shallow" parameter - tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/, - - matchers = [{ - // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter - re: /^\.([\w\-]+)(?:\((true|false)\))?/, - method: filterByXType - },{ - // checks for [attribute=value] - re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/, - method: filterByAttribute - }, { - // checks for #cmpItemId - re: /^#([\w\-]+)/, - method: filterById - }, { - // checks for :() - re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/, - method: filterByPseudo - }, { - // checks for {} - re: /^(?:\{([^\}]+)\})/, - method: filterFnPattern - }]; - - /** - * @class Ext.ComponentQuery.Query - * @extends Object - * @private - */ - cq.Query = Ext.extend(Object, { - constructor: function(cfg) { - cfg = cfg || {}; - Ext.apply(this, cfg); - }, - - /** - * @private - * Executes this Query upon the selected root. - * The root provides the initial source of candidate Component matches which are progressively - * filtered by iterating through this Query's operations cache. - * If no root is provided, all registered Components are searched via the ComponentManager. - * root may be a Container who's descendant Components are filtered - * root may be a Component with an implementation of getRefItems which provides some nested Components such as the - * docked items within a Panel. - * root may be an array of candidate Components to filter using this Query. - */ - execute : function(root) { - var operations = this.operations, - i = 0, - length = operations.length, - operation, - workingItems; - - // no root, use all Components in the document - if (!root) { - workingItems = Ext.ComponentManager.all.getArray(); - } - // Root is a candidate Array - else if (Ext.isArray(root)) { - workingItems = root; - } - - // We are going to loop over our operations and take care of them - // one by one. - for (; i < length; i++) { - operation = operations[i]; - - // The mode operation requires some custom handling. - // All other operations essentially filter down our current - // working items, while mode replaces our current working - // items by getting children from each one of our current - // working items. The type of mode determines the type of - // children we get. (e.g. > only gets direct children) - if (operation.mode === '^') { - workingItems = getAncestors(workingItems || [root]); - } - else if (operation.mode) { - workingItems = getItems(workingItems || [root], operation.mode); - } - else { - workingItems = filterItems(workingItems || getItems([root]), operation); - } - - // If this is the last operation, it means our current working - // items are the final matched items. Thus return them! - if (i === length -1) { - return workingItems; - } - } - return []; - }, - - is: function(component) { - var operations = this.operations, - components = Ext.isArray(component) ? component : [component], - originalLength = components.length, - lastOperation = operations[operations.length-1], - ln, i; - - components = filterItems(components, lastOperation); - if (components.length === originalLength) { - if (operations.length > 1) { - for (i = 0, ln = components.length; i < ln; i++) { - if (Ext.Array.indexOf(this.execute(), components[i]) === -1) { - return false; - } - } - } - return true; - } - return false; - } - }); - - Ext.apply(this, { - - // private cache of selectors and matching ComponentQuery.Query objects - cache: {}, - - // private cache of pseudo class filter functions - pseudos: { - not: function(components, selector){ - var CQ = Ext.ComponentQuery, - i = 0, - length = components.length, - results = [], - index = -1, - component; - - for(; i < length; ++i) { - component = components[i]; - if (!CQ.is(component, selector)) { - results[++index] = component; - } - } - return results; - } - }, - - /** - * Returns an array of matched Components from within the passed root object. - * - * This method filters returned Components in a similar way to how CSS selector based DOM - * queries work using a textual selector string. - * - * See class summary for details. - * - * @param {String} selector The selector string to filter returned Components - * @param {Ext.container.Container} root The Container within which to perform the query. - * If omitted, all Components within the document are included in the search. - * - * This parameter may also be an array of Components to filter according to the selector.

        - * @returns {[Ext.Component]} The matched Components. - * - * @member Ext.ComponentQuery - */ - query: function(selector, root) { - var selectors = selector.split(','), - length = selectors.length, - i = 0, - results = [], - noDupResults = [], - dupMatcher = {}, - query, resultsLn, cmp; - - for (; i < length; i++) { - selector = Ext.String.trim(selectors[i]); - query = this.cache[selector]; - if (!query) { - this.cache[selector] = query = this.parse(selector); - } - results = results.concat(query.execute(root)); - } - - // multiple selectors, potential to find duplicates - // lets filter them out. - if (length > 1) { - resultsLn = results.length; - for (i = 0; i < resultsLn; i++) { - cmp = results[i]; - if (!dupMatcher[cmp.id]) { - noDupResults.push(cmp); - dupMatcher[cmp.id] = true; - } - } - results = noDupResults; - } - return results; - }, + constructor: function(config) { + Ext.apply(this, config || {}); /** - * Tests whether the passed Component matches the selector string. - * @param {Ext.Component} component The Component to test - * @param {String} selector The selector string to test against. - * @return {Boolean} True if the Component matches the selector. - * @member Ext.ComponentQuery + * @property {Ext.util.HashMap} all + * Contains all of the items currently managed */ - is: function(component, selector) { - if (!selector) { - return true; - } - var query = this.cache[selector]; - if (!query) { - this.cache[selector] = query = this.parse(selector); - } - return query.is(component); - }, - - parse: function(selector) { - var operations = [], - length = matchers.length, - lastSelector, - tokenMatch, - matchedChar, - modeMatch, - selectorMatch, - i, matcher, method; - - // We are going to parse the beginning of the selector over and - // over again, slicing off the selector any portions we converted into an - // operation, until it is an empty string. - while (selector && lastSelector !== selector) { - lastSelector = selector; - - // First we check if we are dealing with a token like #, * or an xtype - tokenMatch = selector.match(tokenRe); - - if (tokenMatch) { - matchedChar = tokenMatch[1]; - - // If the token is prefixed with a # we push a filterById operation to our stack - if (matchedChar === '#') { - operations.push({ - method: filterById, - args: [Ext.String.trim(tokenMatch[2])] - }); - } - // If the token is prefixed with a . we push a filterByClassName operation to our stack - // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix - else if (matchedChar === '.') { - operations.push({ - method: filterByClassName, - args: [Ext.String.trim(tokenMatch[2])] - }); - } - // If the token is a * or an xtype string, we push a filterByXType - // operation to the stack. - else { - operations.push({ - method: filterByXType, - args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])] - }); - } - - // Now we slice of the part we just converted into an operation - selector = selector.replace(tokenMatch[0], ''); - } - - // If the next part of the query is not a space or > or ^, it means we - // are going to check for more things that our current selection - // has to comply to. - while (!(modeMatch = selector.match(modeRe))) { - // Lets loop over each type of matcher and execute it - // on our current selector. - for (i = 0; selector && i < length; i++) { - matcher = matchers[i]; - selectorMatch = selector.match(matcher.re); - method = matcher.method; - - // If we have a match, add an operation with the method - // associated with this matcher, and pass the regular - // expression matches are arguments to the operation. - if (selectorMatch) { - operations.push({ - method: Ext.isString(matcher.method) - // Turn a string method into a function by formatting the string with our selector matche expression - // A new method is created for different match expressions, eg {id=='textfield-1024'} - // Every expression may be different in different selectors. - ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1)))) - : matcher.method, - args: selectorMatch.slice(1) - }); - selector = selector.replace(selectorMatch[0], ''); - break; // Break on match - } - // Exhausted all matches: It's an error - if (i === (length - 1)) { - Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"'); - } - } - } - - // Now we are going to check for a mode change. This means a space - // or a > to determine if we are going to select all the children - // of the currently matched items, or a ^ if we are going to use the - // ownerCt axis as the candidate source. - if (modeMatch[1]) { // Assignment, and test for truthiness! - operations.push({ - mode: modeMatch[2]||modeMatch[1] - }); - selector = selector.replace(modeMatch[0], ''); - } - } - - // Now that we have all our operations in an array, we are going - // to create a new Query using these operations. - return new cq.Query({ - operations: operations - }); - } - }); -}); -/** - * @class Ext.util.Filter - * @extends Object - *

        Represents a filter that can be applied to a {@link Ext.util.MixedCollection MixedCollection}. Can either simply - * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the context - * of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching on their - * records. Example usage:

        -
        
        -//set up a fictional MixedCollection containing a few people to filter on
        -var allNames = new Ext.util.MixedCollection();
        -allNames.addAll([
        -    {id: 1, name: 'Ed',    age: 25},
        -    {id: 2, name: 'Jamie', age: 37},
        -    {id: 3, name: 'Abe',   age: 32},
        -    {id: 4, name: 'Aaron', age: 26},
        -    {id: 5, name: 'David', age: 32}
        -]);
        -
        -var ageFilter = new Ext.util.Filter({
        -    property: 'age',
        -    value   : 32
        -});
        -
        -var longNameFilter = new Ext.util.Filter({
        -    filterFn: function(item) {
        -        return item.name.length > 4;
        -    }
        -});
        -
        -//a new MixedCollection with the 3 names longer than 4 characters
        -var longNames = allNames.filter(longNameFilter);
        -
        -//a new MixedCollection with the 2 people of age 24:
        -var youngFolk = allNames.filter(ageFilter);
        -
        - */ -Ext.define('Ext.util.Filter', { + this.all = Ext.create('Ext.util.HashMap'); - /* Begin Definitions */ + this.types = {}; + }, - /* End Definitions */ - /** - * @cfg {String} property The property to filter on. Required unless a {@link #filterFn} is passed - */ - /** - * @cfg {Function} filterFn A custom filter function which is passed each item in the {@link Ext.util.MixedCollection} - * in turn. Should return true to accept each item or false to reject it + * Returns an item by id. + * For additional details see {@link Ext.util.HashMap#get}. + * @param {String} id The id of the item + * @return {Object} The item, undefined if not found. */ - + get : function(id) { + return this.all.get(id); + }, + /** - * @cfg {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false + * Registers an item to be managed + * @param {Object} item The item to register */ - anyMatch: false, - + register: function(item) { + var all = this.all, + key = all.getKey(item); + + if (all.containsKey(key)) { + Ext.Error.raise('Registering duplicate id "' + key + '" with this manager'); + } + this.all.add(item); + }, + /** - * @cfg {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. - * Ignored if anyMatch is true. + * Unregisters an item by removing it from this manager + * @param {Object} item The item to unregister */ - exactMatch: false, - + unregister: function(item) { + this.all.remove(item); + }, + /** - * @cfg {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false. + * Registers a new item constructor, keyed by a type key. + * @param {String} type The mnemonic string by which the class may be looked up. + * @param {Function} cls The new instance class. */ - caseSensitive: false, - + registerType : function(type, cls) { + this.types[type] = cls; + cls[this.typeName] = type; + }, + /** - * @cfg {String} root Optional root property. This is mostly useful when filtering a Store, in which case we set the - * root to 'data' to make the filter pull the {@link #property} out of the data object of each item + * Checks if an item type is registered. + * @param {String} type The mnemonic string by which the class may be looked up + * @return {Boolean} Whether the type is registered. */ + isRegistered : function(type){ + return this.types[type] !== undefined; + }, /** - * Creates new Filter. - * @param {Object} config (optional) Config object + * Creates and returns an instance of whatever this manager manages, based on the supplied type and + * config object. + * @param {Object} config The config object + * @param {String} defaultType If no type is discovered in the config object, we fall back to this type + * @return {Object} The instance of whatever this manager is managing */ - constructor: function(config) { - Ext.apply(this, config); - - //we're aliasing filter to filterFn mostly for API cleanliness reasons, despite the fact it dirties the code here. - //Ext.util.Sorter takes a sorterFn property but allows .sort to be called - we do the same here - this.filter = this.filter || this.filterFn; - - if (this.filter == undefined) { - if (this.property == undefined || this.value == undefined) { - // Commented this out temporarily because it stops us using string ids in models. TODO: Remove this once - // Model has been updated to allow string ids - - // Ext.Error.raise("A Filter requires either a property or a filterFn to be set"); - } else { - this.filter = this.createFilterFn(); - } - - this.filterFn = this.filter; + create: function(config, defaultType) { + var type = config[this.typeName] || config.type || defaultType, + Constructor = this.types[type]; + + if (Constructor === undefined) { + Ext.Error.raise("The '" + type + "' type has not been registered with this manager"); } + + return new Constructor(config); }, - + /** - * @private - * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter + * Registers a function that will be called when an item with the specified id is added to the manager. + * This will happen on instantiation. + * @param {String} id The item id + * @param {Function} fn The callback function. Called with a single parameter, the item. + * @param {Object} scope The scope (this reference) in which the callback is executed. + * Defaults to the item. */ - createFilterFn: function() { - var me = this, - matcher = me.createValueMatcher(), - property = me.property; + onAvailable : function(id, fn, scope){ + var all = this.all, + item; - return function(item) { - return matcher.test(me.getRoot.call(me, item)[property]); - }; + if (all.containsKey(id)) { + item = all.get(id); + fn.call(scope || item, item); + } else { + all.on('add', function(map, key, item){ + if (key == id) { + fn.call(scope || item, item); + all.un('add', fn, scope); + } + }); + } }, /** - * @private - * Returns the root property of the given item, based on the configured {@link #root} property - * @param {Object} item The item - * @return {Object} The root property of the object + * Executes the specified function once for each item in the collection. + * @param {Function} fn The function to execute. + * @param {String} fn.key The key of the item + * @param {Number} fn.value The value of the item + * @param {Number} fn.length The total number of items in the collection + * @param {Boolean} fn.return False to cease iteration. + * @param {Object} scope The scope to execute in. Defaults to `this`. */ - getRoot: function(item) { - return this.root == undefined ? item : item[this.root]; + each: function(fn, scope){ + this.all.each(fn, scope || this); }, /** - * @private - * Returns a regular expression based on the given value and matching options + * Gets the number of items in the collection. + * @return {Number} The number of items in the collection. */ - createValueMatcher : function() { - var me = this, - value = me.value, - anyMatch = me.anyMatch, - exactMatch = me.exactMatch, - caseSensitive = me.caseSensitive, - escapeRe = Ext.String.escapeRegex; - - if (!value.exec) { // not a regex - value = String(value); + getCount: function(){ + return this.all.getCount(); + } +}); - if (anyMatch === true) { - value = escapeRe(value); - } else { - value = '^' + escapeRe(value); - if (exactMatch === true) { - value += '$'; - } - } - value = new RegExp(value, caseSensitive ? '' : 'i'); - } - - return value; - } -}); /** - * @class Ext.util.Sorter - * @extends Object - -Represents a single sorter that can be applied to a Store. The sorter is used -to compare two values against each other for the purpose of ordering them. Ordering -is achieved by specifying either: -- {@link #property A sorting property} -- {@link #sorterFn A sorting function} - -As a contrived example, we can specify a custom sorter that sorts by rank: - - Ext.define('Person', { - extend: 'Ext.data.Model', - fields: ['name', 'rank'] - }); - - Ext.create('Ext.data.Store', { - model: 'Person', - proxy: 'memory', - sorters: [{ - sorterFn: function(o1, o2){ - var getRank = function(o){ - var name = o.get('rank'); - if (name === 'first') { - return 1; - } else if (name === 'second') { - return 2; - } else { - return 3; - } - }, - rank1 = getRank(o1), - rank2 = getRank(o2); - - if (rank1 === rank2) { - return 0; - } - - return rank1 < rank2 ? -1 : 1; - } - }], - data: [{ - name: 'Person1', - rank: 'second' - }, { - name: 'Person2', - rank: 'third' - }, { - name: 'Person3', - rank: 'first' - }] - }); - - * @markdown + * @class Ext.ComponentManager + * @extends Ext.AbstractManager + *

        Provides a registry of all Components (instances of {@link Ext.Component} or any subclass + * thereof) on a page so that they can be easily accessed by {@link Ext.Component component} + * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).

        + *

        This object also provides a registry of available Component classes + * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}. + * The xtype provides a way to avoid instantiating child Components + * when creating a full, nested config object for a complete Ext page.

        + *

        A child Component may be specified simply as a config object + * as long as the correct {@link Ext.Component#xtype xtype} is specified so that if and when the Component + * needs rendering, the correct type can be looked up for lazy instantiation.

        + *

        For a list of all available {@link Ext.Component#xtype xtypes}, see {@link Ext.Component}.

        + * @singleton */ -Ext.define('Ext.util.Sorter', { - - /** - * @cfg {String} property The property to sort by. Required unless {@link #sorterFn} is provided. - * The property is extracted from the object directly and compared for sorting using the built in - * comparison operators. - */ - - /** - * @cfg {Function} sorterFn A specific sorter function to execute. Can be passed instead of {@link #property}. - * This sorter function allows for any kind of custom/complex comparisons. - * The sorterFn receives two arguments, the objects being compared. The function should return: - *
          - *
        • -1 if o1 is "less than" o2
        • - *
        • 0 if o1 is "equal" to o2
        • - *
        • 1 if o1 is "greater than" o2
        • - *
        - */ +Ext.define('Ext.ComponentManager', { + extend: 'Ext.AbstractManager', + alternateClassName: 'Ext.ComponentMgr', - /** - * @cfg {String} root Optional root property. This is mostly useful when sorting a Store, in which case we set the - * root to 'data' to make the filter pull the {@link #property} out of the data object of each item - */ + singleton: true, - /** - * @cfg {Function} transform A function that will be run on each value before - * it is compared in the sorter. The function will receive a single argument, - * the value. - */ + typeName: 'xtype', /** - * @cfg {String} direction The direction to sort by. Defaults to ASC + * Creates a new Component from the specified config object using the + * config object's xtype to determine the class to instantiate. + * @param {Object} config A configuration object for the Component you wish to create. + * @param {Function} defaultType (optional) The constructor to provide the default Component type if + * the config object does not contain a xtype. (Optional if the config contains a xtype). + * @return {Ext.Component} The newly instantiated Component. */ - direction: "ASC", - - constructor: function(config) { - var me = this; - - Ext.apply(me, config); - - if (me.property === undefined && me.sorterFn === undefined) { - Ext.Error.raise("A Sorter requires either a property or a sorter function"); + create: function(component, defaultType){ + if (component instanceof Ext.AbstractComponent) { + return component; } - - me.updateSortFunction(); - }, - - /** - * @private - * Creates and returns a function which sorts an array by the given property and direction - * @return {Function} A function which sorts by the property/direction combination provided - */ - createSortFunction: function(sorterFn) { - var me = this, - property = me.property, - direction = me.direction || "ASC", - modifier = direction.toUpperCase() == "DESC" ? -1 : 1; - - //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater, - //-1 if object 2 is greater or 0 if they are equal - return function(o1, o2) { - return modifier * sorterFn.call(me, o1, o2); - }; - }, - - /** - * @private - * Basic default sorter function that just compares the defined property of each object - */ - defaultSorterFn: function(o1, o2) { - var me = this, - transform = me.transform, - v1 = me.getRoot(o1)[me.property], - v2 = me.getRoot(o2)[me.property]; + else if (Ext.isString(component)) { + return Ext.createByAlias('widget.' + component); + } + else { + var type = component.xtype || defaultType, + config = component; - if (transform) { - v1 = transform(v1); - v2 = transform(v2); + return Ext.createByAlias('widget.' + type, config); } - - return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); - }, - - /** - * @private - * Returns the root property of the given item, based on the configured {@link #root} property - * @param {Object} item The item - * @return {Object} The root property of the object - */ - getRoot: function(item) { - return this.root === undefined ? item : item[this.root]; - }, - - /** - * Set the sorting direction for this sorter. - * @param {String} direction The direction to sort in. Should be either 'ASC' or 'DESC'. - */ - setDirection: function(direction) { - var me = this; - me.direction = direction; - me.updateSortFunction(); - }, - - /** - * Toggles the sorting direction for this sorter. - */ - toggle: function() { - var me = this; - me.direction = Ext.String.toggle(me.direction, "ASC", "DESC"); - me.updateSortFunction(); }, - - /** - * Update the sort function for this sorter. - * @param {Function} fn (Optional) A new sorter function for this sorter. If not specified it will use the - * default sorting function. - */ - updateSortFunction: function(fn) { - var me = this; - fn = fn || me.sorterFn || me.defaultSorterFn; - me.sort = me.createSortFunction(fn); + + registerType: function(type, cls) { + this.types[type] = cls; + cls[this.typeName] = type; + cls.prototype[this.typeName] = type; } }); /** - * @class Ext.ElementLoader - * A class used to load remote content to an Element. Sample usage: - *
        
        -Ext.get('el').load({
        -    url: 'myPage.php',
        -    scripts: true,
        -    params: {
        -        id: 1
        -    }
        -});
        - * 
        - *

        - * In general this class will not be instanced directly, rather the {@link Ext.core.Element#load} method - * will be used. - *

        + * An abstract base class which provides shared methods for Components across the Sencha product line. + * + * Please refer to sub class's documentation + * @private */ -Ext.define('Ext.ElementLoader', { +Ext.define('Ext.AbstractComponent', { /* Begin Definitions */ + requires: [ + 'Ext.ComponentQuery', + 'Ext.ComponentManager' + ], mixins: { - observable: 'Ext.util.Observable' + observable: 'Ext.util.Observable', + animate: 'Ext.util.Animate', + state: 'Ext.state.Stateful' }, + // The "uses" property specifies class which are used in an instantiated AbstractComponent. + // They do *not* have to be loaded before this class may be defined - that is what "requires" is for. uses: [ - 'Ext.data.Connection', - 'Ext.Ajax' + 'Ext.PluginManager', + 'Ext.ComponentManager', + 'Ext.Element', + 'Ext.DomHelper', + 'Ext.XTemplate', + 'Ext.ComponentQuery', + 'Ext.ComponentLoader', + 'Ext.EventManager', + 'Ext.layout.Layout', + 'Ext.layout.component.Auto', + 'Ext.LoadMask', + 'Ext.ZIndexManager' ], - + statics: { - Renderer: { - Html: function(loader, response, active){ - loader.getTarget().update(response.responseText, active.scripts === true); - return true; - } - } + AUTO_ID: 1000 }, /* End Definitions */ + isComponent: true, + + getAutoId: function() { + return ++Ext.AbstractComponent.AUTO_ID; + }, + + /** - * @cfg {String} url The url to retrieve the content from. Defaults to null. + * @cfg {String} id + * The **unique id of this component instance.** + * + * It should not be necessary to use this configuration except for singleton objects in your application. Components + * created with an id may be accessed globally using {@link Ext#getCmp Ext.getCmp}. + * + * Instead of using assigned ids, use the {@link #itemId} config, and {@link Ext.ComponentQuery ComponentQuery} + * which provides selector-based searching for Sencha Components analogous to DOM querying. The {@link + * Ext.container.Container Container} class contains {@link Ext.container.Container#down shortcut methods} to query + * its descendant Components by selector. + * + * Note that this id will also be used as the element id for the containing HTML element that is rendered to the + * page for this component. This allows you to write id-based CSS rules to style the specific instance of this + * component uniquely, and also to select sub-elements using this component's id as the parent. + * + * **Note**: to avoid complications imposed by a unique id also see `{@link #itemId}`. + * + * **Note**: to access the container of a Component see `{@link #ownerCt}`. + * + * Defaults to an {@link #getId auto-assigned id}. */ - url: null, /** - * @cfg {Object} params Any params to be attached to the Ajax request. These parameters will - * be overridden by any params in the load options. Defaults to null. + * @cfg {String} itemId + * An itemId can be used as an alternative way to get a reference to a component when no object reference is + * available. Instead of using an `{@link #id}` with {@link Ext}.{@link Ext#getCmp getCmp}, use `itemId` with + * {@link Ext.container.Container}.{@link Ext.container.Container#getComponent getComponent} which will retrieve + * `itemId`'s or {@link #id}'s. Since `itemId`'s are an index to the container's internal MixedCollection, the + * `itemId` is scoped locally to the container -- avoiding potential conflicts with {@link Ext.ComponentManager} + * which requires a **unique** `{@link #id}`. + * + * var c = new Ext.panel.Panel({ // + * {@link Ext.Component#height height}: 300, + * {@link #renderTo}: document.body, + * {@link Ext.container.Container#layout layout}: 'auto', + * {@link Ext.container.Container#items items}: [ + * { + * itemId: 'p1', + * {@link Ext.panel.Panel#title title}: 'Panel 1', + * {@link Ext.Component#height height}: 150 + * }, + * { + * itemId: 'p2', + * {@link Ext.panel.Panel#title title}: 'Panel 2', + * {@link Ext.Component#height height}: 150 + * } + * ] + * }) + * p1 = c.{@link Ext.container.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()} + * p2 = p1.{@link #ownerCt}.{@link Ext.container.Container#getComponent getComponent}('p2'); // reference via a sibling + * + * Also see {@link #id}, `{@link Ext.container.Container#query}`, `{@link Ext.container.Container#down}` and + * `{@link Ext.container.Container#child}`. + * + * **Note**: to access the container of an item see {@link #ownerCt}. */ - params: null, /** - * @cfg {Object} baseParams Params that will be attached to every request. These parameters - * will not be overridden by any params in the load options. Defaults to null. + * @property {Ext.Container} ownerCt + * This Component's owner {@link Ext.container.Container Container} (is set automatically + * when this Component is added to a Container). Read-only. + * + * **Note**: to access items within the Container see {@link #itemId}. */ - baseParams: null, /** - * @cfg {Boolean/Object} autoLoad True to have the loader make a request as soon as it is created. Defaults to false. - * This argument can also be a set of options that will be passed to {@link #load} is called. + * @property {Boolean} layoutManagedWidth + * @private + * Flag set by the container layout to which this Component is added. + * If the layout manages this Component's width, it sets the value to 1. + * If it does NOT manage the width, it sets it to 2. + * If the layout MAY affect the width, but only if the owning Container has a fixed width, this is set to 0. */ - autoLoad: false, /** - * @cfg {Mixed} target The target element for the loader. It can be the DOM element, the id or an Ext.Element. + * @property {Boolean} layoutManagedHeight + * @private + * Flag set by the container layout to which this Component is added. + * If the layout manages this Component's height, it sets the value to 1. + * If it does NOT manage the height, it sets it to 2. + * If the layout MAY affect the height, but only if the owning Container has a fixed height, this is set to 0. */ - target: null, /** - * @cfg {Mixed} loadMask True or a string to show when the element is loading. + * @cfg {String/Object} autoEl + * A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will + * encapsulate this Component. + * + * You do not normally need to specify this. For the base classes {@link Ext.Component} and + * {@link Ext.container.Container}, this defaults to **'div'**. The more complex Sencha classes use a more + * complex DOM structure specified by their own {@link #renderTpl}s. + * + * This is intended to allow the developer to create application-specific utility Components encapsulated by + * different DOM elements. Example usage: + * + * { + * xtype: 'component', + * autoEl: { + * tag: 'img', + * src: 'http://www.example.com/example.jpg' + * } + * }, { + * xtype: 'component', + * autoEl: { + * tag: 'blockquote', + * html: 'autoEl is cool!' + * } + * }, { + * xtype: 'container', + * autoEl: 'ul', + * cls: 'ux-unordered-list', + * items: { + * xtype: 'component', + * autoEl: 'li', + * html: 'First list item' + * } + * } */ - loadMask: false, /** - * @cfg {Object} ajaxOptions Any additional options to be passed to the request, for example timeout or headers. Defaults to null. + * @cfg {Ext.XTemplate/String/String[]} renderTpl + * An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's encapsulating + * {@link #getEl Element}. + * + * You do not normally need to specify this. For the base classes {@link Ext.Component} and + * {@link Ext.container.Container}, this defaults to **`null`** which means that they will be initially rendered + * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch + * classes which use a more complex DOM structure, provide their own template definitions. + * + * This is intended to allow the developer to create application-specific utility Components with customized + * internal structure. + * + * Upon rendering, any created child elements may be automatically imported into object properties using the + * {@link #renderSelectors} and {@link #childEls} options. */ - ajaxOptions: null, - + renderTpl: null, + /** - * @cfg {Boolean} scripts True to parse any inline script tags in the response. + * @cfg {Object} renderData + * + * The data used by {@link #renderTpl} in addition to the following property values of the component: + * + * - id + * - ui + * - uiCls + * - baseCls + * - componentCls + * - frame + * + * See {@link #renderSelectors} and {@link #childEls} for usage examples. */ - scripts: false, /** - * @cfg {Function} success A function to be called when a load request is successful. + * @cfg {Object} renderSelectors + * An object containing properties specifying {@link Ext.DomQuery DomQuery} selectors which identify child elements + * created by the render process. + * + * After the Component's internal structure is rendered according to the {@link #renderTpl}, this object is iterated through, + * and the found Elements are added as properties to the Component using the `renderSelector` property name. + * + * For example, a Component which renderes a title and description into its element: + * + * Ext.create('Ext.Component', { + * renderTo: Ext.getBody(), + * renderTpl: [ + * '

        {title}

        ', + * '

        {desc}

        ' + * ], + * renderData: { + * title: "Error", + * desc: "Something went wrong" + * }, + * renderSelectors: { + * titleEl: 'h1.title', + * descEl: 'p' + * }, + * listeners: { + * afterrender: function(cmp){ + * // After rendering the component will have a titleEl and descEl properties + * cmp.titleEl.setStyle({color: "red"}); + * } + * } + * }); + * + * For a faster, but less flexible, alternative that achieves the same end result (properties for child elements on the + * Component after render), see {@link #childEls} and {@link #addChildEls}. */ /** - * @cfg {Function} failure A function to be called when a load request fails. + * @cfg {Object[]} childEls + * An array describing the child elements of the Component. Each member of the array + * is an object with these properties: + * + * - `name` - The property name on the Component for the child element. + * - `itemId` - The id to combine with the Component's id that is the id of the child element. + * - `id` - The id of the child element. + * + * If the array member is a string, it is equivalent to `{ name: m, itemId: m }`. + * + * For example, a Component which renders a title and body text: + * + * Ext.create('Ext.Component', { + * renderTo: Ext.getBody(), + * renderTpl: [ + * '

        {title}

        ', + * '

        {msg}

        ', + * ], + * renderData: { + * title: "Error", + * msg: "Something went wrong" + * }, + * childEls: ["title"], + * listeners: { + * afterrender: function(cmp){ + * // After rendering the component will have a title property + * cmp.title.setStyle({color: "red"}); + * } + * } + * }); + * + * A more flexible, but somewhat slower, approach is {@link #renderSelectors}. */ /** - * @cfg {Object} scope The scope to execute the {@link #success} and {@link #failure} functions in. + * @cfg {String/HTMLElement/Ext.Element} renderTo + * Specify the id of the element, a DOM element or an existing Element that this component will be rendered into. + * + * **Notes:** + * + * Do *not* use this option if the Component is to be a child item of a {@link Ext.container.Container Container}. + * It is the responsibility of the {@link Ext.container.Container Container}'s + * {@link Ext.container.Container#layout layout manager} to render and manage its child items. + * + * When using this config, a call to render() is not required. + * + * See `{@link #render}` also. */ - + /** - * @cfg {Function} renderer A custom function to render the content to the element. The passed parameters - * are - *
          - *
        • The loader
        • - *
        • The response
        • - *
        • The active request
        • - *
        + * @cfg {Boolean} frame + * Specify as `true` to have the Component inject framing elements within the Component at render time to provide a + * graphical rounded frame around the Component content. + * + * This is only necessary when running on outdated, or non standard-compliant browsers such as Microsoft's Internet + * Explorer prior to version 9 which do not support rounded corners natively. + * + * The extra space taken up by this framing is available from the read only property {@link #frameSize}. */ - isLoader: true, + /** + * @property {Object} frameSize + * Read-only property indicating the width of any framing elements which were added within the encapsulating element + * to provide graphical, rounded borders. See the {@link #frame} config. + * + * This is an object containing the frame width in pixels for all four sides of the Component containing the + * following properties: + * + * @property {Number} frameSize.top The width of the top framing element in pixels. + * @property {Number} frameSize.right The width of the right framing element in pixels. + * @property {Number} frameSize.bottom The width of the bottom framing element in pixels. + * @property {Number} frameSize.left The width of the left framing element in pixels. + */ - constructor: function(config) { - var me = this, - autoLoad; - - config = config || {}; - Ext.apply(me, config); - me.setTarget(me.target); - me.addEvents( - /** - * @event beforeload - * Fires before a load request is made to the server. - * Returning false from an event listener can prevent the load - * from occurring. - * @param {Ext.ElementLoader} this - * @param {Object} options The options passed to the request - */ - 'beforeload', + /** + * @cfg {String/Object} componentLayout + * The sizing and positioning of a Component's internal Elements is the responsibility of the Component's layout + * manager which sizes a Component's internal structure in response to the Component being sized. + * + * Generally, developers will not use this configuration as all provided Components which need their internal + * elements sizing (Such as {@link Ext.form.field.Base input fields}) come with their own componentLayout managers. + * + * The {@link Ext.layout.container.Auto default layout manager} will be used on instances of the base Ext.Component + * class which simply sizes the Component's encapsulating element to the height and width specified in the + * {@link #setSize} method. + */ - /** - * @event exception - * Fires after an unsuccessful load. - * @param {Ext.ElementLoader} this - * @param {Object} response The response from the server - * @param {Object} options The options passed to the request - */ - 'exception', + /** + * @cfg {Ext.XTemplate/Ext.Template/String/String[]} tpl + * An {@link Ext.Template}, {@link Ext.XTemplate} or an array of strings to form an Ext.XTemplate. Used in + * conjunction with the `{@link #data}` and `{@link #tplWriteMode}` configurations. + */ - /** - * @event exception - * Fires after a successful load. - * @param {Ext.ElementLoader} this - * @param {Object} response The response from the server - * @param {Object} options The options passed to the request - */ - 'load' - ); + /** + * @cfg {Object} data + * The initial set of data to apply to the `{@link #tpl}` to update the content area of the Component. + */ - // don't pass config because we have already applied it. - me.mixins.observable.constructor.call(me); + /** + * @cfg {String} xtype + * The `xtype` configuration option can be used to optimize Component creation and rendering. It serves as a + * shortcut to the full componet name. For example, the component `Ext.button.Button` has an xtype of `button`. + * + * You can define your own xtype on a custom {@link Ext.Component component} by specifying the + * {@link Ext.Class#alias alias} config option with a prefix of `widget`. For example: + * + * Ext.define('PressMeButton', { + * extend: 'Ext.button.Button', + * alias: 'widget.pressmebutton', + * text: 'Press Me' + * }) + * + * Any Component can be created implicitly as an object config with an xtype specified, allowing it to be + * declared and passed into the rendering pipeline without actually being instantiated as an object. Not only is + * rendering deferred, but the actual creation of the object itself is also deferred, saving memory and resources + * until they are actually needed. In complex, nested layouts containing many Components, this can make a + * noticeable improvement in performance. + * + * // Explicit creation of contained Components: + * var panel = new Ext.Panel({ + * ... + * items: [ + * Ext.create('Ext.button.Button', { + * text: 'OK' + * }) + * ] + * }; + * + * // Implicit creation using xtype: + * var panel = new Ext.Panel({ + * ... + * items: [{ + * xtype: 'button', + * text: 'OK' + * }] + * }; + * + * In the first example, the button will always be created immediately during the panel's initialization. With + * many added Components, this approach could potentially slow the rendering of the page. In the second example, + * the button will not be created or rendered until the panel is actually displayed in the browser. If the panel + * is never displayed (for example, if it is a tab that remains hidden) then the button will never be created and + * will never consume any resources whatsoever. + */ - if (me.autoLoad) { - autoLoad = me.autoLoad; - if (autoLoad === true) { - autoLoad = {}; - } - me.load(autoLoad); - } - }, + /** + * @cfg {String} tplWriteMode + * The Ext.(X)Template method to use when updating the content area of the Component. + * See `{@link Ext.XTemplate#overwrite}` for information on default mode. + */ + tplWriteMode: 'overwrite', /** - * Set an {Ext.Element} as the target of this loader. Note that if the target is changed, - * any active requests will be aborted. - * @param {Mixed} target The element + * @cfg {String} [baseCls='x-component'] + * The base CSS class to apply to this components's element. This will also be prepended to elements within this + * component like Panel's body will get a class x-panel-body. This means that if you create a subclass of Panel, and + * you want it to get all the Panels styling for the element and the body, you leave the baseCls x-panel and use + * componentCls to add specific styling for this component. */ - setTarget: function(target){ - var me = this; - target = Ext.get(target); - if (me.target && me.target != target) { - me.abort(); - } - me.target = target; - }, + baseCls: Ext.baseCSSPrefix + 'component', /** - * Get the target of this loader. - * @return {Ext.Component} target The target, null if none exists. + * @cfg {String} componentCls + * CSS Class to be added to a components root level element to give distinction to it via styling. */ - getTarget: function(){ - return this.target || null; - }, /** - * Aborts the active load request + * @cfg {String} [cls=''] + * An optional extra CSS class that will be added to this component's Element. This can be useful + * for adding customized styles to the component or any of its children using standard CSS rules. */ - abort: function(){ - var active = this.active; - if (active !== undefined) { - Ext.Ajax.abort(active.request); - if (active.mask) { - this.removeMask(); - } - delete this.active; - } - }, - + /** - * Remove the mask on the target - * @private + * @cfg {String} [overCls=''] + * An optional extra CSS class that will be added to this component's Element when the mouse moves over the Element, + * and removed when the mouse moves out. This can be useful for adding customized 'active' or 'hover' styles to the + * component or any of its children using standard CSS rules. */ - removeMask: function(){ - this.target.unmask(); - }, - + /** - * Add the mask on the target - * @private - * @param {Mixed} mask The mask configuration + * @cfg {String} [disabledCls='x-item-disabled'] + * CSS class to add when the Component is disabled. Defaults to 'x-item-disabled'. */ - addMask: function(mask){ - this.target.mask(mask === true ? null : mask); - }, + disabledCls: Ext.baseCSSPrefix + 'item-disabled', /** - * Load new data from the server. - * @param {Object} options The options for the request. They can be any configuration option that can be specified for - * the class, with the exception of the target option. Note that any options passed to the method will override any - * class defaults. + * @cfg {String/String[]} ui + * A set style for a component. Can be a string or an Array of multiple strings (UIs) */ - load: function(options) { - if (!this.target) { - Ext.Error.raise('A valid target is required when loading content'); - } + ui: 'default', - options = Ext.apply({}, options); + /** + * @cfg {String[]} uiCls + * An array of of classNames which are currently applied to this component + * @private + */ + uiCls: [], - var me = this, - target = me.target, - mask = Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask, - params = Ext.apply({}, options.params), - ajaxOptions = Ext.apply({}, options.ajaxOptions), - callback = options.callback || me.callback, - scope = options.scope || me.scope || me, - request; + /** + * @cfg {String} style + * A custom style specification to be applied to this component's Element. Should be a valid argument to + * {@link Ext.Element#applyStyles}. + * + * new Ext.panel.Panel({ + * title: 'Some Title', + * renderTo: Ext.getBody(), + * width: 400, height: 300, + * layout: 'form', + * items: [{ + * xtype: 'textarea', + * style: { + * width: '95%', + * marginBottom: '10px' + * } + * }, + * new Ext.button.Button({ + * text: 'Send', + * minWidth: '100', + * style: { + * marginBottom: '10px' + * } + * }) + * ] + * }); + */ - Ext.applyIf(ajaxOptions, me.ajaxOptions); - Ext.applyIf(options, ajaxOptions); + /** + * @cfg {Number} width + * The width of this component in pixels. + */ - Ext.applyIf(params, me.params); - Ext.apply(params, me.baseParams); + /** + * @cfg {Number} height + * The height of this component in pixels. + */ - Ext.applyIf(options, { - url: me.url - }); + /** + * @cfg {Number/String} border + * Specifies the border for this component. The border can be a single numeric value to apply to all sides or it can + * be a CSS style specification for each style, for example: '10 5 3 10'. + */ - if (!options.url) { - Ext.Error.raise('You must specify the URL from which content should be loaded'); - } + /** + * @cfg {Number/String} padding + * Specifies the padding for this component. The padding can be a single numeric value to apply to all sides or it + * can be a CSS style specification for each style, for example: '10 5 3 10'. + */ - Ext.apply(options, { - scope: me, - params: params, - callback: me.onComplete - }); + /** + * @cfg {Number/String} margin + * Specifies the margin for this component. The margin can be a single numeric value to apply to all sides or it can + * be a CSS style specification for each style, for example: '10 5 3 10'. + */ - if (me.fireEvent('beforeload', me, options) === false) { - return; - } + /** + * @cfg {Boolean} hidden + * True to hide the component. + */ + hidden: false, - if (mask) { - me.addMask(mask); - } + /** + * @cfg {Boolean} disabled + * True to disable the component. + */ + disabled: false, - request = Ext.Ajax.request(options); - me.active = { - request: request, - options: options, - mask: mask, - scope: scope, - callback: callback, - success: options.success || me.success, - failure: options.failure || me.failure, - renderer: options.renderer || me.renderer, - scripts: Ext.isDefined(options.scripts) ? options.scripts : me.scripts - }; - me.setOptions(me.active, options); - }, - /** - * Set any additional options on the active request - * @private - * @param {Object} active The active request - * @param {Object} options The initial options + * @cfg {Boolean} [draggable=false] + * Allows the component to be dragged. */ - setOptions: Ext.emptyFn, /** - * Parse the response after the request completes - * @private - * @param {Object} options Ajax options - * @param {Boolean} success Success status of the request - * @param {Object} response The response object + * @property {Boolean} draggable + * Read-only property indicating whether or not the component can be dragged */ - onComplete: function(options, success, response) { - var me = this, - active = me.active, - scope = active.scope, - renderer = me.getRenderer(active.renderer); + draggable: false, + /** + * @cfg {Boolean} floating + * Create the Component as a floating and use absolute positioning. + * + * The z-index of floating Components is handled by a ZIndexManager. If you simply render a floating Component into the DOM, it will be managed + * by the global {@link Ext.WindowManager WindowManager}. + * + * If you include a floating Component as a child item of a Container, then upon render, ExtJS will seek an ancestor floating Component to house a new + * ZIndexManager instance to manage its descendant floaters. If no floating ancestor can be found, the global WindowManager will be used. + * + * When a floating Component which has a ZindexManager managing descendant floaters is destroyed, those descendant floaters will also be destroyed. + */ + floating: false, - if (success) { - success = renderer.call(me, me, response, active); - } + /** + * @cfg {String} hideMode + * A String which specifies how this Component's encapsulating DOM element will be hidden. Values may be: + * + * - `'display'` : The Component will be hidden using the `display: none` style. + * - `'visibility'` : The Component will be hidden using the `visibility: hidden` style. + * - `'offsets'` : The Component will be hidden by absolutely positioning it out of the visible area of the document. + * This is useful when a hidden Component must maintain measurable dimensions. Hiding using `display` results in a + * Component having zero dimensions. + */ + hideMode: 'display', - if (success) { - Ext.callback(active.success, scope, [me, response, options]); - me.fireEvent('load', me, response, options); - } else { - Ext.callback(active.failure, scope, [me, response, options]); - me.fireEvent('exception', me, response, options); - } - Ext.callback(active.callback, scope, [me, success, response, options]); + /** + * @cfg {String} contentEl + * Specify an existing HTML element, or the `id` of an existing HTML element to use as the content for this component. + * + * This config option is used to take an existing HTML element and place it in the layout element of a new component + * (it simply moves the specified DOM element _after the Component is rendered_ to use as the content. + * + * **Notes:** + * + * The specified HTML element is appended to the layout element of the component _after any configured + * {@link #html HTML} has been inserted_, and so the document will not contain this element at the time + * the {@link #render} event is fired. + * + * The specified HTML element used will not participate in any **`{@link Ext.container.Container#layout layout}`** + * scheme that the Component may use. It is just HTML. Layouts operate on child + * **`{@link Ext.container.Container#items items}`**. + * + * Add either the `x-hidden` or the `x-hide-display` CSS class to prevent a brief flicker of the content before it + * is rendered to the panel. + */ - if (active.mask) { - me.removeMask(); - } + /** + * @cfg {String/Object} [html=''] + * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element content. + * The HTML content is added after the component is rendered, so the document will not contain this HTML at the time + * the {@link #render} event is fired. This content is inserted into the body _before_ any configured {@link #contentEl} + * is appended. + */ - delete me.active; - }, + /** + * @cfg {Boolean} styleHtmlContent + * True to automatically style the html inside the content target of this component (body for panels). + */ + styleHtmlContent: false, /** - * Gets the renderer to use - * @private - * @param {String/Function} renderer The renderer to use - * @return {Function} A rendering function to use. + * @cfg {String} [styleHtmlCls='x-html'] + * The class that is added to the content target when you set styleHtmlContent to true. */ - getRenderer: function(renderer){ - if (Ext.isFunction(renderer)) { - return renderer; - } - return this.statics().Renderer.Html; - }, - + styleHtmlCls: Ext.baseCSSPrefix + 'html', + /** - * Automatically refreshes the content over a specified period. - * @param {Number} interval The interval to refresh in ms. - * @param {Object} options (optional) The options to pass to the load method. See {@link #load} + * @cfg {Number} minHeight + * The minimum value in pixels which this Component will set its height to. + * + * **Warning:** This will override any size management applied by layout managers. */ - startAutoRefresh: function(interval, options){ - var me = this; - me.stopAutoRefresh(); - me.autoRefresh = setInterval(function(){ - me.load(options); - }, interval); - }, - /** - * Clears any auto refresh. See {@link #startAutoRefresh}. + * @cfg {Number} minWidth + * The minimum value in pixels which this Component will set its width to. + * + * **Warning:** This will override any size management applied by layout managers. */ - stopAutoRefresh: function(){ - clearInterval(this.autoRefresh); - delete this.autoRefresh; - }, - /** - * Checks whether the loader is automatically refreshing. See {@link #startAutoRefresh}. - * @return {Boolean} True if the loader is automatically refreshing + * @cfg {Number} maxHeight + * The maximum value in pixels which this Component will set its height to. + * + * **Warning:** This will override any size management applied by layout managers. */ - isAutoRefreshing: function(){ - return Ext.isDefined(this.autoRefresh); - }, - /** - * Destroys the loader. Any active requests will be aborted. + * @cfg {Number} maxWidth + * The maximum value in pixels which this Component will set its width to. + * + * **Warning:** This will override any size management applied by layout managers. */ - destroy: function(){ - var me = this; - me.stopAutoRefresh(); - delete me.target; - me.abort(); - me.clearListeners(); - } -}); - -/** - * @class Ext.layout.Layout - * @extends Object - * Base Layout class - extended by ComponentLayout and ContainerLayout - */ -Ext.define('Ext.layout.Layout', { - - /* Begin Definitions */ - - /* End Definitions */ - - isLayout: true, - initialized: false, - - statics: { - create: function(layout, defaultType) { - var type; - if (layout instanceof Ext.layout.Layout) { - return Ext.createByAlias('layout.' + layout); - } else { - if (!layout || typeof layout === 'string') { - type = layout || defaultType; - layout = {}; - } - else { - type = layout.type; - } - return Ext.createByAlias('layout.' + type, layout || {}); - } - } - }, - - constructor : function(config) { - this.id = Ext.id(null, this.type + '-'); - Ext.apply(this, config); - }, /** - * @private + * @cfg {Ext.ComponentLoader/Object} loader + * A configuration object or an instance of a {@link Ext.ComponentLoader} to load remote content for this Component. */ - layout : function() { - var me = this; - me.layoutBusy = true; - me.initLayout(); - - if (me.beforeLayout.apply(me, arguments) !== false) { - me.layoutCancelled = false; - me.onLayout.apply(me, arguments); - me.childrenChanged = false; - me.owner.needsLayout = false; - me.layoutBusy = false; - me.afterLayout.apply(me, arguments); - } - else { - me.layoutCancelled = true; - } - me.layoutBusy = false; - me.doOwnerCtLayouts(); - }, - beforeLayout : function() { - this.renderItems(this.getLayoutItems(), this.getRenderTarget()); - return true; - }, + /** + * @cfg {Boolean} autoShow + * True to automatically show the component upon creation. This config option may only be used for + * {@link #floating} components or components that use {@link #autoRender}. Defaults to false. + */ + autoShow: false, /** - * @private - * Iterates over all passed items, ensuring they are rendered. If the items are already rendered, - * also determines if the items are in the proper place dom. + * @cfg {Boolean/String/HTMLElement/Ext.Element} autoRender + * This config is intended mainly for non-{@link #floating} Components which may or may not be shown. Instead of using + * {@link #renderTo} in the configuration, and rendering upon construction, this allows a Component to render itself + * upon first _{@link #show}_. If {@link #floating} is true, the value of this config is omited as if it is `true`. + * + * Specify as `true` to have this Component render to the document body upon first show. + * + * Specify as an element, or the ID of an element to have this Component render to a specific element upon first + * show. + * + * **This defaults to `true` for the {@link Ext.window.Window Window} class.** */ - renderItems : function(items, target) { - var ln = items.length, - i = 0, - item; + autoRender: false, - for (; i < ln; i++) { - item = items[i]; - if (item && !item.rendered) { - this.renderItem(item, target, i); - } - else if (!this.isValidParent(item, target, i)) { - this.moveItem(item, target, i); - } - } - }, + needsLayout: false, - // @private - Validates item is in the proper place in the dom. - isValidParent : function(item, target, position) { - var dom = item.el ? item.el.dom : Ext.getDom(item); - if (dom && target && target.dom) { - if (Ext.isNumber(position) && dom !== target.dom.childNodes[position]) { - return false; - } - return (dom.parentNode == (target.dom || target)); - } - return false; - }, + // @private + allowDomMove: true, /** - * @private - * Renders the given Component into the target Element. - * @param {Ext.Component} item The Component to render - * @param {Ext.core.Element} target The target Element - * @param {Number} position The position within the target to render the item to + * @cfg {Object/Object[]} plugins + * An object or array of objects that will provide custom functionality for this component. The only requirement for + * a valid plugin is that it contain an init method that accepts a reference of type Ext.Component. When a component + * is created, if any plugins are available, the component will call the init method on each plugin, passing a + * reference to itself. Each plugin can then call methods or respond to events on the component as needed to provide + * its functionality. */ - renderItem : function(item, target, position) { - var me = this; - if (!item.rendered) { - if (me.itemCls) { - item.addCls(me.itemCls); - } - if (me.owner.itemCls) { - item.addCls(me.owner.itemCls); - } - item.render(target, position); - me.configureItem(item); - me.childrenChanged = true; - } - }, /** - * @private - * Moved Component to the provided target instead. + * @property {Boolean} rendered + * Read-only property indicating whether or not the component has been rendered. */ - moveItem : function(item, target, position) { - // Make sure target is a dom element - target = target.dom || target; - if (typeof position == 'number') { - position = target.childNodes[position]; - } - target.insertBefore(item.el.dom, position || null); - item.container = Ext.get(target); - this.configureItem(item); - this.childrenChanged = true; - }, + rendered: false, /** + * @property {Number} componentLayoutCounter * @private - * Adds the layout's targetCls if necessary and sets - * initialized flag when complete. + * The number of component layout calls made on this object. */ - initLayout : function() { - if (!this.initialized && !Ext.isEmpty(this.targetCls)) { - this.getTarget().addCls(this.targetCls); - } - this.initialized = true; - }, + componentLayoutCounter: 0, - // @private Sets the layout owner - setOwner : function(owner) { - this.owner = owner; - }, + weight: 0, + + trimRe: /^\s+|\s+$/g, + spacesRe: /\s+/, - // @private - Returns empty array - getLayoutItems : function() { - return []; - }, /** - * @private - * Applies itemCls - * Empty template method + * @property {Boolean} maskOnDisable + * This is an internal flag that you use when creating custom components. By default this is set to true which means + * that every component gets a mask when its disabled. Components like FieldContainer, FieldSet, Field, Button, Tab + * override this property to false since they want to implement custom disable logic. */ - configureItem: Ext.emptyFn, - - // Placeholder empty functions for subclasses to extend - onLayout : Ext.emptyFn, - afterLayout : Ext.emptyFn, - onRemove : Ext.emptyFn, - onDestroy : Ext.emptyFn, - doOwnerCtLayouts : Ext.emptyFn, + maskOnDisable: true, /** - * @private - * Removes itemCls + * Creates new Component. + * @param {Object} config (optional) Config object. */ - afterRemove : function(item) { + constructor : function(config) { var me = this, - el = item.el, - owner = me.owner; - - // Clear managed dimensions flag when removed from the layout. - if (item.rendered) { - if (me.itemCls) { - el.removeCls(me.itemCls); - } - if (owner.itemCls) { - el.removeCls(owner.itemCls); - } - } + i, len; - // These flags are set at the time a child item is added to a layout. - // The layout must decide if it is managing the item's width, or its height, or both. - // See AbstractComponent for docs on these properties. - delete item.layoutManagedWidth; - delete item.layoutManagedHeight; - }, + config = config || {}; + me.initialConfig = config; + Ext.apply(me, config); - /* - * Destroys this layout. This is a template method that is empty by default, but should be implemented - * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes. - * @protected - */ - destroy : function() { - if (!Ext.isEmpty(this.targetCls)) { - var target = this.getTarget(); - if (target) { - target.removeCls(this.targetCls); - } - } - this.onDestroy(); - } -}); -/** - * @class Ext.layout.component.Component - * @extends Ext.layout.Layout - * @private - *

        This class is intended to be extended or created via the {@link Ext.Component#componentLayout layout} - * configuration property. See {@link Ext.Component#componentLayout} for additional details.

        - */ + me.addEvents( + /** + * @event beforeactivate + * Fires before a Component has been visually activated. Returning false from an event listener can prevent + * the activate from occurring. + * @param {Ext.Component} this + */ + 'beforeactivate', + /** + * @event activate + * Fires after a Component has been visually activated. + * @param {Ext.Component} this + */ + 'activate', + /** + * @event beforedeactivate + * Fires before a Component has been visually deactivated. Returning false from an event listener can + * prevent the deactivate from occurring. + * @param {Ext.Component} this + */ + 'beforedeactivate', + /** + * @event deactivate + * Fires after a Component has been visually deactivated. + * @param {Ext.Component} this + */ + 'deactivate', + /** + * @event added + * Fires after a Component had been added to a Container. + * @param {Ext.Component} this + * @param {Ext.container.Container} container Parent Container + * @param {Number} pos position of Component + */ + 'added', + /** + * @event disable + * Fires after the component is disabled. + * @param {Ext.Component} this + */ + 'disable', + /** + * @event enable + * Fires after the component is enabled. + * @param {Ext.Component} this + */ + 'enable', + /** + * @event beforeshow + * Fires before the component is shown when calling the {@link #show} method. Return false from an event + * handler to stop the show. + * @param {Ext.Component} this + */ + 'beforeshow', + /** + * @event show + * Fires after the component is shown when calling the {@link #show} method. + * @param {Ext.Component} this + */ + 'show', + /** + * @event beforehide + * Fires before the component is hidden when calling the {@link #hide} method. Return false from an event + * handler to stop the hide. + * @param {Ext.Component} this + */ + 'beforehide', + /** + * @event hide + * Fires after the component is hidden. Fires after the component is hidden when calling the {@link #hide} + * method. + * @param {Ext.Component} this + */ + 'hide', + /** + * @event removed + * Fires when a component is removed from an Ext.container.Container + * @param {Ext.Component} this + * @param {Ext.container.Container} ownerCt Container which holds the component + */ + 'removed', + /** + * @event beforerender + * Fires before the component is {@link #rendered}. Return false from an event handler to stop the + * {@link #render}. + * @param {Ext.Component} this + */ + 'beforerender', + /** + * @event render + * Fires after the component markup is {@link #rendered}. + * @param {Ext.Component} this + */ + 'render', + /** + * @event afterrender + * Fires after the component rendering is finished. + * + * The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed by any + * afterRender method defined for the Component. + * @param {Ext.Component} this + */ + 'afterrender', + /** + * @event beforedestroy + * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the + * {@link #destroy}. + * @param {Ext.Component} this + */ + 'beforedestroy', + /** + * @event destroy + * Fires after the component is {@link #destroy}ed. + * @param {Ext.Component} this + */ + 'destroy', + /** + * @event resize + * Fires after the component is resized. + * @param {Ext.Component} this + * @param {Number} adjWidth The box-adjusted width that was set + * @param {Number} adjHeight The box-adjusted height that was set + */ + 'resize', + /** + * @event move + * Fires after the component is moved. + * @param {Ext.Component} this + * @param {Number} x The new x position + * @param {Number} y The new y position + */ + 'move' + ); -Ext.define('Ext.layout.component.Component', { + me.getId(); - /* Begin Definitions */ + me.mons = []; + me.additionalCls = []; + me.renderData = me.renderData || {}; + me.renderSelectors = me.renderSelectors || {}; - extend: 'Ext.layout.Layout', + if (me.plugins) { + me.plugins = [].concat(me.plugins); + me.constructPlugins(); + } - /* End Definitions */ + me.initComponent(); - type: 'component', + // ititComponent gets a chance to change the id property before registering + Ext.ComponentManager.register(me); - monitorChildren: true, + // Dont pass the config so that it is not applied to 'this' again + me.mixins.observable.constructor.call(me); + me.mixins.state.constructor.call(me, config); - initLayout : function() { - var me = this, - owner = me.owner, - ownerEl = owner.el; + // Save state on resize. + this.addStateEvents('resize'); - if (!me.initialized) { - if (owner.frameSize) { - me.frameSize = owner.frameSize; - } - else { - owner.frameSize = me.frameSize = { - top: 0, - left: 0, - bottom: 0, - right: 0 - }; + // Move this into Observable? + if (me.plugins) { + me.plugins = [].concat(me.plugins); + for (i = 0, len = me.plugins.length; i < len; i++) { + me.plugins[i] = me.initPlugin(me.plugins[i]); } } - me.callParent(arguments); - }, - - beforeLayout : function(width, height, isSetSize, callingContainer) { - this.callParent(arguments); - - var me = this, - owner = me.owner, - ownerCt = owner.ownerCt, - layout = owner.layout, - isVisible = owner.isVisible(true), - ownerElChild = owner.el.child, - layoutCollection; - // Cache the size we began with so we can see if there has been any effect. - me.previousComponentSize = me.lastComponentSize; + me.loader = me.getLoader(); - //Do not allow autoing of any dimensions which are fixed, unless we are being told to do so by the ownerCt's layout. - if (!isSetSize && ((!Ext.isNumber(width) && owner.isFixedWidth()) || (!Ext.isNumber(height) && owner.isFixedHeight())) && callingContainer !== ownerCt) { - me.doContainerLayout(); - return false; + if (me.renderTo) { + me.render(me.renderTo); + // EXTJSIV-1935 - should be a way to do afterShow or something, but that + // won't work. Likewise, rendering hidden and then showing (w/autoShow) has + // implications to afterRender so we cannot do that. } - // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag. - // If the owner itself is a directly hidden floater, set the needsLayout object on that for when it is shown. - if (!isVisible && (owner.hiddenAncestor || owner.floating)) { - if (owner.hiddenAncestor) { - layoutCollection = owner.hiddenAncestor.layoutOnShow; - layoutCollection.remove(owner); - layoutCollection.add(owner); - } - owner.needsLayout = { - width: width, - height: height, - isSetSize: false - }; + if (me.autoShow) { + me.show(); } - if (isVisible && this.needsLayout(width, height)) { - return owner.beforeComponentLayout(width, height, isSetSize, callingContainer); - } - else { - return false; + if (Ext.isDefined(me.disabledClass)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.'); + } + me.disabledCls = me.disabledClass; + delete me.disabledClass; } }, - /** - * Check if the new size is different from the current size and only - * trigger a layout if it is necessary. - * @param {Mixed} width The new width to set. - * @param {Mixed} height The new height to set. - */ - needsLayout : function(width, height) { - var me = this, - widthBeingChanged, - heightBeingChanged; - me.lastComponentSize = me.lastComponentSize || { - width: -Infinity, - height: -Infinity - }; - - // If autoWidthing, or an explicitly different width is passed, then the width is being changed. - widthBeingChanged = !Ext.isDefined(width) || me.lastComponentSize.width !== width; - - // If autoHeighting, or an explicitly different height is passed, then the height is being changed. - heightBeingChanged = !Ext.isDefined(height) || me.lastComponentSize.height !== height; - - - // isSizing flag added to prevent redundant layouts when going up the layout chain - return !me.isSizing && (me.childrenChanged || widthBeingChanged || heightBeingChanged); + initComponent: function () { + // This is called again here to allow derived classes to add plugin configs to the + // plugins array before calling down to this, the base initComponent. + this.constructPlugins(); }, /** - * Set the size of any element supporting undefined, null, and values. - * @param {Mixed} width The new width to set. - * @param {Mixed} height The new height to set. - */ - setElementSize: function(el, width, height) { - if (width !== undefined && height !== undefined) { - el.setSize(width, height); - } - else if (height !== undefined) { - el.setHeight(height); - } - else if (width !== undefined) { - el.setWidth(width); - } - }, - - /** - * Returns the owner component's resize element. - * @return {Ext.core.Element} - */ - getTarget : function() { - return this.owner.el; - }, - - /** - *

        Returns the element into which rendering must take place. Defaults to the owner Component's encapsulating element.

        - * May be overridden in Component layout managers which implement an inner element. - * @return {Ext.core.Element} + * The supplied default state gathering method for the AbstractComponent class. + * + * This method returns dimension settings such as `flex`, `anchor`, `width` and `height` along with `collapsed` + * state. + * + * Subclasses which implement more complex state should call the superclass's implementation, and apply their state + * to the result if this basic state is to be saved. + * + * Note that Component state will only be saved if the Component has a {@link #stateId} and there as a StateProvider + * configured for the document. + * + * @return {Object} */ - getRenderTarget : function() { - return this.owner.el; - }, - - /** - * Set the size of the target element. - * @param {Mixed} width The new width to set. - * @param {Mixed} height The new height to set. - */ - setTargetSize : function(width, height) { - var me = this; - me.setElementSize(me.owner.el, width, height); - - if (me.owner.frameBody) { - var targetInfo = me.getTargetInfo(), - padding = targetInfo.padding, - border = targetInfo.border, - frameSize = me.frameSize; + getState: function() { + var me = this, + layout = me.ownerCt ? (me.shadowOwnerCt || me.ownerCt).getLayout() : null, + state = { + collapsed: me.collapsed + }, + width = me.width, + height = me.height, + cm = me.collapseMemento, + anchors; - me.setElementSize(me.owner.frameBody, - Ext.isNumber(width) ? (width - frameSize.left - frameSize.right - padding.left - padding.right - border.left - border.right) : width, - Ext.isNumber(height) ? (height - frameSize.top - frameSize.bottom - padding.top - padding.bottom - border.top - border.bottom) : height - ); + // If a Panel-local collapse has taken place, use remembered values as the dimensions. + // TODO: remove this coupling with Panel's privates! All collapse/expand logic should be refactored into one place. + if (me.collapsed && cm) { + if (Ext.isDefined(cm.data.width)) { + width = cm.width; + } + if (Ext.isDefined(cm.data.height)) { + height = cm.height; + } } - me.autoSized = { - width: !Ext.isNumber(width), - height: !Ext.isNumber(height) - }; - - me.lastComponentSize = { - width: width, - height: height - }; - }, + // If we have flex, only store the perpendicular dimension. + if (layout && me.flex) { + state.flex = me.flex; + if (layout.perpendicularPrefix) { + state[layout.perpendicularPrefix] = me['get' + layout.perpendicularPrefixCap](); + } else { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.Component: Specified a flex value on a component not inside a Box layout'); + } + } + } + // If we have anchor, only store dimensions which are *not* being anchored + else if (layout && me.anchor) { + state.anchor = me.anchor; + anchors = me.anchor.split(' ').concat(null); + if (!anchors[0]) { + if (me.width) { + state.width = width; + } + } + if (!anchors[1]) { + if (me.height) { + state.height = height; + } + } + } + // Store dimensions. + else { + if (me.width) { + state.width = width; + } + if (me.height) { + state.height = height; + } + } - getTargetInfo : function() { - if (!this.targetInfo) { - var target = this.getTarget(), - body = this.owner.getTargetEl(); + // Don't save dimensions if they are unchanged from the original configuration. + if (state.width == me.initialConfig.width) { + delete state.width; + } + if (state.height == me.initialConfig.height) { + delete state.height; + } - this.targetInfo = { - padding: { - top: target.getPadding('t'), - right: target.getPadding('r'), - bottom: target.getPadding('b'), - left: target.getPadding('l') - }, - border: { - top: target.getBorderWidth('t'), - right: target.getBorderWidth('r'), - bottom: target.getBorderWidth('b'), - left: target.getBorderWidth('l') - }, - bodyMargin: { - top: body.getMargin('t'), - right: body.getMargin('r'), - bottom: body.getMargin('b'), - left: body.getMargin('l') - } - }; + // If a Box layout was managing the perpendicular dimension, don't save that dimension + if (layout && layout.align && (layout.align.indexOf('stretch') !== -1)) { + delete state[layout.perpendicularPrefix]; } - return this.targetInfo; + return state; }, - // Start laying out UP the ownerCt's layout when flagged to do so. - doOwnerCtLayouts: function() { - var owner = this.owner, - ownerCt = owner.ownerCt, - ownerCtComponentLayout, ownerCtContainerLayout, - curSize = this.lastComponentSize, - prevSize = this.previousComponentSize, - widthChange = (prevSize && curSize && curSize.width) ? curSize.width !== prevSize.width : true, - heightChange = (prevSize && curSize && curSize.height) ? curSize.height !== prevSize.height : true; + show: Ext.emptyFn, + animate: function(animObj) { + var me = this, + to; - // If size has not changed, do not inform upstream layouts - if (!ownerCt || (!widthChange && !heightChange)) { - return; + animObj = animObj || {}; + to = animObj.to || {}; + + if (Ext.fx.Manager.hasFxBlock(me.id)) { + return me; } - - ownerCtComponentLayout = ownerCt.componentLayout; - ownerCtContainerLayout = ownerCt.layout; + // Special processing for animating Component dimensions. + if (!animObj.dynamic && (to.height || to.width)) { + var curWidth = me.getWidth(), + w = curWidth, + curHeight = me.getHeight(), + h = curHeight, + needsResize = false; - if (!owner.floating && ownerCtComponentLayout && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) { - if (!ownerCt.suspendLayout && ownerCtContainerLayout && !ownerCtContainerLayout.layoutBusy) { + if (to.height && to.height > curHeight) { + h = to.height; + needsResize = true; + } + if (to.width && to.width > curWidth) { + w = to.width; + needsResize = true; + } - // If the owning Container may be adjusted in any of the the dimension which have changed, perform its Component layout - if (((widthChange && !ownerCt.isFixedWidth()) || (heightChange && !ownerCt.isFixedHeight()))) { - // Set the isSizing flag so that the upstream Container layout (called after a Component layout) can omit this component from sizing operations - this.isSizing = true; - ownerCt.doComponentLayout(); - this.isSizing = false; + // If any dimensions are being increased, we must resize the internal structure + // of the Component, but then clip it by sizing its encapsulating element back to original dimensions. + // The animation will then progressively reveal the larger content. + if (needsResize) { + var clearWidth = !Ext.isNumber(me.width), + clearHeight = !Ext.isNumber(me.height); + + me.componentLayout.childrenChanged = true; + me.setSize(w, h, me.ownerCt); + me.el.setSize(curWidth, curHeight); + if (clearWidth) { + delete me.width; } - // Execute upstream Container layout - else if (ownerCtContainerLayout.bindToOwnerCtContainer === true) { - ownerCtContainerLayout.layout(); + if (clearHeight) { + delete me.height; } } } + return me.mixins.animate.animate.apply(me, arguments); }, - doContainerLayout: function() { - var me = this, - owner = me.owner, - ownerCt = owner.ownerCt, - layout = owner.layout, - ownerCtComponentLayout; + /** + * This method finds the topmost active layout who's processing will eventually determine the size and position of + * this Component. + * + * This method is useful when dynamically adding Components into Containers, and some processing must take place + * after the final sizing and positioning of the Component has been performed. + * + * @return {Ext.Component} + */ + findLayoutController: function() { + return this.findParentBy(function(c) { + // Return true if we are at the root of the Container tree + // or this Container's layout is busy but the next one up is not. + return !c.ownerCt || (c.layout.layoutBusy && !c.ownerCt.layout.layoutBusy); + }); + }, - // Run the container layout if it exists (layout for child items) - // **Unless automatic laying out is suspended, or the layout is currently running** - if (!owner.suspendLayout && layout && layout.isLayout && !layout.layoutBusy && !layout.isAutoDock) { - layout.layout(); + onShow : function() { + // Layout if needed + var needsLayout = this.needsLayout; + if (Ext.isObject(needsLayout)) { + this.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize, needsLayout.ownerCt); } + }, - // Tell the ownerCt that it's child has changed and can be re-layed by ignoring the lastComponentSize cache. - if (ownerCt && ownerCt.componentLayout) { - ownerCtComponentLayout = ownerCt.componentLayout; - if (!owner.floating && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) { - ownerCtComponentLayout.childrenChanged = true; - } + constructPlugin: function(plugin) { + if (plugin.ptype && typeof plugin.init != 'function') { + plugin.cmp = this; + plugin = Ext.PluginManager.create(plugin); } + else if (typeof plugin == 'string') { + plugin = Ext.PluginManager.create({ + ptype: plugin, + cmp: this + }); + } + return plugin; }, - afterLayout : function(width, height, isSetSize, layoutOwner) { - this.doContainerLayout(); - this.owner.afterComponentLayout(width, height, isSetSize, layoutOwner); - } -}); - -/** - * @class Ext.state.Manager - * This is the global state manager. By default all components that are "state aware" check this class - * for state information if you don't pass them a custom state provider. In order for this class - * to be useful, it must be initialized with a provider when your application initializes. Example usage: -
        
        -// in your initialization function
        -init : function(){
        -   Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
        -   var win = new Window(...);
        -   win.restoreState();
        -}
        - 
        - * This class passes on calls from components to the underlying {@link Ext.state.Provider} so that - * there is a common interface that can be used without needing to refer to a specific provider instance - * in every component. - * @singleton - * @docauthor Evan Trimboli - */ -Ext.define('Ext.state.Manager', { - singleton: true, - requires: ['Ext.state.Provider'], - constructor: function() { - this.provider = Ext.create('Ext.state.Provider'); - }, - - /** - * Configures the default state provider for your application - * @param {Provider} stateProvider The state provider to set + * Ensures that the plugins array contains fully constructed plugin instances. This converts any configs into their + * appropriate instances. */ - setProvider : function(stateProvider){ - this.provider = stateProvider; - }, + constructPlugins: function() { + var me = this, + plugins = me.plugins, + i, len; - /** - * Returns the current value for a key - * @param {String} name The key name - * @param {Mixed} defaultValue The default value to return if the key lookup does not match - * @return {Mixed} The state data - */ - get : function(key, defaultValue){ - return this.provider.get(key, defaultValue); + if (plugins) { + for (i = 0, len = plugins.length; i < len; i++) { + // this just returns already-constructed plugin instances... + plugins[i] = me.constructPlugin(plugins[i]); + } + } }, - /** - * Sets the value for a key - * @param {String} name The key name - * @param {Mixed} value The state data - */ - set : function(key, value){ - this.provider.set(key, value); + // @private + initPlugin : function(plugin) { + plugin.init(this); + + return plugin; }, /** - * Clears a value from the state - * @param {String} name The key name + * Handles autoRender. Floating Components may have an ownerCt. If they are asking to be constrained, constrain them + * within that ownerCt, and have their z-index managed locally. Floating Components are always rendered to + * document.body */ - clear : function(key){ - this.provider.clear(key); + doAutoRender: function() { + var me = this; + if (me.floating) { + me.render(document.body); + } else { + me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender); + } }, - /** - * Gets the currently configured state provider - * @return {Provider} The state provider - */ - getProvider : function(){ - return this.provider; - } -}); -/** - * @class Ext.state.Stateful - * A mixin for being able to save the state of an object to an underlying - * {@link Ext.state.Provider}. - */ -Ext.define('Ext.state.Stateful', { + // @private + render : function(container, position) { + var me = this; - /* Begin Definitions */ + if (!me.rendered && me.fireEvent('beforerender', me) !== false) { - mixins: { - observable: 'Ext.util.Observable' - }, + // Flag set during the render process. + // It can be used to inhibit event-driven layout calls during the render phase + me.rendering = true; - requires: ['Ext.state.Manager'], + // If this.el is defined, we want to make sure we are dealing with + // an Ext Element. + if (me.el) { + me.el = Ext.get(me.el); + } - /* End Definitions */ + // Perform render-time processing for floating Components + if (me.floating) { + me.onFloatRender(); + } - /** - * @cfg {Boolean} stateful - *

        A flag which causes the object to attempt to restore the state of - * internal properties from a saved state on startup. The object must have - * a {@link #stateId} for state to be managed. - * Auto-generated ids are not guaranteed to be stable across page loads and - * cannot be relied upon to save and restore the same state for a object.

        - *

        For state saving to work, the state manager's provider must have been - * set to an implementation of {@link Ext.state.Provider} which overrides the - * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get} - * methods to save and recall name/value pairs. A built-in implementation, - * {@link Ext.state.CookieProvider} is available.

        - *

        To set the state provider for the current page:

        - *
        
        -Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
        -    expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
        -}));
        -     * 
        - *

        A stateful object attempts to save state when one of the events - * listed in the {@link #stateEvents} configuration fires.

        - *

        To save state, a stateful object first serializes its state by - * calling {@link #getState}. By default, this function does - * nothing. The developer must provide an implementation which returns an - * object hash which represents the restorable state of the object.

        - *

        The value yielded by getState is passed to {@link Ext.state.Manager#set} - * which uses the configured {@link Ext.state.Provider} to save the object - * keyed by the {@link #stateId}

        . - *

        During construction, a stateful object attempts to restore - * its state by calling {@link Ext.state.Manager#get} passing the - * {@link #stateId}

        - *

        The resulting object is passed to {@link #applyState}. - * The default implementation of {@link #applyState} simply copies - * properties into the object, but a developer may override this to support - * more behaviour.

        - *

        You can perform extra processing on state save and restore by attaching - * handlers to the {@link #beforestaterestore}, {@link #staterestore}, - * {@link #beforestatesave} and {@link #statesave} events.

        - */ - stateful: true, + container = me.initContainer(container); - /** - * @cfg {String} stateId - * The unique id for this object to use for state management purposes. - *

        See {@link #stateful} for an explanation of saving and restoring state.

        - */ + me.onRender(container, position); - /** - * @cfg {Array} stateEvents - *

        An array of events that, when fired, should trigger this object to - * save its state (defaults to none). stateEvents may be any type - * of event supported by this object, including browser or custom events - * (e.g., ['click', 'customerchange']).

        - *

        See {@link #stateful} for an explanation of saving and - * restoring object state.

        - */ + // Tell the encapsulating element to hide itself in the way the Component is configured to hide + // This means DISPLAY, VISIBILITY or OFFSETS. + me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]); - /** - * @cfg {Number} saveBuffer A buffer to be applied if many state events are fired within - * a short period. Defaults to 100. - */ - saveDelay: 100, + if (me.overCls) { + me.el.hover(me.addOverCls, me.removeOverCls, me); + } - autoGenIdRe: /^((\w+-)|(ext-comp-))\d{4,}$/i, + me.fireEvent('render', me); - constructor: function(config) { - var me = this; + me.initContent(); - config = config || {}; - if (Ext.isDefined(config.stateful)) { - me.stateful = config.stateful; - } - if (Ext.isDefined(config.saveDelay)) { - me.saveDelay = config.saveDelay; - } - me.stateId = me.stateId || config.stateId; + me.afterRender(container); + me.fireEvent('afterrender', me); - if (!me.stateEvents) { - me.stateEvents = []; - } - if (config.stateEvents) { - me.stateEvents.concat(config.stateEvents); - } - this.addEvents( - /** - * @event beforestaterestore - * Fires before the state of the object is restored. Return false from an event handler to stop the restore. - * @param {Ext.state.Stateful} this - * @param {Object} state The hash of state values returned from the StateProvider. If this - * event is not vetoed, then the state object is passed to applyState. By default, - * that simply copies property values into this object. The method maybe overriden to - * provide custom state restoration. - */ - 'beforestaterestore', + me.initEvents(); - /** - * @event staterestore - * Fires after the state of the object is restored. - * @param {Ext.state.Stateful} this - * @param {Object} state The hash of state values returned from the StateProvider. This is passed - * to applyState. By default, that simply copies property values into this - * object. The method maybe overriden to provide custom state restoration. - */ - 'staterestore', + if (me.hidden) { + // Hiding during the render process should not perform any ancillary + // actions that the full hide process does; It is not hiding, it begins in a hidden state.' + // So just make the element hidden according to the configured hideMode + me.el.hide(); + } - /** - * @event beforestatesave - * Fires before the state of the object is saved to the configured state provider. Return false to stop the save. - * @param {Ext.state.Stateful} this - * @param {Object} state The hash of state values. This is determined by calling - * getState() on the object. This method must be provided by the - * developer to return whetever representation of state is required, by default, Ext.state.Stateful - * has a null implementation. - */ - 'beforestatesave', + if (me.disabled) { + // pass silent so the event doesn't fire the first time. + me.disable(true); + } - /** - * @event statesave - * Fires after the state of the object is saved to the configured state provider. - * @param {Ext.state.Stateful} this - * @param {Object} state The hash of state values. This is determined by calling - * getState() on the object. This method must be provided by the - * developer to return whetever representation of state is required, by default, Ext.state.Stateful - * has a null implementation. - */ - 'statesave' - ); - me.mixins.observable.constructor.call(me); - if (me.stateful !== false) { - me.initStateEvents(); - me.initState(); + // Delete the flag once the rendering is done. + delete me.rendering; } + return me; }, - /** - * Initializes any state events for this object. - * @private - */ - initStateEvents: function() { - this.addStateEvents(this.stateEvents); - }, - - /** - * Add events that will trigger the state to be saved. - * @param {String/Array} events The event name or an array of event names. - */ - addStateEvents: function(events){ - if (!Ext.isArray(events)) { - events = [events]; - } - + // @private + onRender : function(container, position) { var me = this, - i = 0, - len = events.length; + el = me.el, + styles = me.initStyles(), + renderTpl, renderData, i; - for (; i < len; ++i) { - me.on(events[i], me.onStateChange, me); + position = me.getInsertPosition(position); + + if (!el) { + if (position) { + el = Ext.DomHelper.insertBefore(position, me.getElConfig(), true); + } + else { + el = Ext.DomHelper.append(container, me.getElConfig(), true); + } + } + else if (me.allowDomMove !== false) { + if (position) { + container.dom.insertBefore(el.dom, position); + } else { + container.dom.appendChild(el.dom); + } + } + + if (Ext.scopeResetCSS && !me.ownerCt) { + // If this component's el is the body element, we add the reset class to the html tag + if (el.dom == Ext.getBody().dom) { + el.parent().addCls(Ext.baseCSSPrefix + 'reset'); + } + else { + // Else we wrap this element in an element that adds the reset class. + me.resetEl = el.wrap({ + cls: Ext.baseCSSPrefix + 'reset' + }); + } + } + + me.setUI(me.ui); + + el.addCls(me.initCls()); + el.setStyle(styles); + + // Here we check if the component has a height set through style or css. + // If it does then we set the this.height to that value and it won't be + // considered an auto height component + // if (this.height === undefined) { + // var height = el.getHeight(); + // // This hopefully means that the panel has an explicit height set in style or css + // if (height - el.getPadding('tb') - el.getBorderWidth('tb') > 0) { + // this.height = height; + // } + // } + + me.el = el; + + me.initFrame(); + + renderTpl = me.initRenderTpl(); + if (renderTpl) { + renderData = me.initRenderData(); + renderTpl.append(me.getTargetEl(), renderData); } + + me.applyRenderSelectors(); + + me.rendered = true; }, - /** - * This method is called when any of the {@link #stateEvents} are fired. - * @private - */ - onStateChange: function(){ + // @private + afterRender : function() { var me = this, - delay = me.saveDelay; + pos, + xy; - if (delay > 0) { - if (!me.stateTask) { - me.stateTask = Ext.create('Ext.util.DelayedTask', me.saveState, me); - } - me.stateTask.delay(me.saveDelay); + me.getComponentLayout(); + + // Set the size if a size is configured, or if this is the outermost Container. + // Also, if this is a collapsed Panel, it needs an initial component layout + // to lay out its header so that it can have a height determined. + if (me.collapsed || (!me.ownerCt || (me.height || me.width))) { + me.setSize(me.width, me.height); } else { - me.saveState(); + // It is expected that child items be rendered before this method returns and + // the afterrender event fires. Since we aren't going to do the layout now, we + // must render the child items. This is handled implicitly above in the layout + // caused by setSize. + me.renderChildren(); + } + + // For floaters, calculate x and y if they aren't defined by aligning + // the sized element to the center of either the container or the ownerCt + if (me.floating && (me.x === undefined || me.y === undefined)) { + if (me.floatParent) { + xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c'); + pos = me.floatParent.getTargetEl().translatePoints(xy[0], xy[1]); + } else { + xy = me.el.getAlignToXY(me.container, 'c-c'); + pos = me.container.translatePoints(xy[0], xy[1]); + } + me.x = me.x === undefined ? pos.left: me.x; + me.y = me.y === undefined ? pos.top: me.y; + } + + if (Ext.isDefined(me.x) || Ext.isDefined(me.y)) { + me.setPosition(me.x, me.y); + } + + if (me.styleHtmlContent) { + me.getTargetEl().addCls(me.styleHtmlCls); } }, /** - * Saves the state of the object to the persistence store. * @private + * Called by Component#doAutoRender + * + * Register a Container configured `floating: true` with this Component's {@link Ext.ZIndexManager ZIndexManager}. + * + * Components added in ths way will not participate in any layout, but will be rendered + * upon first show in the way that {@link Ext.window.Window Window}s are. */ - saveState: function() { + registerFloatingItem: function(cmp) { + var me = this; + if (!me.floatingItems) { + me.floatingItems = Ext.create('Ext.ZIndexManager', me); + } + me.floatingItems.register(cmp); + }, + + renderChildren: function () { var me = this, - id, - state; + layout = me.getComponentLayout(); - if (me.stateful !== false) { - id = me.getStateId(); - if (id) { - state = me.getState(); - if (me.fireEvent('beforestatesave', me, state) !== false) { - Ext.state.Manager.set(id, state); - me.fireEvent('statesave', me, state); - } - } - } + me.suspendLayout = true; + layout.renderChildren(); + delete me.suspendLayout; }, - /** - * Gets the current state of the object. By default this function returns null, - * it should be overridden in subclasses to implement methods for getting the state. - * @return {Object} The current state - */ - getState: function(){ - return null; + frameCls: Ext.baseCSSPrefix + 'frame', + + frameIdRegex: /[-]frame\d+[TMB][LCR]$/, + + frameElementCls: { + tl: [], + tc: [], + tr: [], + ml: [], + mc: [], + mr: [], + bl: [], + bc: [], + br: [] }, + frameTpl: [ + '', + '
        {parent.baseCls}-{parent.ui}-{.}-tl" style="background-position: {tl}; padding-left: {frameWidth}px" role="presentation">', + '
        {parent.baseCls}-{parent.ui}-{.}-tr" style="background-position: {tr}; padding-right: {frameWidth}px" role="presentation">', + '
        {parent.baseCls}-{parent.ui}-{.}-tc" style="background-position: {tc}; height: {frameWidth}px" role="presentation">
        ', + '
        ', + '
        ', + '
        ', + '
        {parent.baseCls}-{parent.ui}-{.}-ml" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation">', + '
        {parent.baseCls}-{parent.ui}-{.}-mr" style="background-position: {mr}; padding-right: {frameWidth}px" role="presentation">', + '
        {parent.baseCls}-{parent.ui}-{.}-mc" role="presentation">
        ', + '
        ', + '
        ', + '', + '
        {parent.baseCls}-{parent.ui}-{.}-bl" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation">', + '
        {parent.baseCls}-{parent.ui}-{.}-br" style="background-position: {br}; padding-right: {frameWidth}px" role="presentation">', + '
        {parent.baseCls}-{parent.ui}-{.}-bc" style="background-position: {bc}; height: {frameWidth}px" role="presentation">
        ', + '
        ', + '
        ', + '
        ' + ], + + frameTableTpl: [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
        {parent.baseCls}-{parent.ui}-{.}-tl" style="background-position: {tl}; padding-left:{frameWidth}px" role="presentation"> {parent.baseCls}-{parent.ui}-{.}-tc" style="background-position: {tc}; height: {frameWidth}px" role="presentation"> {parent.baseCls}-{parent.ui}-{.}-tr" style="background-position: {tr}; padding-left: {frameWidth}px" role="presentation">
        {parent.baseCls}-{parent.ui}-{.}-ml" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"> {parent.baseCls}-{parent.ui}-{.}-mc" style="background-position: 0 0;" role="presentation"> {parent.baseCls}-{parent.ui}-{.}-mr" style="background-position: {mr}; padding-left: {frameWidth}px" role="presentation">
        {parent.baseCls}-{parent.ui}-{.}-bl" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"> {parent.baseCls}-{parent.ui}-{.}-bc" style="background-position: {bc}; height: {frameWidth}px" role="presentation"> {parent.baseCls}-{parent.ui}-{.}-br" style="background-position: {br}; padding-left: {frameWidth}px" role="presentation">
        ' + ], + /** - * Applies the state to the object. This should be overridden in subclasses to do - * more complex state operations. By default it applies the state properties onto - * the current object. - * @param {Object} state The state + * @private */ - applyState: function(state) { - if (state) { - Ext.apply(this, state); + initFrame : function() { + if (Ext.supports.CSS3BorderRadius) { + return false; } - }, - /** - * Gets the state id for this object. - * @return {String} The state id, null if not found. - */ - getStateId: function() { var me = this, - id = me.stateId; + frameInfo = me.getFrameInfo(), + frameWidth = frameInfo.width, + frameTpl = me.getFrameTpl(frameInfo.table), + frameGenId; - if (!id) { - id = me.autoGenIdRe.test(String(me.id)) ? null : me.id; + if (me.frame) { + // since we render id's into the markup and id's NEED to be unique, we have a + // simple strategy for numbering their generations. + me.frameGenId = frameGenId = (me.frameGenId || 0) + 1; + frameGenId = me.id + '-frame' + frameGenId; + + // Here we render the frameTpl to this component. This inserts the 9point div or the table framing. + frameTpl.insertFirst(me.el, Ext.apply({}, { + fgid: frameGenId, + ui: me.ui, + uiCls: me.uiCls, + frameCls: me.frameCls, + baseCls: me.baseCls, + frameWidth: frameWidth, + top: !!frameInfo.top, + left: !!frameInfo.left, + right: !!frameInfo.right, + bottom: !!frameInfo.bottom + }, me.getFramePositions(frameInfo))); + + // The frameBody is returned in getTargetEl, so that layouts render items to the correct target.= + me.frameBody = me.el.down('.' + me.frameCls + '-mc'); + + // Clean out the childEls for the old frame elements (the majority of the els) + me.removeChildEls(function (c) { + return c.id && me.frameIdRegex.test(c.id); + }); + + // Add the childEls for each of the new frame elements + Ext.each(['TL','TC','TR','ML','MC','MR','BL','BC','BR'], function (suffix) { + me.childEls.push({ name: 'frame' + suffix, id: frameGenId + suffix }); + }); } - return id; }, - /** - * Initializes the state of the object upon construction. - * @private - */ - initState: function(){ + updateFrame: function() { + if (Ext.supports.CSS3BorderRadius) { + return false; + } + var me = this, - id = me.getStateId(), - state; + wasTable = this.frameSize && this.frameSize.table, + oldFrameTL = this.frameTL, + oldFrameBL = this.frameBL, + oldFrameML = this.frameML, + oldFrameMC = this.frameMC, + newMCClassName; - if (me.stateful !== false) { - if (id) { - state = Ext.state.Manager.get(id); - if (state) { - state = Ext.apply({}, state); - if (me.fireEvent('beforestaterestore', me, state) !== false) { - me.applyState(state); - me.fireEvent('staterestore', me, state); + this.initFrame(); + + if (oldFrameMC) { + if (me.frame) { + // Reapply render selectors + delete me.frameTL; + delete me.frameTC; + delete me.frameTR; + delete me.frameML; + delete me.frameMC; + delete me.frameMR; + delete me.frameBL; + delete me.frameBC; + delete me.frameBR; + this.applyRenderSelectors(); + + // Store the class names set on the new mc + newMCClassName = this.frameMC.dom.className; + + // Replace the new mc with the old mc + oldFrameMC.insertAfter(this.frameMC); + this.frameMC.remove(); + + // Restore the reference to the old frame mc as the framebody + this.frameBody = this.frameMC = oldFrameMC; + + // Apply the new mc classes to the old mc element + oldFrameMC.dom.className = newMCClassName; + + // Remove the old framing + if (wasTable) { + me.el.query('> table')[1].remove(); + } + else { + if (oldFrameTL) { + oldFrameTL.remove(); + } + if (oldFrameBL) { + oldFrameBL.remove(); } + oldFrameML.remove(); } } + else { + // We were framed but not anymore. Move all content from the old frame to the body + + } + } + else if (me.frame) { + this.applyRenderSelectors(); } }, - /** - * Destroys this stateful object. - */ - destroy: function(){ - var task = this.stateTask; - if (task) { - task.cancel(); + getFrameInfo: function() { + if (Ext.supports.CSS3BorderRadius) { + return false; } - this.clearListeners(); - } + var me = this, + left = me.el.getStyle('background-position-x'), + top = me.el.getStyle('background-position-y'), + info, frameInfo = false, max; -}); + // Some browsers dont support background-position-x and y, so for those + // browsers let's split background-position into two parts. + if (!left && !top) { + info = me.el.getStyle('background-position').split(' '); + left = info[0]; + top = info[1]; + } -/** - * @class Ext.AbstractManager - * @extends Object - * Base Manager class - */ -Ext.define('Ext.AbstractManager', { + // We actually pass a string in the form of '[type][tl][tr]px [type][br][bl]px' as + // the background position of this.el from the css to indicate to IE that this component needs + // framing. We parse it here and change the markup accordingly. + if (parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000) { + max = Math.max; - /* Begin Definitions */ + frameInfo = { + // Table markup starts with 110, div markup with 100. + table: left.substr(0, 3) == '110', - requires: ['Ext.util.HashMap'], + // Determine if we are dealing with a horizontal or vertical component + vertical: top.substr(0, 3) == '110', - /* End Definitions */ + // Get and parse the different border radius sizes + top: max(left.substr(3, 2), left.substr(5, 2)), + right: max(left.substr(5, 2), top.substr(3, 2)), + bottom: max(top.substr(3, 2), top.substr(5, 2)), + left: max(top.substr(5, 2), left.substr(3, 2)) + }; - typeName: 'type', + frameInfo.width = max(frameInfo.top, frameInfo.right, frameInfo.bottom, frameInfo.left); - constructor: function(config) { - Ext.apply(this, config || {}); + // Just to be sure we set the background image of the el to none. + me.el.setStyle('background-image', 'none'); + } - /** - * Contains all of the items currently managed - * @property all - * @type Ext.util.MixedCollection - */ - this.all = Ext.create('Ext.util.HashMap'); + // This happens when you set frame: true explicitly without using the x-frame mixin in sass. + // This way IE can't figure out what sizes to use and thus framing can't work. + if (me.frame === true && !frameInfo) { + Ext.Error.raise("You have set frame: true explicity on this component while it doesn't have any " + + "framing defined in the CSS template. In this case IE can't figure out what sizes " + + "to use and thus framing on this component will be disabled."); + } - this.types = {}; - }, + me.frame = me.frame || !!frameInfo; + me.frameSize = frameInfo || false; - /** - * Returns an item by id. - * For additional details see {@link Ext.util.HashMap#get}. - * @param {String} id The id of the item - * @return {Mixed} The item, undefined if not found. - */ - get : function(id) { - return this.all.get(id); + return frameInfo; }, - /** - * Registers an item to be managed - * @param {Mixed} item The item to register - */ - register: function(item) { - this.all.add(item); - }, + getFramePositions: function(frameInfo) { + var me = this, + frameWidth = frameInfo.width, + dock = me.dock, + positions, tc, bc, ml, mr; - /** - * Unregisters an item by removing it from this manager - * @param {Mixed} item The item to unregister - */ - unregister: function(item) { - this.all.remove(item); - }, + if (frameInfo.vertical) { + tc = '0 -' + (frameWidth * 0) + 'px'; + bc = '0 -' + (frameWidth * 1) + 'px'; - /** - *

        Registers a new item constructor, keyed by a type key. - * @param {String} type The mnemonic string by which the class may be looked up. - * @param {Constructor} cls The new instance class. - */ - registerType : function(type, cls) { - this.types[type] = cls; - cls[this.typeName] = type; + if (dock && dock == "right") { + tc = 'right -' + (frameWidth * 0) + 'px'; + bc = 'right -' + (frameWidth * 1) + 'px'; + } + + positions = { + tl: '0 -' + (frameWidth * 0) + 'px', + tr: '0 -' + (frameWidth * 1) + 'px', + bl: '0 -' + (frameWidth * 2) + 'px', + br: '0 -' + (frameWidth * 3) + 'px', + + ml: '-' + (frameWidth * 1) + 'px 0', + mr: 'right 0', + + tc: tc, + bc: bc + }; + } else { + ml = '-' + (frameWidth * 0) + 'px 0'; + mr = 'right 0'; + + if (dock && dock == "bottom") { + ml = 'left bottom'; + mr = 'right bottom'; + } + + positions = { + tl: '0 -' + (frameWidth * 2) + 'px', + tr: 'right -' + (frameWidth * 3) + 'px', + bl: '0 -' + (frameWidth * 4) + 'px', + br: 'right -' + (frameWidth * 5) + 'px', + + ml: ml, + mr: mr, + + tc: '0 -' + (frameWidth * 0) + 'px', + bc: '0 -' + (frameWidth * 1) + 'px' + }; + } + + return positions; }, /** - * Checks if an item type is registered. - * @param {String} type The mnemonic string by which the class may be looked up - * @return {Boolean} Whether the type is registered. + * @private */ - isRegistered : function(type){ - return this.types[type] !== undefined; + getFrameTpl : function(table) { + return table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl'); }, /** - * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object - * @param {Object} config The config object - * @param {String} defaultType If no type is discovered in the config object, we fall back to this type - * @return {Mixed} The instance of whatever this manager is managing + * Creates an array of class names from the configurations to add to this Component's `el` on render. + * + * Private, but (possibly) used by ComponentQuery for selection by class name if Component is not rendered. + * + * @return {String[]} An array of class names with which the Component's element will be rendered. + * @private */ - create: function(config, defaultType) { - var type = config[this.typeName] || config.type || defaultType, - Constructor = this.types[type]; + initCls: function() { + var me = this, + cls = []; - if (Constructor == undefined) { - Ext.Error.raise("The '" + type + "' type has not been registered with this manager"); - } + cls.push(me.baseCls); - return new Constructor(config); - }, + if (Ext.isDefined(me.cmpCls)) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.'); + } + me.componentCls = me.cmpCls; + delete me.cmpCls; + } - /** - * Registers a function that will be called when an item with the specified id is added to the manager. This will happen on instantiation. - * @param {String} id The item id - * @param {Function} fn The callback function. Called with a single parameter, the item. - * @param {Object} scope The scope (this reference) in which the callback is executed. Defaults to the item. - */ - onAvailable : function(id, fn, scope){ - var all = this.all, - item; - - if (all.containsKey(id)) { - item = all.get(id); - fn.call(scope || item, item); + if (me.componentCls) { + cls.push(me.componentCls); } else { - all.on('add', function(map, key, item){ - if (key == id) { - fn.call(scope || item, item); - all.un('add', fn, scope); - } - }); + me.componentCls = me.baseCls; + } + if (me.cls) { + cls.push(me.cls); + delete me.cls; } + + return cls.concat(me.additionalCls); }, - + /** - * Executes the specified function once for each item in the collection. - * Returning false from the function will cease iteration. - * - * The paramaters passed to the function are: - *

          - *
        • key : String

          The key of the item

        • - *
        • value : Number

          The value of the item

        • - *
        • length : Number

          The total number of items in the collection

        • - *
        - * @param {Object} fn The function to execute. - * @param {Object} scope The scope to execute in. Defaults to this. + * Sets the UI for the component. This will remove any existing UIs on the component. It will also loop through any + * uiCls set on the component and rename them so they include the new UI + * @param {String} ui The new UI for the component */ - each: function(fn, scope){ - this.all.each(fn, scope || this); - }, - - /** - * Gets the number of items in the collection. - * @return {Number} The number of items in the collection. - */ - getCount: function(){ - return this.all.getCount(); - } -}); + setUI: function(ui) { + var me = this, + oldUICls = Ext.Array.clone(me.uiCls), + newUICls = [], + classes = [], + cls, + i; -/** - * @class Ext.PluginManager - * @extends Ext.AbstractManager - *

        Provides a registry of available Plugin classes indexed by a mnemonic code known as the Plugin's ptype. - * The {@link Ext.Component#xtype xtype} provides a way to avoid instantiating child Components - * when creating a full, nested config object for a complete Ext page.

        - *

        A child Component may be specified simply as a config object - * as long as the correct {@link Ext.Component#xtype xtype} is specified so that if and when the Component - * needs rendering, the correct type can be looked up for lazy instantiation.

        - *

        For a list of all available {@link Ext.Component#xtype xtypes}, see {@link Ext.Component}.

        - * @singleton - */ -Ext.define('Ext.PluginManager', { - extend: 'Ext.AbstractManager', - alternateClassName: 'Ext.PluginMgr', - singleton: true, - typeName: 'ptype', + //loop through all exisiting uiCls and update the ui in them + for (i = 0; i < oldUICls.length; i++) { + cls = oldUICls[i]; - /** - * Creates a new Plugin from the specified config object using the - * config object's ptype to determine the class to instantiate. - * @param {Object} config A configuration object for the Plugin you wish to create. - * @param {Constructor} defaultType The constructor to provide the default Plugin type if - * the config object does not contain a ptype. (Optional if the config contains a ptype). - * @return {Ext.Component} The newly instantiated Plugin. - */ - //create: function(plugin, defaultType) { - // if (plugin instanceof this) { - // return plugin; - // } else { - // var type, config = {}; - // - // if (Ext.isString(plugin)) { - // type = plugin; - // } - // else { - // type = plugin[this.typeName] || defaultType; - // config = plugin; - // } - // - // return Ext.createByAlias('plugin.' + type, config); - // } - //}, + classes = classes.concat(me.removeClsWithUI(cls, true)); + newUICls.push(cls); + } - create : function(config, defaultType){ - if (config.init) { - return config; - } else { - return Ext.createByAlias('plugin.' + (config.ptype || defaultType), config); + if (classes.length) { + me.removeCls(classes); } - - // Prior system supported Singleton plugins. - //var PluginCls = this.types[config.ptype || defaultType]; - //if (PluginCls.init) { - // return PluginCls; - //} else { - // return new PluginCls(config); - //} - }, - /** - * Returns all plugins registered with the given type. Here, 'type' refers to the type of plugin, not its ptype. - * @param {String} type The type to search for - * @param {Boolean} defaultsOnly True to only return plugins of this type where the plugin's isDefault property is truthy - * @return {Array} All matching plugins - */ - findByType: function(type, defaultsOnly) { - var matches = [], - types = this.types; + //remove the UI from the element + me.removeUIFromElement(); - for (var name in types) { - if (!types.hasOwnProperty(name)) { - continue; - } - var item = types[name]; + //set the UI + me.ui = ui; - if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) { - matches.push(item); - } + //add the new UI to the elemend + me.addUIToElement(); + + //loop through all exisiting uiCls and update the ui in them + classes = []; + for (i = 0; i < newUICls.length; i++) { + cls = newUICls[i]; + classes = classes.concat(me.addClsWithUI(cls, true)); } - return matches; - } -}, function() { - /** - * Shorthand for {@link Ext.PluginManager#registerType} - * @param {String} ptype The ptype mnemonic string by which the Plugin class - * may be looked up. - * @param {Constructor} cls The new Plugin class. - * @member Ext - * @method preg - */ - Ext.preg = function() { - return Ext.PluginManager.registerType.apply(Ext.PluginManager, arguments); - }; -}); + if (classes.length) { + me.addCls(classes); + } + }, -/** - * @class Ext.ComponentManager - * @extends Ext.AbstractManager - *

        Provides a registry of all Components (instances of {@link Ext.Component} or any subclass - * thereof) on a page so that they can be easily accessed by {@link Ext.Component component} - * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).

        - *

        This object also provides a registry of available Component classes - * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}. - * The xtype provides a way to avoid instantiating child Components - * when creating a full, nested config object for a complete Ext page.

        - *

        A child Component may be specified simply as a config object - * as long as the correct {@link Ext.Component#xtype xtype} is specified so that if and when the Component - * needs rendering, the correct type can be looked up for lazy instantiation.

        - *

        For a list of all available {@link Ext.Component#xtype xtypes}, see {@link Ext.Component}.

        - * @singleton - */ -Ext.define('Ext.ComponentManager', { - extend: 'Ext.AbstractManager', - alternateClassName: 'Ext.ComponentMgr', - - singleton: true, - - typeName: 'xtype', - /** - * Creates a new Component from the specified config object using the - * config object's xtype to determine the class to instantiate. - * @param {Object} config A configuration object for the Component you wish to create. - * @param {Constructor} defaultType The constructor to provide the default Component type if - * the config object does not contain a xtype. (Optional if the config contains a xtype). - * @return {Ext.Component} The newly instantiated Component. + * Adds a cls to the uiCls array, which will also call {@link #addUIClsToElement} and adds to all elements of this + * component. + * @param {String/String[]} cls A string or an array of strings to add to the uiCls + * @param {Object} skip (Boolean) skip True to skip adding it to the class and do it later (via the return) */ - create: function(component, defaultType){ - if (component instanceof Ext.AbstractComponent) { - return component; - } - else if (Ext.isString(component)) { - return Ext.createByAlias('widget.' + component); - } - else { - var type = component.xtype || defaultType, - config = component; - - return Ext.createByAlias('widget.' + type, config); - } - }, + addClsWithUI: function(cls, skip) { + var me = this, + classes = [], + i; - registerType: function(type, cls) { - this.types[type] = cls; - cls[this.typeName] = type; - cls.prototype[this.typeName] = type; - } -}); -/** - * @class Ext.XTemplate - * @extends Ext.Template - *

        A template class that supports advanced functionality like:

          - *
        • Autofilling arrays using templates and sub-templates
        • - *
        • Conditional processing with basic comparison operators
        • - *
        • Basic math function support
        • - *
        • Execute arbitrary inline code with special built-in template variables
        • - *
        • Custom member functions
        • - *
        • Many special tags and built-in operators that aren't defined as part of - * the API, but are supported in the templates that can be created
        • - *

        - *

        XTemplate provides the templating mechanism built into:

          - *
        • {@link Ext.view.View}
        • - *

        - * - * The {@link Ext.Template} describes - * the acceptable parameters to pass to the constructor. The following - * examples demonstrate all of the supported features.

        - * - *
          - * - *
        • Sample Data - *
          - *

          This is the data object used for reference in each code example:

          - *
          
          -var data = {
          -name: 'Tommy Maintz',
          -title: 'Lead Developer',
          -company: 'Sencha Inc.',
          -email: 'tommy@sencha.com',
          -address: '5 Cups Drive',
          -city: 'Palo Alto',
          -state: 'CA',
          -zip: '44102',
          -drinks: ['Coffee', 'Soda', 'Water'],
          -kids: [{
          -        name: 'Joshua',
          -        age:3
          -    },{
          -        name: 'Matthew',
          -        age:2
          -    },{
          -        name: 'Solomon',
          -        age:0
          -}]
          -};
          - 
          - *
          - *
        • - * - * - *
        • Auto filling of arrays - *
          - *

          The tpl tag and the for operator are used - * to process the provided data object: - *

            - *
          • If the value specified in for is an array, it will auto-fill, - * repeating the template block inside the tpl tag for each item in the - * array.
          • - *
          • If for="." is specified, the data object provided is examined.
          • - *
          • While processing an array, the special variable {#} - * will provide the current array index + 1 (starts at 1, not 0).
          • - *
          - *

          - *
          
          -<tpl for=".">...</tpl>       // loop through array at root node
          -<tpl for="foo">...</tpl>     // loop through array at foo node
          -<tpl for="foo.bar">...</tpl> // loop through array at foo.bar node
          - 
          - * Using the sample data above: - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Kids: ',
          -    '<tpl for=".">',       // process the data.kids node
          -        '<p>{#}. {name}</p>',  // use current array index to autonumber
          -    '</tpl></p>'
          -);
          -tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
          - 
          - *

          An example illustrating how the for property can be leveraged - * to access specified members of the provided data object to populate the template:

          - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Name: {name}</p>',
          -    '<p>Title: {title}</p>',
          -    '<p>Company: {company}</p>',
          -    '<p>Kids: ',
          -    '<tpl for="kids">',     // interrogate the kids property within the data
          -        '<p>{name}</p>',
          -    '</tpl></p>'
          -);
          -tpl.overwrite(panel.body, data);  // pass the root node of the data object
          - 
          - *

          Flat arrays that contain values (and not objects) can be auto-rendered - * using the special {.} variable inside a loop. This variable - * will represent the value of the array at the current index:

          - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>{name}\'s favorite beverages:</p>',
          -    '<tpl for="drinks">',
          -        '<div> - {.}</div>',
          -    '</tpl>'
          -);
          -tpl.overwrite(panel.body, data);
          - 
          - *

          When processing a sub-template, for example while looping through a child array, - * you can access the parent object's members via the parent object:

          - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Name: {name}</p>',
          -    '<p>Kids: ',
          -    '<tpl for="kids">',
          -        '<tpl if="age &gt; 1">',
          -            '<p>{name}</p>',
          -            '<p>Dad: {parent.name}</p>',
          -        '</tpl>',
          -    '</tpl></p>'
          -);
          -tpl.overwrite(panel.body, data);
          - 
          - *
          - *
        • - * - * - *
        • Conditional processing with basic comparison operators - *
          - *

          The tpl tag and the if operator are used - * to provide conditional checks for deciding whether or not to render specific - * parts of the template. Notes:

            - *
          • Double quotes must be encoded if used within the conditional
          • - *
          • There is no else operator — if needed, two opposite - * if statements should be used.
          • - *
          - *
          
          -<tpl if="age > 1 && age < 10">Child</tpl>
          -<tpl if="age >= 10 && age < 18">Teenager</tpl>
          -<tpl if="this.isGirl(name)">...</tpl>
          -<tpl if="id==\'download\'">...</tpl>
          -<tpl if="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
          -// no good:
          -<tpl if="name == "Tommy"">Hello</tpl>
          -// encode " if it is part of the condition, e.g.
          -<tpl if="name == &quot;Tommy&quot;">Hello</tpl>
          - * 
          - * Using the sample data above: - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Name: {name}</p>',
          -    '<p>Kids: ',
          -    '<tpl for="kids">',
          -        '<tpl if="age &gt; 1">',
          -            '<p>{name}</p>',
          -        '</tpl>',
          -    '</tpl></p>'
          -);
          -tpl.overwrite(panel.body, data);
          - 
          - *
          - *
        • - * - * - *
        • Basic math support - *
          - *

          The following basic math operators may be applied directly on numeric - * data values:

          - * + - * /
          - * 
          - * For example: - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Name: {name}</p>',
          -    '<p>Kids: ',
          -    '<tpl for="kids">',
          -        '<tpl if="age &gt; 1">',  // <-- Note that the > is encoded
          -            '<p>{#}: {name}</p>',  // <-- Auto-number each item
          -            '<p>In 5 Years: {age+5}</p>',  // <-- Basic math
          -            '<p>Dad: {parent.name}</p>',
          -        '</tpl>',
          -    '</tpl></p>'
          -);
          -tpl.overwrite(panel.body, data);
          - 
          - *
          - *
        • - * - * - *
        • Execute arbitrary inline code with special built-in template variables - *
          - *

          Anything between {[ ... ]} is considered code to be executed - * in the scope of the template. There are some special variables available in that code: - *

            - *
          • values: The values in the current scope. If you are using - * scope changing sub-templates, you can change what values is.
          • - *
          • parent: The scope (values) of the ancestor template.
          • - *
          • xindex: If you are in a looping template, the index of the - * loop you are in (1-based).
          • - *
          • xcount: If you are in a looping template, the total length - * of the array you are looping.
          • - *
          - * This example demonstrates basic row striping using an inline code block and the - * xindex variable:

          - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Name: {name}</p>',
          -    '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
          -    '<p>Kids: ',
          -    '<tpl for="kids">',
          -        '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
          -        '{name}',
          -        '</div>',
          -    '</tpl></p>'
          - );
          -tpl.overwrite(panel.body, data);
          - 
          - *
          - *
        • - * - *
        • Template member functions - *
          - *

          One or more member functions can be specified in a configuration - * object passed into the XTemplate constructor for more complex processing:

          - *
          
          -var tpl = new Ext.XTemplate(
          -    '<p>Name: {name}</p>',
          -    '<p>Kids: ',
          -    '<tpl for="kids">',
          -        '<tpl if="this.isGirl(name)">',
          -            '<p>Girl: {name} - {age}</p>',
          -        '</tpl>',
          -         // use opposite if statement to simulate 'else' processing:
          -        '<tpl if="this.isGirl(name) == false">',
          -            '<p>Boy: {name} - {age}</p>',
          -        '</tpl>',
          -        '<tpl if="this.isBaby(age)">',
          -            '<p>{name} is a baby!</p>',
          -        '</tpl>',
          -    '</tpl></p>',
          -    {
          -        // XTemplate configuration:
          -        compiled: true,
          -        // member functions:
          -        isGirl: function(name){
          -           return name == 'Sara Grace';
          -        },
          -        isBaby: function(age){
          -           return age < 1;
          +        if (!Ext.isArray(cls)) {
          +            cls = [cls];
                   }
          -    }
          -);
          -tpl.overwrite(panel.body, data);
          - 
          - *
          - *
        • - * - *
        - * - * @param {Mixed} config - */ - -Ext.define('Ext.XTemplate', { - - /* Begin Definitions */ - extend: 'Ext.Template', + for (i = 0; i < cls.length; i++) { + if (cls[i] && !me.hasUICls(cls[i])) { + me.uiCls = Ext.Array.clone(me.uiCls); + me.uiCls.push(cls[i]); - statics: { - /** - * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. - * @param {String/HTMLElement} el A DOM element or its id - * @return {Ext.Template} The created template - * @static - */ - from: function(el, config) { - el = Ext.getDom(el); - return new this(el.value || el.innerHTML, config || {}); + classes = classes.concat(me.addUIClsToElement(cls[i])); + } } - }, - /* End Definitions */ + if (skip !== true) { + me.addCls(classes); + } - argsRe: /]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/, - nameRe: /^]*?for="(.*?)"/, - ifRe: /^]*?if="(.*?)"/, - execRe: /^]*?exec="(.*?)"/, - constructor: function() { - this.callParent(arguments); + return classes; + }, + /** + * Removes a cls to the uiCls array, which will also call {@link #removeUIClsFromElement} and removes it from all + * elements of this component. + * @param {String/String[]} cls A string or an array of strings to remove to the uiCls + */ + removeClsWithUI: function(cls, skip) { var me = this, - html = me.html, - argsRe = me.argsRe, - nameRe = me.nameRe, - ifRe = me.ifRe, - execRe = me.execRe, - id = 0, - tpls = [], - VALUES = 'values', - PARENT = 'parent', - XINDEX = 'xindex', - XCOUNT = 'xcount', - RETURN = 'return ', - WITHVALUES = 'with(values){ ', - m, matchName, matchIf, matchExec, exp, fn, exec, name, i; - - html = ['', html, ''].join(''); - - while ((m = html.match(argsRe))) { - exp = null; - fn = null; - exec = null; - matchName = m[0].match(nameRe); - matchIf = m[0].match(ifRe); - matchExec = m[0].match(execRe); + classes = [], + i; - exp = matchIf ? matchIf[1] : null; - if (exp) { - fn = Ext.functionFactory(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + 'try{' + RETURN + Ext.String.htmlDecode(exp) + ';}catch(e){return;}}'); - } + if (!Ext.isArray(cls)) { + cls = [cls]; + } - exp = matchExec ? matchExec[1] : null; - if (exp) { - exec = Ext.functionFactory(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + Ext.String.htmlDecode(exp) + ';}'); - } + for (i = 0; i < cls.length; i++) { + if (cls[i] && me.hasUICls(cls[i])) { + me.uiCls = Ext.Array.remove(me.uiCls, cls[i]); - name = matchName ? matchName[1] : null; - if (name) { - if (name === '.') { - name = VALUES; - } else if (name === '..') { - name = PARENT; - } - name = Ext.functionFactory(VALUES, PARENT, 'try{' + WITHVALUES + RETURN + name + ';}}catch(e){return;}'); + classes = classes.concat(me.removeUIClsFromElement(cls[i])); } - - tpls.push({ - id: id, - target: name, - exec: exec, - test: fn, - body: m[1] || '' - }); - - html = html.replace(m[0], '{xtpl' + id + '}'); - id = id + 1; } - for (i = tpls.length - 1; i >= 0; --i) { - me.compileTpl(tpls[i]); + if (skip !== true) { + me.removeCls(classes); } - me.master = tpls[tpls.length - 1]; - me.tpls = tpls; - }, - // @private - applySubTemplate: function(id, values, parent, xindex, xcount) { - var me = this, t = me.tpls[id]; - return t.compiled.call(me, values, parent, xindex, xcount); + return classes; }, + /** - * @cfg {RegExp} codeRe The regular expression used to match code variables (default: matches {[expression]}). + * Checks if there is currently a specified uiCls + * @param {String} cls The cls to check */ - codeRe: /\{\[((?:\\\]|.|\n)*?)\]\}/g, + hasUICls: function(cls) { + var me = this, + uiCls = me.uiCls || []; - re: /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?\}/g, + return Ext.Array.contains(uiCls, cls); + }, - // @private - compileTpl: function(tpl) { - var fm = Ext.util.Format, - me = this, - useFormat = me.disableFormats !== true, - body, bodyReturn, evaluatedFn; + /** + * Method which adds a specified UI + uiCls to the components element. Can be overridden to remove the UI from more + * than just the components element. + * @param {String} ui The UI to remove from the element + */ + addUIClsToElement: function(cls, force) { + var me = this, + result = [], + frameElementCls = me.frameElementCls; - function fn(m, name, format, args, math) { - var v; - // name is what is inside the {} - // Name begins with xtpl, use a Sub Template - if (name.substr(0, 4) == 'xtpl') { - return "',this.applySubTemplate(" + name.substr(4) + ", values, parent, xindex, xcount),'"; - } - // name = "." - Just use the values object. - if (name == '.') { - // filter to not include arrays/objects/nulls - v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""'; - } + result.push(Ext.baseCSSPrefix + cls); + result.push(me.baseCls + '-' + cls); + result.push(me.baseCls + '-' + me.ui + '-' + cls); - // name = "#" - Use the xindex - else if (name == '#') { - v = 'xindex'; - } - else if (name.substr(0, 7) == "parent.") { - v = name; - } - // name has a . in it - Use object literal notation, starting from values - else if (name.indexOf('.') != -1) { - v = "values." + name; - } + if (!force && me.frame && !Ext.supports.CSS3BorderRadius) { + // define each element of the frame + var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'], + classes, i, j, el; - // name is a property of values - else { - v = "values['" + name + "']"; - } - if (math) { - v = '(' + v + math + ')'; - } - if (format && useFormat) { - args = args ? ',' + args : ""; - if (format.substr(0, 5) != "this.") { - format = "fm." + format + '('; - } - else { - format = 'this.' + format.substr(5) + '('; + // loop through each of them, and if they are defined add the ui + for (i = 0; i < els.length; i++) { + el = me['frame' + els[i].toUpperCase()]; + classes = [me.baseCls + '-' + me.ui + '-' + els[i], me.baseCls + '-' + me.ui + '-' + cls + '-' + els[i]]; + if (el && el.dom) { + el.addCls(classes); + } else { + for (j = 0; j < classes.length; j++) { + if (Ext.Array.indexOf(frameElementCls[els[i]], classes[j]) == -1) { + frameElementCls[els[i]].push(classes[j]); + } + } } } - else { - args = ''; - format = "(" + v + " === undefined ? '' : "; - } - return "'," + format + v + args + "),'"; - } - - function codeFn(m, code) { - // Single quotes get escaped when the template is compiled, however we want to undo this when running code. - return "',(" + code.replace(me.compileARe, "'") + "),'"; } - bodyReturn = tpl.body.replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn).replace(me.codeRe, codeFn); - body = "evaluatedFn = function(values, parent, xindex, xcount){return ['" + bodyReturn + "'].join('');};"; - eval(body); + me.frameElementCls = frameElementCls; - tpl.compiled = function(values, parent, xindex, xcount) { - var vs, - length, - buffer, - i; + return result; + }, - if (tpl.test && !tpl.test.call(me, values, parent, xindex, xcount)) { - return ''; - } + /** + * Method which removes a specified UI + uiCls from the components element. The cls which is added to the element + * will be: `this.baseCls + '-' + ui` + * @param {String} ui The UI to add to the element + */ + removeUIClsFromElement: function(cls, force) { + var me = this, + result = [], + frameElementCls = me.frameElementCls; - vs = tpl.target ? tpl.target.call(me, values, parent) : values; - if (!vs) { - return ''; - } + result.push(Ext.baseCSSPrefix + cls); + result.push(me.baseCls + '-' + cls); + result.push(me.baseCls + '-' + me.ui + '-' + cls); - parent = tpl.target ? values : parent; - if (tpl.target && Ext.isArray(vs)) { - buffer = []; - length = vs.length; - if (tpl.exec) { - for (i = 0; i < length; i++) { - buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length); - tpl.exec.call(me, vs[i], parent, i + 1, length); - } + if (!force && me.frame && !Ext.supports.CSS3BorderRadius) { + // define each element of the frame + var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'], + i, el; + cls = me.baseCls + '-' + me.ui + '-' + cls + '-' + els[i]; + // loop through each of them, and if they are defined add the ui + for (i = 0; i < els.length; i++) { + el = me['frame' + els[i].toUpperCase()]; + if (el && el.dom) { + el.removeCls(cls); } else { - for (i = 0; i < length; i++) { - buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length); - } + Ext.Array.remove(frameElementCls[els[i]], cls); } - return buffer.join(''); } + } - if (tpl.exec) { - tpl.exec.call(me, vs, parent, xindex, xcount); - } - return evaluatedFn.call(me, vs, parent, xindex, xcount); - }; + me.frameElementCls = frameElementCls; - return this; + return result; }, /** - * Returns an HTML fragment of this template with the specified values applied. - * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @return {String} The HTML fragment + * Method which adds a specified UI to the components element. + * @private */ - applyTemplate: function(values) { - return this.master.compiled.call(this, values, {}, 1, 1); - }, + addUIToElement: function(force) { + var me = this, + frameElementCls = me.frameElementCls; - /** - * Compile the template to a function for optimized performance. Recommended if the template will be used frequently. - * @return {Function} The compiled function - */ - compile: function() { - return this; - } -}, function() { - /** - * Alias for {@link #applyTemplate} - * Returns an HTML fragment of this template with the specified values applied. - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @return {String} The HTML fragment - * @member Ext.XTemplate - * @method apply - */ - this.createAlias('apply', 'applyTemplate'); -}); - -/** - * @class Ext.util.AbstractMixedCollection - */ -Ext.define('Ext.util.AbstractMixedCollection', { - requires: ['Ext.util.Filter'], - - mixins: { - observable: 'Ext.util.Observable' - }, + me.addCls(me.baseCls + '-' + me.ui); - constructor: function(allowFunctions, keyFn) { - var me = this; + if (me.frame && !Ext.supports.CSS3BorderRadius) { + // define each element of the frame + var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'], + i, el, cls; - me.items = []; - me.map = {}; - me.keys = []; - me.length = 0; + // loop through each of them, and if they are defined add the ui + for (i = 0; i < els.length; i++) { + el = me['frame' + els[i].toUpperCase()]; + cls = me.baseCls + '-' + me.ui + '-' + els[i]; + if (el) { + el.addCls(cls); + } else { + if (!Ext.Array.contains(frameElementCls[els[i]], cls)) { + frameElementCls[els[i]].push(cls); + } + } + } + } + }, - me.addEvents( - /** - * @event clear - * Fires when the collection is cleared. - */ - 'clear', + /** + * Method which removes a specified UI from the components element. + * @private + */ + removeUIFromElement: function() { + var me = this, + frameElementCls = me.frameElementCls; - /** - * @event add - * Fires when an item is added to the collection. - * @param {Number} index The index at which the item was added. - * @param {Object} o The item added. - * @param {String} key The key associated with the added item. - */ - 'add', + me.removeCls(me.baseCls + '-' + me.ui); - /** - * @event replace - * Fires when an item is replaced in the collection. - * @param {String} key he key associated with the new added. - * @param {Object} old The item being replaced. - * @param {Object} new The new item. - */ - 'replace', + if (me.frame && !Ext.supports.CSS3BorderRadius) { + // define each element of the frame + var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'], + i, j, el, cls; - /** - * @event remove - * Fires when an item is removed from the collection. - * @param {Object} o The item being removed. - * @param {String} key (optional) The key associated with the removed item. - */ - 'remove' - ); + // loop through each of them, and if they are defined add the ui + for (i = 0; i < els.length; i++) { + el = me['frame' + els[i].toUpperCase()]; + cls = me.baseCls + '-' + me.ui + '-' + els[i]; - me.allowFunctions = allowFunctions === true; + if (el) { + el.removeCls(cls); + } else { + Ext.Array.remove(frameElementCls[els[i]], cls); + } + } + } + }, - if (keyFn) { - me.getKey = keyFn; + getElConfig : function() { + if (Ext.isString(this.autoEl)) { + this.autoEl = { + tag: this.autoEl + }; } - me.mixins.observable.constructor.call(me); + var result = this.autoEl || {tag: 'div'}; + result.id = this.id; + return result; }, - - /** - * @cfg {Boolean} allowFunctions Specify true if the {@link #addAll} - * function should add function references to the collection. Defaults to - * false. - */ - allowFunctions : false, /** - * Adds an item to the collection. Fires the {@link #add} event when complete. - * @param {String} key

        The key to associate with the item, or the new item.

        - *

        If a {@link #getKey} implementation was specified for this MixedCollection, - * or if the key of the stored items is in a property called id, - * the MixedCollection will be able to derive the key for the new item. - * In this case just pass the new item in this parameter.

        - * @param {Object} o The item to add. - * @return {Object} The item added. + * This function takes the position argument passed to onRender and returns a DOM element that you can use in the + * insertBefore. + * @param {String/Number/Ext.Element/HTMLElement} position Index, element id or element you want to put this + * component before. + * @return {HTMLElement} DOM element that you can use in the insertBefore */ - add : function(key, obj){ - var me = this, - myObj = obj, - myKey = key, - old; - - if (arguments.length == 1) { - myObj = myKey; - myKey = me.getKey(myObj); - } - if (typeof myKey != 'undefined' && myKey !== null) { - old = me.map[myKey]; - if (typeof old != 'undefined') { - return me.replace(myKey, myObj); + getInsertPosition: function(position) { + // Convert the position to an element to insert before + if (position !== undefined) { + if (Ext.isNumber(position)) { + position = this.container.dom.childNodes[position]; + } + else { + position = Ext.getDom(position); } - me.map[myKey] = myObj; } - me.length++; - me.items.push(myObj); - me.keys.push(myKey); - me.fireEvent('add', me.length - 1, myObj, myKey); - return myObj; + + return position; }, /** - * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation - * simply returns item.id but you can provide your own implementation - * to return a different value as in the following examples:
        
        -// normal way
        -var mc = new Ext.util.MixedCollection();
        -mc.add(someEl.dom.id, someEl);
        -mc.add(otherEl.dom.id, otherEl);
        -//and so on
        +     * Adds ctCls to container.
        +     * @return {Ext.Element} The initialized container
        +     * @private
        +     */
        +    initContainer: function(container) {
        +        var me = this;
         
        -// using getKey
        -var mc = new Ext.util.MixedCollection();
        -mc.getKey = function(el){
        -   return el.dom.id;
        -};
        -mc.add(someEl);
        -mc.add(otherEl);
        +        // If you render a component specifying the el, we get the container
        +        // of the el, and make sure we dont move the el around in the dom
        +        // during the render
        +        if (!container && me.el) {
        +            container = me.el.dom.parentNode;
        +            me.allowDomMove = false;
        +        }
         
        -// or via the constructor
        -var mc = new Ext.util.MixedCollection(false, function(el){
        -   return el.dom.id;
        -});
        -mc.add(someEl);
        -mc.add(otherEl);
        -     * 
        - * @param {Object} item The item for which to find the key. - * @return {Object} The key for the passed item. - */ - getKey : function(o){ - return o.id; + me.container = Ext.get(container); + + if (me.ctCls) { + me.container.addCls(me.ctCls); + } + + return me.container; }, /** - * Replaces an item in the collection. Fires the {@link #replace} event when complete. - * @param {String} key

        The key associated with the item to replace, or the replacement item.

        - *

        If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key - * of your stored items is in a property called id, then the MixedCollection - * will be able to derive the key of the replacement item. If you want to replace an item - * with one having the same key value, then just pass the replacement item in this parameter.

        - * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate - * with that key. - * @return {Object} The new item. + * Initialized the renderData to be used when rendering the renderTpl. + * @return {Object} Object with keys and values that are going to be applied to the renderTpl + * @private */ - replace : function(key, o){ - var me = this, - old, - index; + initRenderData: function() { + var me = this; - if (arguments.length == 1) { - o = arguments[0]; - key = me.getKey(o); - } - old = me.map[key]; - if (typeof key == 'undefined' || key === null || typeof old == 'undefined') { - return me.add(key, o); - } - index = me.indexOfKey(key); - me.items[index] = o; - me.map[key] = o; - me.fireEvent('replace', key, old, o); - return o; + return Ext.applyIf(me.renderData, { + id: me.id, + ui: me.ui, + uiCls: me.uiCls, + baseCls: me.baseCls, + componentCls: me.componentCls, + frame: me.frame + }); }, /** - * Adds all elements of an Array or an Object to the collection. - * @param {Object/Array} objs An Object containing properties which will be added - * to the collection, or an Array of values, each of which are added to the collection. - * Functions references will be added to the collection if {@link #allowFunctions} - * has been set to true. + * @private */ - addAll : function(objs){ + getTpl: function(name) { var me = this, - i = 0, - args, - len, - key; + prototype = me.self.prototype, + ownerPrototype, + tpl; - if (arguments.length > 1 || Ext.isArray(objs)) { - args = arguments.length > 1 ? arguments : objs; - for (len = args.length; i < len; i++) { - me.add(args[i]); + if (me.hasOwnProperty(name)) { + tpl = me[name]; + if (tpl && !(tpl instanceof Ext.XTemplate)) { + me[name] = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl); } - } else { - for (key in objs) { - if (objs.hasOwnProperty(key)) { - if (me.allowFunctions || typeof objs[key] != 'function') { - me.add(key, objs[key]); + + return me[name]; + } + + if (!(prototype[name] instanceof Ext.XTemplate)) { + ownerPrototype = prototype; + + do { + if (ownerPrototype.hasOwnProperty(name)) { + tpl = ownerPrototype[name]; + if (tpl && !(tpl instanceof Ext.XTemplate)) { + ownerPrototype[name] = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl); + break; } } - } + + ownerPrototype = ownerPrototype.superclass; + } while (ownerPrototype); } + + return prototype[name]; }, /** - * Executes the specified function once for every item in the collection, passing the following arguments: - *
          - *
        • item : Mixed

          The collection item

        • - *
        • index : Number

          The item's index

        • - *
        • length : Number

          The total number of items in the collection

        • - *
        - * The function should return a boolean value. Returning false from the function will stop the iteration. - * @param {Function} fn The function to execute for each item. - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the current item in the iteration. + * Initializes the renderTpl. + * @return {Ext.XTemplate} The renderTpl XTemplate instance. + * @private */ - each : function(fn, scope){ - var items = [].concat(this.items), // each safe for removal - i = 0, - len = items.length, - item; - - for (; i < len; i++) { - item = items[i]; - if (fn.call(scope || item, item, i, len) === false) { - break; - } - } + initRenderTpl: function() { + return this.getTpl('renderTpl'); }, /** - * Executes the specified function once for every key in the collection, passing each - * key, and its associated item as the first two parameters. - * @param {Function} fn The function to execute for each item. - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the browser window. + * Converts style definitions to String. + * @return {String} A CSS style string with style, padding, margin and border. + * @private */ - eachKey : function(fn, scope){ - var keys = this.keys, - items = this.items, - i = 0, - len = keys.length; + initStyles: function() { + var style = {}, + me = this, + Element = Ext.Element; - for (; i < len; i++) { - fn.call(scope || window, keys[i], items[i], i, len); + if (Ext.isString(me.style)) { + style = Element.parseStyles(me.style); + } else { + style = Ext.apply({}, me.style); } - }, - - /** - * Returns the first item in the collection which elicits a true return value from the - * passed selection function. - * @param {Function} fn The selection function to execute for each item. - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the browser window. - * @return {Object} The first item in the collection which returned true from the selection function. - */ - findBy : function(fn, scope) { - var keys = this.keys, - items = this.items, - i = 0, - len = items.length; - for (; i < len; i++) { - if (fn.call(scope || window, items[i], keys[i])) { - return items[i]; - } + // Convert the padding, margin and border properties from a space seperated string + // into a proper style string + if (me.padding !== undefined) { + style.padding = Element.unitizeBox((me.padding === true) ? 5 : me.padding); } - return null; - }, - find : function() { - if (Ext.isDefined(Ext.global.console)) { - Ext.global.console.warn('Ext.util.MixedCollection: find has been deprecated. Use findBy instead.'); + if (me.margin !== undefined) { + style.margin = Element.unitizeBox((me.margin === true) ? 5 : me.margin); } - return this.findBy.apply(this, arguments); + + delete me.style; + return style; }, /** - * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete. - * @param {Number} index The index to insert the item at. - * @param {String} key The key to associate with the new item, or the item itself. - * @param {Object} o (optional) If the second parameter was a key, the new item. - * @return {Object} The item inserted. + * Initializes this components contents. It checks for the properties html, contentEl and tpl/data. + * @private */ - insert : function(index, key, obj){ + initContent: function() { var me = this, - myKey = key, - myObj = obj; + target = me.getTargetEl(), + contentEl, + pre; - if (arguments.length == 2) { - myObj = myKey; - myKey = me.getKey(myObj); - } - if (me.containsKey(myKey)) { - me.suspendEvents(); - me.removeAtKey(myKey); - me.resumeEvents(); + if (me.html) { + target.update(Ext.DomHelper.markup(me.html)); + delete me.html; } - if (index >= me.length) { - return me.add(myKey, myObj); + + if (me.contentEl) { + contentEl = Ext.get(me.contentEl); + pre = Ext.baseCSSPrefix; + contentEl.removeCls([pre + 'hidden', pre + 'hide-display', pre + 'hide-offsets', pre + 'hide-nosize']); + target.appendChild(contentEl.dom); } - me.length++; - Ext.Array.splice(me.items, index, 0, myObj); - if (typeof myKey != 'undefined' && myKey !== null) { - me.map[myKey] = myObj; + + if (me.tpl) { + // Make sure this.tpl is an instantiated XTemplate + if (!me.tpl.isTemplate) { + me.tpl = Ext.create('Ext.XTemplate', me.tpl); + } + + if (me.data) { + me.tpl[me.tplWriteMode](target, me.data); + delete me.data; + } } - Ext.Array.splice(me.keys, index, 0, myKey); - me.fireEvent('add', index, myObj, myKey); - return myObj; }, - /** - * Remove an item from the collection. - * @param {Object} o The item to remove. - * @return {Object} The item removed or false if no item was removed. - */ - remove : function(o){ - return this.removeAt(this.indexOf(o)); + // @private + initEvents : function() { + var me = this, + afterRenderEvents = me.afterRenderEvents, + el, + property, + fn = function(listeners){ + me.mon(el, listeners); + }; + if (afterRenderEvents) { + for (property in afterRenderEvents) { + if (afterRenderEvents.hasOwnProperty(property)) { + el = me[property]; + if (el && el.on) { + Ext.each(afterRenderEvents[property], fn); + } + } + } + } }, /** - * Remove all items in the passed array from the collection. - * @param {Array} items An array of items to be removed. - * @return {Ext.util.MixedCollection} this object + * Adds each argument passed to this method to the {@link #childEls} array. */ - removeAll : function(items){ - Ext.each(items || [], function(item) { - this.remove(item); - }, this); + addChildEls: function () { + var me = this, + childEls = me.childEls || (me.childEls = []); - return this; + childEls.push.apply(childEls, arguments); }, /** - * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete. - * @param {Number} index The index within the collection of the item to remove. - * @return {Object} The item removed or false if no item was removed. + * Removes items in the childEls array based on the return value of a supplied test function. The function is called + * with a entry in childEls and if the test function return true, that entry is removed. If false, that entry is + * kept. + * @param {Function} testFn The test function. */ - removeAt : function(index){ + removeChildEls: function (testFn) { var me = this, - o, - key; + old = me.childEls, + keepers = (me.childEls = []), + n, i, cel; - if (index < me.length && index >= 0) { - me.length--; - o = me.items[index]; - Ext.Array.erase(me.items, index, 1); - key = me.keys[index]; - if (typeof key != 'undefined') { - delete me.map[key]; + for (i = 0, n = old.length; i < n; ++i) { + cel = old[i]; + if (!testFn(cel)) { + keepers.push(cel); } - Ext.Array.erase(me.keys, index, 1); - me.fireEvent('remove', o, key); - return o; } - return false; }, /** - * Removed an item associated with the passed key fom the collection. - * @param {String} key The key of the item to remove. - * @return {Object} The item removed or false if no item was removed. + * Sets references to elements inside the component. This applies {@link #renderSelectors} + * as well as {@link #childEls}. + * @private */ - removeAtKey : function(key){ - return this.removeAt(this.indexOfKey(key)); + applyRenderSelectors: function() { + var me = this, + childEls = me.childEls, + selectors = me.renderSelectors, + el = me.el, + dom = el.dom, + baseId, childName, childId, i, selector; + + if (childEls) { + baseId = me.id + '-'; + for (i = childEls.length; i--; ) { + childName = childId = childEls[i]; + if (typeof(childName) != 'string') { + childId = childName.id || (baseId + childName.itemId); + childName = childName.name; + } else { + childId = baseId + childId; + } + + // We don't use Ext.get because that is 3x (or more) slower on IE6-8. Since + // we know the el's are children of our el we use getById instead: + me[childName] = el.getById(childId); + } + } + + // We still support renderSelectors. There are a few places in the framework that + // need them and they are a documented part of the API. In fact, we support mixing + // childEls and renderSelectors (no reason not to). + if (selectors) { + for (selector in selectors) { + if (selectors.hasOwnProperty(selector) && selectors[selector]) { + me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom)); + } + } + } }, /** - * Returns the number of items in the collection. - * @return {Number} the number of items in the collection. + * Tests whether this Component matches the selector string. + * @param {String} selector The selector string to test against. + * @return {Boolean} True if this Component matches the selector. */ - getCount : function(){ - return this.length; + is: function(selector) { + return Ext.ComponentQuery.is(this, selector); }, /** - * Returns index within the collection of the passed Object. - * @param {Object} o The item to find the index of. - * @return {Number} index of the item. Returns -1 if not found. + * Walks up the `ownerCt` axis looking for an ancestor Container which matches the passed simple selector. + * + * Example: + * + * var owningTabPanel = grid.up('tabpanel'); + * + * @param {String} [selector] The simple selector to test. + * @return {Ext.container.Container} The matching ancestor Container (or `undefined` if no match was found). */ - indexOf : function(o){ - return Ext.Array.indexOf(this.items, o); + up: function(selector) { + var result = this.ownerCt; + if (selector) { + for (; result; result = result.ownerCt) { + if (Ext.ComponentQuery.is(result, selector)) { + return result; + } + } + } + return result; }, /** - * Returns index within the collection of the passed key. - * @param {String} key The key to find the index of. - * @return {Number} index of the key. + * Returns the next sibling of this Component. + * + * Optionally selects the next sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector. + * + * May also be refered to as **`next()`** + * + * Note that this is limited to siblings, and if no siblings of the item match, `null` is returned. Contrast with + * {@link #nextNode} + * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following items. + * @return {Ext.Component} The next sibling (or the next sibling which matches the selector). + * Returns null if there is no matching sibling. */ - indexOfKey : function(key){ - return Ext.Array.indexOf(this.keys, key); + nextSibling: function(selector) { + var o = this.ownerCt, it, last, idx, c; + if (o) { + it = o.items; + idx = it.indexOf(this) + 1; + if (idx) { + if (selector) { + for (last = it.getCount(); idx < last; idx++) { + if ((c = it.getAt(idx)).is(selector)) { + return c; + } + } + } else { + if (idx < it.getCount()) { + return it.getAt(idx); + } + } + } + } + return null; }, /** - * Returns the item associated with the passed key OR index. - * Key has priority over index. This is the equivalent - * of calling {@link #key} first, then if nothing matched calling {@link #getAt}. - * @param {String/Number} key The key or index of the item. - * @return {Object} If the item is found, returns the item. If the item was not found, returns undefined. - * If an item was found, but is a Class, returns null. + * Returns the previous sibling of this Component. + * + * Optionally selects the previous sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} + * selector. + * + * May also be refered to as **`prev()`** + * + * Note that this is limited to siblings, and if no siblings of the item match, `null` is returned. Contrast with + * {@link #previousNode} + * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding items. + * @return {Ext.Component} The previous sibling (or the previous sibling which matches the selector). + * Returns null if there is no matching sibling. */ - get : function(key) { - var me = this, - mk = me.map[key], - item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined; - return typeof item != 'function' || me.allowFunctions ? item : null; // for prototype! + previousSibling: function(selector) { + var o = this.ownerCt, it, idx, c; + if (o) { + it = o.items; + idx = it.indexOf(this); + if (idx != -1) { + if (selector) { + for (--idx; idx >= 0; idx--) { + if ((c = it.getAt(idx)).is(selector)) { + return c; + } + } + } else { + if (idx) { + return it.getAt(--idx); + } + } + } + } + return null; }, /** - * Returns the item at the specified index. - * @param {Number} index The index of the item. - * @return {Object} The item at the specified index. + * Returns the previous node in the Component tree in tree traversal order. + * + * Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will walk the + * tree in reverse order to attempt to find a match. Contrast with {@link #previousSibling}. + * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding nodes. + * @return {Ext.Component} The previous node (or the previous node which matches the selector). + * Returns null if there is no matching node. */ - getAt : function(index) { - return this.items[index]; + previousNode: function(selector, includeSelf) { + var node = this, + result, + it, len, i; + + // If asked to include self, test me + if (includeSelf && node.is(selector)) { + return node; + } + + result = this.prev(selector); + if (result) { + return result; + } + + if (node.ownerCt) { + for (it = node.ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) { + if (it[i].query) { + result = it[i].query(selector); + result = result[result.length - 1]; + if (result) { + return result; + } + } + } + return node.ownerCt.previousNode(selector, true); + } }, /** - * Returns the item associated with the passed key. - * @param {String/Number} key The key of the item. - * @return {Object} The item associated with the passed key. + * Returns the next node in the Component tree in tree traversal order. + * + * Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will walk the + * tree to attempt to find a match. Contrast with {@link #nextSibling}. + * @param {String} [selector] A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following nodes. + * @return {Ext.Component} The next node (or the next node which matches the selector). + * Returns null if there is no matching node. */ - getByKey : function(key) { - return this.map[key]; + nextNode: function(selector, includeSelf) { + var node = this, + result, + it, len, i; + + // If asked to include self, test me + if (includeSelf && node.is(selector)) { + return node; + } + + result = this.next(selector); + if (result) { + return result; + } + + if (node.ownerCt) { + for (it = node.ownerCt.items, i = it.indexOf(node) + 1, it = it.items, len = it.length; i < len; i++) { + if (it[i].down) { + result = it[i].down(selector); + if (result) { + return result; + } + } + } + return node.ownerCt.nextNode(selector); + } }, /** - * Returns true if the collection contains the passed Object as an item. - * @param {Object} o The Object to look for in the collection. - * @return {Boolean} True if the collection contains the Object as an item. + * Retrieves the id of this component. Will autogenerate an id if one has not already been set. + * @return {String} */ - contains : function(o){ - return Ext.Array.contains(this.items, o); + getId : function() { + return this.id || (this.id = 'ext-comp-' + (this.getAutoId())); }, - /** - * Returns true if the collection contains the passed Object as a key. - * @param {String} key The key to look for in the collection. - * @return {Boolean} True if the collection contains the Object as a key. - */ - containsKey : function(key){ - return typeof this.map[key] != 'undefined'; + getItemId : function() { + return this.itemId || this.id; }, /** - * Removes all items from the collection. Fires the {@link #clear} event when complete. + * Retrieves the top level element representing this component. + * @return {Ext.core.Element} */ - clear : function(){ - var me = this; - - me.length = 0; - me.items = []; - me.keys = []; - me.map = {}; - me.fireEvent('clear'); + getEl : function() { + return this.el; }, /** - * Returns the first item in the collection. - * @return {Object} the first item in the collection.. + * This is used to determine where to insert the 'html', 'contentEl' and 'items' in this component. + * @private */ - first : function() { - return this.items[0]; + getTargetEl: function() { + return this.frameBody || this.el; }, /** - * Returns the last item in the collection. - * @return {Object} the last item in the collection.. + * Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended + * from the xtype (default) or whether it is directly of the xtype specified (shallow = true). + * + * **If using your own subclasses, be aware that a Component must register its own xtype to participate in + * determination of inherited xtypes.** + * + * For a list of all available xtypes, see the {@link Ext.Component} header. + * + * Example usage: + * + * var t = new Ext.form.field.Text(); + * var isText = t.isXType('textfield'); // true + * var isBoxSubclass = t.isXType('field'); // true, descended from Ext.form.field.Base + * var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.form.field.Base instance + * + * @param {String} xtype The xtype to check for this Component + * @param {Boolean} [shallow=false] True to check whether this Component is directly of the specified xtype, false to + * check whether this Component is descended from the xtype. + * @return {Boolean} True if this component descends from the specified xtype, false otherwise. */ - last : function() { - return this.items[this.length - 1]; + isXType: function(xtype, shallow) { + //assume a string by default + if (Ext.isFunction(xtype)) { + xtype = xtype.xtype; + //handle being passed the class, e.g. Ext.Component + } else if (Ext.isObject(xtype)) { + xtype = xtype.statics().xtype; + //handle being passed an instance + } + + return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1: this.self.xtype == xtype; }, /** - * Collects all of the values of the given property and returns their sum - * @param {String} property The property to sum by - * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when - * summing fields in records, where the fields are all stored inside the 'data' object - * @param {Number} start (optional) The record index to start at (defaults to 0) - * @param {Number} end (optional) The record index to end at (defaults to -1) - * @return {Number} The total + * Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all available xtypes, see the + * {@link Ext.Component} header. + * + * **If using your own subclasses, be aware that a Component must register its own xtype to participate in + * determination of inherited xtypes.** + * + * Example usage: + * + * var t = new Ext.form.field.Text(); + * alert(t.getXTypes()); // alerts 'component/field/textfield' + * + * @return {String} The xtype hierarchy string */ - sum: function(property, root, start, end) { - var values = this.extractValues(property, root), - length = values.length, - sum = 0, - i; + getXTypes: function() { + var self = this.self, + xtypes, parentPrototype, parentXtypes; - start = start || 0; - end = (end || end === 0) ? end : length - 1; + if (!self.xtypes) { + xtypes = []; + parentPrototype = this; - for (i = start; i <= end; i++) { - sum += values[i]; + while (parentPrototype) { + parentXtypes = parentPrototype.xtypes; + + if (parentXtypes !== undefined) { + xtypes.unshift.apply(xtypes, parentXtypes); + } + + parentPrototype = parentPrototype.superclass; + } + + self.xtypeChain = xtypes; + self.xtypes = xtypes.join('/'); } - return sum; + return self.xtypes; }, /** - * Collects unique values of a particular property in this MixedCollection - * @param {String} property The property to collect on - * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when - * summing fields in records, where the fields are all stored inside the 'data' object - * @param {Boolean} allowBlank (optional) Pass true to allow null, undefined or empty string values - * @return {Array} The unique values + * Update the content area of a component. + * @param {String/Object} htmlOrData If this component has been configured with a template via the tpl config then + * it will use this argument as data to populate the template. If this component was not configured with a template, + * the components content area will be updated via Ext.Element update + * @param {Boolean} [loadScripts=false] Only legitimate when using the html configuration. + * @param {Function} [callback] Only legitimate when using the html configuration. Callback to execute when + * scripts have finished loading */ - collect: function(property, root, allowNull) { - var values = this.extractValues(property, root), - length = values.length, - hits = {}, - unique = [], - value, strValue, i; - - for (i = 0; i < length; i++) { - value = values[i]; - strValue = String(value); + update : function(htmlOrData, loadScripts, cb) { + var me = this; - if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) { - hits[strValue] = true; - unique.push(value); + if (me.tpl && !Ext.isString(htmlOrData)) { + me.data = htmlOrData; + if (me.rendered) { + me.tpl[me.tplWriteMode](me.getTargetEl(), htmlOrData || {}); + } + } else { + me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData; + if (me.rendered) { + me.getTargetEl().update(me.html, loadScripts, cb); } } - return unique; + if (me.rendered) { + me.doComponentLayout(); + } }, /** - * @private - * Extracts all of the given property values from the items in the MC. Mainly used as a supporting method for - * functions like sum and collect. - * @param {String} property The property to extract - * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when - * extracting field data from Model instances, where the fields are stored inside the 'data' object - * @return {Array} The extracted values + * Convenience function to hide or show this component by boolean. + * @param {Boolean} visible True to show, false to hide + * @return {Ext.Component} this */ - extractValues: function(property, root) { - var values = this.items; - - if (root) { - values = Ext.Array.pluck(values, root); - } - - return Ext.Array.pluck(values, property); + setVisible : function(visible) { + return this[visible ? 'show': 'hide'](); }, /** - * Returns a range of items in this collection - * @param {Number} startIndex (optional) The starting index. Defaults to 0. - * @param {Number} endIndex (optional) The ending index. Defaults to the last item. - * @return {Array} An array of items + * Returns true if this component is visible. + * + * @param {Boolean} [deep=false] Pass `true` to interrogate the visibility status of all parent Containers to + * determine whether this Component is truly visible to the user. + * + * Generally, to determine whether a Component is hidden, the no argument form is needed. For example when creating + * dynamically laid out UIs in a hidden Container before showing them. + * + * @return {Boolean} True if this component is visible, false otherwise. */ - getRange : function(start, end){ + isVisible: function(deep) { var me = this, - items = me.items, - range = [], - i; + child = me, + visible = !me.hidden, + ancestor = me.ownerCt; - if (items.length < 1) { - return range; + // Clear hiddenOwnerCt property + me.hiddenAncestor = false; + if (me.destroyed) { + return false; } - start = start || 0; - end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1); - if (start <= end) { - for (i = start; i <= end; i++) { - range[range.length] = items[i]; - } - } else { - for (i = start; i >= end; i--) { - range[range.length] = items[i]; + if (deep && visible && me.rendered && ancestor) { + while (ancestor) { + // If any ancestor is hidden, then this is hidden. + // If an ancestor Panel (only Panels have a collapse method) is collapsed, + // then its layoutTarget (body) is hidden, so this is hidden unless its within a + // docked item; they are still visible when collapsed (Unless they themseves are hidden) + if (ancestor.hidden || (ancestor.collapsed && + !(ancestor.getDockedItems && Ext.Array.contains(ancestor.getDockedItems(), child)))) { + // Store hiddenOwnerCt property if needed + me.hiddenAncestor = ancestor; + visible = false; + break; + } + child = ancestor; + ancestor = ancestor.ownerCt; } } - return range; + return visible; }, /** - *

        Filters the objects in this collection by a set of {@link Ext.util.Filter Filter}s, or by a single - * property/value pair with optional parameters for substring matching and case sensitivity. See - * {@link Ext.util.Filter Filter} for an example of using Filter objects (preferred). Alternatively, - * MixedCollection can be easily filtered by property like this:

        -
        
        -//create a simple store with a few people defined
        -var people = new Ext.util.MixedCollection();
        -people.addAll([
        -    {id: 1, age: 25, name: 'Ed'},
        -    {id: 2, age: 24, name: 'Tommy'},
        -    {id: 3, age: 24, name: 'Arne'},
        -    {id: 4, age: 26, name: 'Aaron'}
        -]);
        -
        -//a new MixedCollection containing only the items where age == 24
        -var middleAged = people.filter('age', 24);
        -
        - * - * - * @param {Array/String} property A property on your objects, or an array of {@link Ext.util.Filter Filter} objects - * @param {String/RegExp} value Either string that the property values - * should start with or a RegExp to test against the property - * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning - * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False). - * @return {MixedCollection} The new filtered collection + * Enable the component + * @param {Boolean} [silent=false] Passing true will supress the 'enable' event from being fired. */ - filter : function(property, value, anyMatch, caseSensitive) { - var filters = [], - filterFn; + enable: function(silent) { + var me = this; - //support for the simple case of filtering by property/value - if (Ext.isString(property)) { - filters.push(Ext.create('Ext.util.Filter', { - property : property, - value : value, - anyMatch : anyMatch, - caseSensitive: caseSensitive - })); - } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) { - filters = filters.concat(property); + if (me.rendered) { + me.el.removeCls(me.disabledCls); + me.el.dom.disabled = false; + me.onEnable(); } - //at this point we have an array of zero or more Ext.util.Filter objects to filter with, - //so here we construct a function that combines these filters by ANDing them together - filterFn = function(record) { - var isMatch = true, - length = filters.length, - i; - - for (i = 0; i < length; i++) { - var filter = filters[i], - fn = filter.filterFn, - scope = filter.scope; - - isMatch = isMatch && fn.call(scope, record); - } + me.disabled = false; - return isMatch; - }; + if (silent !== true) { + me.fireEvent('enable', me); + } - return this.filterBy(filterFn); + return me; }, /** - * Filter by a function. Returns a new collection that has been filtered. - * The passed function will be called with each object in the collection. - * If the function returns true, the value is included otherwise it is filtered. - * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key) - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to this MixedCollection. - * @return {MixedCollection} The new filtered collection + * Disable the component. + * @param {Boolean} [silent=false] Passing true will supress the 'disable' event from being fired. */ - filterBy : function(fn, scope) { - var me = this, - newMC = new this.self(), - keys = me.keys, - items = me.items, - length = items.length, - i; + disable: function(silent) { + var me = this; - newMC.getKey = me.getKey; + if (me.rendered) { + me.el.addCls(me.disabledCls); + me.el.dom.disabled = true; + me.onDisable(); + } - for (i = 0; i < length; i++) { - if (fn.call(scope || me, items[i], keys[i])) { - newMC.add(keys[i], items[i]); - } + me.disabled = true; + + if (silent !== true) { + me.fireEvent('disable', me); } - return newMC; + return me; }, - /** - * Finds the index of the first matching object in this collection by a specific property/value. - * @param {String} property The name of a property on your objects. - * @param {String/RegExp} value A string that the property values - * should start with or a RegExp to test against the property. - * @param {Number} start (optional) The index to start searching at (defaults to 0). - * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning. - * @param {Boolean} caseSensitive (optional) True for case sensitive comparison. - * @return {Number} The matched index or -1 - */ - findIndex : function(property, value, start, anyMatch, caseSensitive){ - if(Ext.isEmpty(value, false)){ - return -1; + // @private + onEnable: function() { + if (this.maskOnDisable) { + this.el.unmask(); } - value = this.createValueMatcher(value, anyMatch, caseSensitive); - return this.findIndexBy(function(o){ - return o && value.test(o[property]); - }, null, start); }, - /** - * Find the index of the first matching object in this collection by a function. - * If the function returns true it is considered a match. - * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key). - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to this MixedCollection. - * @param {Number} start (optional) The index to start searching at (defaults to 0). - * @return {Number} The matched index or -1 - */ - findIndexBy : function(fn, scope, start){ - var me = this, - keys = me.keys, - items = me.items, - i = start || 0, - len = items.length; - - for (; i < len; i++) { - if (fn.call(scope || me, items[i], keys[i])) { - return i; - } + // @private + onDisable : function() { + if (this.maskOnDisable) { + this.el.mask(); } - return -1; }, /** - * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering, - * and by Ext.data.Store#filter - * @private - * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe - * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false - * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false. - * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true. + * Method to determine whether this Component is currently disabled. + * @return {Boolean} the disabled state of this Component. */ - createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) { - if (!value.exec) { // not a regex - var er = Ext.String.escapeRegex; - value = String(value); - - if (anyMatch === true) { - value = er(value); - } else { - value = '^' + er(value); - if (exactMatch === true) { - value += '$'; - } - } - value = new RegExp(value, caseSensitive ? '' : 'i'); - } - return value; + isDisabled : function() { + return this.disabled; }, /** - * Creates a shallow copy of this collection - * @return {MixedCollection} + * Enable or disable the component. + * @param {Boolean} disabled True to disable. */ - clone : function() { - var me = this, - copy = new this.self(), - keys = me.keys, - items = me.items, - i = 0, - len = items.length; - - for(; i < len; i++){ - copy.add(keys[i], items[i]); - } - copy.getKey = me.getKey; - return copy; - } -}); - -/** - * @class Ext.util.Sortable - -A mixin which allows a data component to be sorted. This is used by e.g. {@link Ext.data.Store} and {@link Ext.data.TreeStore}. + setDisabled : function(disabled) { + return this[disabled ? 'disable': 'enable'](); + }, -**NOTE**: This mixin is mainly for internal library use and most users should not need to use it directly. It -is more likely you will want to use one of the component classes that import this mixin, such as -{@link Ext.data.Store} or {@link Ext.data.TreeStore}. - * @markdown - * @docauthor Tommy Maintz - */ -Ext.define("Ext.util.Sortable", { /** - * @property isSortable - * @type Boolean - * Flag denoting that this object is sortable. Always true. + * Method to determine whether this Component is currently set to hidden. + * @return {Boolean} the hidden state of this Component. */ - isSortable: true, - - /** - * The default sort direction to use if one is not specified (defaults to "ASC") - * @property defaultSortDirection - * @type String - */ - defaultSortDirection: "ASC", - - requires: [ - 'Ext.util.Sorter' - ], + isHidden : function() { + return this.hidden; + }, /** - * The property in each item that contains the data to sort. - * @type String - */ - - /** - * Performs initialization of this mixin. Component classes using this mixin should call this method - * during their own initialization. + * Adds a CSS class to the top level element representing this component. + * @param {String} cls The CSS class name to add + * @return {Ext.Component} Returns the Component to allow method chaining. */ - initSortable: function() { - var me = this, - sorters = me.sorters; - - /** - * The collection of {@link Ext.util.Sorter Sorters} currently applied to this Store - * @property sorters - * @type Ext.util.MixedCollection - */ - me.sorters = Ext.create('Ext.util.AbstractMixedCollection', false, function(item) { - return item.id || item.property; - }); - - if (sorters) { - me.sorters.addAll(me.decodeSorters(sorters)); + addCls : function(className) { + var me = this; + if (!className) { + return me; + } + if (!Ext.isArray(className)){ + className = className.replace(me.trimRe, '').split(me.spacesRe); + } + if (me.rendered) { + me.el.addCls(className); + } + else { + me.additionalCls = Ext.Array.unique(me.additionalCls.concat(className)); } + return me; }, /** - *

        Sorts the data in the Store by one or more of its properties. Example usage:

        -
        
        -//sort by a single field
        -myStore.sort('myField', 'DESC');
        -
        -//sorting by multiple fields
        -myStore.sort([
        -    {
        -        property : 'age',
        -        direction: 'ASC'
        +     * Adds a CSS class to the top level element representing this component.
        +     * @param {String} cls The CSS class name to add
        +     * @return {Ext.Component} Returns the Component to allow method chaining.
        +     */
        +    addClass : function() {
        +        return this.addCls.apply(this, arguments);
             },
        -    {
        -        property : 'name',
        -        direction: 'DESC'
        -    }
        -]);
        -
        - *

        Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates the actual - * sorting to its internal {@link Ext.util.MixedCollection}.

        - *

        When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code:

        -
        
        -store.sort('myField');
        -store.sort('myField');
        -     
        - *

        Is equivalent to this code, because Store handles the toggling automatically:

        -
        
        -store.sort('myField', 'ASC');
        -store.sort('myField', 'DESC');
        -
        - * @param {String|Array} sorters Either a string name of one of the fields in this Store's configured {@link Ext.data.Model Model}, - * or an Array of sorter configurations. - * @param {String} direction The overall direction to sort the data by. Defaults to "ASC". + + /** + * Removes a CSS class from the top level element representing this component. + * @param {Object} className + * @return {Ext.Component} Returns the Component to allow method chaining. */ - sort: function(sorters, direction, where, doSort) { - var me = this, - sorter, sorterFn, - newSorters; - - if (Ext.isArray(sorters)) { - doSort = where; - where = direction; - newSorters = sorters; + removeCls : function(className) { + var me = this; + + if (!className) { + return me; } - else if (Ext.isObject(sorters)) { - doSort = where; - where = direction; - newSorters = [sorters]; + if (!Ext.isArray(className)){ + className = className.replace(me.trimRe, '').split(me.spacesRe); } - else if (Ext.isString(sorters)) { - sorter = me.sorters.get(sorters); - - if (!sorter) { - sorter = { - property : sorters, - direction: direction - }; - newSorters = [sorter]; - } - else if (direction === undefined) { - sorter.toggle(); - } - else { - sorter.setDirection(direction); - } + if (me.rendered) { + me.el.removeCls(className); } - - if (newSorters && newSorters.length) { - newSorters = me.decodeSorters(newSorters); - if (Ext.isString(where)) { - if (where === 'prepend') { - sorters = me.sorters.clone().items; - - me.sorters.clear(); - me.sorters.addAll(newSorters); - me.sorters.addAll(sorters); - } - else { - me.sorters.addAll(newSorters); - } - } - else { - me.sorters.clear(); - me.sorters.addAll(newSorters); - } - - if (doSort !== false) { - me.onBeforeSort(newSorters); - } + else if (me.additionalCls.length) { + Ext.each(className, function(cls) { + Ext.Array.remove(me.additionalCls, cls); + }); } - - if (doSort !== false) { - sorters = me.sorters.items; - if (sorters.length) { - //construct an amalgamated sorter function which combines all of the Sorters passed - sorterFn = function(r1, r2) { - var result = sorters[0].sort(r1, r2), - length = sorters.length, - i; - - //if we have more than one sorter, OR any additional sorter functions together - for (i = 1; i < length; i++) { - result = result || sorters[i].sort.call(this, r1, r2); - } - - return result; - }; + return me; + }, - me.doSort(sorterFn); - } + removeClass : function() { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.warn('Ext.Component: removeClass has been deprecated. Please use removeCls.'); } - - return sorters; + return this.removeCls.apply(this, arguments); }, - - onBeforeSort: Ext.emptyFn, - - /** - * @private - * Normalizes an array of sorter objects, ensuring that they are all Ext.util.Sorter instances - * @param {Array} sorters The sorters array - * @return {Array} Array of Ext.util.Sorter objects - */ - decodeSorters: function(sorters) { - if (!Ext.isArray(sorters)) { - if (sorters === undefined) { - sorters = []; - } else { - sorters = [sorters]; - } + + addOverCls: function() { + var me = this; + if (!me.disabled) { + me.el.addCls(me.overCls); } + }, - var length = sorters.length, - Sorter = Ext.util.Sorter, - fields = this.model ? this.model.prototype.fields : null, - field, - config, i; + removeOverCls: function() { + this.el.removeCls(this.overCls); + }, - for (i = 0; i < length; i++) { - config = sorters[i]; + addListener : function(element, listeners, scope, options) { + var me = this, + fn, + option; - if (!(config instanceof Sorter)) { - if (Ext.isString(config)) { - config = { - property: config - }; - } - - Ext.applyIf(config, { - root : this.sortRoot, - direction: "ASC" - }); + if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) { + if (options.element) { + fn = listeners; - //support for 3.x style sorters where a function can be defined as 'fn' - if (config.fn) { - config.sorterFn = config.fn; + listeners = {}; + listeners[element] = fn; + element = options.element; + if (scope) { + listeners.scope = scope; } - //support a function to be passed as a sorter definition - if (typeof config == 'function') { - config = { - sorterFn: config - }; + for (option in options) { + if (options.hasOwnProperty(option)) { + if (me.eventOptionsRe.test(option)) { + listeners[option] = options[option]; + } + } } + } - // ensure sortType gets pushed on if necessary - if (fields && !config.transform) { - field = fields.get(config.property); - config.transform = field ? field.sortType : undefined; + // At this point we have a variable called element, + // and a listeners object that can be passed to on + if (me[element] && me[element].on) { + me.mon(me[element], listeners); + } else { + me.afterRenderEvents = me.afterRenderEvents || {}; + if (!me.afterRenderEvents[element]) { + me.afterRenderEvents[element] = []; } - sorters[i] = Ext.create('Ext.util.Sorter', config); + me.afterRenderEvents[element].push(listeners); } } - return sorters; + return me.mixins.observable.addListener.apply(me, arguments); }, - - getSorters: function() { - return this.sorters.items; - } -}); -/** - * @class Ext.util.MixedCollection - *

        - * Represents a collection of a set of key and value pairs. Each key in the MixedCollection - * must be unique, the same key cannot exist twice. This collection is ordered, items in the - * collection can be accessed by index or via the key. Newly added items are added to - * the end of the collection. This class is similar to {@link Ext.util.HashMap} however it - * is heavier and provides more functionality. Sample usage: - *

        
        -var coll = new Ext.util.MixedCollection();
        -coll.add('key1', 'val1');
        -coll.add('key2', 'val2');
        -coll.add('key3', 'val3');
         
        -console.log(coll.get('key1')); // prints 'val1'
        -console.log(coll.indexOfKey('key3')); // prints 2
        - * 
        - * - *

        - * The MixedCollection also has support for sorting and filtering of the values in the collection. - *

        
        -var coll = new Ext.util.MixedCollection();
        -coll.add('key1', 100);
        -coll.add('key2', -100);
        -coll.add('key3', 17);
        -coll.add('key4', 0);
        -var biggerThanZero = coll.filterBy(function(value){
        -    return value > 0;
        -});
        -console.log(biggerThanZero.getCount()); // prints 2
        - * 
        - *

        - */ -Ext.define('Ext.util.MixedCollection', { - extend: 'Ext.util.AbstractMixedCollection', - mixins: { - sortable: 'Ext.util.Sortable' + // inherit docs + removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){ + var me = this, + element = managedListener.options ? managedListener.options.element : null; + + if (element) { + element = me[element]; + if (element && element.un) { + if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) { + element.un(managedListener.ename, managedListener.fn, managedListener.scope); + if (!isClear) { + Ext.Array.remove(me.managedListeners, managedListener); + } + } + } + } else { + return me.mixins.observable.removeManagedListenerItem.apply(me, arguments); + } }, /** - * Creates new MixedCollection. - * @param {Boolean} allowFunctions Specify true if the {@link #addAll} - * function should add function references to the collection. Defaults to - * false. - * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection - * and return the key value for that item. This is used when available to look up the key on items that - * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is - * equivalent to providing an implementation for the {@link #getKey} method. + * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy. + * @return {Ext.container.Container} the Container which owns this Component. */ - constructor: function() { - var me = this; - me.callParent(arguments); - me.addEvents('sort'); - me.mixins.sortable.initSortable.call(me); + getBubbleTarget : function() { + return this.ownerCt; }, - doSort: function(sorterFn) { - this.sortBy(sorterFn); + /** + * Method to determine whether this Component is floating. + * @return {Boolean} the floating state of this component. + */ + isFloating : function() { + return this.floating; }, /** - * @private - * Performs the actual sorting based on a direction and a sorting function. Internally, - * this creates a temporary array of all items in the MixedCollection, sorts it and then writes - * the sorted array data back into this.items and this.keys - * @param {String} property Property to sort by ('key', 'value', or 'index') - * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'. - * @param {Function} fn (optional) Comparison function that defines the sort order. - * Defaults to sorting by numeric value. + * Method to determine whether this Component is draggable. + * @return {Boolean} the draggable state of this component. */ - _sort : function(property, dir, fn){ - var me = this, - i, len, - dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1, - - //this is a temporary array used to apply the sorting function - c = [], - keys = me.keys, - items = me.items; - - //default to a simple sorter function if one is not provided - fn = fn || function(a, b) { - return a - b; - }; - - //copy all the items into a temporary array, which we will sort - for(i = 0, len = items.length; i < len; i++){ - c[c.length] = { - key : keys[i], - value: items[i], - index: i - }; - } - - //sort the temporary array - Ext.Array.sort(c, function(a, b){ - var v = fn(a[property], b[property]) * dsc; - if(v === 0){ - v = (a.index < b.index ? -1 : 1); - } - return v; - }); - - //copy the temporary array back into the main this.items and this.keys objects - for(i = 0, len = c.length; i < len; i++){ - items[i] = c[i].value; - keys[i] = c[i].key; - } - - me.fireEvent('sort', me); + isDraggable : function() { + return !!this.draggable; }, /** - * Sorts the collection by a single sorter function - * @param {Function} sorterFn The function to sort by + * Method to determine whether this Component is droppable. + * @return {Boolean} the droppable state of this component. */ - sortBy: function(sorterFn) { - var me = this, - items = me.items, - keys = me.keys, - length = items.length, - temp = [], - i; - - //first we create a copy of the items array so that we can sort it - for (i = 0; i < length; i++) { - temp[i] = { - key : keys[i], - value: items[i], - index: i - }; - } + isDroppable : function() { + return !!this.droppable; + }, - Ext.Array.sort(temp, function(a, b) { - var v = sorterFn(a.value, b.value); - if (v === 0) { - v = (a.index < b.index ? -1 : 1); - } + /** + * @private + * Method to manage awareness of when components are added to their + * respective Container, firing an added event. + * References are established at add time rather than at render time. + * @param {Ext.container.Container} container Container which holds the component + * @param {Number} pos Position at which the component was added + */ + onAdded : function(container, pos) { + this.ownerCt = container; + this.fireEvent('added', this, container, pos); + }, - return v; - }); + /** + * @private + * Method to manage awareness of when components are removed from their + * respective Container, firing an removed event. References are properly + * cleaned up after removing a component from its owning container. + */ + onRemoved : function() { + var me = this; - //copy the temporary array back into the main this.items and this.keys objects - for (i = 0; i < length; i++) { - items[i] = temp[i].value; - keys[i] = temp[i].key; - } - - me.fireEvent('sort', me, items, keys); + me.fireEvent('removed', me, me.ownerCt); + delete me.ownerCt; }, + // @private + beforeDestroy : Ext.emptyFn, + // @private + // @private + onResize : Ext.emptyFn, + /** - * Reorders each of the items based on a mapping from old index to new index. Internally this - * just translates into a sort. The 'sort' event is fired whenever reordering has occured. - * @param {Object} mapping Mapping from old item index to new item index + * Sets the width and height of this Component. This method fires the {@link #resize} event. This method can accept + * either width and height as separate arguments, or you can pass a size object like `{width:10, height:20}`. + * + * @param {Number/String/Object} width The new width to set. This may be one of: + * + * - A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels). + * - A String used to set the CSS width style. + * - A size object in the format `{width: widthValue, height: heightValue}`. + * - `undefined` to leave the width unchanged. + * + * @param {Number/String} height The new height to set (not required if a size object is passed as the first arg). + * This may be one of: + * + * - A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels). + * - A String used to set the CSS height style. Animation may **not** be used. + * - `undefined` to leave the height unchanged. + * + * @return {Ext.Component} this */ - reorder: function(mapping) { + setSize : function(width, height) { var me = this, - items = me.items, - index = 0, - length = items.length, - order = [], - remaining = [], - oldIndex; - - me.suspendEvents(); + layoutCollection; - //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition} - for (oldIndex in mapping) { - order[mapping[oldIndex]] = items[oldIndex]; + // support for standard size objects + if (Ext.isObject(width)) { + height = width.height; + width = width.width; } - for (index = 0; index < length; index++) { - if (mapping[index] == undefined) { - remaining.push(items[index]); - } + // Constrain within configured maxima + if (Ext.isNumber(width)) { + width = Ext.Number.constrain(width, me.minWidth, me.maxWidth); + } + if (Ext.isNumber(height)) { + height = Ext.Number.constrain(height, me.minHeight, me.maxHeight); } - for (index = 0; index < length; index++) { - if (order[index] == undefined) { - order[index] = remaining.shift(); + if (!me.rendered || !me.isVisible()) { + // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag. + if (me.hiddenAncestor) { + layoutCollection = me.hiddenAncestor.layoutOnShow; + layoutCollection.remove(me); + layoutCollection.add(me); + } + me.needsLayout = { + width: width, + height: height, + isSetSize: true + }; + if (!me.rendered) { + me.width = (width !== undefined) ? width : me.width; + me.height = (height !== undefined) ? height : me.height; } + return me; } + me.doComponentLayout(width, height, true); - me.clear(); - me.addAll(order); - - me.resumeEvents(); - me.fireEvent('sort', me); + return me; }, - /** - * Sorts this collection by keys. - * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'. - * @param {Function} fn (optional) Comparison function that defines the sort order. - * Defaults to sorting by case insensitive string. - */ - sortByKey : function(dir, fn){ - this._sort('key', dir, fn || function(a, b){ - var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase(); - return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); - }); - } -}); - -/** - * @class Ext.data.StoreManager - * @extends Ext.util.MixedCollection - *

        Contains a collection of all stores that are created that have an identifier. - * An identifier can be assigned by setting the {@link Ext.data.AbstractStore#storeId storeId} - * property. When a store is in the StoreManager, it can be referred to via it's identifier: - *

        
        -Ext.create('Ext.data.Store', {
        -    model: 'SomeModel',
        -    storeId: 'myStore'
        -});
        +    isFixedWidth: function() {
        +        var me = this,
        +            layoutManagedWidth = me.layoutManagedWidth;
         
        -var store = Ext.data.StoreManager.lookup('myStore');
        - * 
        - * Also note that the {@link #lookup} method is aliased to {@link Ext#getStore} for convenience.

        - *

        - * If a store is registered with the StoreManager, you can also refer to the store by it's identifier when - * registering it with any Component that consumes data from a store: - *

        
        -Ext.create('Ext.data.Store', {
        -    model: 'SomeModel',
        -    storeId: 'myStore'
        -});
        +        if (Ext.isDefined(me.width) || layoutManagedWidth == 1) {
        +            return true;
        +        }
        +        if (layoutManagedWidth == 2) {
        +            return false;
        +        }
        +        return (me.ownerCt && me.ownerCt.isFixedWidth());
        +    },
         
        -Ext.create('Ext.view.View', {
        -    store: 'myStore',
        -    // other configuration here
        -});
        - * 
        - *

        - * @singleton - * @docauthor Evan Trimboli - * TODO: Make this an AbstractMgr - */ -Ext.define('Ext.data.StoreManager', { - extend: 'Ext.util.MixedCollection', - alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'], - singleton: true, - uses: ['Ext.data.ArrayStore'], - - /** - * @cfg {Object} listeners @hide - */ + isFixedHeight: function() { + var me = this, + layoutManagedHeight = me.layoutManagedHeight; - /** - * Registers one or more Stores with the StoreManager. You do not normally need to register stores - * manually. Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. - * @param {Ext.data.Store} store1 A Store instance - * @param {Ext.data.Store} store2 (optional) - * @param {Ext.data.Store} etc... (optional) - */ - register : function() { - for (var i = 0, s; (s = arguments[i]); i++) { - this.add(s); + if (Ext.isDefined(me.height) || layoutManagedHeight == 1) { + return true; + } + if (layoutManagedHeight == 2) { + return false; } + return (me.ownerCt && me.ownerCt.isFixedHeight()); }, - /** - * Unregisters one or more Stores with the StoreManager - * @param {String/Object} id1 The id of the Store, or a Store instance - * @param {String/Object} id2 (optional) - * @param {String/Object} etc... (optional) - */ - unregister : function() { - for (var i = 0, s; (s = arguments[i]); i++) { - this.remove(this.lookup(s)); + setCalculatedSize : function(width, height, callingContainer) { + var me = this, + layoutCollection; + + // support for standard size objects + if (Ext.isObject(width)) { + callingContainer = width.ownerCt; + height = width.height; + width = width.width; + } + + // Constrain within configured maxima + if (Ext.isNumber(width)) { + width = Ext.Number.constrain(width, me.minWidth, me.maxWidth); + } + if (Ext.isNumber(height)) { + height = Ext.Number.constrain(height, me.minHeight, me.maxHeight); + } + + if (!me.rendered || !me.isVisible()) { + // If an ownerCt is hidden, add my reference onto the layoutOnShow stack. Set the needsLayout flag. + if (me.hiddenAncestor) { + layoutCollection = me.hiddenAncestor.layoutOnShow; + layoutCollection.remove(me); + layoutCollection.add(me); + } + me.needsLayout = { + width: width, + height: height, + isSetSize: false, + ownerCt: callingContainer + }; + return me; } + me.doComponentLayout(width, height, false, callingContainer); + + return me; }, /** - * Gets a registered Store by id - * @param {String/Object} id The id of the Store, or a Store instance, or a store configuration - * @return {Ext.data.Store} + * This method needs to be called whenever you change something on this component that requires the Component's + * layout to be recalculated. + * @param {Object} width + * @param {Object} height + * @param {Object} isSetSize + * @param {Object} callingContainer + * @return {Ext.container.Container} this */ - lookup : function(store) { - // handle the case when we are given an array or an array of arrays. - if (Ext.isArray(store)) { - var fields = ['field1'], - expand = !Ext.isArray(store[0]), - data = store, - i, - len; - - if(expand){ - data = []; - for (i = 0, len = store.length; i < len; ++i) { - data.push([store[i]]); + doComponentLayout : function(width, height, isSetSize, callingContainer) { + var me = this, + componentLayout = me.getComponentLayout(), + lastComponentSize = componentLayout.lastComponentSize || { + width: undefined, + height: undefined + }; + + // collapsed state is not relevant here, so no testing done. + // Only Panels have a collapse method, and that just sets the width/height such that only + // a single docked Header parallel to the collapseTo side are visible, and the Panel body is hidden. + if (me.rendered && componentLayout) { + // If no width passed, then only insert a value if the Component is NOT ALLOWED to autowidth itself. + if (!Ext.isDefined(width)) { + if (me.isFixedWidth()) { + width = Ext.isDefined(me.width) ? me.width : lastComponentSize.width; } - } else { - for(i = 2, len = store[0].length; i <= len; ++i){ - fields.push('field' + i); + } + // If no height passed, then only insert a value if the Component is NOT ALLOWED to autoheight itself. + if (!Ext.isDefined(height)) { + if (me.isFixedHeight()) { + height = Ext.isDefined(me.height) ? me.height : lastComponentSize.height; } } - return Ext.create('Ext.data.ArrayStore', { - data : data, - fields: fields, - autoDestroy: true, - autoCreated: true, - expanded: expand - }); - } - - if (Ext.isString(store)) { - // store id - return this.get(store); - } else { - // store instance or store config - return Ext.data.AbstractStore.create(store); + + if (isSetSize) { + me.width = width; + me.height = height; + } + + componentLayout.layout(width, height, isSetSize, callingContainer); } + + return me; }, - // getKey implementation for MixedCollection - getKey : function(o) { - return o.storeId; - } -}, function() { /** - *

        Creates a new store for the given id and config, then registers it with the {@link Ext.data.StoreManager Store Mananger}. - * Sample usage:

        -
        
        -    Ext.regStore('AllUsers', {
        -        model: 'User'
        -    });
        -
        -    //the store can now easily be used throughout the application
        -    new Ext.List({
        -        store: 'AllUsers',
        -        ... other config
        -    });
        -    
        - * @param {String} id The id to set on the new store - * @param {Object} config The store config - * @param {Constructor} cls The new Component class. - * @member Ext - * @method regStore + * Forces this component to redo its componentLayout. */ - Ext.regStore = function(name, config) { - var store; + forceComponentLayout: function () { + this.doComponentLayout(); + }, - if (Ext.isObject(name)) { - config = name; - } else { - config.storeId = name; + // @private + setComponentLayout : function(layout) { + var currentLayout = this.componentLayout; + if (currentLayout && currentLayout.isLayout && currentLayout != layout) { + currentLayout.setOwner(null); } + this.componentLayout = layout; + layout.setOwner(this); + }, - if (config instanceof Ext.data.Store) { - store = config; - } else { - store = Ext.create('Ext.data.Store', config); - } + getComponentLayout : function() { + var me = this; - return Ext.data.StoreManager.register(store); - }; + if (!me.componentLayout || !me.componentLayout.isLayout) { + me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent')); + } + return me.componentLayout; + }, /** - * Gets a registered Store by id (shortcut to {@link Ext.data.StoreManager#lookup}) - * @param {String/Object} id The id of the Store, or a Store instance - * @return {Ext.data.Store} - * @member Ext - * @method getStore + * Occurs after componentLayout is run. + * @param {Number} adjWidth The box-adjusted width that was set + * @param {Number} adjHeight The box-adjusted height that was set + * @param {Boolean} isSetSize Whether or not the height/width are stored on the component permanently + * @param {Ext.Component} callingContainer Container requesting the layout. Only used when isSetSize is false. */ - Ext.getStore = function(name) { - return Ext.data.StoreManager.lookup(name); - }; -}); + afterComponentLayout: function(width, height, isSetSize, callingContainer) { + var me = this, + layout = me.componentLayout, + oldSize = me.preLayoutSize; -/** - * @class Ext.LoadMask - * A simple utility class for generically masking elements while loading data. If the {@link #store} - * config option is specified, the masking will be automatically synchronized with the store's loading - * process and the mask element will be cached for reuse. - *

        Example usage:

        - *
        
        -// Basic mask:
        -var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
        -myMask.show();
        -
        + ++me.componentLayoutCounter; + if (!oldSize || ((width !== oldSize.width) || (height !== oldSize.height))) { + me.fireEvent('resize', me, width, height); + } + }, - */ + /** + * Occurs before componentLayout is run. Returning false from this method will prevent the componentLayout from + * being executed. + * @param {Number} adjWidth The box-adjusted width that was set + * @param {Number} adjHeight The box-adjusted height that was set + * @param {Boolean} isSetSize Whether or not the height/width are stored on the component permanently + * @param {Ext.Component} callingContainer Container requesting sent the layout. Only used when isSetSize is false. + */ + beforeComponentLayout: function(width, height, isSetSize, callingContainer) { + this.preLayoutSize = this.componentLayout.lastComponentSize; + return true; + }, -Ext.define('Ext.LoadMask', { + /** + * Sets the left and top of the component. To set the page XY position instead, use + * {@link Ext.Component#setPagePosition setPagePosition}. This method fires the {@link #move} event. + * @param {Number} left The new left + * @param {Number} top The new top + * @return {Ext.Component} this + */ + setPosition : function(x, y) { + var me = this; - /* Begin Definitions */ + if (Ext.isObject(x)) { + y = x.y; + x = x.x; + } - mixins: { - observable: 'Ext.util.Observable' - }, + if (!me.rendered) { + return me; + } - requires: ['Ext.data.StoreManager'], + if (x !== undefined || y !== undefined) { + me.el.setBox(x, y); + me.onPosition(x, y); + me.fireEvent('move', me, x, y); + } + return me; + }, - /* End Definitions */ + /** + * @private + * Called after the component is moved, this method is empty by default but can be implemented by any + * subclass that needs to perform custom logic after a move occurs. + * @param {Number} x The new x position + * @param {Number} y The new y position + */ + onPosition: Ext.emptyFn, /** - * @cfg {Ext.data.Store} store - * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and - * hidden on either load success, or load fail. + * Sets the width of the component. This method fires the {@link #resize} event. + * + * @param {Number} width The new width to setThis may be one of: + * + * - A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels). + * - A String used to set the CSS width style. + * + * @return {Ext.Component} this */ + setWidth : function(width) { + return this.setSize(width); + }, /** - * @cfg {String} msg - * The text to display in a centered loading message box (defaults to 'Loading...') + * Sets the height of the component. This method fires the {@link #resize} event. + * + * @param {Number} height The new height to set. This may be one of: + * + * - A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels). + * - A String used to set the CSS height style. + * - _undefined_ to leave the height unchanged. + * + * @return {Ext.Component} this */ - msg : 'Loading...', + setHeight : function(height) { + return this.setSize(undefined, height); + }, + /** - * @cfg {String} msgCls - * The CSS class to apply to the loading message element (defaults to "x-mask-loading") + * Gets the current size of the component's underlying element. + * @return {Object} An object containing the element's size {width: (element width), height: (element height)} */ - msgCls : Ext.baseCSSPrefix + 'mask-loading', - + getSize : function() { + return this.el.getSize(); + }, + /** - * @cfg {Boolean} useMsg - * Whether or not to use a loading message class or simply mask the bound element. + * Gets the current width of the component's underlying element. + * @return {Number} */ - useMsg: true, + getWidth : function() { + return this.el.getWidth(); + }, /** - * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false) - * @type Boolean + * Gets the current height of the component's underlying element. + * @return {Number} */ - disabled: false, + getHeight : function() { + return this.el.getHeight(); + }, /** - * Creates new LoadMask. - * @param {Mixed} el The element, element ID, or DOM node you wish to mask. - * Also, may be a Component who's element you wish to mask. - * @param {Object} config (optional) The config object + * Gets the {@link Ext.ComponentLoader} for this Component. + * @return {Ext.ComponentLoader} The loader instance, null if it doesn't exist. */ - constructor : function(el, config) { - var me = this; + getLoader: function(){ + var me = this, + autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null, + loader = me.loader || autoLoad; - if (el.isComponent) { - me.bindComponent(el); - } else { - me.el = Ext.get(el); - } - Ext.apply(me, config); + if (loader) { + if (!loader.isLoader) { + me.loader = Ext.create('Ext.ComponentLoader', Ext.apply({ + target: me, + autoLoad: autoLoad + }, loader)); + } else { + loader.setTarget(me); + } + return me.loader; - me.addEvents('beforeshow', 'show', 'hide'); - if (me.store) { - me.bindStore(me.store, true); } - me.mixins.observable.constructor.call(me, config); + return null; }, - bindComponent: function(comp) { + /** + * This method allows you to show or hide a LoadMask on top of this component. + * + * @param {Boolean/Object/String} load True to show the default LoadMask, a config object that will be passed to the + * LoadMask constructor, or a message String to show. False to hide the current LoadMask. + * @param {Boolean} [targetEl=false] True to mask the targetEl of this Component instead of the `this.el`. For example, + * setting this to true on a Panel will cause only the body to be masked. + * @return {Ext.LoadMask} The LoadMask instance that has just been shown. + */ + setLoading : function(load, targetEl) { var me = this, - listeners = { - resize: me.onComponentResize, - scope: me - }; + config; - if (comp.el) { - me.onComponentRender(comp); - } else { - listeners.render = { - fn: me.onComponentRender, - scope: me, - single: true - }; + if (me.rendered) { + if (load !== false && !me.collapsed) { + if (Ext.isObject(load)) { + config = load; + } + else if (Ext.isString(load)) { + config = {msg: load}; + } + else { + config = {}; + } + me.loadMask = me.loadMask || Ext.create('Ext.LoadMask', targetEl ? me.getTargetEl() : me.el, config); + me.loadMask.show(); + } else if (me.loadMask) { + Ext.destroy(me.loadMask); + me.loadMask = null; + } } - me.mon(comp, listeners); + + return me.loadMask; }, /** - * @private - * Called if we were configured with a Component, and that Component was not yet rendered. Collects the element to mask. + * Sets the dock position of this component in its parent panel. Note that this only has effect if this item is part + * of the dockedItems collection of a parent that has a DockLayout (note that any Panel has a DockLayout by default) + * @param {Object} dock The dock position. + * @param {Boolean} [layoutParent=false] True to re-layout parent. + * @return {Ext.Component} this */ - onComponentRender: function(comp) { - this.el = comp.getContentTarget(); + setDocked : function(dock, layoutParent) { + var me = this; + + me.dock = dock; + if (layoutParent && me.ownerCt && me.rendered) { + me.ownerCt.doComponentLayout(); + } + return me; }, - /** - * @private - * Called when this LoadMask's Component is resized. The isMasked method also re-centers any displayed message. - */ - onComponentResize: function(comp, w, h) { - this.el.isMasked(); + onDestroy : function() { + var me = this; + + if (me.monitorResize && Ext.EventManager.resizeEvent) { + Ext.EventManager.resizeEvent.removeListener(me.setSize, me); + } + // Destroying the floatingItems ZIndexManager will also destroy descendant floating Components + Ext.destroy( + me.componentLayout, + me.loadMask, + me.floatingItems + ); }, /** - * Changes the data store bound to this LoadMask. - * @param {Store} store The store to bind to this LoadMask + * Remove any references to elements added via renderSelectors/childEls + * @private */ - bindStore : function(store, initial) { - var me = this; + cleanElementRefs: function(){ + var me = this, + i = 0, + childEls = me.childEls, + selectors = me.renderSelectors, + selector, + name, + len; - if (!initial && me.store) { - me.mun(me.store, { - scope: me, - beforeload: me.onBeforeLoad, - load: me.onLoad, - exception: me.onLoad - }); - if(!store) { - me.store = null; + if (me.rendered) { + if (childEls) { + for (len = childEls.length; i < len; ++i) { + name = childEls[i]; + if (typeof(name) != 'string') { + name = name.name; + } + delete me[name]; + } } - } - if (store) { - store = Ext.data.StoreManager.lookup(store); - me.mon(store, { - scope: me, - beforeload: me.onBeforeLoad, - load: me.onLoad, - exception: me.onLoad - }); + if (selectors) { + for (selector in selectors) { + if (selectors.hasOwnProperty(selector)) { + delete me[selector]; + } + } + } } - me.store = store; - if (store && store.isLoading()) { - me.onBeforeLoad(); - } + delete me.rendered; + delete me.el; + delete me.frameBody; }, /** - * Disables the mask to prevent it from being displayed + * Destroys the Component. */ - disable : function() { + destroy : function() { var me = this; - me.disabled = true; - if (me.loading) { - me.onLoad(); - } - }, + if (!me.isDestroyed) { + if (me.fireEvent('beforedestroy', me) !== false) { + me.destroying = true; + me.beforeDestroy(); - /** - * Enables the mask so that it can be displayed - */ - enable : function() { - this.disabled = false; - }, + if (me.floating) { + delete me.floatParent; + // A zIndexManager is stamped into a *floating* Component when it is added to a Container. + // If it has no zIndexManager at render time, it is assigned to the global Ext.WindowManager instance. + if (me.zIndexManager) { + me.zIndexManager.unregister(me); + } + } else if (me.ownerCt && me.ownerCt.remove) { + me.ownerCt.remove(me, false); + } - /** - * Method to determine whether this LoadMask is currently disabled. - * @return {Boolean} the disabled state of this LoadMask. - */ - isDisabled : function() { - return this.disabled; - }, + me.onDestroy(); - // private - onLoad : function() { - var me = this; + // Attempt to destroy all plugins + Ext.destroy(me.plugins); - me.loading = false; - me.el.unmask(); - me.fireEvent('hide', me, me.el, me.store); - }, + if (me.rendered) { + me.el.remove(); + } - // private - onBeforeLoad : function() { - var me = this; + me.fireEvent('destroy', me); + Ext.ComponentManager.unregister(me); - if (!me.disabled && !me.loading && me.fireEvent('beforeshow', me, me.el, me.store) !== false) { - if (me.useMsg) { - me.el.mask(me.msg, me.msgCls, false); - } else { - me.el.mask(); + me.mixins.state.destroy.call(me); + + me.clearListeners(); + // make sure we clean up the element references after removing all events + me.cleanElementRefs(); + me.destroying = false; + me.isDestroyed = true; } - - me.fireEvent('show', me, me.el, me.store); - me.loading = true; } }, /** - * Show this LoadMask over the configured Element. + * Retrieves a plugin by its pluginId which has been bound to this component. + * @param {Object} pluginId + * @return {Ext.AbstractPlugin} plugin instance. */ - show: function() { - this.onBeforeLoad(); + getPlugin: function(pluginId) { + var i = 0, + plugins = this.plugins, + ln = plugins.length; + for (; i < ln; i++) { + if (plugins[i].pluginId === pluginId) { + return plugins[i]; + } + } }, /** - * Hide this LoadMask. + * Determines whether this component is the descendant of a particular container. + * @param {Ext.Container} container + * @return {Boolean} True if it is. */ - hide: function() { - this.onLoad(); - }, - - // private - destroy : function() { - this.hide(); - this.clearListeners(); + isDescendantOf: function(container) { + return !!this.findParentBy(function(p){ + return p === container; + }); } +}, function() { + this.createAlias({ + on: 'addListener', + prev: 'previousSibling', + next: 'nextSibling' + }); }); /** - * @class Ext.ComponentLoader - * @extends Ext.ElementLoader - * - * This class is used to load content via Ajax into a {@link Ext.Component}. In general - * this class will not be instanced directly, rather a loader configuration will be passed to the - * constructor of the {@link Ext.Component}. - * - * ## HTML Renderer - * By default, the content loaded will be processed as raw html. The response text - * from the request is taken and added to the component. This can be used in - * conjunction with the {@link #scripts} option to execute any inline scripts in - * the resulting content. Using this renderer has the same effect as passing the - * {@link Ext.Component#html} configuration option. - * - * ## Data Renderer - * This renderer allows content to be added by using JSON data and a {@link Ext.XTemplate}. - * The content received from the response is passed to the {@link Ext.Component#update} method. - * This content is run through the attached {@link Ext.Component#tpl} and the data is added to - * the Component. Using this renderer has the same effect as using the {@link Ext.Component#data} - * configuration in conjunction with a {@link Ext.Component#tpl}. - * - * ## Component Renderer - * This renderer can only be used with a {@link Ext.container.Container} and subclasses. It allows for - * Components to be loaded remotely into a Container. The response is expected to be a single/series of - * {@link Ext.Component} configuration objects. When the response is received, the data is decoded - * and then passed to {@link Ext.container.Container#add}. Using this renderer has the same effect as specifying - * the {@link Ext.container.Container#items} configuration on a Container. - * - * ## Custom Renderer - * A custom function can be passed to handle any other special case, see the {@link #renderer} option. - * - * ## Example Usage - * new Ext.Component({ - * tpl: '{firstName} - {lastName}', - * loader: { - * url: 'myPage.php', - * renderer: 'data', - * params: { - * userId: 1 - * } - * } - * }); + * The AbstractPlugin class is the base class from which user-implemented plugins should inherit. + * + * This class defines the essential API of plugins as used by Components by defining the following methods: + * + * - `init` : The plugin initialization method which the owning Component calls at Component initialization time. + * + * The Component passes itself as the sole parameter. + * + * Subclasses should set up bidirectional links between the plugin and its client Component here. + * + * - `destroy` : The plugin cleanup method which the owning Component calls at Component destruction time. + * + * Use this method to break links between the plugin and the Component and to free any allocated resources. + * + * - `enable` : The base implementation just sets the plugin's `disabled` flag to `false` + * + * - `disable` : The base implementation just sets the plugin's `disabled` flag to `true` */ -Ext.define('Ext.ComponentLoader', { - - /* Begin Definitions */ - - extend: 'Ext.ElementLoader', - - statics: { - Renderer: { - Data: function(loader, response, active){ - var success = true; - try { - loader.getTarget().update(Ext.decode(response.responseText)); - } catch (e) { - success = false; - } - return success; - }, - - Component: function(loader, response, active){ - var success = true, - target = loader.getTarget(), - items = []; - - if (!target.isContainer) { - Ext.Error.raise({ - target: target, - msg: 'Components can only be loaded into a container' - }); - } - - try { - items = Ext.decode(response.responseText); - } catch (e) { - success = false; - } +Ext.define('Ext.AbstractPlugin', { + disabled: false, - if (success) { - if (active.removeAll) { - target.removeAll(); - } - target.add(items); - } - return success; - } + constructor: function(config) { + if (!config.cmp && Ext.global.console) { + Ext.global.console.warn("Attempted to attach a plugin "); } + Ext.apply(this, config); }, - /* End Definitions */ - - /** - * @cfg {Ext.Component/String} target The target {@link Ext.Component} for the loader. Defaults to null. - * If a string is passed it will be looked up via the id. - */ - target: null, - - /** - * @cfg {Mixed} loadMask True or a {@link Ext.LoadMask} configuration to enable masking during loading. Defaults to false. - */ - loadMask: false, - - /** - * @cfg {Boolean} scripts True to parse any inline script tags in the response. This only used when using the html - * {@link #renderer}. - */ - - /** - * @cfg {String/Function} renderer - -The type of content that is to be loaded into, which can be one of 3 types: - -+ **html** : Loads raw html content, see {@link Ext.Component#html} -+ **data** : Loads raw html content, see {@link Ext.Component#data} -+ **component** : Loads child {Ext.Component} instances. This option is only valid when used with a Container. - -Defaults to `html`. - -Alternatively, you can pass a function which is called with the following parameters. - -+ loader - Loader instance -+ response - The server response -+ active - The active request - -The function must return false is loading is not successful. Below is a sample of using a custom renderer: - - new Ext.Component({ - loader: { - url: 'myPage.php', - renderer: function(loader, response, active) { - var text = response.responseText; - loader.getTarget().update('The response is ' + text); - return true; - } - } - }); - * @markdown - */ - renderer: 'html', + getCmp: function() { + return this.cmp; + }, /** - * Set a {Ext.Component} as the target of this loader. Note that if the target is changed, - * any active requests will be aborted. - * @param {String/Ext.Component} target The component to be the target of this loader. If a string is passed - * it will be looked up via its id. + * @method + * The init method is invoked after initComponent method has been run for the client Component. + * + * The supplied implementation is empty. Subclasses should perform plugin initialization, and set up bidirectional + * links between the plugin and its client Component in their own implementation of this method. + * @param {Ext.Component} client The client Component which owns this plugin. */ - setTarget: function(target){ - var me = this; - - if (Ext.isString(target)) { - target = Ext.getCmp(target); - } + init: Ext.emptyFn, - if (me.target && me.target != target) { - me.abort(); - } - me.target = target; - }, - - // inherit docs - removeMask: function(){ - this.target.setLoading(false); - }, - /** - * Add the mask on the target - * @private - * @param {Mixed} mask The mask configuration + * @method + * The destroy method is invoked by the owning Component at the time the Component is being destroyed. + * + * The supplied implementation is empty. Subclasses should perform plugin cleanup in their own implementation of + * this method. */ - addMask: function(mask){ - this.target.setLoading(mask); - }, + destroy: Ext.emptyFn, /** - * Get the target of this loader. - * @return {Ext.Component} target The target, null if none exists. + * The base implementation just sets the plugin's `disabled` flag to `false` + * + * Plugin subclasses which need more complex processing may implement an overriding implementation. */ - - setOptions: function(active, options){ - active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll; + enable: function() { + this.disabled = false; }, /** - * Gets the renderer to use - * @private - * @param {String/Function} renderer The renderer to use - * @return {Function} A rendering function to use. + * The base implementation just sets the plugin's `disabled` flag to `true` + * + * Plugin subclasses which need more complex processing may implement an overriding implementation. */ - getRenderer: function(renderer){ - if (Ext.isFunction(renderer)) { - return renderer; - } - - var renderers = this.statics().Renderer; - switch (renderer) { - case 'component': - return renderers.Component; - case 'data': - return renderers.Data; - default: - return Ext.ElementLoader.Renderer.Html; - } + disable: function() { + this.disabled = true; } }); - /** - * @class Ext.layout.component.Auto - * @extends Ext.layout.component.Component - * @private + * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either + * to a configured URL, or to a URL specified at request time. * - *

        The AutoLayout is the default layout manager delegated by {@link Ext.Component} to - * render any child Elements when no {@link Ext.Component#layout layout} is configured.

        - */ - -Ext.define('Ext.layout.component.Auto', { - - /* Begin Definitions */ - - alias: 'layout.autocomponent', - - extend: 'Ext.layout.component.Component', - - /* End Definitions */ - - type: 'autocomponent', - - onLayout : function(width, height) { - this.setTargetSize(width, height); - } -}); -/** - * @class Ext.AbstractComponent - *

        An abstract base class which provides shared methods for Components across the Sencha product line.

        - *

        Please refer to sub class's documentation

        + * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available + * to the statement immediately following the {@link #request} call. To process returned data, use a success callback + * in the request options object, or an {@link #requestcomplete event listener}. + * + * # File Uploads + * + * File uploads are not performed using normal "Ajax" techniques, that is they are not performed using XMLHttpRequests. + * Instead the form is submitted in the standard manner with the DOM <form> element temporarily modified to have its + * target set to refer to a dynamically generated, hidden <iframe> which is inserted into the document but removed + * after the return data has been gathered. + * + * The server response is parsed by the browser to create the document for the IFRAME. If the server is using JSON to + * send the return object, then the Content-Type header must be set to "text/html" in order to tell the browser to + * insert the text unchanged into the document body. + * + * Characters which are significant to an HTML parser must be sent as HTML entities, so encode `<` as `<`, `&` as + * `&` etc. + * + * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a + * responseText property in order to conform to the requirements of event handlers and callbacks. + * + * Be aware that file upload packets are sent with the content type multipart/form and some server technologies + * (notably JEE) may require some custom processing in order to retrieve parameter names and parameter values from the + * packet content. + * + * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire. */ - -Ext.define('Ext.AbstractComponent', { - - /* Begin Definitions */ - +Ext.define('Ext.data.Connection', { mixins: { - observable: 'Ext.util.Observable', - animate: 'Ext.util.Animate', - state: 'Ext.state.Stateful' + observable: 'Ext.util.Observable' }, - requires: [ - 'Ext.PluginManager', - 'Ext.ComponentManager', - 'Ext.core.Element', - 'Ext.core.DomHelper', - 'Ext.XTemplate', - 'Ext.ComponentQuery', - 'Ext.LoadMask', - 'Ext.ComponentLoader', - 'Ext.EventManager', - 'Ext.layout.Layout', - 'Ext.layout.component.Auto' - ], - - // Please remember to add dependencies whenever you use it - // I had to fix these many times already - uses: [ - 'Ext.ZIndexManager' - ], - statics: { - AUTO_ID: 1000 - }, - - /* End Definitions */ - - isComponent: true, - - getAutoId: function() { - return ++Ext.AbstractComponent.AUTO_ID; + requestId: 0 }, + url: null, + async: true, + method: null, + username: '', + password: '', /** - * @cfg {String} id - *

        The unique id of this component instance (defaults to an {@link #getId auto-assigned id}).

        - *

        It should not be necessary to use this configuration except for singleton objects in your application. - * Components created with an id may be accessed globally using {@link Ext#getCmp Ext.getCmp}.

        - *

        Instead of using assigned ids, use the {@link #itemId} config, and {@link Ext.ComponentQuery ComponentQuery} which - * provides selector-based searching for Sencha Components analogous to DOM querying. The {@link Ext.container.Container Container} - * class contains {@link Ext.container.Container#down shortcut methods} to query its descendant Components by selector.

        - *

        Note that this id will also be used as the element id for the containing HTML element - * that is rendered to the page for this component. This allows you to write id-based CSS - * rules to style the specific instance of this component uniquely, and also to select - * sub-elements using this component's id as the parent.

        - *

        Note: to avoid complications imposed by a unique id also see {@link #itemId}.

        - *

        Note: to access the container of a Component see {@link #ownerCt}.

        + * @cfg {Boolean} disableCaching + * True to add a unique cache-buster param to GET requests. */ + disableCaching: true, /** - * @cfg {String} itemId - *

        An itemId can be used as an alternative way to get a reference to a component - * when no object reference is available. Instead of using an {@link #id} with - * {@link Ext}.{@link Ext#getCmp getCmp}, use itemId with - * {@link Ext.container.Container}.{@link Ext.container.Container#getComponent getComponent} which will retrieve - * itemId's or {@link #id}'s. Since itemId's are an index to the - * container's internal MixedCollection, the itemId is scoped locally to the container -- - * avoiding potential conflicts with {@link Ext.ComponentManager} which requires a unique - * {@link #id}.

        - *
        
        -var c = new Ext.panel.Panel({ //
        -    {@link Ext.Component#height height}: 300,
        -    {@link #renderTo}: document.body,
        -    {@link Ext.container.Container#layout layout}: 'auto',
        -    {@link Ext.container.Container#items items}: [
        -        {
        -            itemId: 'p1',
        -            {@link Ext.panel.Panel#title title}: 'Panel 1',
        -            {@link Ext.Component#height height}: 150
        -        },
        -        {
        -            itemId: 'p2',
        -            {@link Ext.panel.Panel#title title}: 'Panel 2',
        -            {@link Ext.Component#height height}: 150
        -        }
        -    ]
        -})
        -p1 = c.{@link Ext.container.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
        -p2 = p1.{@link #ownerCt}.{@link Ext.container.Container#getComponent getComponent}('p2'); // reference via a sibling
        -     * 
        - *

        Also see {@link #id}, {@link Ext.container.Container#query}, - * {@link Ext.container.Container#down} and {@link Ext.container.Container#child}.

        - *

        Note: to access the container of an item see {@link #ownerCt}.

        + * @cfg {Boolean} withCredentials + * True to set `withCredentials = true` on the XHR object */ + withCredentials: false, /** - * This Component's owner {@link Ext.container.Container Container} (defaults to undefined, and is set automatically when - * this Component is added to a Container). Read-only. - *

        Note: to access items within the Container see {@link #itemId}.

        - * @type Ext.Container - * @property ownerCt + * @cfg {Boolean} cors + * True to enable CORS support on the XHR object. Currently the only effect of this option + * is to use the XDomainRequest object instead of XMLHttpRequest if the browser is IE8 or above. */ - - /** - * @private - * Flag set by the container layout to which this Component is added. - * If the layout manages this Component's width, it sets the value to 1. - * If it does NOT manage the width, it sets it to 2. - * If the layout MAY affect the width, but only if the owning Container has a fixed width, this is set to 0. - * @type boolean - * @property layoutManagedWidth - */ - - /** - * @private - * Flag set by the container layout to which this Component is added. - * If the layout manages this Component's height, it sets the value to 1. - * If it does NOT manage the height, it sets it to 2. - * If the layout MAY affect the height, but only if the owning Container has a fixed height, this is set to 0. - * @type boolean - * @property layoutManagedHeight - */ + cors: false, /** - * @cfg {Mixed} autoEl - *

        A tag name or {@link Ext.core.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will - * encapsulate this Component.

        - *

        You do not normally need to specify this. For the base classes {@link Ext.Component} and {@link Ext.container.Container}, - * this defaults to 'div'. The more complex Sencha classes use a more complex - * DOM structure specified by their own {@link #renderTpl}s.

        - *

        This is intended to allow the developer to create application-specific utility Components encapsulated by - * different DOM elements. Example usage:

        
        -{
        -    xtype: 'component',
        -    autoEl: {
        -        tag: 'img',
        -        src: 'http://www.example.com/example.jpg'
        -    }
        -}, {
        -    xtype: 'component',
        -    autoEl: {
        -        tag: 'blockquote',
        -        html: 'autoEl is cool!'
        -    }
        -}, {
        -    xtype: 'container',
        -    autoEl: 'ul',
        -    cls: 'ux-unordered-list',
        -    items: {
        -        xtype: 'component',
        -        autoEl: 'li',
        -        html: 'First list item'
        -    }
        -}
        -
        + * @cfg {String} disableCachingParam + * Change the parameter which is sent went disabling caching through a cache buster. */ + disableCachingParam: '_dc', /** - * @cfg {Mixed} renderTpl - *

        An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's - * encapsulating {@link #getEl Element}.

        - *

        You do not normally need to specify this. For the base classes {@link Ext.Component} - * and {@link Ext.container.Container}, this defaults to null which means that they will be initially rendered - * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch classes - * which use a more complex DOM structure, provide their own template definitions.

        - *

        This is intended to allow the developer to create application-specific utility Components with customized - * internal structure.

        - *

        Upon rendering, any created child elements may be automatically imported into object properties using the - * {@link #renderSelectors} option.

        + * @cfg {Number} timeout + * The timeout in milliseconds to be used for requests. */ - renderTpl: null, + timeout : 30000, /** - * @cfg {Object} renderSelectors + * @cfg {Object} extraParams + * Any parameters to be appended to the request. + */ -An object containing properties specifying {@link Ext.DomQuery DomQuery} selectors which identify child elements -created by the render process. + useDefaultHeader : true, + defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8', + useDefaultXhrHeader : true, + defaultXhrHeader : 'XMLHttpRequest', -After the Component's internal structure is rendered according to the {@link #renderTpl}, this object is iterated through, -and the found Elements are added as properties to the Component using the `renderSelector` property name. + constructor : function(config) { + config = config || {}; + Ext.apply(this, config); -For example, a Component which rendered an image, and description into its element might use the following properties -coded into its prototype: + this.addEvents( + /** + * @event beforerequest + * Fires before a network request is made to retrieve a data object. + * @param {Ext.data.Connection} conn This Connection object. + * @param {Object} options The options config object passed to the {@link #request} method. + */ + 'beforerequest', + /** + * @event requestcomplete + * Fires if the request was successfully completed. + * @param {Ext.data.Connection} conn This Connection object. + * @param {Object} response The XHR object containing the response data. + * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details. + * @param {Object} options The options config object passed to the {@link #request} method. + */ + 'requestcomplete', + /** + * @event requestexception + * Fires if an error HTTP status was returned from the server. + * See [HTTP Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) + * for details of HTTP status codes. + * @param {Ext.data.Connection} conn This Connection object. + * @param {Object} response The XHR object containing the response data. + * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details. + * @param {Object} options The options config object passed to the {@link #request} method. + */ + 'requestexception' + ); + this.requests = {}; + this.mixins.observable.constructor.call(this); + }, - renderTpl: '<img src="{imageUrl}" class="x-image-component-img"><div class="x-image-component-desc">{description}>/div<', + /** + * Sends an HTTP request to a remote server. + * + * **Important:** Ajax server requests are asynchronous, and this call will + * return before the response has been received. Process any returned data + * in a callback function. + * + * Ext.Ajax.request({ + * url: 'ajax_demo/sample.json', + * success: function(response, opts) { + * var obj = Ext.decode(response.responseText); + * console.dir(obj); + * }, + * failure: function(response, opts) { + * console.log('server-side failure with status code ' + response.status); + * } + * }); + * + * To execute a callback function in the correct scope, use the `scope` option. + * + * @param {Object} options An object which may contain the following properties: + * + * (The options object may also contain any other property which might be needed to perform + * postprocessing in a callback because it is passed to callback functions.) + * + * @param {String/Function} options.url The URL to which to send the request, or a function + * to call which returns a URL string. The scope of the function is specified by the `scope` option. + * Defaults to the configured `url`. + * + * @param {Object/String/Function} options.params An object containing properties which are + * used as parameters to the request, a url encoded string or a function to call to get either. The scope + * of the function is specified by the `scope` option. + * + * @param {String} options.method The HTTP method to use + * for the request. Defaults to the configured method, or if no method was configured, + * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that + * the method name is case-sensitive and should be all caps. + * + * @param {Function} options.callback The function to be called upon receipt of the HTTP response. + * The callback is called regardless of success or failure and is passed the following parameters: + * @param {Object} options.callback.options The parameter to the request call. + * @param {Boolean} options.callback.success True if the request succeeded. + * @param {Object} options.callback.response The XMLHttpRequest object containing the response data. + * See [www.w3.org/TR/XMLHttpRequest/](http://www.w3.org/TR/XMLHttpRequest/) for details about + * accessing elements of the response. + * + * @param {Function} options.success The function to be called upon success of the request. + * The callback is passed the following parameters: + * @param {Object} options.success.response The XMLHttpRequest object containing the response data. + * @param {Object} options.success.options The parameter to the request call. + * + * @param {Function} options.failure The function to be called upon success of the request. + * The callback is passed the following parameters: + * @param {Object} options.failure.response The XMLHttpRequest object containing the response data. + * @param {Object} options.failure.options The parameter to the request call. + * + * @param {Object} options.scope The scope in which to execute the callbacks: The "this" object for + * the callback function. If the `url`, or `params` options were specified as functions from which to + * draw values, then this also serves as the scope for those function calls. Defaults to the browser + * window. + * + * @param {Number} options.timeout The timeout in milliseconds to be used for this request. + * Defaults to 30 seconds. + * + * @param {Ext.Element/HTMLElement/String} options.form The `
        ` Element or the id of the `` + * to pull parameters from. + * + * @param {Boolean} options.isUpload **Only meaningful when used with the `form` option.** + * + * True if the form object is a file upload (will be set automatically if the form was configured + * with **`enctype`** `"multipart/form-data"`). + * + * File uploads are not performed using normal "Ajax" techniques, that is they are **not** + * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the + * DOM `` element temporarily modified to have its [target][] set to refer to a dynamically + * generated, hidden `', + '', { compiled: true, disableFormats: true @@ -104939,47 +109339,58 @@ Ext.define('Ext.form.field.HtmlEditor', { ], /** - * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true) + * @cfg {Boolean} enableFormat + * Enable the bold, italic and underline buttons */ enableFormat : true, /** - * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true) + * @cfg {Boolean} enableFontSize + * Enable the increase/decrease font size buttons */ enableFontSize : true, /** - * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true) + * @cfg {Boolean} enableColors + * Enable the fore/highlight color buttons */ enableColors : true, /** - * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true) + * @cfg {Boolean} enableAlignments + * Enable the left, center, right alignment buttons */ enableAlignments : true, /** - * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true) + * @cfg {Boolean} enableLists + * Enable the bullet and numbered list buttons. Not available in Safari. */ enableLists : true, /** - * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true) + * @cfg {Boolean} enableSourceEdit + * Enable the switch to source edit button. Not available in Safari. */ enableSourceEdit : true, /** - * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true) + * @cfg {Boolean} enableLinks + * Enable the create link button. Not available in Safari. */ enableLinks : true, /** - * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true) + * @cfg {Boolean} enableFont + * Enable font selection. Not available in Safari. */ enableFont : true, /** - * @cfg {String} createLinkText The default text for the create link prompt + * @cfg {String} createLinkText + * The default text for the create link prompt */ createLinkText : 'Please enter the URL for the link:', /** - * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /) + * @cfg {String} [defaultLinkValue='http://'] + * The default value for the create link prompt */ defaultLinkValue : 'http:/'+'/', /** - * @cfg {Array} fontFamilies An array of available font families + * @cfg {String[]} fontFamilies + * An array of available font families */ fontFamilies : [ 'Arial', @@ -104990,7 +109401,9 @@ Ext.define('Ext.form.field.HtmlEditor', { ], defaultFont: 'tahoma', /** - * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to   (Non-breaking space) in Opera and IE6, ​ (Zero-width space) in all other browsers). + * @cfg {String} defaultValue + * A default value to be put into the editor to resolve focus issues (defaults to (Non-breaking space) in Opera + * and IE6, ​(Zero-width space) in all other browsers). */ defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '​', @@ -105006,7 +109419,7 @@ Ext.define('Ext.form.field.HtmlEditor', { hideMode:'offsets', maskOnDisable: true, - + // private initComponent : function(){ var me = this; @@ -105020,23 +109433,22 @@ Ext.define('Ext.form.field.HtmlEditor', { 'initialize', /** * @event activate - * Fires when the editor is first receives the focus. Any insertion must wait - * until after this event. + * Fires when the editor is first receives the focus. Any insertion must wait until after this event. * @param {Ext.form.field.HtmlEditor} this */ 'activate', /** * @event beforesync - * Fires before the textarea is updated with content from the editor iframe. Return false - * to cancel the sync. + * Fires before the textarea is updated with content from the editor iframe. Return false to cancel the + * sync. * @param {Ext.form.field.HtmlEditor} this * @param {String} html */ 'beforesync', /** * @event beforepush - * Fires before the iframe editor is updated with content from the textarea. Return false - * to cancel the push. + * Fires before the iframe editor is updated with content from the textarea. Return false to cancel the + * push. * @param {Ext.form.field.HtmlEditor} this * @param {String} html */ @@ -105071,11 +109483,11 @@ Ext.define('Ext.form.field.HtmlEditor', { me.initField(); }, - /* - * Protected method that will not generally be called directly. It - * is called when the editor creates its toolbar. Override this method if you need to + /** + * Called when the editor creates its toolbar. Override this method if you need to * add custom toolbar buttons. * @param {Ext.form.field.HtmlEditor} editor + * @protected */ createToolbar : function(editor){ var me = this, @@ -105103,7 +109515,7 @@ Ext.define('Ext.form.field.HtmlEditor', { if (me.enableFont && !Ext.isSafari2) { fontSelectItem = Ext.widget('component', { renderTpl: [ - '', '', '>{.}', '', @@ -105114,9 +109526,7 @@ Ext.define('Ext.form.field.HtmlEditor', { fonts: me.fontFamilies, defaultFont: me.defaultFont }, - renderSelectors: { - selectEl: 'select' - }, + childEls: ['selectEl'], onDisable: function() { var selectEl = this.selectEl; if (selectEl) { @@ -105316,14 +109726,14 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Protected method that will not generally be called directly. It - * is called when the editor initializes the iframe with HTML contents. Override this method if you + * Called when the editor initializes the iframe with HTML contents. Override this method if you * want to change the initialization markup of the iframe (e.g. to add stylesheets). * - * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility. + * **Note:** IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility. * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web * Developer Tools to manually set the document mode, that will take precedence and override what this * code sets by default. This can be confusing when developing, but is not a user-facing issue. + * @protected */ getDocMarkup: function() { var me = this, @@ -105349,16 +109759,11 @@ Ext.define('Ext.form.field.HtmlEditor', { // private onRender: function() { - var me = this, - renderSelectors = me.renderSelectors; + var me = this; - Ext.applyIf(renderSelectors, me.getLabelableSelectors()); + me.onLabelableRender(); - Ext.applyIf(renderSelectors, { - toolbarWrap: 'div.' + Ext.baseCSSPrefix + 'html-editor-tb', - iframeEl: 'iframe', - textareaEl: 'textarea' - }); + me.addChildEls('toolbarWrap', 'iframeEl', 'textareaEl'); me.callParent(arguments); @@ -105390,6 +109795,8 @@ Ext.define('Ext.form.field.HtmlEditor', { getSubTplData: function() { var cssPrefix = Ext.baseCSSPrefix; return { + cmpId: this.id, + id: this.getInputId(), toolbarWrapCls: cssPrefix + 'html-editor-tb', textareaCls: cssPrefix + 'hidden', iframeName: Ext.id(), @@ -105399,7 +109806,8 @@ Ext.define('Ext.form.field.HtmlEditor', { }, getSubTplMarkup: function() { - return this.getTpl('fieldSubTpl').apply(this.getSubTplData()); + var data = this.getSubTplData(); + return this.getTpl('fieldSubTpl').apply(data); }, getBodyNaturalWidth: function() { @@ -105443,8 +109851,9 @@ Ext.define('Ext.form.field.HtmlEditor', { } }, - /* private - * set current design mode. To enable, mode can be true or 'on', off otherwise + /** + * @private + * Sets current design mode. To enable, mode can be true or 'on', off otherwise */ setDesignMode: function(mode) { var me = this, @@ -105538,10 +109947,10 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Protected method that will not generally be called directly. If you need/want - * custom HTML cleanup, this is the method you should override. + * If you need/want custom HTML cleanup, this is the method you should override. * @param {String} html The HTML to be cleaned * @return {String} The cleaned HTML + * @protected */ cleanHtml: function(html) { html = String(html); @@ -105561,8 +109970,8 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * @protected method that will not generally be called directly. Syncs the contents - * of the editor iframe with the textarea. + * Syncs the contents of the editor iframe with the textarea. + * @protected */ syncValue : function(){ var me = this, @@ -105598,8 +110007,8 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * @protected method that will not generally be called directly. Pushes the value of the textarea - * into the iframe editor. + * Pushes the value of the textarea into the iframe editor. + * @protected */ pushValue: function() { var me = this, @@ -105645,7 +110054,7 @@ Ext.define('Ext.form.field.HtmlEditor', { ss['background-attachment'] = 'fixed'; // w3c dbody.bgProperties = 'fixed'; // ie - Ext.core.DomHelper.applyStyles(dbody, ss); + Ext.DomHelper.applyStyles(dbody, ss); doc = me.getDoc(); @@ -105728,7 +110137,7 @@ Ext.define('Ext.form.field.HtmlEditor', { } catch(e) { // ignore (why?) } - Ext.destroyMembers('tb', 'toolbarWrap', 'iframeEl', 'textareaEl'); + Ext.destroyMembers(me, 'tb', 'toolbarWrap', 'iframeEl', 'textareaEl'); } me.callParent(); }, @@ -105820,8 +110229,8 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Protected method that will not generally be called directly. It triggers - * a toolbar update by reading the markup state of the current selection in the editor. + * Triggers a toolbar update by reading the markup state of the current selection in the editor. + * @protected */ updateToolbar: function() { var me = this, @@ -105873,10 +110282,10 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Executes a Midas editor command on the editor document and performs necessary focus and - * toolbar updates. This should only be called after the editor is initialized. + * Executes a Midas editor command on the editor document and performs necessary focus and toolbar updates. + * **This should only be called after the editor is initialized.** * @param {String} cmd The Midas command - * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null) + * @param {String/Boolean} [value=null] The value to pass to the command */ relayCmd: function(cmd, value) { Ext.defer(function() { @@ -105888,9 +110297,8 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Executes a Midas editor command directly on the editor document. - * For visual commands, you should use {@link #relayCmd} instead. - * This should only be called after the editor is initialized. + * Executes a Midas editor command directly on the editor document. For visual commands, you should use + * {@link #relayCmd} instead. **This should only be called after the editor is initialized.** * @param {String} cmd The Midas command * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null) */ @@ -105931,8 +110339,8 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated - * to insert text. + * Inserts the passed text at the current cursor position. + * Note: the editor must be initialized and activated to insert text. * @param {String} text */ insertAtCursor : function(text){ @@ -106020,7 +110428,7 @@ Ext.define('Ext.form.field.HtmlEditor', { }(), /** - * Returns the editor's toolbar. This is only available after the editor has been rendered. + * Returns the editor's toolbar. **This is only available after the editor has been rendered.** * @return {Ext.toolbar.Toolbar} */ getToolbar : function(){ @@ -106028,24 +110436,22 @@ Ext.define('Ext.form.field.HtmlEditor', { }, /** - * Object collection of toolbar tooltips for the buttons in the editor. The key - * is the command id associated with that button and the value is a valid QuickTips object. - * For example: -
        
        -{
        -    bold : {
        -        title: 'Bold (Ctrl+B)',
        -        text: 'Make the selected text bold.',
        -        cls: 'x-html-editor-tip'
        -    },
        -    italic : {
        -        title: 'Italic (Ctrl+I)',
        -        text: 'Make the selected text italic.',
        -        cls: 'x-html-editor-tip'
        -    },
        -    ...
        -
        - * @type Object + * @property {Object} buttonTips + * Object collection of toolbar tooltips for the buttons in the editor. The key is the command id associated with + * that button and the value is a valid QuickTips object. For example: + * + * { + * bold : { + * title: 'Bold (Ctrl+B)', + * text: 'Make the selected text bold.', + * cls: 'x-html-editor-tip' + * }, + * italic : { + * title: 'Italic (Ctrl+I)', + * text: 'Make the selected text italic.', + * cls: 'x-html-editor-tip' + * }, + * ... */ buttonTips : { bold : { @@ -106159,7 +110565,7 @@ Ext.define('Ext.form.field.HtmlEditor', { * @cfg {String} msgFx @hide */ /** - * @cfg {Boolean} allowDomMove @hide + * @cfg {Boolean} allowDomMove @hide */ /** * @cfg {String} applyTo @hide @@ -106177,178 +110583,177 @@ Ext.define('Ext.form.field.HtmlEditor', { }); /** - * @class Ext.form.field.Radio - * @extends Ext.form.field.Checkbox - -Single radio field. Similar to checkbox, but automatically handles making sure only one radio is checked -at a time within a group of radios with the same name. - -__Labeling:__ -In addition to the {@link Ext.form.Labelable standard field labeling options}, radio buttons -may be given an optional {@link #boxLabel} which will be displayed immediately to the right of the input. Also -see {@link Ext.form.RadioGroup} for a convenient method of grouping related radio buttons. - -__Values:__ -The main value of a Radio field is a boolean, indicating whether or not the radio is checked. - -The following values will check the radio: -* `true` -* `'true'` -* `'1'` -* `'on'` - -Any other value will uncheck it. - -In addition to the main boolean value, you may also specify a separate {@link #inputValue}. This will be sent -as the parameter value when the form is {@link Ext.form.Basic#submit submitted}. You will want to set this -value if you have multiple radio buttons with the same {@link #name}, as is almost always the case. -{@img Ext.form.Radio/Ext.form.Radio.png Ext.form.Radio component} -__Example usage:__ - - Ext.create('Ext.form.Panel', { - title : 'Order Form', - width : 300, - bodyPadding: 10, - renderTo : Ext.getBody(), - items: [ - { - xtype : 'fieldcontainer', - fieldLabel : 'Size', - defaultType: 'radiofield', - defaults: { - flex: 1 - }, - layout: 'hbox', - items: [ - { - boxLabel : 'M', - name : 'size', - inputValue: 'm', - id : 'radio1' - }, { - boxLabel : 'L', - name : 'size', - inputValue: 'l', - id : 'radio2' - }, { - boxLabel : 'XL', - name : 'size', - inputValue: 'xl', - id : 'radio3' - } - ] - }, - { - xtype : 'fieldcontainer', - fieldLabel : 'Color', - defaultType: 'radiofield', - defaults: { - flex: 1 - }, - layout: 'hbox', - items: [ - { - boxLabel : 'Blue', - name : 'color', - inputValue: 'blue', - id : 'radio4' - }, { - boxLabel : 'Grey', - name : 'color', - inputValue: 'grey', - id : 'radio5' - }, { - boxLabel : 'Black', - name : 'color', - inputValue: 'black', - id : 'radio6' - } - ] - } - ], - bbar: [ - { - text: 'Smaller Size', - handler: function() { - var radio1 = Ext.getCmp('radio1'), - radio2 = Ext.getCmp('radio2'), - radio3 = Ext.getCmp('radio3'); - - //if L is selected, change to M - if (radio2.getValue()) { - radio1.setValue(true); - return; - } - - //if XL is selected, change to L - if (radio3.getValue()) { - radio2.setValue(true); - return; - } - - //if nothing is set, set size to S - radio1.setValue(true); - } - }, - { - text: 'Larger Size', - handler: function() { - var radio1 = Ext.getCmp('radio1'), - radio2 = Ext.getCmp('radio2'), - radio3 = Ext.getCmp('radio3'); - - //if M is selected, change to L - if (radio1.getValue()) { - radio2.setValue(true); - return; - } - - //if L is selected, change to XL - if (radio2.getValue()) { - radio3.setValue(true); - return; - } - - //if nothing is set, set size to XL - radio3.setValue(true); - } - }, - '-', - { - text: 'Select color', - menu: { - indent: false, - items: [ - { - text: 'Blue', - handler: function() { - var radio = Ext.getCmp('radio4'); - radio.setValue(true); - } - }, - { - text: 'Grey', - handler: function() { - var radio = Ext.getCmp('radio5'); - radio.setValue(true); - } - }, - { - text: 'Black', - handler: function() { - var radio = Ext.getCmp('radio6'); - radio.setValue(true); - } - } - ] - } - } - ] - }); - - * @docauthor Robert Dougan - * @markdown + * + * Single radio field. Similar to checkbox, but automatically handles making sure only one radio is checked + * at a time within a group of radios with the same name. + * + * # Labeling + * + * In addition to the {@link Ext.form.Labelable standard field labeling options}, radio buttons + * may be given an optional {@link #boxLabel} which will be displayed immediately to the right of the input. Also + * see {@link Ext.form.RadioGroup} for a convenient method of grouping related radio buttons. + * + * # Values + * + * The main value of a Radio field is a boolean, indicating whether or not the radio is checked. + * + * The following values will check the radio: + * + * - `true` + * - `'true'` + * - `'1'` + * - `'on'` + * + * Any other value will uncheck it. + * + * In addition to the main boolean value, you may also specify a separate {@link #inputValue}. This will be sent + * as the parameter value when the form is {@link Ext.form.Basic#submit submitted}. You will want to set this + * value if you have multiple radio buttons with the same {@link #name}, as is almost always the case. + * + * # Example usage + * + * @example + * Ext.create('Ext.form.Panel', { + * title : 'Order Form', + * width : 300, + * bodyPadding: 10, + * renderTo : Ext.getBody(), + * items: [ + * { + * xtype : 'fieldcontainer', + * fieldLabel : 'Size', + * defaultType: 'radiofield', + * defaults: { + * flex: 1 + * }, + * layout: 'hbox', + * items: [ + * { + * boxLabel : 'M', + * name : 'size', + * inputValue: 'm', + * id : 'radio1' + * }, { + * boxLabel : 'L', + * name : 'size', + * inputValue: 'l', + * id : 'radio2' + * }, { + * boxLabel : 'XL', + * name : 'size', + * inputValue: 'xl', + * id : 'radio3' + * } + * ] + * }, + * { + * xtype : 'fieldcontainer', + * fieldLabel : 'Color', + * defaultType: 'radiofield', + * defaults: { + * flex: 1 + * }, + * layout: 'hbox', + * items: [ + * { + * boxLabel : 'Blue', + * name : 'color', + * inputValue: 'blue', + * id : 'radio4' + * }, { + * boxLabel : 'Grey', + * name : 'color', + * inputValue: 'grey', + * id : 'radio5' + * }, { + * boxLabel : 'Black', + * name : 'color', + * inputValue: 'black', + * id : 'radio6' + * } + * ] + * } + * ], + * bbar: [ + * { + * text: 'Smaller Size', + * handler: function() { + * var radio1 = Ext.getCmp('radio1'), + * radio2 = Ext.getCmp('radio2'), + * radio3 = Ext.getCmp('radio3'); + * + * //if L is selected, change to M + * if (radio2.getValue()) { + * radio1.setValue(true); + * return; + * } + * + * //if XL is selected, change to L + * if (radio3.getValue()) { + * radio2.setValue(true); + * return; + * } + * + * //if nothing is set, set size to S + * radio1.setValue(true); + * } + * }, + * { + * text: 'Larger Size', + * handler: function() { + * var radio1 = Ext.getCmp('radio1'), + * radio2 = Ext.getCmp('radio2'), + * radio3 = Ext.getCmp('radio3'); + * + * //if M is selected, change to L + * if (radio1.getValue()) { + * radio2.setValue(true); + * return; + * } + * + * //if L is selected, change to XL + * if (radio2.getValue()) { + * radio3.setValue(true); + * return; + * } + * + * //if nothing is set, set size to XL + * radio3.setValue(true); + * } + * }, + * '-', + * { + * text: 'Select color', + * menu: { + * indent: false, + * items: [ + * { + * text: 'Blue', + * handler: function() { + * var radio = Ext.getCmp('radio4'); + * radio.setValue(true); + * } + * }, + * { + * text: 'Grey', + * handler: function() { + * var radio = Ext.getCmp('radio5'); + * radio.setValue(true); + * } + * }, + * { + * text: 'Black', + * handler: function() { + * var radio = Ext.getCmp('radio6'); + * radio.setValue(true); + * } + * } + * ] + * } + * } + * ] + * }); */ Ext.define('Ext.form.field.Radio', { extend:'Ext.form.field.Checkbox', @@ -106386,9 +110791,9 @@ Ext.define('Ext.form.field.Radio', { }, /** - * Sets either the checked/unchecked status of this Radio, or, if a string value - * is passed, checks a sibling Radio of the same name whose value is the value specified. - * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check. + * Sets either the checked/unchecked status of this Radio, or, if a string value is passed, checks a sibling Radio + * of the same name whose value is the value specified. + * @param {String/Boolean} value Checked value, or the value of the sibling radio button to check. * @return {Ext.form.field.Radio} this */ setValue: function(v) { @@ -106408,7 +110813,7 @@ Ext.define('Ext.form.field.Radio', { /** * Returns the submit value for the checkbox which can be used when submitting forms. - * @return {Boolean/null} True if checked, null if not. + * @return {Boolean/Object} True if checked, null if not. */ getSubmitValue: function() { return this.checked ? this.inputValue : null; @@ -106432,12 +110837,6 @@ Ext.define('Ext.form.field.Radio', { } }, - // inherit docs - beforeDestroy: function(){ - this.callParent(); - this.getManager().removeAtKey(this.id); - }, - // inherit docs getManager: function() { return Ext.form.RadioManager; @@ -106445,28 +110844,23 @@ Ext.define('Ext.form.field.Radio', { }); /** - * @class Ext.picker.Time - * @extends Ext.view.BoundList - *

        A time picker which provides a list of times from which to choose. This is used by the - * {@link Ext.form.field.Time} class to allow browsing and selection of valid times, but could also be used - * with other components.

        - *

        By default, all times starting at midnight and incrementing every 15 minutes will be presented. - * This list of available times can be controlled using the {@link #minValue}, {@link #maxValue}, and - * {@link #increment} configuration properties. The format of the times presented in the list can be - * customized with the {@link #format} config.

        - *

        To handle when the user selects a time from the list, you can subscribe to the {@link #selectionchange} - * event.

        - * - * {@img Ext.picker.Time/Ext.picker.Time.png Ext.picker.Time component} - * - * ## Code - new Ext.create('Ext.picker.Time', { - width: 60, - minValue: Ext.Date.parse('04:30:00 AM', 'h:i:s A'), - maxValue: Ext.Date.parse('08:00:00 AM', 'h:i:s A'), - renderTo: Ext.getBody() - }); + * A time picker which provides a list of times from which to choose. This is used by the Ext.form.field.Time + * class to allow browsing and selection of valid times, but could also be used with other components. * + * By default, all times starting at midnight and incrementing every 15 minutes will be presented. This list of + * available times can be controlled using the {@link #minValue}, {@link #maxValue}, and {@link #increment} + * configuration properties. The format of the times presented in the list can be customized with the {@link #format} + * config. + * + * To handle when the user selects a time from the list, you can subscribe to the {@link #selectionchange} event. + * + * @example + * Ext.create('Ext.picker.Time', { + * width: 60, + * minValue: Ext.Date.parse('04:30:00 AM', 'h:i:s A'), + * maxValue: Ext.Date.parse('08:00:00 AM', 'h:i:s A'), + * renderTo: Ext.getBody() + * }); */ Ext.define('Ext.picker.Time', { extend: 'Ext.view.BoundList', @@ -106475,27 +110869,27 @@ Ext.define('Ext.picker.Time', { /** * @cfg {Date} minValue - * The minimum time to be shown in the list of times. This must be a Date object (only the time fields - * will be used); no parsing of String values will be done. Defaults to undefined. + * The minimum time to be shown in the list of times. This must be a Date object (only the time fields will be + * used); no parsing of String values will be done. */ /** * @cfg {Date} maxValue - * The maximum time to be shown in the list of times. This must be a Date object (only the time fields - * will be used); no parsing of String values will be done. Defaults to undefined. + * The maximum time to be shown in the list of times. This must be a Date object (only the time fields will be + * used); no parsing of String values will be done. */ /** * @cfg {Number} increment - * The number of minutes between each time value in the list (defaults to 15). + * The number of minutes between each time value in the list. */ increment: 15, /** * @cfg {String} format - * The default time format string which can be overriden for localization support. The format must be - * valid according to {@link Ext.Date#parse} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time - * format try 'H:i' instead. + * The default time format string which can be overriden for localization support. The format must be valid + * according to {@link Ext.Date#parse} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time format try 'H:i' + * instead. */ format : "g:i A", @@ -106510,7 +110904,7 @@ Ext.define('Ext.picker.Time', { * @private * Year, month, and day that all times will be normalized into internally. */ - initDate: [2008,1,1], + initDate: [2008,0,1], componentCls: Ext.baseCSSPrefix + 'timepicker', @@ -106523,21 +110917,21 @@ Ext.define('Ext.picker.Time', { var me = this, dateUtil = Ext.Date, clearTime = dateUtil.clearTime, - initDate = me.initDate.join('/'); + initDate = me.initDate; // Set up absolute min and max for the entire day - me.absMin = clearTime(new Date(initDate)); - me.absMax = dateUtil.add(clearTime(new Date(initDate)), 'mi', (24 * 60) - 1); + me.absMin = clearTime(new Date(initDate[0], initDate[1], initDate[2])); + me.absMax = dateUtil.add(clearTime(new Date(initDate[0], initDate[1], initDate[2])), 'mi', (24 * 60) - 1); me.store = me.createStore(); me.updateList(); - this.callParent(); + me.callParent(); }, /** - * Set the {@link #minValue} and update the list of available times. This must be a Date - * object (only the time fields will be used); no parsing of String values will be done. + * Set the {@link #minValue} and update the list of available times. This must be a Date object (only the time + * fields will be used); no parsing of String values will be done. * @param {Date} value */ setMinValue: function(value) { @@ -106546,8 +110940,8 @@ Ext.define('Ext.picker.Time', { }, /** - * Set the {@link #maxValue} and update the list of available times. This must be a Date - * object (only the time fields will be used); no parsing of String values will be done. + * Set the {@link #maxValue} and update the list of available times. This must be a Date object (only the time + * fields will be used); no parsing of String values will be done. * @param {Date} value */ setMaxValue: function(value) { @@ -106563,13 +110957,13 @@ Ext.define('Ext.picker.Time', { */ normalizeDate: function(date) { var initDate = this.initDate; - date.setFullYear(initDate[0], initDate[1] - 1, initDate[2]); + date.setFullYear(initDate[0], initDate[1], initDate[2]); return date; }, /** - * Update the list of available times in the list to be constrained within the - * {@link #minValue} and {@link #maxValue}. + * Update the list of available times in the list to be constrained within the {@link #minValue} + * and {@link #maxValue}. */ updateList: function() { var me = this, @@ -106612,42 +111006,42 @@ Ext.define('Ext.picker.Time', { }); /** - * @class Ext.form.field.Time - * @extends Ext.form.field.Picker - *

        Provides a time input field with a time dropdown and automatic time validation.

        - *

        This field recognizes and uses JavaScript Date objects as its main {@link #value} type (only the time - * portion of the date is used; the month/day/year are ignored). In addition, it recognizes string values which - * are parsed according to the {@link #format} and/or {@link #altFormats} configs. These may be reconfigured - * to use time formats appropriate for the user's locale.

        - *

        The field may be limited to a certain range of times by using the {@link #minValue} and {@link #maxValue} - * configs, and the interval between time options in the dropdown can be changed with the {@link #increment} config.

        - * {@img Ext.form.Time/Ext.form.Time.png Ext.form.Time component} - *

        Example usage:

        - *
        
        -Ext.create('Ext.form.Panel', {
        -    title: 'Time Card',
        -    width: 300,
        -    bodyPadding: 10,
        -    renderTo: Ext.getBody(),        
        -    items: [{
        -        xtype: 'timefield',
        -        name: 'in',
        -        fieldLabel: 'Time In',
        -        minValue: '6:00 AM',
        -        maxValue: '8:00 PM',
        -        increment: 30,
        -        anchor: '100%'
        -    }, {
        -        xtype: 'timefield',
        -        name: 'out',
        -        fieldLabel: 'Time Out',
        -        minValue: '6:00 AM',
        -        maxValue: '8:00 PM',
        -        increment: 30,
        -        anchor: '100%'
        -   }]
        -});
        -
        + * Provides a time input field with a time dropdown and automatic time validation. + * + * This field recognizes and uses JavaScript Date objects as its main {@link #value} type (only the time portion of the + * date is used; the month/day/year are ignored). In addition, it recognizes string values which are parsed according to + * the {@link #format} and/or {@link #altFormats} configs. These may be reconfigured to use time formats appropriate for + * the user's locale. + * + * The field may be limited to a certain range of times by using the {@link #minValue} and {@link #maxValue} configs, + * and the interval between time options in the dropdown can be changed with the {@link #increment} config. + * + * Example usage: + * + * @example + * Ext.create('Ext.form.Panel', { + * title: 'Time Card', + * width: 300, + * bodyPadding: 10, + * renderTo: Ext.getBody(), + * items: [{ + * xtype: 'timefield', + * name: 'in', + * fieldLabel: 'Time In', + * minValue: '6:00 AM', + * maxValue: '8:00 PM', + * increment: 30, + * anchor: '100%' + * }, { + * xtype: 'timefield', + * name: 'out', + * fieldLabel: 'Time Out', + * minValue: '6:00 AM', + * maxValue: '8:00 PM', + * increment: 30, + * anchor: '100%' + * }] + * }); */ Ext.define('Ext.form.field.Time', { extend:'Ext.form.field.Picker', @@ -106657,80 +111051,78 @@ Ext.define('Ext.form.field.Time', { /** * @cfg {String} triggerCls - * An additional CSS class used to style the trigger button. The trigger will always get the - * {@link #triggerBaseCls} by default and triggerCls will be appended if specified. - * Defaults to 'x-form-time-trigger' for the Time field trigger. + * An additional CSS class used to style the trigger button. The trigger will always get the {@link #triggerBaseCls} + * by default and triggerCls will be **appended** if specified. Defaults to 'x-form-time-trigger' for the Time field + * trigger. */ triggerCls: Ext.baseCSSPrefix + 'form-time-trigger', /** * @cfg {Date/String} minValue - * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string - * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined). + * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string time in a + * valid format -- see {@link #format} and {@link #altFormats}. */ /** * @cfg {Date/String} maxValue - * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string - * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined). + * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string time in a + * valid format -- see {@link #format} and {@link #altFormats}. */ /** * @cfg {String} minText - * The error text to display when the entered time is before {@link #minValue} (defaults to - * 'The time in this field must be equal to or after {0}'). + * The error text to display when the entered time is before {@link #minValue}. */ minText : "The time in this field must be equal to or after {0}", /** * @cfg {String} maxText - * The error text to display when the entered time is after {@link #maxValue} (defaults to - * 'The time in this field must be equal to or before {0}'). + * The error text to display when the entered time is after {@link #maxValue}. */ maxText : "The time in this field must be equal to or before {0}", /** * @cfg {String} invalidText - * The error text to display when the time in the field is invalid (defaults to - * '{value} is not a valid time'). + * The error text to display when the time in the field is invalid. */ invalidText : "{0} is not a valid time", /** * @cfg {String} format - * The default time format string which can be overriden for localization support. The format must be - * valid according to {@link Ext.Date#parse} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time - * format try 'H:i' instead. + * The default time format string which can be overriden for localization support. The format must be valid + * according to {@link Ext.Date#parse} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time format try 'H:i' + * instead. */ format : "g:i A", /** - * @cfg {String} submitFormat The date format string which will be submitted to the server. - * The format must be valid according to {@link Ext.Date#parse} (defaults to {@link #format}). + * @cfg {String} submitFormat + * The date format string which will be submitted to the server. The format must be valid according to {@link + * Ext.Date#parse} (defaults to {@link #format}). */ /** * @cfg {String} altFormats * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined - * format (defaults to 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A'). + * format. */ altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A", /** * @cfg {Number} increment - * The number of minutes between each time value in the list (defaults to 15). + * The number of minutes between each time value in the list. */ increment: 15, /** * @cfg {Number} pickerMaxHeight - * The maximum height of the {@link Ext.picker.Time} dropdown. Defaults to 300. + * The maximum height of the {@link Ext.picker.Time} dropdown. */ pickerMaxHeight: 300, /** * @cfg {Boolean} selectOnTab - * Whether the Tab key should select the currently highlighted item. Defaults to true. + * Whether the Tab key should select the currently highlighted item. */ selectOnTab: true, @@ -106825,12 +111217,12 @@ Ext.define('Ext.form.field.Time', { }, /** - * Runs all of Time's validations and returns an array of any errors. Note that this first - * runs Text's validations, so the returned array is an amalgamation of all field errors. - * The additional validation checks are testing that the time format is valid, that the chosen - * time is within the {@link #minValue} and {@link #maxValue} constraints set. - * @param {Mixed} value The value to get errors for (defaults to the current field value) - * @return {Array} All validation errors for this field + * Runs all of Time's validations and returns an array of any errors. Note that this first runs Text's validations, + * so the returned array is an amalgamation of all field errors. The additional validation checks are testing that + * the time format is valid, that the chosen time is within the {@link #minValue} and {@link #maxValue} constraints + * set. + * @param {Object} [value] The value to get errors for (defaults to the current field value) + * @return {String[]} All validation errors for this field */ getErrors: function(value) { var me = this, @@ -106929,6 +111321,7 @@ Ext.define('Ext.form.field.Time', { createPicker: function() { var me = this, picker = Ext.create('Ext.picker.Time', { + pickerField: me, selModel: { mode: 'SINGLE' }, @@ -107133,15 +111526,11 @@ Ext.define('Ext.grid.CellEditor', { * *

        This class is used only by the grid's HeaderContainer docked child.

        * - *

        This class adds the ability to shrink the vertical size of the inner container element back if a grouped + *

        It adds the ability to shrink the vertical size of the inner container element back if a grouped * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down.

        * - *

        It also enforces the grid's HeaderContainer's forceFit config by, after every calaculateChildBoxes call, converting - * all pixel widths into flex values, so that propertions are maintained upon width change of the grid.

        - * *

        Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls - * setPadding on the columns so that they lay out correctly. TODO: implement a ColumnHeader component - * layout which takes responsibility for this, and will run upon resize.

        + * setPadding on the columns so that they lay out correctly.

        */ Ext.define('Ext.grid.ColumnLayout', { extend: 'Ext.layout.container.HBox', @@ -107150,8 +111539,10 @@ Ext.define('Ext.grid.ColumnLayout', { reserveOffset: false, + shrinkToFit: false, + // Height-stretched innerCt must be able to revert back to unstretched height - clearInnerCtOnLayout: false, + clearInnerCtOnLayout: true, beforeLayout: function() { var me = this, @@ -107173,19 +111564,17 @@ Ext.define('Ext.grid.ColumnLayout', { me.innerCt.setHeight(23); // Unstretch child items before the layout which stretches them. - if (me.align == 'stretchmax') { - for (; i < len; i++) { - item = items[i]; - item.el.setStyle({ - height: 'auto' - }); - item.titleContainer.setStyle({ - height: 'auto', - paddingTop: '0' - }); - if (item.componentLayout && item.componentLayout.lastComponentSize) { - item.componentLayout.lastComponentSize.height = item.el.dom.offsetHeight; - } + for (; i < len; i++) { + item = items[i]; + item.el.setStyle({ + height: 'auto' + }); + item.titleContainer.setStyle({ + height: 'auto', + paddingTop: '0' + }); + if (item.componentLayout && item.componentLayout.lastComponentSize) { + item.componentLayout.lastComponentSize.height = item.el.dom.offsetHeight; } } return returnValue; @@ -107199,7 +111588,7 @@ Ext.define('Ext.grid.ColumnLayout', { metaData = calculations.meta, len = boxes.length, i = 0, box, item; - if (targetSize.width && !me.isColumn) { + if (targetSize.width && !me.isHeader) { // If configured forceFit then all columns will be flexed if (me.owner.forceFit) { @@ -107228,16 +111617,84 @@ Ext.define('Ext.grid.ColumnLayout', { afterLayout: function() { var me = this, + owner = me.owner, + topGrid, + bothHeaderCts, + otherHeaderCt, + thisHeight, + otherHeight, + modifiedGrid, i = 0, - items = me.getLayoutItems(), - len = items.length; + items, + len, + headerHeight; me.callParent(arguments); // Set up padding in items - if (!me.owner.hideHeaders && me.align == 'stretchmax') { + if (!me.owner.hideHeaders) { + + // If this is one HeaderContainer of a pair in a side-by-side locking view, then find the height + // of the highest one, and sync the other one to that height. + if (owner.lockableInjected) { + topGrid = owner.up('tablepanel').up('tablepanel'); + bothHeaderCts = topGrid.query('headercontainer:not([isHeader])'); + otherHeaderCt = (bothHeaderCts[0] === owner) ? bothHeaderCts[1] : bothHeaderCts[0]; + + // Both sides must be rendered for this syncing operation to work. + if (!otherHeaderCt.rendered) { + return; + } + + // Get the height of the highest of both HeaderContainers + otherHeight = otherHeaderCt.layout.getRenderTarget().getViewSize().height; + if (!otherHeight) { + return; + } + thisHeight = this.getRenderTarget().getViewSize().height; + if (!thisHeight) { + return; + } + + // Prevent recursion back into here when the "other" grid, after adjusting to the new hight of its headerCt, attempts to inform its ownerCt + // Block the upward notification by flagging the top grid's component layout as busy. + topGrid.componentLayout.layoutBusy = true; + + // Assume that the correct header height is the height of this HeaderContainer + headerHeight = thisHeight; + + // Synch the height of the smaller HeaderContainer to the height of the highest one. + if (thisHeight > otherHeight) { + otherHeaderCt.layout.align = 'stretch'; + otherHeaderCt.setCalculatedSize(otherHeaderCt.getWidth(), owner.getHeight(), otherHeaderCt.ownerCt); + delete otherHeaderCt.layout.align; + modifiedGrid = otherHeaderCt.up('tablepanel'); + } else if (otherHeight > thisHeight) { + headerHeight = otherHeight; + this.align = 'stretch'; + owner.setCalculatedSize(owner.getWidth(), otherHeaderCt.getHeight(), owner.ownerCt); + delete this.align; + modifiedGrid = owner.up('tablepanel'); + } + topGrid.componentLayout.layoutBusy = false; + + // Gather all Header items across both Grids. + items = bothHeaderCts[0].layout.getLayoutItems().concat(bothHeaderCts[1].layout.getLayoutItems()); + } else { + headerHeight = this.getRenderTarget().getViewSize().height; + items = me.getLayoutItems(); + } + + len = items.length; for (; i < len; i++) { - items[i].setPadding(); + items[i].setPadding(headerHeight); + } + + // Size the View within the grid which has had its HeaderContainer entallened (That's a perfectly cromulent word BTW) + if (modifiedGrid) { + setTimeout(function() { + modifiedGrid.doLayout(); + }, 1); } } }, @@ -107249,7 +111706,7 @@ Ext.define('Ext.grid.ColumnLayout', { extra; // Columns must not account for scroll offset - if (!me.isColumn) { + if (!me.isHeader) { me.tooNarrow = calcs.meta.tooNarrow; extra = (me.reserveOffset ? me.availableSpaceOffset : 0); @@ -107273,18 +111730,18 @@ Ext.define('Ext.grid.ColumnLayout', { /** * @class Ext.grid.LockingView * This class is used internally to provide a single interface when using - * a locking grid. Internally, the locking grid creates 2 separate grids, + * a locking grid. Internally, the locking grid creates two separate grids, * so this class is used to map calls appropriately. * @ignore */ Ext.define('Ext.grid.LockingView', { - + mixins: { observable: 'Ext.util.Observable' }, - + eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell)/, - + constructor: function(config){ var me = this, eventNames = [], @@ -107293,7 +111750,7 @@ Ext.define('Ext.grid.LockingView', { normal = config.normal.getView(), events, event; - + Ext.apply(me, { lockedView: locked, normalView: normal, @@ -107302,7 +111759,7 @@ Ext.define('Ext.grid.LockingView', { panel: config.panel }); me.mixins.observable.constructor.call(me, config); - + // relay events events = locked.events; for (event in events) { @@ -107312,49 +111769,49 @@ Ext.define('Ext.grid.LockingView', { } me.relayEvents(locked, eventNames); me.relayEvents(normal, eventNames); - + normal.on({ scope: me, itemmouseleave: me.onItemMouseLeave, itemmouseenter: me.onItemMouseEnter }); - + locked.on({ scope: me, itemmouseleave: me.onItemMouseLeave, itemmouseenter: me.onItemMouseEnter }); }, - + getGridColumns: function() { var cols = this.lockedGrid.headerCt.getGridColumns(); return cols.concat(this.normalGrid.headerCt.getGridColumns()); }, - + getEl: function(column){ return this.getViewForColumn(column).getEl(); }, - + getViewForColumn: function(column) { var view = this.lockedView, inLocked; - + view.headerCt.cascade(function(col){ if (col === column) { inLocked = true; return false; } }); - + return inLocked ? view : this.normalView; }, - + onItemMouseEnter: function(view, record){ var me = this, locked = me.lockedView, other = me.normalView, item; - + if (view.trackOver) { if (view !== locked) { other = locked; @@ -107363,12 +111820,12 @@ Ext.define('Ext.grid.LockingView', { other.highlightItem(item); } }, - + onItemMouseLeave: function(view, record){ var me = this, locked = me.lockedView, other = me.normalView; - + if (view.trackOver) { if (view !== locked) { other = locked; @@ -107376,37 +111833,37 @@ Ext.define('Ext.grid.LockingView', { other.clearHighlight(); } }, - + relayFn: function(name, args){ args = args || []; - + var view = this.lockedView; - view[name].apply(view, args || []); + view[name].apply(view, args || []); view = this.normalView; - view[name].apply(view, args || []); + view[name].apply(view, args || []); }, - + getSelectionModel: function(){ - return this.panel.getSelectionModel(); + return this.panel.getSelectionModel(); }, - + getStore: function(){ return this.panel.store; }, - + getNode: function(nodeInfo){ // default to the normal view return this.normalView.getNode(nodeInfo); }, - + getCell: function(record, column){ var view = this.getViewForColumn(column), row; - + row = view.getNode(record); return Ext.fly(row).down(column.getCellSelector()); }, - + getRecord: function(node){ var result = this.lockedView.getRecord(node); if (!node) { @@ -107414,31 +111871,31 @@ Ext.define('Ext.grid.LockingView', { } return result; }, - + addElListener: function(eventName, fn, scope){ this.relayFn('addElListener', arguments); }, - + refreshNode: function(){ this.relayFn('refreshNode', arguments); }, - + refresh: function(){ this.relayFn('refresh', arguments); }, - + bindStore: function(){ this.relayFn('bindStore', arguments); }, - + addRowCls: function(){ this.relayFn('addRowCls', arguments); }, - + removeRowCls: function(){ this.relayFn('removeRowCls', arguments); } - + }); /** * @class Ext.grid.Lockable @@ -107448,24 +111905,25 @@ Ext.define('Ext.grid.LockingView', { * TablePanel subclass such as GridPanel or TreePanel. TablePanel will * automatically inject the Ext.grid.Lockable mixin in when one of the * these conditions are met: - * - The TablePanel has the lockable configuration set to true - * - One of the columns in the TablePanel has locked set to true/false * - * Each TablePanel subclass *must* register an alias. It should have an array + * - The TablePanel has the lockable configuration set to true + * - One of the columns in the TablePanel has locked set to true/false + * + * Each TablePanel subclass must register an alias. It should have an array * of configurations to copy to the 2 separate tablepanel's that will be generated * to note what configurations should be copied. These are named normalCfgCopy and * lockedCfgCopy respectively. * - * Columns which are locked must specify a fixed width. They do *NOT* support a + * Columns which are locked must specify a fixed width. They do NOT support a * flex width. * * Configurations which are specified in this class will be available on any grid or * tree which is using the lockable functionality. */ Ext.define('Ext.grid.Lockable', { - + requires: ['Ext.grid.LockingView'], - + /** * @cfg {Boolean} syncRowHeight Synchronize rowHeight between the normal and * locked grid view. This is turned on by default. If your grid is guaranteed @@ -107473,14 +111931,14 @@ Ext.define('Ext.grid.Lockable', { * optimize performance. */ syncRowHeight: true, - + /** * @cfg {String} subGridXType The xtype of the subgrid to specify. If this is * not specified lockable will determine the subgrid xtype to create by the * following rule. Use the superclasses xtype if the superclass is NOT * tablepanel, otherwise use the xtype itself. */ - + /** * @cfg {Object} lockedViewConfig A view configuration to be applied to the * locked side of the grid. Any conflicting configurations between lockedViewConfig @@ -107492,14 +111950,16 @@ Ext.define('Ext.grid.Lockable', { * normal/unlocked side of the grid. Any conflicting configurations between normalViewConfig * and viewConfig will be overwritten by the normalViewConfig. */ - + // private variable to track whether or not the spacer is hidden/visible spacerHidden: true, - + + headerCounter: 0, + // i8n text unlockText: 'Unlock', lockText: 'Lock', - + determineXTypeToCreate: function() { var me = this, typeToCreate; @@ -107511,17 +111971,17 @@ Ext.define('Ext.grid.Lockable', { xtypesLn = xtypes.length, xtype = xtypes[xtypesLn - 1], superxtype = xtypes[xtypesLn - 2]; - + if (superxtype !== 'tablepanel') { typeToCreate = superxtype; } else { typeToCreate = xtype; } } - + return typeToCreate; }, - + // injectLockable will be invoked before initComponent's parent class implementation // is called, so throughout this method this. are configurations injectLockable: function() { @@ -107559,9 +112019,9 @@ Ext.define('Ext.grid.Lockable', { columns, lockedHeaderCt, normalHeaderCt; - + me.addCls(Ext.baseCSSPrefix + 'grid-locked'); - + // copy appropriate configurations to the respective // aggregated tablepanel instances and then delete them // from the master tablepanel. @@ -107573,59 +112033,79 @@ Ext.define('Ext.grid.Lockable', { for (i = 0; i < me.lockedCfgCopy.length; i++) { delete me[me.lockedCfgCopy[i]]; } - + + me.addEvents( + /** + * @event lockcolumn + * Fires when a column is locked. + * @param {Ext.grid.Panel} this The gridpanel. + * @param {Ext.grid.column.Column} column The column being locked. + */ + 'lockcolumn', + + /** + * @event unlockcolumn + * Fires when a column is unlocked. + * @param {Ext.grid.Panel} this The gridpanel. + * @param {Ext.grid.column.Column} column The column being unlocked. + */ + 'unlockcolumn' + ); + + me.addStateEvents(['lockcolumn', 'unlockcolumn']); + me.lockedHeights = []; me.normalHeights = []; - + columns = me.processColumns(me.columns); - lockedGrid.width = columns.lockedWidth; + lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0); lockedGrid.columns = columns.locked; normalGrid.columns = columns.normal; - + me.store = Ext.StoreManager.lookup(me.store); lockedGrid.store = me.store; normalGrid.store = me.store; - + // normal grid should flex the rest of the width normalGrid.flex = 1; lockedGrid.viewConfig = me.lockedViewConfig || {}; lockedGrid.viewConfig.loadingUseMsg = false; normalGrid.viewConfig = me.normalViewConfig || {}; - + Ext.applyIf(lockedGrid.viewConfig, me.viewConfig); Ext.applyIf(normalGrid.viewConfig, me.viewConfig); - + me.normalGrid = Ext.ComponentManager.create(normalGrid); me.lockedGrid = Ext.ComponentManager.create(lockedGrid); - + me.view = Ext.create('Ext.grid.LockingView', { locked: me.lockedGrid, normal: me.normalGrid, - panel: me + panel: me }); - + if (me.syncRowHeight) { me.lockedGrid.getView().on({ refresh: me.onLockedGridAfterRefresh, itemupdate: me.onLockedGridAfterUpdate, scope: me }); - + me.normalGrid.getView().on({ refresh: me.onNormalGridAfterRefresh, itemupdate: me.onNormalGridAfterUpdate, scope: me }); } - + lockedHeaderCt = me.lockedGrid.headerCt; normalHeaderCt = me.normalGrid.headerCt; - + lockedHeaderCt.lockedCt = true; lockedHeaderCt.lockableInjected = true; normalHeaderCt.lockableInjected = true; - + lockedHeaderCt.on({ columnshow: me.onLockedHeaderShow, columnhide: me.onLockedHeaderHide, @@ -107634,39 +112114,42 @@ Ext.define('Ext.grid.Lockable', { columnresize: me.onLockedHeaderResize, scope: me }); - + normalHeaderCt.on({ columnmove: me.onNormalHeaderMove, sortchange: me.onNormalHeaderSortChange, scope: me }); - + me.normalGrid.on({ scrollershow: me.onScrollerShow, scrollerhide: me.onScrollerHide, scope: me }); - + me.lockedGrid.on('afterlayout', me.onLockedGridAfterLayout, me, {single: true}); - + me.modifyHeaderCt(); me.items = [me.lockedGrid, me.normalGrid]; + me.relayHeaderCtEvents(lockedHeaderCt); + me.relayHeaderCtEvents(normalHeaderCt); + me.layout = { type: 'hbox', align: 'stretch' }; }, - + processColumns: function(columns){ // split apart normal and lockedWidths var i = 0, len = columns.length, - lockedWidth = 0, + lockedWidth = 1, lockedHeaders = [], normalHeaders = [], column; - + for (; i < len; ++i) { column = columns[i]; // mark the column as processed so that the locked attribute does not @@ -107676,60 +112159,71 @@ Ext.define('Ext.grid.Lockable', { if (column.flex) { Ext.Error.raise("Columns which are locked do NOT support a flex width. You must set a width on the " + columns[i].text + "column."); } - lockedWidth += column.width; + if (!column.hidden) { + lockedWidth += column.width || Ext.grid.header.Container.prototype.defaultWidth; + } lockedHeaders.push(column); } else { normalHeaders.push(column); } + if (!column.headerId) { + column.headerId = (column.initialConfig || column).id || ('L' + (++this.headerCounter)); + } } return { lockedWidth: lockedWidth, locked: lockedHeaders, - normal: normalHeaders + normal: normalHeaders }; }, - + // create a new spacer after the table is refreshed onLockedGridAfterLayout: function() { var me = this, lockedView = me.lockedGrid.getView(); lockedView.on({ - refresh: me.createSpacer, beforerefresh: me.destroySpacer, scope: me }); }, - + // trigger a pseudo refresh on the normal side onLockedHeaderMove: function() { if (this.syncRowHeight) { this.onNormalGridAfterRefresh(); } }, - + // trigger a pseudo refresh on the locked side onNormalHeaderMove: function() { if (this.syncRowHeight) { this.onLockedGridAfterRefresh(); } }, - + // create a spacer in lockedsection and store a reference // TODO: Should destroy before refreshing content - createSpacer: function() { + getSpacerEl: function() { var me = this, + w, + view, + el; + + if (!me.spacerEl) { // This affects scrolling all the way to the bottom of a locked grid // additional test, sort a column and make sure it synchronizes - w = Ext.getScrollBarWidth() + (Ext.isIE ? 2 : 0), - view = me.lockedGrid.getView(), + w = Ext.getScrollBarWidth() + (Ext.isIE ? 2 : 0); + view = me.lockedGrid.getView(); el = view.el; - me.spacerEl = Ext.core.DomHelper.append(el, { - cls: me.spacerHidden ? (Ext.baseCSSPrefix + 'hidden') : '', - style: 'height: ' + w + 'px;' - }, true); + me.spacerEl = Ext.DomHelper.append(el, { + cls: me.spacerHidden ? (Ext.baseCSSPrefix + 'hidden') : '', + style: 'height: ' + w + 'px;' + }, true); + } + return me.spacerEl; }, - + destroySpacer: function() { var me = this; if (me.spacerEl) { @@ -107737,7 +112231,7 @@ Ext.define('Ext.grid.Lockable', { delete me.spacerEl; } }, - + // cache the heights of all locked rows and sync rowheights onLockedGridAfterRefresh: function() { var me = this, @@ -107746,16 +112240,16 @@ Ext.define('Ext.grid.Lockable', { rowEls = el.query(view.getItemSelector()), ln = rowEls.length, i = 0; - + // reset heights each time. me.lockedHeights = []; - + for (; i < ln; i++) { me.lockedHeights[i] = rowEls[i].clientHeight; } me.syncRowHeights(); }, - + // cache the heights of all normal rows and sync rowheights onNormalGridAfterRefresh: function() { var me = this, @@ -107764,28 +112258,28 @@ Ext.define('Ext.grid.Lockable', { rowEls = el.query(view.getItemSelector()), ln = rowEls.length, i = 0; - + // reset heights each time. me.normalHeights = []; - + for (; i < ln; i++) { me.normalHeights[i] = rowEls[i].clientHeight; } me.syncRowHeights(); }, - + // rows can get bigger/smaller onLockedGridAfterUpdate: function(record, index, node) { this.lockedHeights[index] = node.clientHeight; this.syncRowHeights(); }, - + // rows can get bigger/smaller onNormalGridAfterUpdate: function(record, index, node) { this.normalHeights[index] = node.clientHeight; this.syncRowHeights(); }, - + // match the rowheights to the biggest rowheight on either // side syncRowHeights: function() { @@ -107822,9 +112316,9 @@ Ext.define('Ext.grid.Lockable', { // invalidate the scroller and sync the scrollers me.normalGrid.invalidateScroller(); - + // synchronize the view with the scroller, if we have a virtualScrollTop - // then the user is using a PagingScroller + // then the user is using a PagingScroller if (vertScroller && vertScroller.setViewScrollTop) { vertScroller.setViewScrollTop(me.virtualScrollTop); } else { @@ -107835,55 +112329,56 @@ Ext.define('Ext.grid.Lockable', { normalView.el.dom.scrollTop = scrollTop; lockedView.el.dom.scrollTop = scrollTop; } - + // reset the heights me.lockedHeights = []; me.normalHeights = []; } }, - + // track when scroller is shown onScrollerShow: function(scroller, direction) { if (direction === 'horizontal') { this.spacerHidden = false; - this.spacerEl.removeCls(Ext.baseCSSPrefix + 'hidden'); + this.getSpacerEl().removeCls(Ext.baseCSSPrefix + 'hidden'); } }, - + // track when scroller is hidden onScrollerHide: function(scroller, direction) { if (direction === 'horizontal') { this.spacerHidden = true; - this.spacerEl.addCls(Ext.baseCSSPrefix + 'hidden'); + if (this.spacerEl) { + this.spacerEl.addCls(Ext.baseCSSPrefix + 'hidden'); + } } }, - + // inject Lock and Unlock text modifyHeaderCt: function() { var me = this; me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(true); me.normalGrid.headerCt.getMenuItems = me.getMenuItems(false); }, - + onUnlockMenuClick: function() { this.unlock(); }, - + onLockMenuClick: function() { this.lock(); }, - + getMenuItems: function(locked) { var me = this, unlockText = me.unlockText, lockText = me.lockText, - // TODO: Refactor to use Ext.baseCSSPrefix - unlockCls = 'xg-hmenu-unlock', - lockCls = 'xg-hmenu-lock', + unlockCls = Ext.baseCSSPrefix + 'hmenu-unlock', + lockCls = Ext.baseCSSPrefix + 'hmenu-lock', unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me), lockHandler = Ext.Function.bind(me.onLockMenuClick, me); - + // runs in the scope of headerCt return function() { var o = Ext.grid.header.Container.prototype.getMenuItems.call(this); @@ -107902,7 +112397,7 @@ Ext.define('Ext.grid.Lockable', { return o; }; }, - + // going from unlocked section to locked /** * Locks the activeHeader as determined by which menu is open OR a header @@ -107917,18 +112412,19 @@ Ext.define('Ext.grid.Lockable', { lockedGrid = me.lockedGrid, normalHCt = normalGrid.headerCt, lockedHCt = lockedGrid.headerCt; - + activeHd = activeHd || normalHCt.getMenu().activeHeader; - + // if column was previously flexed, get/set current width // and remove the flex if (activeHd.flex) { activeHd.width = activeHd.getWidth(); delete activeHd.flex; } - + normalHCt.remove(activeHd, false); lockedHCt.suspendLayout = true; + activeHd.locked = true; if (Ext.isDefined(toIdx)) { lockedHCt.insert(toIdx, activeHd); } else { @@ -107936,35 +112432,38 @@ Ext.define('Ext.grid.Lockable', { } lockedHCt.suspendLayout = false; me.syncLockedSection(); + + me.fireEvent('lockcolumn', me, activeHd); }, - + syncLockedSection: function() { var me = this; me.syncLockedWidth(); me.lockedGrid.getView().refresh(); me.normalGrid.getView().refresh(); }, - + // adjust the locked section to the width of its respective // headerCt syncLockedWidth: function() { var me = this, width = me.lockedGrid.headerCt.getFullWidth(true); - me.lockedGrid.setWidth(width); + me.lockedGrid.setWidth(width+1); // +1 for border pixel + me.doComponentLayout(); }, - + onLockedHeaderResize: function() { this.syncLockedWidth(); }, - + onLockedHeaderHide: function() { this.syncLockedWidth(); }, - + onLockedHeaderShow: function() { this.syncLockedWidth(); }, - + onLockedHeaderSortChange: function(headerCt, header, sortState) { if (sortState) { // no real header, and silence the event so we dont get into an @@ -107972,7 +112471,7 @@ Ext.define('Ext.grid.Lockable', { this.normalGrid.headerCt.clearOtherSortStates(null, true); } }, - + onNormalHeaderSortChange: function(headerCt, header, sortState) { if (sortState) { // no real header, and silence the event so we dont get into an @@ -107980,7 +112479,7 @@ Ext.define('Ext.grid.Lockable', { this.lockedGrid.headerCt.clearOtherSortStates(null, true); } }, - + // going from locked section to unlocked /** * Unlocks the activeHeader as determined by which menu is open OR a header @@ -108000,30 +112499,97 @@ Ext.define('Ext.grid.Lockable', { toIdx = 0; } activeHd = activeHd || lockedHCt.getMenu().activeHeader; - + lockedHCt.remove(activeHd, false); me.syncLockedWidth(); me.lockedGrid.getView().refresh(); + activeHd.locked = false; normalHCt.insert(toIdx, activeHd); me.normalGrid.getView().refresh(); + + me.fireEvent('unlockcolumn', me, activeHd); }, - + + applyColumnsState: function (columns) { + var me = this, + lockedGrid = me.lockedGrid, + lockedHeaderCt = lockedGrid.headerCt, + normalHeaderCt = me.normalGrid.headerCt, + lockedCols = lockedHeaderCt.items, + normalCols = normalHeaderCt.items, + existing, + locked = [], + normal = [], + lockedDefault, + lockedWidth = 1; + + Ext.each(columns, function (col) { + function matches (item) { + return item.headerId == col.id; + } + + lockedDefault = true; + if (!(existing = lockedCols.findBy(matches))) { + existing = normalCols.findBy(matches); + lockedDefault = false; + } + + if (existing) { + if (existing.applyColumnState) { + existing.applyColumnState(col); + } + if (!Ext.isDefined(existing.locked)) { + existing.locked = lockedDefault; + } + if (existing.locked) { + locked.push(existing); + if (!existing.hidden && Ext.isNumber(existing.width)) { + lockedWidth += existing.width; + } + } else { + normal.push(existing); + } + } + }); + + // state and config must have the same columns (compare counts for now): + if (locked.length + normal.length == lockedCols.getCount() + normalCols.getCount()) { + lockedHeaderCt.removeAll(false); + normalHeaderCt.removeAll(false); + + lockedHeaderCt.add(locked); + normalHeaderCt.add(normal); + + lockedGrid.setWidth(lockedWidth); + } + }, + + getColumnsState: function () { + var me = this, + locked = me.lockedGrid.headerCt.getColumnsState(), + normal = me.normalGrid.headerCt.getColumnsState(); + + return locked.concat(normal); + }, + // we want to totally override the reconfigure behaviour here, since we're creating 2 sub-grids reconfigureLockable: function(store, columns) { var me = this, lockedGrid = me.lockedGrid, normalGrid = me.normalGrid; - + if (columns) { + lockedGrid.headerCt.suspendLayout = true; + normalGrid.headerCt.suspendLayout = true; lockedGrid.headerCt.removeAll(); normalGrid.headerCt.removeAll(); - + columns = me.processColumns(columns); lockedGrid.setWidth(columns.lockedWidth); lockedGrid.headerCt.add(columns.locked); normalGrid.headerCt.add(columns.normal); } - + if (store) { store = Ext.data.StoreManager.lookup(store); me.store = store; @@ -108033,23 +112599,25 @@ Ext.define('Ext.grid.Lockable', { lockedGrid.getView().refresh(); normalGrid.getView().refresh(); } + + if (columns) { + lockedGrid.headerCt.suspendLayout = false; + normalGrid.headerCt.suspendLayout = false; + lockedGrid.headerCt.forceComponentLayout(); + normalGrid.headerCt.forceComponentLayout(); + } } }); /** - * @class Ext.grid.Scroller - * @extends Ext.Component - * * Docked in an Ext.grid.Panel, controls virtualized scrolling and synchronization * across different sections. - * - * @private */ Ext.define('Ext.grid.Scroller', { extend: 'Ext.Component', alias: 'widget.gridscroller', weight: 110, - cls: Ext.baseCSSPrefix + 'scroller', + baseCls: Ext.baseCSSPrefix + 'scroller', focusable: false, reservedSpace: 0, @@ -108062,23 +112630,21 @@ Ext.define('Ext.grid.Scroller', { initComponent: function() { var me = this, dock = me.dock, - cls = Ext.baseCSSPrefix + 'scroller-vertical', - sizeProp = 'width'; + cls = Ext.baseCSSPrefix + 'scroller-vertical'; me.offsets = {bottom: 0}; me.scrollProp = 'scrollTop'; me.vertical = true; + me.sizeProp = 'width'; if (dock === 'top' || dock === 'bottom') { cls = Ext.baseCSSPrefix + 'scroller-horizontal'; - sizeProp = 'height'; + me.sizeProp = 'height'; me.scrollProp = 'scrollLeft'; me.vertical = false; me.weight += 5; } - me[sizeProp] = me.scrollerSize = Ext.getScrollbarSize()[sizeProp]; - me.cls += (' ' + cls); Ext.applyIf(me.renderSelectors, { @@ -108087,6 +112653,13 @@ Ext.define('Ext.grid.Scroller', { }); me.callParent(); }, + + ensureDimension: function(){ + var me = this, + sizeProp = me.sizeProp; + + me[sizeProp] = me.scrollerSize = Ext.getScrollbarSize()[sizeProp]; + }, initRenderData: function () { var me = this, @@ -108314,8 +112887,6 @@ Ext.define('Ext.grid.Scroller', { /** * @class Ext.grid.PagingScroller * @extends Ext.grid.Scroller - * - * @private */ Ext.define('Ext.grid.PagingScroller', { extend: 'Ext.grid.Scroller', @@ -108351,8 +112922,8 @@ Ext.define('Ext.grid.PagingScroller', { var me = this, ds = me.store; - ds.on('guaranteedrange', this.onGuaranteedRange, this); - this.callParent(arguments); + ds.on('guaranteedrange', me.onGuaranteedRange, me); + me.callParent(arguments); }, onGuaranteedRange: function(range, start, end) { @@ -108370,12 +112941,15 @@ Ext.define('Ext.grid.PagingScroller', { if (me.rendered) { me.invalidate(); } else { - me.on('afterrender', this.invalidate, this, {single: true}); + me.on('afterrender', me.invalidate, me, {single: true}); } me.firstLoad = true; } else { // adjust to visible - me.syncTo(); + // only sync if there is a paging scrollbar element and it has a scroll height (meaning it's currently in the DOM) + if (me.scrollEl && me.scrollEl.dom && me.scrollEl.dom.scrollHeight) { + me.syncTo(); + } } }, @@ -108390,6 +112964,7 @@ Ext.define('Ext.grid.PagingScroller', { clientHeight = scrollerElDom.clientHeight, scrollTop = scrollerElDom.scrollTop, useMaximum; + // BrowserBug: clientHeight reports 0 in IE9 StrictMode // Instead we are using offsetHeight and hardcoding borders @@ -108425,7 +113000,7 @@ Ext.define('Ext.grid.PagingScroller', { guaranteedStart = store.guaranteedStart, guaranteedEnd = store.guaranteedEnd, totalCount = store.getTotalCount(), - numFromEdge = Math.ceil(me.percentageFromEdge * store.pageSize), + numFromEdge = Math.ceil(me.percentageFromEdge * pageSize), position = t.scrollTop, visibleStart = Math.floor(position / me.rowHeight), view = panel.down('tableview'), @@ -108433,30 +113008,41 @@ Ext.define('Ext.grid.PagingScroller', { visibleHeight = viewEl.getHeight(), visibleAhead = Math.ceil(visibleHeight / me.rowHeight), visibleEnd = visibleStart + visibleAhead, - prevPage = Math.floor(visibleStart / store.pageSize), - nextPage = Math.floor(visibleEnd / store.pageSize) + 2, - lastPage = Math.ceil(totalCount / store.pageSize), - //requestStart = visibleStart, - requestStart = Math.floor(visibleStart / me.snapIncrement) * me.snapIncrement, + prevPage = Math.floor(visibleStart / pageSize), + nextPage = Math.floor(visibleEnd / pageSize) + 2, + lastPage = Math.ceil(totalCount / pageSize), + snap = me.snapIncrement, + requestStart = Math.floor(visibleStart / snap) * snap, requestEnd = requestStart + pageSize - 1, activePrefetch = me.activePrefetch; me.visibleStart = visibleStart; me.visibleEnd = visibleEnd; - + + me.syncScroll = true; if (totalCount >= pageSize) { // end of request was past what the total is, grab from the end back a pageSize if (requestEnd > totalCount - 1) { - this.cancelLoad(); + me.cancelLoad(); if (store.rangeSatisfied(totalCount - pageSize, totalCount - 1)) { me.syncScroll = true; } store.guaranteeRange(totalCount - pageSize, totalCount - 1); // Out of range, need to reset the current data set - } else if (visibleStart < guaranteedStart || visibleEnd > guaranteedEnd) { + } else if (visibleStart <= guaranteedStart || visibleEnd > guaranteedEnd) { + if (visibleStart <= guaranteedStart) { + // need to scroll up + requestStart -= snap; + requestEnd -= snap; + + if (requestStart < 0) { + requestStart = 0; + requestEnd = pageSize; + } + } if (store.rangeSatisfied(requestStart, requestEnd)) { - this.cancelLoad(); + me.cancelLoad(); store.guaranteeRange(requestStart, requestEnd); } else { store.mask(); @@ -108481,20 +113067,21 @@ Ext.define('Ext.grid.PagingScroller', { getSizeCalculation: function() { // Use the direct ownerCt here rather than the scrollerOwner // because we are calculating widths/heights. - var owner = this.ownerGrid, + var me = this, + owner = me.ownerGrid, view = owner.getView(), - store = this.store, - dock = this.dock, - elDom = this.el.dom, + store = me.store, + dock = me.dock, + elDom = me.el.dom, width = 1, height = 1; - if (!this.rowHeight) { - this.rowHeight = view.el.down(view.getItemSelector()).getHeight(false, true); + if (!me.rowHeight) { + me.rowHeight = view.el.down(view.getItemSelector()).getHeight(false, true); } // If the Store is *locally* filtered, use the filtered count from getCount. - height = store[(!store.remoteFilter && store.isFiltered()) ? 'getCount' : 'getTotalCount']() * this.rowHeight; + height = store[(!store.remoteFilter && store.isFiltered()) ? 'getCount' : 'getTotalCount']() * me.rowHeight; if (isNaN(width)) { width = 1; @@ -108528,7 +113115,8 @@ Ext.define('Ext.grid.PagingScroller', { }, setViewScrollTop: function(scrollTop, useMax) { - var owner = this.getPanel(), + var me = this, + owner = me.getPanel(), items = owner.query('tableview'), i = 0, len = items.length, @@ -108536,15 +113124,15 @@ Ext.define('Ext.grid.PagingScroller', { centerEl, calcScrollTop, maxScrollTop, - scrollerElDom = this.el.dom; + scrollerElDom = me.el.dom; owner.virtualScrollTop = scrollTop; center = items[1] || items[0]; centerEl = center.el.dom; - maxScrollTop = ((owner.store.pageSize * this.rowHeight) - centerEl.clientHeight); - calcScrollTop = (scrollTop % ((owner.store.pageSize * this.rowHeight) + 1)); + maxScrollTop = ((owner.store.pageSize * me.rowHeight) - centerEl.clientHeight); + calcScrollTop = (scrollTop % ((owner.store.pageSize * me.rowHeight) + 1)); if (useMax) { calcScrollTop = maxScrollTop; } @@ -108558,11 +113146,11 @@ Ext.define('Ext.grid.PagingScroller', { } } }); + /** - * @class Ext.panel.Table - * @extends Ext.panel.Panel * @author Nicolas Ferrero - * TablePanel is the basis of both TreePanel and GridPanel. + * + * TablePanel is the basis of both {@link Ext.tree.Panel TreePanel} and {@link Ext.grid.Panel GridPanel}. * * TablePanel aggregates: * @@ -108571,7 +113159,6 @@ Ext.define('Ext.grid.PagingScroller', { * - a Store * - Scrollers * - Ext.grid.header.Container - * */ Ext.define('Ext.panel.Table', { extend: 'Ext.panel.Panel', @@ -108585,64 +113172,124 @@ Ext.define('Ext.panel.Table', { 'Ext.grid.Lockable' ], - cls: Ext.baseCSSPrefix + 'grid', + extraBaseCls: Ext.baseCSSPrefix + 'grid', extraBodyCls: Ext.baseCSSPrefix + 'grid-body', layout: 'fit', /** - * Boolean to indicate that a view has been injected into the panel. - * @property hasView + * @property {Boolean} hasView + * True to indicate that a view has been injected into the panel. */ hasView: false, // each panel should dictate what viewType and selType to use + /** + * @cfg {String} viewType + * An xtype of view to use. This is automatically set to 'gridview' by {@link Ext.grid.Panel Grid} + * and to 'treeview' by {@link Ext.tree.Panel Tree}. + */ viewType: null, + + /** + * @cfg {Object} viewConfig + * A config object that will be applied to the grid's UI view. Any of the config options available for + * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified. + */ + + /** + * @cfg {Ext.view.Table} view + * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to + * view (instead of creating an entire View instance). + */ + + /** + * @cfg {String} selType + * An xtype of selection model to use. Defaults to 'rowmodel'. This is used to create selection model if just + * a config object or nothing at all given in {@link #selModel} config. + */ selType: 'rowmodel', + /** + * @cfg {Ext.selection.Model/Object} selModel + * A {@link Ext.selection.Model selection model} instance or config object. In latter case the {@link #selType} + * config option determines to which type of selection model this config is applied. + */ + + /** + * @cfg {Boolean} multiSelect + * True to enable 'MULTI' selection mode on selection model. See {@link Ext.selection.Model#mode}. + */ + + /** + * @cfg {Boolean} simpleSelect + * True to enable 'SIMPLE' selection mode on selection model. See {@link Ext.selection.Model#mode}. + */ + + /** + * @cfg {Ext.data.Store} store (required) + * The {@link Ext.data.Store Store} the grid should use as its data source. + */ + /** * @cfg {Number} scrollDelta * Number of pixels to scroll when scrolling with mousewheel. - * Defaults to 40. */ scrollDelta: 40, /** * @cfg {String/Boolean} scroll - * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'. - * Defaults to true. + * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'. + * True implies 'both'. False implies 'none'. */ scroll: true, /** - * @cfg {Array} columns - * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each - * column definition provides the header text for the column, and a definition of where the data for that column comes from. + * @cfg {Ext.grid.column.Column[]} columns + * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this + * grid. Each column definition provides the header text for the column, and a definition of where the data for that + * column comes from. */ /** * @cfg {Boolean} forceFit - * Specify as true to force the columns to fit into the available width. Headers are first sized according to configuration, whether that be - * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used.. + * Ttrue to force the columns to fit into the available width. Headers are first sized according to configuration, + * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire + * content width is used. + */ + + /** + * @cfg {Ext.grid.feature.Feature[]} features + * An array of grid Features to be added to this grid. See {@link Ext.grid.feature.Feature} for usage. */ /** - * @cfg {Boolean} hideHeaders - * Specify as true to hide the headers. + * @cfg {Boolean} [hideHeaders=false] + * True to hide column headers. */ /** - * @cfg {Boolean} deferRowRender

        Defaults to true to enable deferred row rendering.

        - *

        This allows the GridView to execute a refresh quickly, with the expensive update of the row - * structure deferred so that layouts with GridPanels appear, and lay out more quickly.

        + * @cfg {Boolean} deferRowRender + * Defaults to true to enable deferred row rendering. + * + * This allows the View to execute a refresh quickly, with the expensive update of the row structure deferred so + * that layouts with GridPanels appear, and lay out more quickly. */ + deferRowRender: true, + /** * @cfg {Boolean} sortableColumns - * Defaults to true. Set to false to disable column sorting via clicking the - * header and via the Sorting menu items. + * False to disable column sorting via clicking the header and via the Sorting menu items. */ sortableColumns: true, + /** + * @cfg {Boolean} [enableLocking=false] + * True to enable locking support for this grid. Alternatively, locking will also be automatically + * enabled if any of the columns in the column configuration contain the locked config option. + */ + enableLocking: false, + verticalScrollDock: 'right', verticalScrollerType: 'gridscroller', @@ -108657,19 +113304,19 @@ Ext.define('Ext.panel.Table', { /** * @cfg {Boolean} enableColumnMove - * Defaults to true. Set to false to disable column dragging within this grid. + * False to disable column dragging within this grid. */ enableColumnMove: true, /** * @cfg {Boolean} enableColumnResize - * Defaults to true. Set to false to disable column resizing within this grid. + * False to disable column resizing within this grid. */ enableColumnResize: true, /** * @cfg {Boolean} enableColumnHide - * Defaults to true. Set to false to disable column hiding within this grid. + * False to disable column hiding within this grid. */ enableColumnHide: true, @@ -108677,9 +113324,6 @@ Ext.define('Ext.panel.Table', { if (!this.viewType) { Ext.Error.raise("You must specify a viewType config."); } - if (!this.store) { - Ext.Error.raise("You must specify a store config"); - } if (this.headers) { Ext.Error.raise("The headers config is not supported. Please specify columns instead."); } @@ -108693,19 +113337,13 @@ Ext.define('Ext.panel.Table', { view, border = me.border; - // We cannot buffer this because that will wait for the 30msec from afterLayout (or what - // ever event triggers it) and we may be in the middle of an animation; that is a bad - // time to injectView because it causes a layout (by calling add on the container). A - // throttled func will be called immediately on first call and then block subsequent - // (rapid fire) calls for 30msec before allowing another call to go through. Similar - // results, but the action moves from the trailing edge of the interval to the leading - // edge. - me.injectView = Ext.Function.createThrottled(me.injectView, 30, me); - if (me.hideHeaders) { border = false; } + // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store. + me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'); + // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer // Either way, we extract a columns property referencing an array of Column definitions. if (headerCtCfg instanceof Ext.grid.header.Container) { @@ -108731,30 +113369,35 @@ Ext.define('Ext.panel.Table', { // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a // special view will be injected by the Ext.grid.Lockable mixin, so no processing of . - if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) { + if (me.enableLocking || Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) { me.self.mixin('lockable', Ext.grid.Lockable); me.injectLockable(); } } - me.store = Ext.data.StoreManager.lookup(me.store); me.addEvents( /** * @event reconfigure - * Fires after a reconfigure + * Fires after a reconfigure. * @param {Ext.panel.Table} this */ 'reconfigure', + /** + * @event viewready + * Fires when the grid view is available (use this for selecting a default row). + * @param {Ext.panel.Table} this + */ + 'viewready', /** * @event scrollerhide - * Fires when a scroller is hidden + * Fires when a scroller is hidden. * @param {Ext.grid.Scroller} scroller * @param {String} orientation Orientation, can be 'vertical' or 'horizontal' */ 'scrollerhide', /** * @event scrollershow - * Fires when a scroller is shown + * Fires when a scroller is shown. * @param {Ext.grid.Scroller} scroller * @param {String} orientation Orientation, can be 'vertical' or 'horizontal' */ @@ -108763,6 +113406,9 @@ Ext.define('Ext.panel.Table', { me.bodyCls = me.bodyCls || ''; me.bodyCls += (' ' + me.extraBodyCls); + + me.cls = me.cls || ''; + me.cls += (' ' + me.extraBaseCls); // autoScroll is not a valid configuration delete me.autoScroll; @@ -108822,9 +113468,12 @@ Ext.define('Ext.panel.Table', { }); } - me.headerCt.on('columnresize', me.onHeaderResize, me); - me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']); + me.headerCt.on('resize', me.onHeaderResize, me); + me.relayHeaderCtEvents(me.headerCt); me.features = me.features || []; + if (!Ext.isArray(me.features)) { + me.features = [me.features]; + } me.dockedItems = me.dockedItems || []; me.dockedItems.unshift(me.headerCt); me.viewConfig = me.viewConfig || {}; @@ -108834,268 +113483,215 @@ Ext.define('Ext.panel.Table', { // getView converts viewConfig into a View instance view = me.getView(); - if (view) { - me.mon(view.store, { - load: me.onStoreLoad, - scope: me - }); - me.mon(view, { - refresh: me.onViewRefresh, - scope: me - }); - this.relayEvents(view, [ - /** - * @event beforeitemmousedown - * Fires before the mousedown event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmousedown', - /** - * @event beforeitemmouseup - * Fires before the mouseup event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmouseup', - /** - * @event beforeitemmouseenter - * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmouseenter', - /** - * @event beforeitemmouseleave - * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemmouseleave', - /** - * @event beforeitemclick - * Fires before the click event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemclick', - /** - * @event beforeitemdblclick - * Fires before the dblclick event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemdblclick', - /** - * @event beforeitemcontextmenu - * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'beforeitemcontextmenu', - /** - * @event itemmousedown - * Fires when there is a mouse down on an item - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmousedown', - /** - * @event itemmouseup - * Fires when there is a mouse up on an item - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmouseup', - /** - * @event itemmouseenter - * Fires when the mouse enters an item. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmouseenter', - /** - * @event itemmouseleave - * Fires when the mouse leaves an item. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemmouseleave', - /** - * @event itemclick - * Fires when an item is clicked. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemclick', - /** - * @event itemdblclick - * Fires when an item is double clicked. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemdblclick', - /** - * @event itemcontextmenu - * Fires when an item is right clicked. - * @param {Ext.view.View} this - * @param {Ext.data.Model} record The record that belongs to the item - * @param {HTMLElement} item The item's element - * @param {Number} index The item's index - * @param {Ext.EventObject} e The raw event object - */ - 'itemcontextmenu', - /** - * @event beforecontainermousedown - * Fires before the mousedown event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermousedown', - /** - * @event beforecontainermouseup - * Fires before the mouseup event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermouseup', - /** - * @event beforecontainermouseover - * Fires before the mouseover event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermouseover', - /** - * @event beforecontainermouseout - * Fires before the mouseout event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainermouseout', - /** - * @event beforecontainerclick - * Fires before the click event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainerclick', - /** - * @event beforecontainerdblclick - * Fires before the dblclick event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainerdblclick', - /** - * @event beforecontainercontextmenu - * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'beforecontainercontextmenu', - /** - * @event containermouseup - * Fires when there is a mouse up on the container - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containermouseup', - /** - * @event containermouseover - * Fires when you move the mouse over the container. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containermouseover', - /** - * @event containermouseout - * Fires when you move the mouse out of the container. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containermouseout', - /** - * @event containerclick - * Fires when the container is clicked. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containerclick', - /** - * @event containerdblclick - * Fires when the container is double clicked. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containerdblclick', - /** - * @event containercontextmenu - * Fires when the container is right clicked. - * @param {Ext.view.View} this - * @param {Ext.EventObject} e The raw event object - */ - 'containercontextmenu', + view.on({ + afterrender: function () { + // hijack the view el's scroll method + view.el.scroll = Ext.Function.bind(me.elScroll, me); + // We use to listen to document.body wheel events, but that's a + // little much. We scope just to the view now. + me.mon(view.el, { + mousewheel: me.onMouseWheel, + scope: me + }); + }, + single: true + }); + me.items = [view]; + me.hasView = true; - /** - * @event selectionchange - * Fires when the selected nodes change. Relayed event from the underlying selection model. - * @param {Ext.view.View} this - * @param {Array} selections Array of the selected nodes - */ - 'selectionchange', - /** - * @event beforeselect - * Fires before a selection is made. If any handlers return false, the selection is cancelled. - * @param {Ext.view.View} this - * @param {HTMLElement} node The node to be selected - * @param {Array} selections Array of currently selected nodes - */ - 'beforeselect' - ]); - } + me.mon(view.store, { + load: me.onStoreLoad, + scope: me + }); + me.mon(view, { + viewReady: me.onViewReady, + resize: me.onViewResize, + refresh: { + fn: me.onViewRefresh, + scope: me, + buffer: 50 + }, + scope: me + }); + this.relayEvents(view, [ + /** + * @event beforeitemmousedown + * @alias Ext.view.View#beforeitemmousedown + */ + 'beforeitemmousedown', + /** + * @event beforeitemmouseup + * @alias Ext.view.View#beforeitemmouseup + */ + 'beforeitemmouseup', + /** + * @event beforeitemmouseenter + * @alias Ext.view.View#beforeitemmouseenter + */ + 'beforeitemmouseenter', + /** + * @event beforeitemmouseleave + * @alias Ext.view.View#beforeitemmouseleave + */ + 'beforeitemmouseleave', + /** + * @event beforeitemclick + * @alias Ext.view.View#beforeitemclick + */ + 'beforeitemclick', + /** + * @event beforeitemdblclick + * @alias Ext.view.View#beforeitemdblclick + */ + 'beforeitemdblclick', + /** + * @event beforeitemcontextmenu + * @alias Ext.view.View#beforeitemcontextmenu + */ + 'beforeitemcontextmenu', + /** + * @event itemmousedown + * @alias Ext.view.View#itemmousedown + */ + 'itemmousedown', + /** + * @event itemmouseup + * @alias Ext.view.View#itemmouseup + */ + 'itemmouseup', + /** + * @event itemmouseenter + * @alias Ext.view.View#itemmouseenter + */ + 'itemmouseenter', + /** + * @event itemmouseleave + * @alias Ext.view.View#itemmouseleave + */ + 'itemmouseleave', + /** + * @event itemclick + * @alias Ext.view.View#itemclick + */ + 'itemclick', + /** + * @event itemdblclick + * @alias Ext.view.View#itemdblclick + */ + 'itemdblclick', + /** + * @event itemcontextmenu + * @alias Ext.view.View#itemcontextmenu + */ + 'itemcontextmenu', + /** + * @event beforecontainermousedown + * @alias Ext.view.View#beforecontainermousedown + */ + 'beforecontainermousedown', + /** + * @event beforecontainermouseup + * @alias Ext.view.View#beforecontainermouseup + */ + 'beforecontainermouseup', + /** + * @event beforecontainermouseover + * @alias Ext.view.View#beforecontainermouseover + */ + 'beforecontainermouseover', + /** + * @event beforecontainermouseout + * @alias Ext.view.View#beforecontainermouseout + */ + 'beforecontainermouseout', + /** + * @event beforecontainerclick + * @alias Ext.view.View#beforecontainerclick + */ + 'beforecontainerclick', + /** + * @event beforecontainerdblclick + * @alias Ext.view.View#beforecontainerdblclick + */ + 'beforecontainerdblclick', + /** + * @event beforecontainercontextmenu + * @alias Ext.view.View#beforecontainercontextmenu + */ + 'beforecontainercontextmenu', + /** + * @event containermouseup + * @alias Ext.view.View#containermouseup + */ + 'containermouseup', + /** + * @event containermouseover + * @alias Ext.view.View#containermouseover + */ + 'containermouseover', + /** + * @event containermouseout + * @alias Ext.view.View#containermouseout + */ + 'containermouseout', + /** + * @event containerclick + * @alias Ext.view.View#containerclick + */ + 'containerclick', + /** + * @event containerdblclick + * @alias Ext.view.View#containerdblclick + */ + 'containerdblclick', + /** + * @event containercontextmenu + * @alias Ext.view.View#containercontextmenu + */ + 'containercontextmenu', + /** + * @event selectionchange + * @alias Ext.selection.Model#selectionchange + */ + 'selectionchange', + /** + * @event beforeselect + * @alias Ext.selection.RowModel#beforeselect + */ + 'beforeselect', + /** + * @event select + * @alias Ext.selection.RowModel#select + */ + 'select', + /** + * @event beforedeselect + * @alias Ext.selection.RowModel#beforedeselect + */ + 'beforedeselect', + /** + * @event deselect + * @alias Ext.selection.RowModel#deselect + */ + 'deselect' + ]); } + me.callParent(arguments); }, + + onRender: function(){ + var vScroll = this.verticalScroller, + hScroll = this.horizontalScroller; + + if (vScroll) { + vScroll.ensureDimension(); + } + if (hScroll) { + hScroll.ensureDimension(); + } + this.callParent(arguments); + }, // state management initStateEvents: function(){ @@ -109140,40 +113736,42 @@ Ext.define('Ext.panel.Table', { return ret; }, - getState: function(){ - var state = this.callParent(), - sorter = this.store.sorters.first(), - headers = this.headerCt.items.items, - header, - len = headers.length, - i = 0; + relayHeaderCtEvents: function (headerCt) { + this.relayEvents(headerCt, [ + /** + * @event columnresize + * @alias Ext.grid.header.Container#columnresize + */ + 'columnresize', + /** + * @event columnmove + * @alias Ext.grid.header.Container#columnmove + */ + 'columnmove', + /** + * @event columnhide + * @alias Ext.grid.header.Container#columnhide + */ + 'columnhide', + /** + * @event columnshow + * @alias Ext.grid.header.Container#columnshow + */ + 'columnshow', + /** + * @event sortchange + * @alias Ext.grid.header.Container#sortchange + */ + 'sortchange' + ]); + }, - state.columns = []; - for (; i < len; i++) { - header = headers[i]; - state.columns[i] = { - id: header.headerId - }; + getState: function(){ + var me = this, + state = me.callParent(), + sorter = me.store.sorters.first(); - // We only store state which has changed from the initial state. - // So that current software settings do not override future software settings. - // Only user-changed state should be saved. - if (header.hidden !== (header.initialConfig.hidden||header.self.prototype.hidden)) { - state.columns[i].hidden = header.hidden; - } - if (header.sortable !== header.initialConfig.sortable) { - state.columns[i].sortable = header.sortable; - } - if (header.flex) { - if (header.flex !== header.initialConfig.flex) { - state.columns[i].flex = header.flex; - } - } else { - if (header.width !== header.initialConfig.width) { - state.columns[i].width = header.width; - } - } - } + state.columns = (me.headerCt || me).getColumnsState(); if (sorter) { state.sort = { @@ -109181,60 +113779,25 @@ Ext.define('Ext.panel.Table', { direction: sorter.direction }; } + return state; }, applyState: function(state) { - var headers = state.columns, - length = headers ? headers.length : 0, - headerCt = this.headerCt, - items = headerCt.items, + var me = this, sorter = state.sort, - store = this.store, - i = 0, - index, - headerState, - header; + store = me.store, + columns = state.columns; - headerCt.suspendLayout = true; + delete state.columns; // Ensure superclass has applied *its* state. // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state. - this.callParent(arguments); + me.callParent(arguments); - for (; i < length; ++i) { - headerState = headers[i]; - header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']'); - index = items.indexOf(header); - if (i !== index) { - headerCt.moveHeader(index, i); - } - - // Only state properties which were saved should be restored. - // (Only user-changed properties were saved by getState) - if (Ext.isDefined(headerState.hidden)) { - header.hidden = headerState.hidden; - } - if (Ext.isDefined(headerState.sortable)) { - header.sortable = headerState.sortable; - } - if (Ext.isDefined(headerState.flex)) { - delete header.width; - header.flex = headerState.flex; - } else if (Ext.isDefined(headerState.width)) { - delete header.flex; - header.minWidth = headerState.width; - if (header.rendered) { - header.setWidth(headerState.width); - } else { - header.width = headerState.width; - } - } + if (columns) { + (me.headerCt || me).applyColumnsState(columns); } - headerCt.suspendLayout = false; - - // After setting width and flexes while layout is suspended, column Container's Container layout needs running. - headerCt.doLayout(); if (sorter) { if (store.remoteSort) { @@ -109268,7 +113831,7 @@ Ext.define('Ext.panel.Table', { if (!me.view) { sm = me.getSelectionModel(); me.view = me.createComponent(Ext.apply({}, me.viewConfig, { - deferRowRender: me.deferRowRender, + deferInitialRefresh: me.deferRowRender, xtype: me.viewType, store: me.store, headerCt: me.headerCt, @@ -109304,71 +113867,33 @@ Ext.define('Ext.panel.Table', { if (direction === "up" || direction === "left") { distance = -distance; } - + if (direction === "down" || direction === "up") { scroller = me.getVerticalScroller(); - scroller.scrollByDeltaY(distance); + + //if the grid does not currently need a vertical scroller don't try to update it (EXTJSIV-3891) + if (scroller) { + scroller.scrollByDeltaY(distance); + } } else { scroller = me.getHorizontalScroller(); - scroller.scrollByDeltaX(distance); - } - }, - - /** - * @private - * Called after this Component has achieved its correct initial size, after all layouts have done their thing. - * This is so we can add the View only after the initial size is known. This method is throttled 30ms. - */ - injectView: function() { - if (!this.hasView && !this.collapsed) { - var me = this, - view = me.getView(); - - me.hasView = true; - me.add(view); - - function viewReady () { - // hijack the view el's scroll method - view.el.scroll = Ext.Function.bind(me.elScroll, me); - // We use to listen to document.body wheel events, but that's a - // little much. We scope just to the view now. - me.mon(view.el, { - mousewheel: me.onMouseWheel, - scope: me - }); - if (!me.height) { - me.doComponentLayout(); - } - } - - if (view.rendered) { - viewReady(); - } else { - view.on({ - afterrender: viewReady, - single: true - }); + + //if the grid does not currently need a horizontal scroller don't try to update it (EXTJSIV-3891) + if (scroller) { + scroller.scrollByDeltaX(distance); } } }, - afterExpand: function() { - // TODO - this is *not* called when part of an accordion! - this.callParent(arguments); - if (!this.hasView) { - this.injectView(); - } - }, - /** * @private - * Process UI events from the view. Propagate them to whatever internal Components need to process them + * Processes UI events from the view. Propagates them to whatever internal Components need to process them. * @param {String} type Event type, eg 'click' - * @param {TableView} view TableView Component - * @param {HtmlElement} cell Cell HtmlElement the event took place within + * @param {Ext.view.Table} view TableView Component + * @param {HTMLElement} cell Cell HtmlElement the event took place within * @param {Number} recordIndex Index of the associated Store Model (-1 if none) * @param {Number} cellIndex Cell index within the row - * @param {EventObject} e Original event + * @param {Ext.EventObject} e Original event */ processEvent: function(type, view, cell, recordIndex, cellIndex, e) { var me = this, @@ -109381,10 +113906,16 @@ Ext.define('Ext.panel.Table', { }, /** - * Request a recalculation of scrollbars and put them in if they are needed. + * Requests a recalculation of scrollbars and puts them in if they are needed. */ determineScrollbars: function() { + // Set a flag so that afterComponentLayout does not recurse back into here. + if (this.determineScrollbarsRunning) { + return; + } + this.determineScrollbarsRunning = true; var me = this, + view = me.view, box, tableEl, scrollWidth, @@ -109398,11 +113929,12 @@ Ext.define('Ext.panel.Table', { reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars - if (!me.collapsed && me.view && me.view.el && me.view.el.dom.firstChild) { + if (!me.collapsed && view && view.viewReady) { // Calculate maximum, *scrollbarless* space which the view has available. // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars - box = me.layout.getLayoutTargetSize(); + box = view.el.getSize(); + clientWidth = box.width + ((curScrollbars & 1) ? verticalScroller.width : 0); clientHeight = box.height + ((curScrollbars & 2) ? horizontalScroller.height : 0); @@ -109415,7 +113947,7 @@ Ext.define('Ext.panel.Table', { if (verticalScroller && verticalScroller.el) { scrollHeight = verticalScroller.getSizeCalculation().height; } else { - tableEl = me.view.el.child('table', true); + tableEl = view.el.child('table', true); scrollHeight = tableEl ? tableEl.offsetHeight : 0; } @@ -109461,31 +113993,27 @@ Ext.define('Ext.panel.Table', { } me.suspendLayout = false; - // After docked scrollers are correctly configured, lay out the Component. - // Set a flag so that afterComponentLayout does not recurse back into here. - me.changingScrollBars = true; - me.doComponentLayout(me.getWidth(), me.getHeight(), false, me.ownerCt); - me.changingScrollBars = false; + // Lay out the Component. + me.doComponentLayout(); + // Lay out me.items + me.getLayout().layout(); } } + delete me.determineScrollbarsRunning; }, - afterComponentLayout: function() { - var me = this; - me.callParent(arguments); - - // Insert the View the first time the Panel has a Component layout performed. - me.injectView(); + onViewResize: function() { + this.determineScrollbars(); + }, - // Avoid recursion - if (!me.changingScrollBars) { - me.determineScrollbars(); - } - me.invalidateScroller(); + afterComponentLayout: function() { + this.callParent(arguments); + this.determineScrollbars(); + this.invalidateScroller(); }, onHeaderResize: function() { - if (this.view && this.view.rendered) { + if (!this.componentLayout.layoutBusy && this.view && this.view.rendered) { this.determineScrollbars(); this.invalidateScroller(); } @@ -109514,7 +114042,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Hide the verticalScroller and remove the horizontalScrollerPresentCls. + * Hides the verticalScroller and removes the horizontalScrollerPresentCls. */ hideHorizontalScroller: function() { var me = this; @@ -109529,7 +114057,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Show the horizontalScroller and add the horizontalScrollerPresentCls. + * Shows the horizontalScroller and add the horizontalScrollerPresentCls. */ showHorizontalScroller: function() { var me = this; @@ -109545,7 +114073,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Hide the verticalScroller and remove the verticalScrollerPresentCls. + * Hides the verticalScroller and removes the verticalScrollerPresentCls. */ hideVerticalScroller: function() { var me = this; @@ -109559,7 +114087,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Show the verticalScroller and add the verticalScrollerPresentCls. + * Shows the verticalScroller and adds the verticalScrollerPresentCls. */ showVerticalScroller: function() { var me = this; @@ -109579,13 +114107,14 @@ Ext.define('Ext.panel.Table', { // only trigger a layout when reserveOffset is changing if (layout && layout.reserveOffset !== reserveOffset) { layout.reserveOffset = reserveOffset; - headerCt.doLayout(); + if (!this.suspendLayout) { + headerCt.doLayout(); + } } }, /** - * Invalides scrollers that are present and forces a recalculation. - * (Not related to showing/hiding the scrollers) + * Invalides scrollers that are present and forces a recalculation. (Not related to showing/hiding the scrollers) */ invalidateScroller: function() { var me = this, @@ -109626,7 +114155,7 @@ Ext.define('Ext.panel.Table', { var me = this, vertScroller = me.getVerticalScroller(), horizScroller = me.getHorizontalScroller(), - scrollDelta = me.scrollDelta / -5, + scrollDelta = -me.scrollDelta, deltas = e.getWheelDeltas(), deltaX = scrollDelta * deltas.x, deltaY = scrollDelta * deltas.y, @@ -109669,18 +114198,36 @@ Ext.define('Ext.panel.Table', { /** * @private - * Determine and invalidate scrollers on view refresh + * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready + */ + onViewReady: function() { + var me = this; + me.fireEvent('viewready', me); + if (me.deferRowRender) { + me.determineScrollbars(); + me.invalidateScroller(); + } + }, + + /** + * @private + * Determines and invalidates scrollers on view refresh */ onViewRefresh: function() { - this.determineScrollbars(); - if (this.invalidateScrollerOnRefresh) { - this.invalidateScroller(); + var me = this; + + // Refresh *during* render must be ignored. + if (!me.rendering) { + this.determineScrollbars(); + if (this.invalidateScrollerOnRefresh) { + this.invalidateScroller(); + } } }, /** * Sets the scrollTop of the TablePanel. - * @param {Number} deltaY + * @param {Number} top */ setScrollTop: function(top) { var me = this, @@ -109715,10 +114262,10 @@ Ext.define('Ext.panel.Table', { /** * Scrolls the TablePanel by deltaX - * @param {Number} deltaY + * @param {Number} deltaX */ scrollByDeltaX: function(deltaX) { - var horizontalScroller = this.getVerticalScroller(); + var horizontalScroller = this.getHorizontalScroller(); if (horizontalScroller) { horizontalScroller.scrollByDeltaX(deltaX); @@ -109726,14 +114273,14 @@ Ext.define('Ext.panel.Table', { }, /** - * Get left hand side marker for header resizing. + * Gets left hand side marker for header resizing. * @private */ getLhsMarker: function() { var me = this; if (!me.lhsMarker) { - me.lhsMarker = Ext.core.DomHelper.append(me.el, { + me.lhsMarker = Ext.DomHelper.append(me.el, { cls: Ext.baseCSSPrefix + 'grid-resize-marker' }, true); } @@ -109741,14 +114288,14 @@ Ext.define('Ext.panel.Table', { }, /** - * Get right hand side marker for header resizing. + * Gets right hand side marker for header resizing. * @private */ getRhsMarker: function() { var me = this; if (!me.rhsMarker) { - me.rhsMarker = Ext.core.DomHelper.append(me.el, { + me.rhsMarker = Ext.DomHelper.append(me.el, { cls: Ext.baseCSSPrefix + 'grid-resize-marker' }, true); } @@ -109756,8 +114303,7 @@ Ext.define('Ext.panel.Table', { }, /** - * Returns the selection model being used and creates it via the configuration - * if it has not been created already. + * Returns the selection model being used and creates it via the configuration if it has not been created already. * @return {Ext.selection.Model} selModel */ getSelectionModel: function(){ @@ -109830,12 +114376,20 @@ Ext.define('Ext.panel.Table', { me.store = store; me.getView().bindStore(store); }, + + beforeDestroy: function(){ + // may be some duplication here since the horizontal and vertical + // scroller may be part of the docked items, but we need to clean + // them up in case they aren't visible. + Ext.destroy(this.horizontalScroller, this.verticalScroller); + this.callParent(); + }, /** - * Reconfigure the table with a new store/column. - * Either the store or the column can be ommitted if you don't wish to change them. - * @param {Ext.data.Store} store The new store. - * @param {Array} columns An array of column configs + * Reconfigures the table with a new store/columns. Either the store or the columns can be ommitted if you don't wish + * to change them. + * @param {Ext.data.Store} store (Optional) The new store. + * @param {Object[]} columns (Optional) An array of column configs */ reconfigure: function(store, columns) { var me = this, @@ -109844,12 +114398,10 @@ Ext.define('Ext.panel.Table', { if (me.lockable) { me.reconfigureLockable(store, columns); } else { - headerCt.suspendLayout = true; - headerCt.removeAll(); if (columns) { + headerCt.suspendLayout = true; + headerCt.removeAll(); headerCt.add(columns); - } else { - headerCt.doLayout(); } if (store) { store = Ext.StoreManager.lookup(store); @@ -109858,6 +114410,7 @@ Ext.define('Ext.panel.Table', { me.getView().refresh(); } if (columns) { + headerCt.suspendLayout = false; me.forceComponentLayout(); } } @@ -109865,24 +114418,17 @@ Ext.define('Ext.panel.Table', { } }); /** - * @class Ext.view.Table - * @extends Ext.view.View - -This class encapsulates the user interface for a tabular data set. -It acts as a centralized manager for controlling the various interface -elements of the view. This includes handling events, such as row and cell -level based DOM events. It also reacts to events from the underlying {@link Ext.selection.Model} -to provide visual feedback to the user. - -This class does not provide ways to manipulate the underlying data of the configured -{@link Ext.data.Store}. - -This is the base class for both {@link Ext.grid.View} and {@link Ext.tree.View} and is not -to be used directly. - - * @markdown - * @abstract - * @author Nicolas Ferrero + * This class encapsulates the user interface for a tabular data set. + * It acts as a centralized manager for controlling the various interface + * elements of the view. This includes handling events, such as row and cell + * level based DOM events. It also reacts to events from the underlying {@link Ext.selection.Model} + * to provide visual feedback to the user. + * + * This class does not provide ways to manipulate the underlying data of the configured + * {@link Ext.data.Store}. + * + * This is the base class for both {@link Ext.grid.View} and {@link Ext.tree.View} and is not + * to be used directly. */ Ext.define('Ext.view.Table', { extend: 'Ext.view.View', @@ -109893,7 +114439,7 @@ Ext.define('Ext.view.Table', { 'Ext.util.MixedCollection' ], - cls: Ext.baseCSSPrefix + 'grid-view', + baseCls: Ext.baseCSSPrefix + 'grid-view', // row itemSelector: '.' + Ext.baseCSSPrefix + 'grid-row', @@ -109912,58 +114458,30 @@ Ext.define('Ext.view.Table', { trackOver: true, /** - * Override this function to apply custom CSS classes to rows during rendering. You can also supply custom - * parameters to the row template for the current row to customize how it is rendered using the rowParams - * parameter. This function should return the CSS class name (or empty string '' for none) that will be added - * to the row's wrapping div. To apply multiple class names, simply return them space-delimited within the string - * (e.g., 'my-class another-class'). Example usage: -
        
        -viewConfig: {
        -    forceFit: true,
        -    showPreview: true, // custom property
        -    enableRowBody: true, // required to create a second, full-width row to show expanded Record data
        -    getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
        -        if(this.showPreview){
        -            rp.body = '<p>'+record.data.excerpt+'</p>';
        -            return 'x-grid3-row-expanded';
        -        }
        -        return 'x-grid3-row-collapsed';
        -    }
        -},
        -    
        - * @param {Model} model The {@link Ext.data.Model} corresponding to the current row. + * Override this function to apply custom CSS classes to rows during rendering. This function should return the + * CSS class name (or empty string '' for none) that will be added to the row's wrapping div. To apply multiple + * class names, simply return them space-delimited within the string (e.g. 'my-class another-class'). + * Example usage: + * + * viewConfig: { + * getRowClass: function(record, rowIndex, rowParams, store){ + * return record.get("valid") ? "row-valid" : "row-error"; + * } + * } + * + * @param {Ext.data.Model} record The record corresponding to the current row. * @param {Number} index The row index. - * @param {Object} rowParams (DEPRECATED) A config object that is passed to the row template during rendering that allows - * customization of various aspects of a grid row. - *

        If {@link #enableRowBody} is configured true, then the following properties may be set - * by this function, and will be used to render a full-width expansion row below each grid row:

        - *
          - *
        • body : String
          An HTML fragment to be used as the expansion row's body content (defaults to '').
        • - *
        • bodyStyle : String
          A CSS style specification that will be applied to the expansion row's <tr> element. (defaults to '').
        • - *
        - * The following property will be passed in, and may be appended to: - *
          - *
        • tstyle : String
          A CSS style specification that willl be applied to the <table> element which encapsulates - * both the standard grid row, and any expansion row.
        • - *
        - * @param {Store} store The {@link Ext.data.Store} this grid is bound to - * @method getRowClass + * @param {Object} rowParams **DEPRECATED.** For row body use the + * {@link Ext.grid.feature.RowBody#getAdditionalData getAdditionalData} method of the rowbody feature. + * @param {Ext.data.Store} store The store this grid is bound to * @return {String} a CSS class name to add to the row. + * @method */ getRowClass: null, initComponent: function() { var me = this; - if (me.deferRowRender !== false) { - me.refresh = function() { - delete me.refresh; - setTimeout(function() { - me.refresh(); - }, 0); - }; - } - me.scrollState = {}; me.selModel.view = me; me.headerCt.view = me; @@ -109978,7 +114496,7 @@ viewConfig: { // this.addEvents( // /** // * @event rowfocus - // * @param {Ext.data.Record} record + // * @param {Ext.data.Model} record // * @param {HTMLElement} row // * @param {Number} rowIdx // */ @@ -110037,7 +114555,7 @@ viewConfig: { /** * Get the cell (td) for a particular record and column. * @param {Ext.data.Model} record - * @param {Ext.grid.column.Colunm} column + * @param {Ext.grid.column.Column} column * @private */ getCell: function(record, column) { @@ -110226,7 +114744,9 @@ viewConfig: { el.select('.' + Ext.baseCSSPrefix + 'grid-col-resizer-'+header.id).setWidth(w); el.select('.' + Ext.baseCSSPrefix + 'grid-table-resizer').setWidth(me.headerCt.getFullWidth()); me.restoreScrollState(); - me.setNewTemplate(); + if (!me.ignoreTemplate) { + me.setNewTemplate(); + } if (!suppressFocus) { me.el.focus(); } @@ -110238,17 +114758,20 @@ viewConfig: { * @private */ onHeaderShow: function(headerCt, header, suppressFocus) { + var me = this; + me.ignoreTemplate = true; // restore headers that were dynamically hidden if (header.oldWidth) { - this.onHeaderResize(header, header.oldWidth, suppressFocus); + me.onHeaderResize(header, header.oldWidth, suppressFocus); delete header.oldWidth; // flexed headers will have a calculated size set // this additional check has to do with the fact that // defaults: {width: 100} will fight with a flex value } else if (header.width && !header.flex) { - this.onHeaderResize(header, header.width, suppressFocus); + me.onHeaderResize(header, header.width, suppressFocus); } - this.setNewTemplate(); + delete me.ignoreTemplate; + me.setNewTemplate(); }, /** @@ -110275,15 +114798,16 @@ viewConfig: { }, /** - * Get the configured chunker or default of Ext.view.TableChunker + * Returns the configured chunker or default of Ext.view.TableChunker */ getTableChunker: function() { return this.chunker || Ext.view.TableChunker; }, /** - * Add a CSS Class to a specific row. - * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model representing this row + * Adds a CSS Class to a specific row. + * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model + * representing this row * @param {String} cls */ addRowCls: function(rowInfo, cls) { @@ -110294,8 +114818,9 @@ viewConfig: { }, /** - * Remove a CSS Class from a specific row. - * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model representing this row + * Removes a CSS Class from a specific row. + * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model + * representing this row * @param {String} cls */ removeRowCls: function(rowInfo, cls) { @@ -110372,9 +114897,10 @@ viewConfig: { }, /** - * Focus a particular row and bring it into view. Will fire the rowfocus event. - * @param {Mixed} rowIdx An HTMLElement template node, index of a template node, the - * id of a template node or the record associated with the node. + * Focuses a particular row and brings it into view. Will fire the rowfocus event. + * @param {HTMLElement/String/Number/Ext.data.Model} rowIdx + * An HTMLElement template node, index of a template node, the id of a template node or the + * record associated with the node. */ focusRow: function(rowIdx) { var me = this, @@ -110449,7 +114975,7 @@ viewConfig: { }, /** - * Scroll by delta. This affects this individual view ONLY and does not + * Scrolls by delta. This affects this individual view ONLY and does not * synchronize across views or scrollers. * @param {Number} delta * @param {String} dir (optional) Valid values are scrollTop and scrollLeft. Defaults to scrollTop. @@ -110466,35 +114992,37 @@ viewConfig: { }, /** - * Save the scrollState in a private variable. - * Must be used in conjunction with restoreScrollState + * Saves the scrollState in a private variable. Must be used in conjunction with restoreScrollState */ saveScrollState: function() { - var dom = this.el.dom, - state = this.scrollState; - - state.left = dom.scrollLeft; - state.top = dom.scrollTop; + if (this.rendered) { + var dom = this.el.dom, + state = this.scrollState; + + state.left = dom.scrollLeft; + state.top = dom.scrollTop; + } }, /** - * Restore the scrollState. + * Restores the scrollState. * Must be used in conjunction with saveScrollState * @private */ restoreScrollState: function() { - var dom = this.el.dom, - state = this.scrollState, - headerEl = this.headerCt.el.dom; - - headerEl.scrollLeft = dom.scrollLeft = state.left; - dom.scrollTop = state.top; + if (this.rendered) { + var dom = this.el.dom, + state = this.scrollState, + headerEl = this.headerCt.el.dom; + + headerEl.scrollLeft = dom.scrollLeft = state.left; + dom.scrollTop = state.top; + } }, /** - * Refresh the grid view. - * Saves and restores the scroll state, generates a new template, stripes rows - * and invalidates the scrollers. + * Refreshes the grid view. Saves and restores the scroll state, generates a new template, stripes rows and + * invalidates the scrollers. */ refresh: function() { this.setNewTemplate(); @@ -110598,7 +115126,7 @@ viewConfig: { onBeforeCellKeyDown: Ext.emptyFn, /** - * Expand a particular header to fit the max content width. + * Expands a particular header to fit the max content width. * This will ONLY expand, not contract. * @private */ @@ -110611,7 +115139,7 @@ viewConfig: { }, /** - * Get the max contentWidth of the header's text and all cells + * Returns the max contentWidth of the header's text and all cells * in the grid under this header. * @private */ @@ -110653,19 +115181,22 @@ viewConfig: { }, /** - * @param {Object} position The current row and column: an object containing the following properties:
          - *
        • row
          The row index
        • - *
        • column
          The column index
        • - *
        + * @param {Object} position The current row and column: an object containing the following properties: + * + * - row - The row index + * - column - The column index + * * @param {String} direction 'up', 'down', 'right' and 'left' * @param {Ext.EventObject} e event * @param {Boolean} preventWrap Set to true to prevent wrap around to the next or previous row. - * @param {Function} verifierFn A function to verify the validity of the calculated position. When using this function, you must return true to allow the newPosition to be returned. - * @param {Scope} scope Scope to run the verifierFn in - * @returns {Object} newPosition An object containing the following properties:
          - *
        • row
          The row index
        • - *
        • column
          The column index
        • - *
        + * @param {Function} verifierFn A function to verify the validity of the calculated position. + * When using this function, you must return true to allow the newPosition to be returned. + * @param {Object} scope Scope to run the verifierFn in + * @returns {Object} newPosition An object containing the following properties: + * + * - row - The row index + * - column - The column index + * * @private */ walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) { @@ -110852,50 +115383,49 @@ viewConfig: { /** * @class Ext.grid.View * @extends Ext.view.Table - -The grid View class provides extra {@link Ext.grid.Panel} specific functionality to the -{@link Ext.view.Table}. In general, this class is not instanced directly, instead a viewConfig -option is passed to the grid: - - Ext.create('Ext.grid.Panel', { - // other options - viewConfig: { - stripeRows: false - } - }); - -__Drag Drop__ -Drag and drop functionality can be achieved in the grid by attaching a {@link Ext.grid.plugin.DragDrop} plugin -when creating the view. - - Ext.create('Ext.grid.Panel', { - // other options - viewConfig: { - plugins: { - ddGroup: 'people-group', - ptype: 'gridviewdragdrop', - enableDrop: false - } - } - }); - - * @markdown + * + * The grid View class provides extra {@link Ext.grid.Panel} specific functionality to the + * {@link Ext.view.Table}. In general, this class is not instanced directly, instead a viewConfig + * option is passed to the grid: + * + * Ext.create('Ext.grid.Panel', { + * // other options + * viewConfig: { + * stripeRows: false + * } + * }); + * + * ## Drag Drop + * + * Drag and drop functionality can be achieved in the grid by attaching a {@link Ext.grid.plugin.DragDrop} plugin + * when creating the view. + * + * Ext.create('Ext.grid.Panel', { + * // other options + * viewConfig: { + * plugins: { + * ddGroup: 'people-group', + * ptype: 'gridviewdragdrop', + * enableDrop: false + * } + * } + * }); */ Ext.define('Ext.grid.View', { extend: 'Ext.view.Table', alias: 'widget.gridview', /** - * @cfg {Boolean} stripeRows true to stripe the rows. Default is false. + * @cfg {Boolean} stripeRows true to stripe the rows. Default is true. *

        This causes the CSS class x-grid-row-alt to be added to alternate rows of * the grid. A default CSS rule is provided which sets a background color, but you can override this * with a rule which either overrides the background-color style using the '!important' * modifier, or which uses a CSS selector of higher specificity.

        */ stripeRows: true, - + invalidateScrollerOnRefresh: true, - + /** * Scroll the GridView to the top by scrolling the scroller. * @private @@ -110904,7 +115434,7 @@ Ext.define('Ext.grid.View', { if (this.rendered) { var section = this.ownerCt, verticalScroller = section.verticalScroller; - + if (verticalScroller) { verticalScroller.scrollToTop(); } @@ -110916,23 +115446,23 @@ Ext.define('Ext.grid.View', { this.callParent(arguments); this.doStripeRows(index); }, - + // after removing a row stripe rows from then on onRemove: function(ds, records, index) { this.callParent(arguments); this.doStripeRows(index); }, - + onUpdate: function(ds, record, operation) { var index = ds.indexOf(record); this.callParent(arguments); this.doStripeRows(index, index); }, - + /** * Stripe rows from a particular row index * @param {Number} startRow - * @param {Number} endRow Optional argument specifying the last row to process. By default process up to the last row. + * @param {Number} endRow (Optional) argument specifying the last row to process. By default process up to the last row. * @private */ doStripeRows: function(startRow, endRow) { @@ -110942,7 +115472,7 @@ Ext.define('Ext.grid.View', { rowsLn = rows.length, i = 0, row; - + for (; i < rowsLn; i++) { row = rows[i]; // Remove prior applied row classes. @@ -110955,7 +115485,7 @@ Ext.define('Ext.grid.View', { } } }, - + refresh: function(firstPass) { this.callParent(arguments); this.doStripeRows(0); @@ -110969,26 +115499,24 @@ Ext.define('Ext.grid.View', { /** * @author Aaron Conran - * @class Ext.grid.Panel - * @extends Ext.panel.Table + * @docauthor Ed Spencer * - * Grids are an excellent way of showing large amounts of tabular data on the client side. Essentially a supercharged + * Grids are an excellent way of showing large amounts of tabular data on the client side. Essentially a supercharged * ``, GridPanel makes it easy to fetch, sort and filter large amounts of data. - * - * Grids are composed of 2 main pieces - a {@link Ext.data.Store Store} full of data and a set of columns to render. * - * {@img Ext.grid.Panel/Ext.grid.Panel1.png Ext.grid.Panel component} + * Grids are composed of two main pieces - a {@link Ext.data.Store Store} full of data and a set of columns to render. * * ## Basic GridPanel * + * @example * Ext.create('Ext.data.Store', { * storeId:'simpsonsStore', * fields:['name', 'email', 'phone'], * data:{'items':[ - * {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"}, - * {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"}, - * {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"}, - * {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"} + * { 'name': 'Lisa', "email":"lisa@simpsons.com", "phone":"555-111-1224" }, + * { 'name': 'Bart', "email":"bart@simpsons.com", "phone":"555-222-1234" }, + * { 'name': 'Homer', "email":"home@simpsons.com", "phone":"555-222-1244" }, + * { 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254" } * ]}, * proxy: { * type: 'memory', @@ -110998,34 +115526,34 @@ Ext.define('Ext.grid.View', { * } * } * }); - * + * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: Ext.data.StoreManager.lookup('simpsonsStore'), * columns: [ - * {header: 'Name', dataIndex: 'name'}, - * {header: 'Email', dataIndex: 'email', flex:1}, - * {header: 'Phone', dataIndex: 'phone'} + * { header: 'Name', dataIndex: 'name' }, + * { header: 'Email', dataIndex: 'email', flex: 1 }, + * { header: 'Phone', dataIndex: 'phone' } * ], * height: 200, * width: 400, * renderTo: Ext.getBody() * }); - * - * The code above produces a simple grid with three columns. We specified a Store which will load JSON data inline. + * + * The code above produces a simple grid with three columns. We specified a Store which will load JSON data inline. * In most apps we would be placing the grid inside another container and wouldn't need to use the * {@link #height}, {@link #width} and {@link #renderTo} configurations but they are included here to make it easy to get * up and running. - * + * * The grid we created above will contain a header bar with a title ('Simpsons'), a row of column headers directly underneath * and finally the grid rows under the headers. - * + * * ## Configuring columns - * + * * By default, each column is sortable and will toggle between ASC and DESC sorting when you click on its header. Each * column header is also reorderable by default, and each gains a drop-down menu with options to hide and show columns. * It's easy to configure each column - here we use the same example as above and just modify the columns config: - * + * * columns: [ * { * header: 'Name', @@ -111045,18 +115573,18 @@ Ext.define('Ext.grid.View', { * width: 100 * } * ] - * + * * We turned off sorting and hiding on the 'Name' column so clicking its header now has no effect. We also made the Email * column hidden by default (it can be shown again by using the menu on any other column). We also set the Phone column to - * a fixed with of 100px and flexed the Name column, which means it takes up all remaining width after the other columns + * a fixed with of 100px and flexed the Name column, which means it takes up all remaining width after the other columns * have been accounted for. See the {@link Ext.grid.column.Column column docs} for more details. - * + * * ## Renderers - * - * As well as customizing columns, it's easy to alter the rendering of individual cells using renderers. A renderer is + * + * As well as customizing columns, it's easy to alter the rendering of individual cells using renderers. A renderer is * tied to a particular column and is passed the value that would be rendered into each cell in that column. For example, * we could define a renderer function for the email column to turn each email address into a mailto link: - * + * * columns: [ * { * header: 'Email', @@ -111066,48 +115594,46 @@ Ext.define('Ext.grid.View', { * } * } * ] - * + * * See the {@link Ext.grid.column.Column column docs} for more information on renderers. - * + * * ## Selection Models - * - * Sometimes all you want is to render data onto the screen for viewing, but usually it's necessary to interact with or + * + * Sometimes all you want is to render data onto the screen for viewing, but usually it's necessary to interact with or * update that data. Grids use a concept called a Selection Model, which is simply a mechanism for selecting some part of * the data in the grid. The two main types of Selection Model are RowSelectionModel, where entire rows are selected, and * CellSelectionModel, where individual cells are selected. - * + * * Grids use a Row Selection Model by default, but this is easy to customise like so: - * + * * Ext.create('Ext.grid.Panel', { * selType: 'cellmodel', * store: ... * }); - * + * * Specifying the `cellmodel` changes a couple of things. Firstly, clicking on a cell now * selects just that cell (using a {@link Ext.selection.RowModel rowmodel} will select the entire row), and secondly the * keyboard navigation will walk from cell to cell instead of row to row. Cell-based selection models are usually used in * conjunction with editing. - * - * {@img Ext.grid.Panel/Ext.grid.Panel2.png Ext.grid.Panel cell editing} * * ## Editing - * + * * Grid has built-in support for in-line editing. There are two chief editing modes - cell editing and row editing. Cell * editing is easy to add to your existing column setup - here we'll just modify the example above to include an editor * on both the name and the email columns: - * + * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: Ext.data.StoreManager.lookup('simpsonsStore'), * columns: [ - * {header: 'Name', dataIndex: 'name', field: 'textfield'}, - * {header: 'Email', dataIndex: 'email', flex:1, - * field:{ - * xtype:'textfield', - * allowBlank:false + * { header: 'Name', dataIndex: 'name', field: 'textfield' }, + * { header: 'Email', dataIndex: 'email', flex: 1, + * field: { + * xtype: 'textfield', + * allowBlank: false * } * }, - * {header: 'Phone', dataIndex: 'phone'} + * { header: 'Phone', dataIndex: 'phone' } * ], * selType: 'cellmodel', * plugins: [ @@ -111119,36 +115645,34 @@ Ext.define('Ext.grid.View', { * width: 400, * renderTo: Ext.getBody() * }); - * - * This requires a little explanation. We're passing in {@link #store store} and {@link #columns columns} as normal, but - * this time we've also specified a {@link #field field} on two of our columns. For the Name column we just want a default - * textfield to edit the value, so we specify 'textfield'. For the Email column we customized the editor slightly by - * passing allowBlank: false, which will provide inline validation. - * + * + * This requires a little explanation. We're passing in {@link #store store} and {@link #columns columns} as normal, but + * this time we've also specified a {@link Ext.grid.column.Column#field field} on two of our columns. For the Name column + * we just want a default textfield to edit the value, so we specify 'textfield'. For the Email column we customized the + * editor slightly by passing allowBlank: false, which will provide inline validation. + * * To support cell editing, we also specified that the grid should use the 'cellmodel' {@link #selType}, and created an * instance of the {@link Ext.grid.plugin.CellEditing CellEditing plugin}, which we configured to activate each editor after a * single click. - * - * {@img Ext.grid.Panel/Ext.grid.Panel3.png Ext.grid.Panel row editing} * * ## Row Editing - * + * * The other type of editing is row-based editing, using the RowEditor component. This enables you to edit an entire row * at a time, rather than editing cell by cell. Row Editing works in exactly the same way as cell editing, all we need to * do is change the plugin type to {@link Ext.grid.plugin.RowEditing}, and set the selType to 'rowmodel': - * + * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: Ext.data.StoreManager.lookup('simpsonsStore'), * columns: [ - * {header: 'Name', dataIndex: 'name', field: 'textfield'}, - * {header: 'Email', dataIndex: 'email', flex:1, - * field:{ - * xtype:'textfield', - * allowBlank:false + * { header: 'Name', dataIndex: 'name', field: 'textfield' }, + * { header: 'Email', dataIndex: 'email', flex:1, + * field: { + * xtype: 'textfield', + * allowBlank: false * } * }, - * {header: 'Phone', dataIndex: 'phone'} + * { header: 'Phone', dataIndex: 'phone' } * ], * selType: 'rowmodel', * plugins: [ @@ -111160,51 +115684,52 @@ Ext.define('Ext.grid.View', { * width: 400, * renderTo: Ext.getBody() * }); - * + * * Again we passed some configuration to our {@link Ext.grid.plugin.RowEditing} plugin, and now when we click each row a row * editor will appear and enable us to edit each of the columns we have specified an editor for. - * + * * ## Sorting & Filtering - * + * * Every grid is attached to a {@link Ext.data.Store Store}, which provides multi-sort and filtering capabilities. It's * easy to set up a grid to be sorted from the start: - * + * * var myGrid = Ext.create('Ext.grid.Panel', { * store: { * fields: ['name', 'email', 'phone'], * sorters: ['name', 'phone'] * }, * columns: [ - * {text: 'Name', dataIndex: 'name'}, - * {text: 'Email', dataIndex: 'email'} + * { text: 'Name', dataIndex: 'name' }, + * { text: 'Email', dataIndex: 'email' } * ] * }); - * - * Sorting at run time is easily accomplished by simply clicking each column header. If you need to perform sorting on + * + * Sorting at run time is easily accomplished by simply clicking each column header. If you need to perform sorting on * more than one field at run time it's easy to do so by adding new sorters to the store: - * + * * myGrid.store.sort([ - * {property: 'name', direction: 'ASC'}, - * {property: 'email', direction: 'DESC'}, + * { property: 'name', direction: 'ASC' }, + * { property: 'email', direction: 'DESC' } * ]); - * - * {@img Ext.grid.Panel/Ext.grid.Panel4.png Ext.grid.Panel grouping} - * + * + * See {@link Ext.data.Store} for examples of filtering. + * * ## Grouping - * - * Grid supports the grouping of rows by any field. For example if we had a set of employee records, we might want to + * + * Grid supports the grouping of rows by any field. For example if we had a set of employee records, we might want to * group by the department that each employee works in. Here's how we might set that up: - * + * + * @example * var store = Ext.create('Ext.data.Store', { * storeId:'employeeStore', * fields:['name', 'senority', 'department'], * groupField: 'department', - * data:{'employees':[ - * {"name":"Michael Scott", "senority":7, "department":"Manangement"}, - * {"name":"Dwight Schrute", "senority":2, "department":"Sales"}, - * {"name":"Jim Halpert", "senority":3, "department":"Sales"}, - * {"name":"Kevin Malone", "senority":4, "department":"Accounting"}, - * {"name":"Angela Martin", "senority":5, "department":"Accounting"} + * data: {'employees':[ + * { "name": "Michael Scott", "senority": 7, "department": "Manangement" }, + * { "name": "Dwight Schrute", "senority": 2, "department": "Sales" }, + * { "name": "Jim Halpert", "senority": 3, "department": "Sales" }, + * { "name": "Kevin Malone", "senority": 4, "department": "Accounting" }, + * { "name": "Angela Martin", "senority": 5, "department": "Accounting" } * ]}, * proxy: { * type: 'memory', @@ -111214,20 +115739,20 @@ Ext.define('Ext.grid.View', { * } * } * }); - * + * * Ext.create('Ext.grid.Panel', { * title: 'Employees', * store: Ext.data.StoreManager.lookup('employeeStore'), * columns: [ - * {header: 'Name', dataIndex: 'name'}, - * {header: 'Senority', dataIndex: 'senority'} - * ], + * { header: 'Name', dataIndex: 'name' }, + * { header: 'Senority', dataIndex: 'senority' } + * ], * features: [{ftype:'grouping'}], * width: 200, * height: 275, * renderTo: Ext.getBody() * }); - * + * * ## Infinite Scrolling * * Grid supports infinite scrolling as an alternative to using a paging toolbar. Your users can scroll through thousands @@ -111243,14 +115768,15 @@ Ext.define('Ext.grid.View', { * disableSelection: true, * // ... * }); - * + * * ## Paging * * Grid supports paging through large sets of data via a PagingToolbar or PagingGridScroller (see the Infinite Scrolling section above). * To leverage paging via a toolbar or scroller, you need to set a pageSize configuration on the Store. * + * @example * var itemsPerPage = 2; // set the number of items you want per page - * + * * var store = Ext.create('Ext.data.Store', { * id:'simpsonsStore', * autoLoad: false, @@ -111266,15 +115792,15 @@ Ext.define('Ext.grid.View', { * } * } * }); - * + * * // specify segment of data you want to load using params * store.load({ * params:{ - * start:0, + * start:0, * limit: itemsPerPage * } * }); - * + * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: store, @@ -111292,11 +115818,7 @@ Ext.define('Ext.grid.View', { * displayInfo: true * }], * renderTo: Ext.getBody() - * }); - * - * {@img Ext.grid.Panel/Ext.grid.Panel5.png Ext.grid.Panel grouping} - * - * @docauthor Ed Spencer + * }); */ Ext.define('Ext.grid.Panel', { extend: 'Ext.panel.Table', @@ -111304,35 +115826,36 @@ Ext.define('Ext.grid.Panel', { alias: ['widget.gridpanel', 'widget.grid'], alternateClassName: ['Ext.list.ListView', 'Ext.ListView', 'Ext.grid.GridPanel'], viewType: 'gridview', - + lockable: false, - + // Required for the Lockable Mixin. These are the configurations which will be copied to the // normal and locked sub tablepanels normalCfgCopy: ['invalidateScrollerOnRefresh', 'verticalScroller', 'verticalScrollDock', 'verticalScrollerType', 'scroll'], lockedCfgCopy: ['invalidateScrollerOnRefresh'], - + /** - * @cfg {Boolean} columnLines Adds column line styling + * @cfg {Boolean} [columnLines=false] Adds column line styling */ - + initComponent: function() { var me = this; if (me.columnLines) { me.setColumnLines(me.columnLines); } - + me.callParent(); }, - + setColumnLines: function(show) { var me = this, method = (show) ? 'addClsWithUI' : 'removeClsWithUI'; - - me[method]('with-col-lines') + + me[method]('with-col-lines'); } }); + // Currently has the following issues: // - Does not handle postEditValue // - Fields without editors need to sync with their values in Store @@ -111763,7 +116286,9 @@ Ext.define('Ext.grid.RowEditor', { // Maintain mapping of fields-to-columns // This will fire events that maintain our container items me.columns.add(field.id, column); - + if (column.hidden) { + me.onColumnHide(column); + } if (me.isVisible() && me.context) { me.renderColumnData(field, me.context.record); } @@ -111827,8 +116352,8 @@ Ext.define('Ext.grid.RowEditor', { /** * Start editing the specified grid at the specified position. - * @param {Model} record The Store data record which backs the row to be edited. - * @param {Model} columnHeader The Column object defining the column to be edited. + * @param {Ext.data.Model} record The Store data record which backs the row to be edited. + * @param {Ext.data.Model} columnHeader The Column object defining the column to be edited. */ startEdit: function(record, columnHeader) { var me = this, @@ -112182,6 +116707,61 @@ Ext.define('Ext.grid.header.Container', { Ext.destroy(this.resizer, this.reorderer); this.callParent(); }, + + applyDefaults: function(config){ + /* + * Ensure header.Container defaults don't get applied to a RowNumberer + * if an xtype is supplied. This isn't an ideal solution however it's + * much more likely that a RowNumberer with no options will be created, + * wanting to use the defaults specified on the class as opposed to + * those setup on the Container. + */ + if (config && !config.isComponent && config.xtype == 'rownumberer') { + return config; + } + return this.callParent([config]); + }, + + applyColumnsState: function(columns) { + if (!columns || !columns.length) { + return; + } + + var me = this, + i = 0, + index, + col; + + Ext.each(columns, function (columnState) { + col = me.down('gridcolumn[headerId=' + columnState.id + ']'); + if (col) { + index = me.items.indexOf(col); + if (i !== index) { + me.moveHeader(index, i); + } + + if (col.applyColumnState) { + col.applyColumnState(columnState); + } + ++i; + } + }); + }, + + getColumnsState: function () { + var me = this, + columns = [], + state; + + me.items.each(function (col) { + state = col.getColumnState && col.getColumnState(); + if (state) { + columns.push(state); + } + }); + + return columns; + }, // Invalidate column cache on add // We cannot refresh the View on every add because this method is called @@ -112189,7 +116769,14 @@ Ext.define('Ext.grid.header.Container', { onAdd: function(c) { var me = this; if (!c.headerId) { - c.headerId = 'h' + (++me.headerCounter); + c.headerId = c.initialConfig.id || ('h' + (++me.headerCounter)); + } + if (Ext.global.console && Ext.global.console.warn) { + if (!me._usedIDs) me._usedIDs = {}; + if (me._usedIDs[c.headerId]) { + Ext.global.console.warn(this.$className, 'attempted to reuse an existing id', c.headerId); + } + me._usedIDs[c.headerId] = true; } me.callParent(arguments); me.purgeCache(); @@ -112245,14 +116832,14 @@ Ext.define('Ext.grid.header.Container', { me.pastLastHeaderEl.removeCls(me.lastHeaderCls); } lastHeaderEl.addCls(me.lastHeaderCls); - me.pastLastHeaderEl = lastHeaderEl + me.pastLastHeaderEl = lastHeaderEl; } } } }, - onHeaderShow: function(header) { + onHeaderShow: function(header, preventLayout) { // Pass up to the GridSection var me = this, gridSection = me.ownerCt, @@ -112303,7 +116890,20 @@ Ext.define('Ext.grid.header.Container', { me.fireEvent('columnshow', me, header); // The header's own hide suppresses cascading layouts, so lay the headers out now - me.doLayout(); + if (preventLayout !== true) { + me.doLayout(); + } + }, + + doComponentLayout: function(){ + var me = this; + if (me.view && me.view.saveScrollState) { + me.view.saveScrollState(); + } + me.callParent(arguments); + if (me.view && me.view.restoreScrollState) { + me.view.restoreScrollState(); + } }, onHeaderHide: function(header, suppressLayout) { @@ -112395,7 +116995,6 @@ Ext.define('Ext.grid.header.Container', { if (this.view && this.view.rendered) { this.view.onHeaderResize(header, w, suppressFocus); } - this.fireEvent('columnresize', this, header, w); }, onHeaderClick: function(header, e, t) { @@ -112449,6 +117048,7 @@ Ext.define('Ext.grid.header.Container', { var me = this; // Delete column cache - column order has changed. delete me.gridDataColumns; + delete me.hideableColumns; // Menu changes when columns are moved. It will be recreated. if (me.menu) { @@ -112461,7 +117061,7 @@ Ext.define('Ext.grid.header.Container', { var me = this, gridSection = me.ownerCt; - if (gridSection) { + if (gridSection && gridSection.onHeaderMove) { gridSection.onHeaderMove(me, header, fromIdx, toIdx); } me.fireEvent("columnmove", me, header, fromIdx, toIdx); @@ -112503,17 +117103,17 @@ Ext.define('Ext.grid.header.Container', { menuItems = [{ itemId: 'ascItem', text: me.sortAscText, - cls: 'xg-hmenu-sort-asc', + cls: Ext.baseCSSPrefix + 'hmenu-sort-asc', handler: me.onSortAscClick, scope: me },{ itemId: 'descItem', text: me.sortDescText, - cls: 'xg-hmenu-sort-desc', + cls: Ext.baseCSSPrefix + 'hmenu-sort-desc', handler: me.onSortDescClick, scope: me }]; - }; + } if (hideableColumns && hideableColumns.length) { menuItems.push('-', { itemId: 'columnItem', @@ -112604,14 +117204,14 @@ Ext.define('Ext.grid.header.Container', { for (; i < headersLn; i++) { header = headers[i]; - if (header.hidden) { + if (header.hidden || header.up('headercontainer[hidden=true]')) { width = 0; } else { width = header.getDesiredWidth(); // IE6 and IE7 bug. // Setting the width of the first TD does not work - ends up with a 1 pixel discrepancy. // We need to increment the passed with in this case. - if ((i == 0) && (Ext.isIE6 || Ext.isIE7)) { + if ((i === 0) && (Ext.isIE6 || Ext.isIE7)) { width += 1; } } @@ -112710,6 +117310,21 @@ Ext.define('Ext.grid.header.Container', { return result; }, + /** + * @private + * For use by column headers in determining whether there are any hideable columns when deciding whether or not + * the header menu should be disabled. + */ + getHideableColumns: function(refreshCache) { + var me = this, + result = refreshCache ? null : me.hideableColumns; + + if (!result) { + result = me.hideableColumns = me.query('[hideable]'); + } + return result; + }, + /** * Get the index of a leaf level header regardless of what the nesting * structure is. @@ -112803,19 +117418,13 @@ Ext.define('Ext.grid.header.Container', { }); /** - * @class Ext.grid.column.Column - * @extends Ext.grid.header.Container - * * This class specifies the definition for a column inside a {@link Ext.grid.Panel}. It encompasses * both the grid header configuration as well as displaying data within the grid itself. If the * {@link #columns} configuration is specified, this column will become a column group and can - * container other columns inside. In general, this class will not be created directly, rather + * contain other columns inside. In general, this class will not be created directly, rather * an array of column configurations will be passed to the grid: * - * {@img Ext.grid.column.Column/Ext.grid.column.Column.png Ext.grid.column.Column grid column} - * - * ## Code - * + * @example * Ext.create('Ext.data.Store', { * storeId:'employeeStore', * fields:['firstname', 'lastname', 'senority', 'dep', 'hired'], @@ -112835,13 +117444,14 @@ Ext.define('Ext.grid.header.Container', { * {text: 'First Name', dataIndex:'firstname'}, * {text: 'Last Name', dataIndex:'lastname'}, * {text: 'Hired Month', dataIndex:'hired', xtype:'datecolumn', format:'M'}, - * {text: 'Deparment (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({senority})'} + * {text: 'Department (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({senority})'} * ], * width: 400, * renderTo: Ext.getBody() * }); * - * ## Convenience Subclasses + * # Convenience Subclasses + * * There are several column subclasses that provide default rendering for various data types * * - {@link Ext.grid.column.Action}: Renders icons that can respond to click events inline @@ -112850,13 +117460,15 @@ Ext.define('Ext.grid.header.Container', { * - {@link Ext.grid.column.Number}: Renders for numeric values * - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data * - * ## Setting Sizes + * # Setting Sizes + * * The columns are laid out by a {@link Ext.layout.container.HBox} layout, so a column can either * be given an explicit width value or a flex configuration. If no width is specified the grid will * automatically the size the column to 100px. For column groups, the size is calculated by measuring * the width of the child columns, so a width option should not be specified in that case. * - * ## Header Options + * # Header Options + * * - {@link #text}: Sets the header text for the column * - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu * - {@link #hideable}: Specifies whether the column can be hidden using the column menu @@ -112864,7 +117476,8 @@ Ext.define('Ext.grid.header.Container', { * - {@link #draggable}: Specifies whether the column header can be reordered by dragging * - {@link #groupable}: Specifies whether the grid can be grouped by the column dataIndex. See also {@link Ext.grid.feature.Grouping} * - * ## Data Options + * # Data Options + * * - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column. * - {@link #renderer}: Allows the underlying store value to be transformed before being displayed in the grid */ @@ -112886,102 +117499,110 @@ Ext.define('Ext.grid.column.Column', { possibleSortStates: ['ASC', 'DESC'], renderTpl: - '
        ' + - '' + + '
        ' + + '' + '{text}' + '' + - '
        ' + + ''+ + '
        '+ + '
        ' + '
        ', /** - * @cfg {Array} columns - *

        An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the columns config.

        - *

        Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out of a group. Note that - * if all sub columns are dragged out of a group, the group is destroyed. + * @cfg {Object[]} columns + * An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the + * `columns` config. + * + * Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out + * of a group. Note that if all sub columns are dragged out of a group, the group is destroyed. */ /** - * @cfg {String} dataIndex

        Required. The name of the field in the - * grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from - * which to draw the column's value.

        + * @cfg {String} dataIndex + * The name of the field in the grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from + * which to draw the column's value. **Required.** */ dataIndex: null, /** - * @cfg {String} text Optional. The header text to be used as innerHTML - * (html tags are accepted) to display in the Grid. Note: to - * have a clickable header with no text displayed you can use the - * default of ' '. + * @cfg {String} text + * The header text to be used as innerHTML (html tags are accepted) to display in the Grid. + * **Note**: to have a clickable header with no text displayed you can use the default of ` ` aka ` `. */ - text: ' ', + text: ' ', /** - * @cfg {Boolean} sortable Optional. true if sorting is to be allowed on this column. - * Whether local/remote sorting is used is specified in {@link Ext.data.Store#remoteSort}. + * @cfg {Boolean} sortable + * False to disable sorting of this column. Whether local/remote sorting is used is specified in + * `{@link Ext.data.Store#remoteSort}`. Defaults to true. */ sortable: true, /** - * @cfg {Boolean} groupable Optional. If the grid uses a {@link Ext.grid.feature.Grouping}, this option - * may be used to disable the header menu item to group by the column selected. By default, - * the header menu group option is enabled. Set to false to disable (but still show) the - * group option in the header menu for the column. + * @cfg {Boolean} groupable + * If the grid uses a {@link Ext.grid.feature.Grouping}, this option may be used to disable the header menu + * item to group by the column selected. By default, the header menu group option is enabled. Set to false to + * disable (but still show) the group option in the header menu for the column. */ /** - * @cfg {Boolean} fixed Prevents the column from being resizable + * @cfg {Boolean} fixed + * @deprecated. + * True to prevent the column from being resizable. */ - + /** - * @cfg {Boolean} resizable This config has no effect on a grid column, please see {@link #fixed} instead. + * @cfg {Boolean} resizable + * Set to false to prevent the column from being resizable. Defaults to true */ /** - * @cfg {Boolean} hideable Optional. Specify as false to prevent the user from hiding this column - * (defaults to true). + * @cfg {Boolean} hideable + * False to prevent the user from hiding this column. Defaults to true. */ hideable: true, /** * @cfg {Boolean} menuDisabled - * True to disabled the column header menu containing sort/hide options. Defaults to false. + * True to disable the column header menu containing sort/hide options. Defaults to false. */ menuDisabled: false, /** - * @method - *

        A renderer is an 'interceptor' method which can be used transform data (value, appearance, etc.) before it - * is rendered. Example:

        - *
        {
        -    renderer: function(value){
        -        if (value === 1) {
        -            return '1 person';
        -        }
        -        return value + ' people';
        -    }
        -}
        -     * 
        - * @param {Mixed} value The data value for the current cell - * @param {Object} metaData A collection of metadata about the current cell; can be used or modified by - * the renderer. Recognized properties are: tdCls, tdAttr, and style. - * @param {Ext.data.Model} record The record for the current row - * @param {Number} rowIndex The index of the current row - * @param {Number} colIndex The index of the current column - * @param {Ext.data.Store} store The data store - * @param {Ext.view.View} view The current view - * @return {String} The HTML to be rendered + * @cfg {Function} renderer + * A renderer is an 'interceptor' method which can be used transform data (value, appearance, etc.) + * before it is rendered. Example: + * + * { + * renderer: function(value){ + * if (value === 1) { + * return '1 person'; + * } + * return value + ' people'; + * } + * } + * + * @cfg {Object} renderer.value The data value for the current cell + * @cfg {Object} renderer.metaData A collection of metadata about the current cell; can be used or modified + * by the renderer. Recognized properties are: tdCls, tdAttr, and style. + * @cfg {Ext.data.Model} renderer.record The record for the current row + * @cfg {Number} renderer.rowIndex The index of the current row + * @cfg {Number} renderer.colIndex The index of the current column + * @cfg {Ext.data.Store} renderer.store The data store + * @cfg {Ext.view.View} renderer.view The current view + * @cfg {String} renderer.return The HTML string to be rendered. */ renderer: false, /** - * @cfg {String} align Sets the alignment of the header and rendered columns. - * Defaults to 'left'. + * @cfg {String} align + * Sets the alignment of the header and rendered columns. Defaults to 'left'. */ align: 'left', /** - * @cfg {Boolean} draggable Indicates whether or not the header can be drag and drop re-ordered. - * Defaults to true. + * @cfg {Boolean} draggable + * False to disable drag-drop reordering of this column. Defaults to true. */ draggable: true, @@ -112990,15 +117611,30 @@ Ext.define('Ext.grid.column.Column', { initDraggable: Ext.emptyFn, /** - * @cfg {String} tdCls

        Optional. A CSS class names to apply to the table cells for this column.

        + * @cfg {String} tdCls + * A CSS class names to apply to the table cells for this column. + */ + + /** + * @cfg {Object/String} editor + * An optional xtype or config object for a {@link Ext.form.field.Field Field} to use for editing. + * Only applicable if the grid is using an {@link Ext.grid.plugin.Editing Editing} plugin. + */ + + /** + * @cfg {Object/String} field + * Alias for {@link #editor}. + * @deprecated 4.0.5 Use {@link #editor} instead. */ /** - * @property {Ext.core.Element} triggerEl + * @property {Ext.Element} triggerEl + * Element that acts as button for column header dropdown menu. */ /** - * @property {Ext.core.Element} textEl + * @property {Ext.Element} textEl + * Element that contains the text in column header. */ /** @@ -113011,7 +117647,8 @@ Ext.define('Ext.grid.column.Column', { initComponent: function() { var me = this, i, - len; + len, + item; if (Ext.isDefined(me.header)) { me.text = me.header; @@ -113053,8 +117690,11 @@ Ext.define('Ext.grid.column.Column', { // Acquire initial width from sub headers for (i = 0, len = me.items.length; i < len; i++) { - me.width += me.items[i].width || Ext.grid.header.Container.prototype.defaultWidth; - if (me.items[i].flex) { + item = me.items[i]; + if (!item.hidden) { + me.width += item.width || Ext.grid.header.Container.prototype.defaultWidth; + } + if (item.flex) { Ext.Error.raise('Ext.grid.column.Column: items of a grouped header do not support flexed values. Each item must explicitly define its width.'); } } @@ -113062,15 +117702,11 @@ Ext.define('Ext.grid.column.Column', { me.cls = (me.cls||'') + ' ' + Ext.baseCSSPrefix + 'group-header'; me.sortable = false; - me.fixed = true; + me.resizable = false; me.align = 'center'; } - Ext.applyIf(me.renderSelectors, { - titleContainer: '.' + Ext.baseCSSPrefix + 'column-header-inner', - triggerEl: '.' + Ext.baseCSSPrefix + 'column-header-trigger', - textEl: '.' + Ext.baseCSSPrefix + 'column-header-text' - }); + me.addChildEls('titleContainer', 'triggerEl', 'textEl'); // Initialize as a HeaderContainer me.callParent(arguments); @@ -113079,11 +117715,13 @@ Ext.define('Ext.grid.column.Column', { onAdd: function(childHeader) { childHeader.isSubHeader = true; childHeader.addCls(Ext.baseCSSPrefix + 'group-sub-header'); + this.callParent(arguments); }, onRemove: function(childHeader) { childHeader.isSubHeader = false; childHeader.removeCls(Ext.baseCSSPrefix + 'group-sub-header'); + this.callParent(arguments); }, initRenderData: function() { @@ -113096,9 +117734,63 @@ Ext.define('Ext.grid.column.Column', { return me.callParent(arguments); }, + applyColumnState: function (state) { + var me = this, + defined = Ext.isDefined; + + // apply any columns + me.applyColumnsState(state.columns); + + // Only state properties which were saved should be restored. + // (Only user-changed properties were saved by getState) + if (defined(state.hidden)) { + me.hidden = state.hidden; + } + if (defined(state.locked)) { + me.locked = state.locked; + } + if (defined(state.sortable)) { + me.sortable = state.sortable; + } + if (defined(state.width)) { + delete me.flex; + me.width = state.width; + } else if (defined(state.flex)) { + delete me.width; + me.flex = state.flex; + } + }, + + getColumnState: function () { + var me = this, + columns = [], + state = { + id: me.headerId + }; + + me.savePropsToState(['hidden', 'sortable', 'locked', 'flex', 'width'], state); + + if (me.isGroupHeader) { + me.items.each(function(column){ + columns.push(column.getColumnState()); + }); + if (columns.length) { + state.columns = columns; + } + } else if (me.isSubHeader && me.ownerCt.hidden) { + // don't set hidden on the children so they can auto height + delete me.hidden; + } + + if ('width' in state) { + delete state.flex; // width wins + } + return state; + }, + /** * Sets the header text for this Column. - * @param text The header to display on this Column. + * @param {String} text The header to display on this Column. */ setText: function(text) { this.text = text; @@ -113114,13 +117806,26 @@ Ext.define('Ext.grid.column.Column', { }, /** - * Returns the true grid column index assiciated with this Column only if this column is a base level Column. - * If it is a group column, it returns false + * Returns the true grid column index associated with this column only if this column is a base level Column. If it + * is a group column, it returns `false`. + * @return {Number} */ getIndex: function() { return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this); }, + onRender: function() { + var me = this, + grid = me.up('tablepanel'); + + // Disable the menu if there's nothing to show in the menu, ie: + // Column cannot be sorted, grouped or locked, and there are no grid columns which may be hidden + if (grid && (!me.sortable || grid.sortableColumns === false) && !me.groupable && !me.lockable && (grid.enableColumnHide === false || !me.getOwnerHeaderCt().getHideableColumns().length)) { + me.menuDisabled = true; + } + me.callParent(arguments); + }, + afterRender: function() { var me = this, el = me.el; @@ -113158,50 +117863,42 @@ Ext.define('Ext.grid.column.Column', { }); }, - setSize: function(width, height) { + /** + * Sets the width of this Column. + * @param {Number} width New width. + */ + setWidth: function(width, /* private - used internally */ doLayout) { var me = this, headerCt = me.ownerCt, - ownerHeaderCt = me.getOwnerHeaderCt(), siblings, len, i, oldWidth = me.getWidth(), - newWidth = 0, - readyForSizing = true, - hidden, + groupWidth = 0, sibling; if (width !== oldWidth) { + me.oldWidth = oldWidth; + + // Non-flexed Headers may never be squeezed in the event of a shortfall so + // always set the minWidth to their current width. + me.minWidth = me.width = width; // Bubble size changes upwards to group headers if (headerCt.isGroupHeader) { siblings = headerCt.items.items; len = siblings.length; - /* - * setSize will be called for each column as it's rendered - * so we want to wait until all sub columns have been rendered - * before we try and calculate the size of the outer container. - * We also take into account hidden columns, because they won't - * be rendered, but we'll still need to make the calculation. - */ for (i = 0; i < len; i++) { sibling = siblings[i]; - hidden = sibling.hidden; - if (!sibling.rendered && !hidden) { - readyForSizing = false; - break; - } - if (!hidden) { - newWidth += (sibling === me) ? width : sibling.getWidth(); + if (!sibling.hidden) { + groupWidth += (sibling === me) ? width : sibling.getWidth(); } } - - if (readyForSizing) { - headerCt.minWidth = newWidth; - headerCt.setWidth(newWidth); - } + headerCt.setWidth(groupWidth, doLayout); + } else if (doLayout !== false) { + // Allow the owning Container to perform the sizing + headerCt.doLayout(); } - me.callParent(arguments); } }, @@ -113217,18 +117914,21 @@ Ext.define('Ext.grid.column.Column', { if (width && !me.isGroupHeader && ownerHeaderCt) { ownerHeaderCt.onHeaderResize(me, width, true); } + if (me.oldWidth && (width !== me.oldWidth)) { + ownerHeaderCt.fireEvent('columnresize', ownerHeaderCt, this, width); + } + delete me.oldWidth; }, // private // After the container has laid out and stretched, it calls this to correctly pad the inner to center the text vertically - setPadding: function() { + // Total available header height must be passed to enable padding for inner elements to be calculated. + setPadding: function(headerHeight) { var me = this, - headerHeight, - lineHeight = parseInt(me.textEl.getStyle('line-height'), 10); + lineHeight = Ext.util.TextMetrics.measure(me.textEl.dom, me.text).height; // Top title containing element must stretch to match height of sibling group headers if (!me.isGroupHeader) { - headerHeight = me.el.getViewSize().height; if (me.titleContainer.getHeight() < headerHeight) { me.titleContainer.dom.style.height = headerHeight + 'px'; } @@ -113250,7 +117950,8 @@ Ext.define('Ext.grid.column.Column', { onDestroy: function() { var me = this; - Ext.destroy(me.keyNav); + // force destroy on the textEl, IE reports a leak + Ext.destroy(me.textEl, me.keyNav); delete me.keyNav; me.callParent(arguments); }, @@ -113310,11 +118011,11 @@ Ext.define('Ext.grid.column.Column', { * @private * Process UI events from the view. The owning TablePanel calls this method, relaying events from the TableView * @param {String} type Event type, eg 'click' - * @param {TableView} view TableView Component - * @param {HtmlElement} cell Cell HtmlElement the event took place within + * @param {Ext.view.Table} view TableView Component + * @param {HTMLElement} cell Cell HtmlElement the event took place within * @param {Number} recordIndex Index of the associated Store Model (-1 if none) * @param {Number} cellIndex Cell index within the row - * @param {EventObject} e Original event + * @param {Ext.EventObject} e Original event */ processEvent: function(type, view, cell, recordIndex, cellIndex, e) { return this.fireEvent.apply(this, arguments); @@ -113342,8 +118043,9 @@ Ext.define('Ext.grid.column.Column', { }, /** - * Returns the parameter to sort upon when sorting this header. By default - * this returns the dataIndex and will not need to be overriden in most cases. + * Returns the parameter to sort upon when sorting this header. By default this returns the dataIndex and will not + * need to be overriden in most cases. + * @return {String} */ getSortParam: function() { return this.dataIndex; @@ -113436,22 +118138,30 @@ Ext.define('Ext.grid.column.Column', { show: function() { var me = this, - ownerCt = me.getOwnerHeaderCt(), - lb, + ownerCt = me.ownerCt, + ownerCtCompLayout = ownerCt.componentLayout, + ownerCtCompLayoutBusy = ownerCtCompLayout.layoutBusy, + ownerCtLayout = ownerCt.layout, + ownerCtLayoutBusy = ownerCtLayout.layoutBusy, items, len, i, + item, newWidth = 0; // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade - lb = me.ownerCt.componentLayout.layoutBusy; - me.ownerCt.componentLayout.layoutBusy = true; + + // Suspend our owner's layouts (both component and container): + ownerCtCompLayout.layoutBusy = ownerCtLayout.layoutBusy = true; + me.callParent(arguments); - me.ownerCt.componentLayout.layoutBusy = lb; + + ownerCtCompLayout.layoutBusy = ownerCtCompLayoutBusy; + ownerCtLayout.layoutBusy = ownerCtLayoutBusy; // If a sub header, ensure that the group header is visible if (me.isSubHeader) { - if (!me.ownerCt.isVisible()) { - me.ownerCt.show(); + if (!ownerCt.isVisible()) { + ownerCt.show(); } } @@ -113459,23 +118169,29 @@ Ext.define('Ext.grid.column.Column', { if (me.isGroupHeader && !me.query(':not([hidden])').length) { items = me.query('>*'); for (i = 0, len = items.length; i < len; i++) { - items[i].show(); + item = items[i]; + item.preventLayout = true; + item.show(); + newWidth += item.getWidth(); + delete item.preventLayout; } + me.setWidth(newWidth); } // Resize the owning group to accommodate - if (me.ownerCt.isGroupHeader) { - items = me.ownerCt.query('>:not([hidden])'); + if (ownerCt.isGroupHeader && me.preventLayout !== true) { + items = ownerCt.query('>:not([hidden])'); for (i = 0, len = items.length; i < len; i++) { newWidth += items[i].getWidth(); } - me.ownerCt.minWidth = newWidth; - me.ownerCt.setWidth(newWidth); + ownerCt.minWidth = newWidth; + ownerCt.setWidth(newWidth); } // Notify owning HeaderContainer + ownerCt = me.getOwnerHeaderCt(); if (ownerCt) { - ownerCt.onHeaderShow(me); + ownerCt.onHeaderShow(me, me.preventLayout); } }, @@ -113518,59 +118234,62 @@ Ext.define('Ext.grid.column.Column', { return (this.el.getRight() - e.getXY()[0] <= this.handleWidth); } - /** - * Retrieves the editing field for editing associated with this header. Returns false if there - * is no field associated with the Header the method will return false. If the - * field has not been instantiated it will be created. Note: These methods only has an implementation - * if a Editing plugin has been enabled on the grid. - * @param record The {@link Ext.data.Model Model} instance being edited. - * @param {Mixed} defaultField An object representing a default field to be created - * @returns {Ext.form.field.Field} field - * @method getEditor - */ // intentionally omit getEditor and setEditor definitions bc we applyIf into columns // when the editing plugin is injected - /** - * Sets the form field to be used for editing. Note: This method only has an implementation - * if an Editing plugin has been enabled on the grid. - * @param {Mixed} field An object representing a field to be created. If no xtype is specified a 'textfield' is assumed. + * @method getEditor + * Retrieves the editing field for editing associated with this header. Returns false if there is no field + * associated with the Header the method will return false. If the field has not been instantiated it will be + * created. Note: These methods only has an implementation if a Editing plugin has been enabled on the grid. + * @param {Object} record The {@link Ext.data.Model Model} instance being edited. + * @param {Object} defaultField An object representing a default field to be created + * @return {Ext.form.field.Field} field + */ + /** * @method setEditor + * Sets the form field to be used for editing. Note: This method only has an implementation if an Editing plugin has + * been enabled on the grid. + * @param {Object} field An object representing a field to be created. If no xtype is specified a 'textfield' is + * assumed. */ }); + /** - * @class Ext.grid.RowNumberer - * @extends Ext.grid.column.Column * This is a utility class that can be passed into a {@link Ext.grid.column.Column} as a column config that provides * an automatic row numbering column. - *
        Usage:
        
        -columns: [
        -    Ext.create('Ext.grid.RowNumberer'),
        -    {text: "Company", flex: 1, sortable: true, dataIndex: 'company'},
        -    {text: "Price", width: 120, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
        -    {text: "Change", width: 120, sortable: true, dataIndex: 'change'},
        -    {text: "% Change", width: 120, sortable: true, dataIndex: 'pctChange'},
        -    {text: "Last Updated", width: 120, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
        -]
        - *
        + * + * Usage: + * + * columns: [ + * {xtype: 'rownumberer'}, + * {text: "Company", flex: 1, sortable: true, dataIndex: 'company'}, + * {text: "Price", width: 120, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'}, + * {text: "Change", width: 120, sortable: true, dataIndex: 'change'}, + * {text: "% Change", width: 120, sortable: true, dataIndex: 'pctChange'}, + * {text: "Last Updated", width: 120, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'} + * ] + * */ Ext.define('Ext.grid.RowNumberer', { extend: 'Ext.grid.column.Column', alias: 'widget.rownumberer', + /** - * @cfg {String} text Any valid text or HTML fragment to display in the header cell for the row - * number column (defaults to ' '). + * @cfg {String} text + * Any valid text or HTML fragment to display in the header cell for the row number column. */ text: " ", /** - * @cfg {Number} width The default width in pixels of the row number column (defaults to 23). + * @cfg {Number} width + * The default width in pixels of the row number column. */ width: 23, /** - * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false). + * @cfg {Boolean} sortable + * True if the row number column is sortable. * @hide */ sortable: false, @@ -113585,7 +118304,7 @@ Ext.define('Ext.grid.RowNumberer', { }, // private - fixed: true, + resizable: false, hideable: false, menuDisabled: true, dataIndex: '', @@ -113638,7 +118357,7 @@ Ext.define('Ext.view.DropZone', { fireViewEvent: function() { var me = this, result; - + me.lock(); result = me.view.fireEvent.apply(me.view, arguments); me.unlock(); @@ -113753,7 +118472,7 @@ Ext.define('Ext.view.DropZone', { // The mouse is over a View node onNodeOver: function(node, dragZone, e, data) { var me = this; - + if (!Ext.Array.contains(data.records, me.view.getRecord(node))) { me.positionIndicator(node, data, e); } @@ -113764,7 +118483,7 @@ Ext.define('Ext.view.DropZone', { // Remove drop position indicator notifyOut: function(node, dragZone, e, data) { var me = this; - + me.callParent(arguments); delete me.overRecord; delete me.currentPosition; @@ -113824,6 +118543,12 @@ Ext.define('Ext.view.DropZone', { } } return performOperation; + }, + + destroy: function(){ + Ext.destroy(this.indicator); + delete this.indicator; + this.callParent(); } }); @@ -113866,14 +118591,10 @@ Ext.define('Ext.grid.ViewDropZone', { } }); /** - * @class Ext.grid.column.Action - * @extends Ext.grid.column.Column - *

        A Grid header type which renders an icon, or a series of icons in a grid cell, and offers a scoped click - * handler for each icon.

        + * A Grid header type which renders an icon, or a series of icons in a grid cell, and offers a scoped click + * handler for each icon. * - * {@img Ext.grid.column.Action/Ext.grid.column.Action.png Ext.grid.column.Action grid column} - * - * ## Code + * @example * Ext.create('Ext.data.Store', { * storeId:'employeeStore', * fields:['firstname', 'lastname', 'senority', 'dep', 'hired'], @@ -113882,10 +118603,10 @@ Ext.define('Ext.grid.ViewDropZone', { * {firstname:"Dwight", lastname:"Schrute"}, * {firstname:"Jim", lastname:"Halpert"}, * {firstname:"Kevin", lastname:"Malone"}, - * {firstname:"Angela", lastname:"Martin"} + * {firstname:"Angela", lastname:"Martin"} * ] * }); - * + * * Ext.create('Ext.grid.Panel', { * title: 'Action Column Demo', * store: Ext.data.StoreManager.lookup('employeeStore'), @@ -113893,30 +118614,31 @@ Ext.define('Ext.grid.ViewDropZone', { * {text: 'First Name', dataIndex:'firstname'}, * {text: 'Last Name', dataIndex:'lastname'}, * { - * xtype:'actioncolumn', + * xtype:'actioncolumn', * width:50, * items: [{ - * icon: 'images/edit.png', // Use a URL in the icon config + * icon: 'extjs/examples/shared/icons/fam/cog_edit.png', // Use a URL in the icon config * tooltip: 'Edit', * handler: function(grid, rowIndex, colIndex) { * var rec = grid.getStore().getAt(rowIndex); * alert("Edit " + rec.get('firstname')); * } * },{ - * icon: 'images/delete.png', + * icon: 'extjs/examples/restful/images/delete.png', * tooltip: 'Delete', * handler: function(grid, rowIndex, colIndex) { * var rec = grid.getStore().getAt(rowIndex); * alert("Terminate " + rec.get('firstname')); - * } + * } * }] * } * ], * width: 250, * renderTo: Ext.getBody() * }); - *

        The action column can be at any index in the columns array, and a grid can have any number of - * action columns.

        + * + * The action column can be at any index in the columns array, and a grid can have any number of + * action columns. */ Ext.define('Ext.grid.column.Action', { extend: 'Ext.grid.column.Column', @@ -113925,87 +118647,104 @@ Ext.define('Ext.grid.column.Action', { /** * @cfg {String} icon - * The URL of an image to display as the clickable element in the column. - * Optional - defaults to {@link Ext#BLANK_IMAGE_URL Ext.BLANK_IMAGE_URL}. + * The URL of an image to display as the clickable element in the column. Defaults to + * `{@link Ext#BLANK_IMAGE_URL Ext.BLANK_IMAGE_URL}`. */ /** * @cfg {String} iconCls - * A CSS class to apply to the icon image. To determine the class dynamically, configure the Column with a {@link #getClass} function. + * A CSS class to apply to the icon image. To determine the class dynamically, configure the Column with + * a `{@link #getClass}` function. */ /** - * @cfg {Function} handler A function called when the icon is clicked. - * The handler is passed the following parameters:
          - *
        • view : TableView
          The owning TableView.
        • - *
        • rowIndex : Number
          The row index clicked on.
        • - *
        • colIndex : Number
          The column index clicked on.
        • - *
        • item : Object
          The clicked item (or this Column if multiple - * {@link #items} were not configured).
        • - *
        • e : Event
          The click event.
        • - *
        + * @cfg {Function} handler + * A function called when the icon is clicked. + * @cfg {Ext.view.Table} handler.view The owning TableView. + * @cfg {Number} handler.rowIndex The row index clicked on. + * @cfg {Number} handler.colIndex The column index clicked on. + * @cfg {Object} handler.item The clicked item (or this Column if multiple {@link #items} were not configured). + * @cfg {Event} handler.e The click event. */ /** - * @cfg {Object} scope The scope (this reference) in which the {@link #handler} - * and {@link #getClass} fuctions are executed. Defaults to this Column. + * @cfg {Object} scope + * The scope (**this** reference) in which the `{@link #handler}` and `{@link #getClass}` fuctions are executed. + * Defaults to this Column. */ /** - * @cfg {String} tooltip A tooltip message to be displayed on hover. {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have - * been initialized. + * @cfg {String} tooltip + * A tooltip message to be displayed on hover. {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must + * have been initialized. + */ + /* @cfg {Boolean} disabled + * If true, the action will not respond to click events, and will be displayed semi-opaque. */ /** - * @cfg {Boolean} stopSelection Defaults to true. Prevent grid row selection upon mousedown. + * @cfg {Boolean} [stopSelection=true] + * Prevent grid _row_ selection upon mousedown. */ /** - * @cfg {Function} getClass A function which returns the CSS class to apply to the icon image. - * The function is passed the following parameters:
          - *
        • v : Object

          The value of the column's configured field (if any).

        • - *
        • metadata : Object

          An object in which you may set the following attributes:

            - *
          • css : String

            A CSS class name to add to the cell's TD element.

          • - *
          • attr : String

            An HTML attribute definition string to apply to the data container element within the table cell - * (e.g. 'style="color:red;"').

          • - *

        • - *
        • r : Ext.data.Record

          The Record providing the data.

        • - *
        • rowIndex : Number

          The row index..

        • - *
        • colIndex : Number

          The column index.

        • - *
        • store : Ext.data.Store

          The Store which is providing the data Model.

        • - *
        + * @cfg {Function} getClass + * A function which returns the CSS class to apply to the icon image. + * + * @cfg {Object} getClass.v The value of the column's configured field (if any). + * + * @cfg {Object} getClass.metadata An object in which you may set the following attributes: + * @cfg {String} getClass.metadata.css A CSS class name to add to the cell's TD element. + * @cfg {String} getClass.metadata.attr An HTML attribute definition string to apply to the data container + * element *within* the table cell (e.g. 'style="color:red;"'). + * + * @cfg {Ext.data.Model} getClass.r The Record providing the data. + * + * @cfg {Number} getClass.rowIndex The row index.. + * + * @cfg {Number} getClass.colIndex The column index. + * + * @cfg {Ext.data.Store} getClass.store The Store which is providing the data Model. */ /** - * @cfg {Array} items An Array which may contain multiple icon definitions, each element of which may contain: - *
          - *
        • icon : String
          The url of an image to display as the clickable element - * in the column.
        • - *
        • iconCls : String
          A CSS class to apply to the icon image. - * To determine the class dynamically, configure the item with a getClass function.
        • - *
        • getClass : Function
          A function which returns the CSS class to apply to the icon image. - * The function is passed the following parameters:
            - *
          • v : Object

            The value of the column's configured field (if any).

          • - *
          • metadata : Object

            An object in which you may set the following attributes:

              - *
            • css : String

              A CSS class name to add to the cell's TD element.

            • - *
            • attr : String

              An HTML attribute definition string to apply to the data container element within the table cell - * (e.g. 'style="color:red;"').

            • - *

          • - *
          • r : Ext.data.Record

            The Record providing the data.

          • - *
          • rowIndex : Number

            The row index..

          • - *
          • colIndex : Number

            The column index.

          • - *
          • store : Ext.data.Store

            The Store which is providing the data Model.

          • - *
        • - *
        • handler : Function
          A function called when the icon is clicked.
        • - *
        • scope : Scope
          The scope (this reference) in which the - * handler and getClass functions are executed. Fallback defaults are this Column's - * configured scope, then this Column.
        • - *
        • tooltip : String
          A tooltip message to be displayed on hover. - * {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have been initialized.
        • - *
        + * @cfg {Object[]} items + * An Array which may contain multiple icon definitions, each element of which may contain: + * + * @cfg {String} items.icon The url of an image to display as the clickable element in the column. + * + * @cfg {String} items.iconCls A CSS class to apply to the icon image. To determine the class dynamically, + * configure the item with a `getClass` function. + * + * @cfg {Function} items.getClass A function which returns the CSS class to apply to the icon image. + * @cfg {Object} items.getClass.v The value of the column's configured field (if any). + * @cfg {Object} items.getClass.metadata An object in which you may set the following attributes: + * @cfg {String} items.getClass.metadata.css A CSS class name to add to the cell's TD element. + * @cfg {String} items.getClass.metadata.attr An HTML attribute definition string to apply to the data + * container element _within_ the table cell (e.g. 'style="color:red;"'). + * @cfg {Ext.data.Model} items.getClass.r The Record providing the data. + * @cfg {Number} items.getClass.rowIndex The row index.. + * @cfg {Number} items.getClass.colIndex The column index. + * @cfg {Ext.data.Store} items.getClass.store The Store which is providing the data Model. + * + * @cfg {Function} items.handler A function called when the icon is clicked. + * + * @cfg {Object} items.scope The scope (`this` reference) in which the `handler` and `getClass` functions + * are executed. Fallback defaults are this Column's configured scope, then this Column. + * + * @cfg {String} items.tooltip A tooltip message to be displayed on hover. + * @cfg {Boolean} items.disabled If true, the action will not respond to click events, and will be displayed semi-opaque. + * {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have been initialized. + */ + /** + * @property {Array} items + * An array of action items copied from the configured {@link #cfg-items items} configuration. Each will have + * an `enable` and `disable` method added which will enable and disable the associated action, and + * update the displayed icon accordingly. */ header: ' ', - actionIdRe: /x-action-col-(\d+)/, + actionIdRe: new RegExp(Ext.baseCSSPrefix + 'action-col-(\\d+)'), /** - * @cfg {String} altText The alt text to use for the image element. Defaults to ''. + * @cfg {String} altText + * The alt text to use for the image element. */ altText: '', - + sortable: false, constructor: function(config) { @@ -114023,7 +118762,7 @@ Ext.define('Ext.grid.column.Action', { // Items is an array property of ActionColumns me.items = items; -// Renderer closure iterates through items creating an element for each and tagging with an identifying +// Renderer closure iterates through items creating an element for each and tagging with an identifying // class name x-action-col-{n} me.renderer = function(v, meta) { // Allow a configured renderer to create initial value (And set the other values in the "metadata" argument!) @@ -114032,8 +118771,10 @@ Ext.define('Ext.grid.column.Action', { meta.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell'; for (i = 0; i < l; i++) { item = items[i]; + item.disable = Ext.Function.bind(me.disableAction, me, [i]); + item.enable = Ext.Function.bind(me.enableAction, me, [i]); v += '' + (item.altText || me.altText) + ''; } @@ -114041,6 +118782,36 @@ Ext.define('Ext.grid.column.Action', { }; }, + /** + * Enables this ActionColumn's action at the specified index. + */ + enableAction: function(index) { + var me = this; + + if (!index) { + index = 0; + } else if (!Ext.isNumber(index)) { + index = Ext.Array.indexOf(me.items, index); + } + me.items[index].disabled = false; + me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).removeCls(me.disabledCls); + }, + + /** + * Disables this ActionColumn's action at the specified index. + */ + disableAction: function(index) { + var me = this; + + if (!index) { + index = 0; + } else if (!Ext.isNumber(index)) { + index = Ext.Array.indexOf(me.items, index); + } + me.items[index].disabled = true; + me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).addCls(me.disabledCls); + }, + destroy: function() { delete this.items; delete this.renderer; @@ -114057,12 +118828,13 @@ Ext.define('Ext.grid.column.Action', { var me = this, match = e.getTarget().className.match(me.actionIdRe), item, fn; + if (match) { item = me.items[parseInt(match[1], 10)]; if (item) { if (type == 'click') { fn = item.handler || me.handler; - if (fn) { + if (fn && !item.disabled) { fn.call(item.scope || me.scope || me, view, recordIndex, cellIndex, item, e); } } else if (type == 'mousedown' && item.stopSelection !== false) { @@ -114083,14 +118855,10 @@ Ext.define('Ext.grid.column.Action', { } }); /** - * @class Ext.grid.column.Boolean - * @extends Ext.grid.column.Column - *

        A Column definition class which renders boolean data fields. See the {@link Ext.grid.column.Column#xtype xtype} - * config option of {@link Ext.grid.column.Column} for more details.

        + * A Column definition class which renders boolean data fields. See the {@link Ext.grid.column.Column#xtype xtype} + * config option of {@link Ext.grid.column.Column} for more details. * - * {@img Ext.grid.column.Boolean/Ext.grid.column.Boolean.png Ext.grid.column.Boolean grid column} - * - * ## Code + * @example * Ext.create('Ext.data.Store', { * storeId:'sampleStore', * fields:[ @@ -114098,10 +118866,10 @@ Ext.define('Ext.grid.column.Action', { * {name: 'rocks', type: 'boolean'} * ], * data:{'items':[ - * {"framework":"Ext JS 4", "rocks":true}, - * {"framework":"Sencha Touch", "rocks":true}, - * {"framework":"Ext GWT", "rocks":true}, - * {"framework":"Other Guys", "rocks":false} + * { 'framework': "Ext JS 4", 'rocks': true }, + * { 'framework': "Sencha Touch", 'rocks': true }, + * { 'framework': "Ext GWT", 'rocks': true }, + * { 'framework': "Other Guys", 'rocks': false } * ]}, * proxy: { * type: 'memory', @@ -114116,13 +118884,14 @@ Ext.define('Ext.grid.column.Action', { * title: 'Boolean Column Demo', * store: Ext.data.StoreManager.lookup('sampleStore'), * columns: [ - * {text: 'Framework', dataIndex: 'framework', flex: 1}, + * { text: 'Framework', dataIndex: 'framework', flex: 1 }, * { * xtype: 'booleancolumn', * text: 'Rocks', * trueText: 'Yes', * falseText: 'No', - * dataIndex: 'rocks'} + * dataIndex: 'rocks' + * } * ], * height: 200, * width: 400, @@ -114136,20 +118905,19 @@ Ext.define('Ext.grid.column.Boolean', { /** * @cfg {String} trueText - * The string returned by the renderer when the column value is not falsey (defaults to 'true'). + * The string returned by the renderer when the column value is not falsey. */ trueText: 'true', /** * @cfg {String} falseText - * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to - * 'false'). + * The string returned by the renderer when the column value is falsey (but not undefined). */ falseText: 'false', /** * @cfg {String} undefinedText - * The string returned by the renderer when the column value is undefined (defaults to ' '). + * The string returned by the renderer when the column value is undefined. */ undefinedText: ' ', @@ -114171,30 +118939,24 @@ Ext.define('Ext.grid.column.Boolean', { } }); /** - * @class Ext.grid.column.Date - * @extends Ext.grid.column.Column - * * A Column definition class which renders a passed date according to the default locale, or a configured * {@link #format}. * - * {@img Ext.grid.column.Date/Ext.grid.column.Date.png Ext.grid.column.Date grid column} - * - * ## Code - * + * @example * Ext.create('Ext.data.Store', { * storeId:'sampleStore', * fields:[ - * {name: 'symbol', type: 'string'}, - * {name: 'date', type: 'date'}, - * {name: 'change', type: 'number'}, - * {name: 'volume', type: 'number'}, - * {name: 'topday', type: 'date'} + * { name: 'symbol', type: 'string' }, + * { name: 'date', type: 'date' }, + * { name: 'change', type: 'number' }, + * { name: 'volume', type: 'number' }, + * { name: 'topday', type: 'date' } * ], * data:[ - * {symbol:"msft", date:'2011/04/22', change:2.43, volume:61606325, topday:'04/01/2010'}, - * {symbol:"goog", date:'2011/04/22', change:0.81, volume:3053782, topday:'04/11/2010'}, - * {symbol:"apple", date:'2011/04/22', change:1.35, volume:24484858, topday:'04/28/2010'}, - * {symbol:"sencha", date:'2011/04/22', change:8.85, volume:5556351, topday:'04/22/2010'} + * { symbol: "msft", date: '2011/04/22', change: 2.43, volume: 61606325, topday: '04/01/2010' }, + * { symbol: "goog", date: '2011/04/22', change: 0.81, volume: 3053782, topday: '04/11/2010' }, + * { symbol: "apple", date: '2011/04/22', change: 1.35, volume: 24484858, topday: '04/28/2010' }, + * { symbol: "sencha", date: '2011/04/22', change: 8.85, volume: 5556351, topday: '04/22/2010' } * ] * }); * @@ -114202,11 +118964,11 @@ Ext.define('Ext.grid.column.Boolean', { * title: 'Date Column Demo', * store: Ext.data.StoreManager.lookup('sampleStore'), * columns: [ - * {text: 'Symbol', dataIndex: 'symbol', flex: 1}, - * {text: 'Date', dataIndex: 'date', xtype: 'datecolumn', format:'Y-m-d'}, - * {text: 'Change', dataIndex: 'change', xtype: 'numbercolumn', format:'0.00'}, - * {text: 'Volume', dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000'}, - * {text: 'Top Day', dataIndex: 'topday', xtype: 'datecolumn', format:'l'} + * { text: 'Symbol', dataIndex: 'symbol', flex: 1 }, + * { text: 'Date', dataIndex: 'date', xtype: 'datecolumn', format:'Y-m-d' }, + * { text: 'Change', dataIndex: 'change', xtype: 'numbercolumn', format:'0.00' }, + * { text: 'Volume', dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000' }, + * { text: 'Top Day', dataIndex: 'topday', xtype: 'datecolumn', format:'l' } * ], * height: 200, * width: 450, @@ -114225,35 +118987,34 @@ Ext.define('Ext.grid.column.Date', { * This defaults to the default date from {@link Ext.Date#defaultFormat} which itself my be overridden * in a locale file. */ - format : Ext.Date.defaultFormat, - constructor: function(cfg){ - this.callParent(arguments); - this.renderer = Ext.util.Format.dateRenderer(this.format); + initComponent: function(){ + var me = this; + + me.callParent(arguments); + if (!me.format) { + me.format = Ext.Date.defaultFormat; + } + me.renderer = Ext.util.Format.dateRenderer(me.format); } }); /** - * @class Ext.grid.column.Number - * @extends Ext.grid.column.Column - * * A Column definition class which renders a numeric data field according to a {@link #format} string. * - * {@img Ext.grid.column.Number/Ext.grid.column.Number.png Ext.grid.column.Number cell editing} - * - * ## Code + * @example * Ext.create('Ext.data.Store', { * storeId:'sampleStore', * fields:[ - * {name: 'symbol', type: 'string'}, - * {name: 'price', type: 'number'}, - * {name: 'change', type: 'number'}, - * {name: 'volume', type: 'number'}, + * { name: 'symbol', type: 'string' }, + * { name: 'price', type: 'number' }, + * { name: 'change', type: 'number' }, + * { name: 'volume', type: 'number' }, * ], * data:[ - * {symbol:"msft", price:25.76, change:2.43, volume:61606325}, - * {symbol:"goog", price:525.73, change:0.81, volume:3053782}, - * {symbol:"apple", price:342.41, change:1.35, volume:24484858}, - * {symbol:"sencha", price:142.08, change:8.85, volume:5556351} + * { symbol: "msft", price: 25.76, change: 2.43, volume: 61606325 }, + * { symbol: "goog", price: 525.73, change: 0.81, volume: 3053782 }, + * { symbol: "apple", price: 342.41, change: 1.35, volume: 24484858 }, + * { symbol: "sencha", price: 142.08, change: 8.85, volume: 5556351 } * ] * }); * @@ -114261,10 +119022,10 @@ Ext.define('Ext.grid.column.Date', { * title: 'Number Column Demo', * store: Ext.data.StoreManager.lookup('sampleStore'), * columns: [ - * {text: 'Symbol', dataIndex: 'symbol', flex: 1}, - * {text: 'Current Price', dataIndex: 'price', renderer: Ext.util.Format.usMoney}, - * {text: 'Change', dataIndex: 'change', xtype: 'numbercolumn', format:'0.00'}, - * {text: 'Volume', dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000'} + * { text: 'Symbol', dataIndex: 'symbol', flex: 1 }, + * { text: 'Current Price', dataIndex: 'price', renderer: Ext.util.Format.usMoney }, + * { text: 'Change', dataIndex: 'change', xtype: 'numbercolumn', format:'0.00' }, + * { text: 'Volume', dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000' } * ], * height: 200, * width: 400, @@ -114279,35 +119040,31 @@ Ext.define('Ext.grid.column.Number', { /** * @cfg {String} format - * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column - * (defaults to '0,000.00'). + * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column. */ format : '0,000.00', + constructor: function(cfg) { this.callParent(arguments); this.renderer = Ext.util.Format.numberRenderer(this.format); } }); /** - * @class Ext.grid.column.Template - * @extends Ext.grid.column.Column - * * A Column definition class which renders a value by processing a {@link Ext.data.Model Model}'s - * {@link Ext.data.Model#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}. + * {@link Ext.data.Model#persistenceProperty data} using a {@link #tpl configured} + * {@link Ext.XTemplate XTemplate}. * - * {@img Ext.grid.column.Template/Ext.grid.column.Template.png Ext.grid.column.Template grid column} - * - * ## Code + * @example * Ext.create('Ext.data.Store', { * storeId:'employeeStore', * fields:['firstname', 'lastname', 'senority', 'department'], * groupField: 'department', * data:[ - * {firstname:"Michael", lastname:"Scott", senority:7, department:"Manangement"}, - * {firstname:"Dwight", lastname:"Schrute", senority:2, department:"Sales"}, - * {firstname:"Jim", lastname:"Halpert", senority:3, department:"Sales"}, - * {firstname:"Kevin", lastname:"Malone", senority:4, department:"Accounting"}, - * {firstname:"Angela", lastname:"Martin", senority:5, department:"Accounting"} + * { firstname: "Michael", lastname: "Scott", senority: 7, department: "Manangement" }, + * { firstname: "Dwight", lastname: "Schrute", senority: 2, department: "Sales" }, + * { firstname: "Jim", lastname: "Halpert", senority: 3, department: "Sales" }, + * { firstname: "Kevin", lastname: "Malone", senority: 4, department: "Accounting" }, + * { firstname: "Angela", lastname: "Martin", senority: 5, department: "Accounting" } * ] * }); * @@ -114315,15 +119072,13 @@ Ext.define('Ext.grid.column.Number', { * title: 'Column Template Demo', * store: Ext.data.StoreManager.lookup('employeeStore'), * columns: [ - * {text: 'Full Name', xtype:'templatecolumn', tpl:'{firstname} {lastname}', flex:1}, - * {text: 'Deparment (Yrs)', xtype:'templatecolumn', tpl:'{department} ({senority})'} + * { text: 'Full Name', xtype: 'templatecolumn', tpl: '{firstname} {lastname}', flex:1 }, + * { text: 'Deparment (Yrs)', xtype: 'templatecolumn', tpl: '{department} ({senority})' } * ], * height: 200, * width: 300, * renderTo: Ext.getBody() * }); - * - * @markdown */ Ext.define('Ext.grid.column.Template', { extend: 'Ext.grid.column.Column', @@ -114332,10 +119087,12 @@ Ext.define('Ext.grid.column.Template', { alternateClassName: 'Ext.grid.TemplateColumn', /** - * @cfg {String/XTemplate} tpl - * An {@link Ext.XTemplate XTemplate}, or an XTemplate definition string to use to process a - * {@link Ext.data.Model Model}'s {@link Ext.data.Model#data data} to produce a column's rendered value. + * @cfg {String/Ext.XTemplate} tpl + * An {@link Ext.XTemplate XTemplate}, or an XTemplate *definition string* to use to process a + * {@link Ext.data.Model Model}'s {@link Ext.data.Model#persistenceProperty data} to produce a + * column's rendered value. */ + constructor: function(cfg){ var me = this, tpl; @@ -114433,6 +119190,7 @@ Ext.define('Ext.grid.feature.Feature', { * * The method must also return the eventName as the first index of the array * to be passed to fireEvent. + * @template */ getFireEventArgs: function(eventName, view, featureTarget, e) { return [eventName, view, featureTarget, e]; @@ -114440,6 +119198,7 @@ Ext.define('Ext.grid.feature.Feature', { /** * Approriate place to attach events to the view, selectionmodel, headerCt, etc + * @template */ attachEvents: function() { @@ -114453,6 +119212,7 @@ Ext.define('Ext.grid.feature.Feature', { * Allows a feature to mutate the metaRowTpl. * The array received as a single argument can be manipulated to add things * on the end/begining of a particular row. + * @template */ mutateMetaRowTpl: function(metaRowTplArray) { @@ -114462,6 +119222,7 @@ Ext.define('Ext.grid.feature.Feature', { * Allows a feature to inject member methods into the metaRowTpl. This is * important for embedding functionality which will become part of the proper * row tpl. + * @template */ getMetaRowTplFragments: function() { return {}; @@ -114477,6 +119238,7 @@ Ext.define('Ext.grid.feature.Feature', { * @param {Number} idx The row index for this record. * @param {Ext.data.Model} record The record instance * @param {Object} orig The original result from the prepareData call to massage. + * @template */ getAdditionalData: function(data, idx, record, orig) { return {}; @@ -114523,7 +119285,7 @@ Ext.define('Ext.grid.feature.AbstractSummary', { /** * Toggle whether or not to show the summary row. - * @param {Boolan} visible True to show the summary row + * @param {Boolean} visible True to show the summary row */ toggleSummaryRow: function(visible){ this.showSummaryRow = !!visible; @@ -114551,9 +119313,10 @@ Ext.define('Ext.grid.feature.AbstractSummary', { * @return {String} The value of the summary row */ printSummaryRow: function(index){ - var inner = this.view.getTableChunker().metaRowTpl.join(''); + var inner = this.view.getTableChunker().metaRowTpl.join(''), + prefix = Ext.baseCSSPrefix; - inner = inner.replace('x-grid-row', 'x-grid-row-summary'); + inner = inner.replace(prefix + 'grid-row', prefix + 'grid-row-summary'); inner = inner.replace('{{id}}', '{gridSummaryValue}'); inner = inner.replace(this.nestedIdRe, '{id$1}'); inner = inner.replace('{[this.embedRowCls()]}', '{rowCls}'); @@ -114597,7 +119360,7 @@ Ext.define('Ext.grid.feature.AbstractSummary', { * be passed to the stores aggregate function. * @param {String} field The field to aggregate on * @param {Boolean} group True to aggregate in grouped mode - * @return {Mixed} See the return type for the store functions. + * @return {Number/String/Object} See the return type for the store functions. */ getSummary: function(store, type, field, group){ if (type) { @@ -114752,8 +119515,10 @@ Ext.define('Ext.grid.feature.Grouping', { eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd', constructor: function() { - this.collapsedState = {}; - this.callParent(arguments); + var me = this; + + me.collapsedState = {}; + me.callParent(arguments); }, /** @@ -114813,33 +119578,31 @@ Ext.define('Ext.grid.feature.Grouping', { hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed', /** - * @cfg {String} groupByText Text displayed in the grid header menu for grouping by header - * (defaults to 'Group By This Field'). + * @cfg {String} groupByText Text displayed in the grid header menu for grouping by header. */ groupByText : 'Group By This Field', /** - * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping - * (defaults to 'Show in Groups'). + * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping. */ showGroupsText : 'Show in Groups', /** - * @cfg {Boolean} hideGroupedHeadertrue to hide the header that is currently grouped (defaults to false) + * @cfg {Boolean} hideGroupedHeadertrue to hide the header that is currently grouped. */ hideGroupedHeader : false, /** - * @cfg {Boolean} startCollapsed true to start all groups collapsed (defaults to false) + * @cfg {Boolean} startCollapsed true to start all groups collapsed */ startCollapsed : false, /** - * @cfg {Boolean} enableGroupingMenu true to enable the grouping control in the header menu (defaults to true) + * @cfg {Boolean} enableGroupingMenu true to enable the grouping control in the header menu */ enableGroupingMenu : true, /** - * @cfg {Boolean} enableNoGroups true to allow the user to turn off grouping (defaults to true) + * @cfg {Boolean} enableNoGroups true to allow the user to turn off grouping */ enableNoGroups : true, @@ -114849,33 +119612,46 @@ Ext.define('Ext.grid.feature.Grouping', { store = view.store, groupToggleMenuItem; + me.lastGroupField = me.getGroupField(); + if (me.lastGroupIndex) { store.group(me.lastGroupIndex); } me.callParent(); groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem'); groupToggleMenuItem.setChecked(true, true); - view.refresh(); + me.refreshIf(); }, disable: function() { var me = this, view = me.view, store = view.store, + remote = store.remoteGroup, groupToggleMenuItem, lastGroup; lastGroup = store.groupers.first(); if (lastGroup) { me.lastGroupIndex = lastGroup.property; - store.groupers.clear(); + me.block(); + store.clearGrouping(); + me.unblock(); } me.callParent(); groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem'); groupToggleMenuItem.setChecked(true, true); groupToggleMenuItem.setChecked(false, true); - view.refresh(); + if (!remote) { + view.refresh(); + } + }, + + refreshIf: function() { + if (this.blockRefresh !== true) { + this.view.refresh(); + } }, getFeatureTpl: function(values, parent, x, xcount) { @@ -114915,8 +119691,7 @@ Ext.define('Ext.grid.feature.Grouping', { // perhaps rename to afterViewRender attachEvents: function() { var me = this, - view = me.view, - header, headerId, menu, menuItem; + view = me.view; view.on({ scope: me, @@ -114930,16 +119705,10 @@ Ext.define('Ext.grid.feature.Grouping', { if (me.enableGroupingMenu) { me.injectGroupingMenu(); } - - if (me.hideGroupedHeader) { - header = view.headerCt.down('gridcolumn[dataIndex=' + me.getGroupField() + ']'); - headerId = header.id; - menu = view.headerCt.getMenu(); - menuItem = menu.down('menuitem[headerId='+ headerId +']'); - if (menuItem) { - menuItem.setChecked(false); - } - } + me.lastGroupField = me.getGroupField(); + me.block(); + me.onGroupChange(); + me.unblock(); }, injectGroupingMenu: function() { @@ -114972,6 +119741,7 @@ Ext.define('Ext.grid.feature.Grouping', { return function() { var o = Ext.grid.header.Container.prototype.getMenuItems.call(this); o.push('-', { + iconCls: Ext.baseCSSPrefix + 'group-by-icon', itemId: 'groupMenuItem', text: groupByText, handler: groupMenuItemClick @@ -114994,15 +119764,30 @@ Ext.define('Ext.grid.feature.Grouping', { * @private */ onGroupMenuItemClick: function(menuItem, e) { - var menu = menuItem.parentMenu, + var me = this, + menu = menuItem.parentMenu, hdr = menu.activeHeader, - view = this.view; + view = me.view, + store = view.store, + remote = store.remoteGroup; - delete this.lastGroupIndex; - this.enable(); - view.store.group(hdr.dataIndex); - this.pruneGroupedHeader(); - + delete me.lastGroupIndex; + me.block(); + me.enable(); + store.group(hdr.dataIndex); + me.pruneGroupedHeader(); + me.unblock(); + if (!remote) { + view.refresh(); + } + }, + + block: function(){ + this.blockRefresh = this.view.blockRefresh = true; + }, + + unblock: function(){ + this.blockRefresh = this.view.blockRefresh = false; }, /** @@ -115059,7 +119844,7 @@ Ext.define('Ext.grid.feature.Grouping', { /** * Expand a group by the groupBody - * @param {Ext.core.Element} groupBd + * @param {Ext.Element} groupBd * @private */ expand: function(groupBd) { @@ -115080,7 +119865,7 @@ Ext.define('Ext.grid.feature.Grouping', { /** * Collapse a group by the groupBody - * @param {Ext.core.Element} groupBd + * @param {Ext.Element} groupBd * @private */ collapse: function(groupBd) { @@ -115100,7 +119885,41 @@ Ext.define('Ext.grid.feature.Grouping', { }, onGroupChange: function(){ - this.view.refresh(); + var me = this, + field = me.getGroupField(), + menuItem; + + if (me.hideGroupedHeader) { + if (me.lastGroupField) { + menuItem = me.getMenuItem(me.lastGroupField); + if (menuItem) { + menuItem.setChecked(true); + } + } + if (field) { + menuItem = me.getMenuItem(field); + if (menuItem) { + menuItem.setChecked(false); + } + } + } + if (me.blockRefresh !== true) { + me.view.refresh(); + } + me.lastGroupField = field; + }, + + /** + * Gets the related menu item for a dataIndex + * @private + * @return {Ext.grid.header.Container} The header + */ + getMenuItem: function(dataIndex){ + var view = this.view, + header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'), + menu = view.headerCt.getMenu(); + + return menu.down('menuitem[headerId='+ header.id +']'); }, /** @@ -115225,12 +120044,12 @@ Ext.define('Ext.grid.feature.Grouping', { /** * @class Ext.grid.feature.GroupingSummary * @extends Ext.grid.feature.Grouping - * + * * This feature adds an aggregate summary row at the bottom of each group that is provided - * by the {@link Ext.grid.feature.Grouping} feature. There are 2 aspects to the summary: - * + * by the {@link Ext.grid.feature.Grouping} feature. There are two aspects to the summary: + * * ## Calculation - * + * * The summary value needs to be calculated for each column in the grid. This is controlled * by the summaryType option specified on the column. There are several built in summary types, * which can be specified as a string on the column configuration. These call underlying methods @@ -115244,9 +120063,9 @@ Ext.define('Ext.grid.feature.Grouping', { * * Alternatively, the summaryType can be a function definition. If this is the case, * the function is called with an array of records to calculate the summary value. - * + * * ## Rendering - * + * * Similar to a column, the summary also supports a summaryRenderer function. This * summaryRenderer is called before displaying a value. The function is optional, if * not specified the default calculated value is shown. The summaryRenderer is called with: @@ -115254,9 +120073,10 @@ Ext.define('Ext.grid.feature.Grouping', { * - value {Object} - The calculated value. * - summaryData {Object} - Contains all raw summary values for the row. * - field {String} - The name of the field we are calculating - * + * * ## Example Usage * + * @example * Ext.define('TestResult', { * extend: 'Ext.data.Model', * fields: ['student', 'subject', { @@ -115264,7 +120084,7 @@ Ext.define('Ext.grid.feature.Grouping', { * type: 'int' * }] * }); - * + * * Ext.create('Ext.grid.Panel', { * width: 200, * height: 240, @@ -115299,7 +120119,7 @@ Ext.define('Ext.grid.feature.Grouping', { * text: 'Name', * summaryType: 'count', * summaryRenderer: function(value){ - * return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); + * return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); * } * }, { * dataIndex: 'mark', @@ -115309,20 +120129,20 @@ Ext.define('Ext.grid.feature.Grouping', { * }); */ Ext.define('Ext.grid.feature.GroupingSummary', { - + /* Begin Definitions */ - + extend: 'Ext.grid.feature.Grouping', - + alias: 'feature.groupingsummary', - + mixins: { summary: 'Ext.grid.feature.AbstractSummary' }, - + /* End Definitions */ - + /** * Modifies the row template to include the summary row. * @private @@ -115330,7 +120150,7 @@ Ext.define('Ext.grid.feature.GroupingSummary', { */ getFeatureTpl: function() { var tpl = this.callParent(arguments); - + if (this.showSummaryRow) { // lop off the end so we can attach it tpl = tpl.replace('', ''); @@ -115338,7 +120158,7 @@ Ext.define('Ext.grid.feature.GroupingSummary', { } return tpl; }, - + /** * Gets any fragments needed for the template. * @private @@ -115347,7 +120167,7 @@ Ext.define('Ext.grid.feature.GroupingSummary', { getFragmentTpl: function() { var me = this, fragments = me.callParent(); - + Ext.apply(fragments, me.getSummaryFragments()); if (me.showSummaryRow) { // this gets called before render, so we'll setup the data here. @@ -115356,7 +120176,7 @@ Ext.define('Ext.grid.feature.GroupingSummary', { } return fragments; }, - + /** * Gets the data for printing a template row * @private @@ -115372,7 +120192,7 @@ Ext.define('Ext.grid.feature.GroupingSummary', { name = me.summaryGroups[index - 1].name, active = me.summaryData[name], column; - + for (; i < length; ++i) { column = columns[i]; column.gridSummaryValue = this.getColumnValue(column, active); @@ -115380,7 +120200,7 @@ Ext.define('Ext.grid.feature.GroupingSummary', { } return data; }, - + /** * Generates all of the summary data to be used when processing the template * @private @@ -115402,16 +120222,15 @@ Ext.define('Ext.grid.feature.GroupingSummary', { root, key, comp; - + for (i = 0, length = groups.length; i < length; ++i) { data[groups[i].name] = {}; } - - /** - * @cfg {String} remoteRoot. The name of the property - * which contains the Array of summary objects. Defaults to undefined. - * It allows to use server-side calculated summaries. - */ + + /** + * @cfg {String} [remoteRoot=undefined] The name of the property which contains the Array of + * summary objects. It allows to use server-side calculated summaries. + */ if (me.remoteRoot && reader.rawData) { // reset reader root and rebuild extractors to extract summaries data root = reader.root; @@ -115424,21 +120243,21 @@ Ext.define('Ext.grid.feature.GroupingSummary', { reader.root = root; reader.buildExtractors(true); } - + for (i = 0, length = columns.length; i < length; ++i) { comp = Ext.getCmp(columns[i].id); fieldData = me.getSummary(store, comp.summaryType, comp.dataIndex, true); - + for (key in fieldData) { if (fieldData.hasOwnProperty(key)) { data[key][comp.id] = fieldData[key]; } } - + for (key in remoteData) { if (remoteData.hasOwnProperty(key)) { remote = remoteData[key][comp.dataIndex]; - if (remote !== undefined) { + if (remote !== undefined && data[key] !== undefined) { data[key][comp.id] = remote; } } @@ -115632,6 +120451,7 @@ Ext.define('Ext.grid.feature.RowWrap', { * * ## Example Usage * + * @example * Ext.define('TestResult', { * extend: 'Ext.data.Model', * fields: ['student', { @@ -115864,7 +120684,7 @@ Ext.define('Ext.grid.header.DropZone', { getTopIndicator: function() { if (!this.topIndicator) { - this.topIndicator = Ext.core.DomHelper.append(Ext.getBody(), { + this.topIndicator = Ext.DomHelper.append(Ext.getBody(), { cls: "col-move-top", html: " " }, true); @@ -115874,7 +120694,7 @@ Ext.define('Ext.grid.header.DropZone', { getBottomIndicator: function() { if (!this.bottomIndicator) { - this.bottomIndicator = Ext.core.DomHelper.append(Ext.getBody(), { + this.bottomIndicator = Ext.DomHelper.append(Ext.getBody(), { cls: "col-move-bottom", html: " " }, true); @@ -116090,18 +120910,13 @@ Ext.define('Ext.grid.header.DropZone', { } }); - /** - * @class Ext.grid.plugin.Editing - -This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}. -The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor} -in the {@link Ext.grid.column.Column column configuration}. - -*Note:* This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and -{@link Ext.grid.plugin.RowEditing}. - - * @markdown + * This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}. + * The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor} + * in the {@link Ext.grid.column.Column column configuration}. + * + * **Note:** This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and + * {@link Ext.grid.plugin.RowEditing}. */ Ext.define('Ext.grid.plugin.Editing', { alias: 'editing.editing', @@ -116117,7 +120932,7 @@ Ext.define('Ext.grid.plugin.Editing', { /** * @cfg {Number} clicksToEdit - * The number of clicks on a grid required to display the editor (defaults to 2). + * The number of clicks on a grid required to display the editor. */ clicksToEdit: 2, @@ -116162,7 +120977,7 @@ Ext.define('Ext.grid.plugin.Editing', { grid.isEditable = true; grid.editingPlugin = grid.view.editingPlugin = me; }, - + /** * Fires after the grid is reconfigured * @private @@ -116336,16 +121151,17 @@ Ext.define('Ext.grid.plugin.Editing', { /** * @private - * @abstract. Template method called before editing begins. + * @template + * Template method called before editing begins. * @param {Object} context The current editing context * @return {Boolean} Return false to cancel the editing process */ beforeEdit: Ext.emptyFn, /** - * Start editing the specified record, using the specified Column definition to define which field is being edited. - * @param {Model} record The Store data record which backs the row to be edited. - * @param {Model} columnHeader The Column object defining the column to be edited. + * Starts editing the specified record, using the specified Column definition to define which field is being edited. + * @param {Ext.data.Model/Number} record The Store data record which backs the row to be edited, or index of the record in Store. + * @param {Ext.grid.column.Column/Number} columnHeader The Column object defining the column to be edited, or index of the column. */ startEdit: function(record, columnHeader) { var me = this, @@ -116360,7 +121176,8 @@ Ext.define('Ext.grid.plugin.Editing', { }, /** - * @private Collects all information necessary for any subclasses to perform their editing functions. + * @private + * Collects all information necessary for any subclasses to perform their editing functions. * @param record * @param columnHeader * @returns {Object} The editing context based upon the passed record and column @@ -116402,14 +121219,14 @@ Ext.define('Ext.grid.plugin.Editing', { }, /** - * Cancel any active edit that is in progress. + * Cancels any active edit that is in progress. */ cancelEdit: function() { this.editing = false; }, /** - * Complete the edit if there is an active edit in progress. + * Completes the edit if there is an active edit in progress. */ completeEdit: function() { var me = this; @@ -116430,13 +121247,11 @@ Ext.define('Ext.grid.plugin.Editing', { return me.fireEvent('validateedit', me, context) !== false && !context.cancel; } }); + /** - * @class Ext.grid.plugin.CellEditing - * @extends Ext.grid.plugin.Editing - * * The Ext.grid.plugin.CellEditing plugin injects editing at a cell level for a Grid. Only a single * cell will be editable at a time. The field that will be used for the editor is defined at the - * {@link Ext.grid.column.Column#field field}. The editor can be a field instance or a field configuration. + * {@link Ext.grid.column.Column#editor editor}. The editor can be a field instance or a field configuration. * * If an editor is not specified for a particular column then that cell will not be editable and it will * be skipped when activated via the mouse or the keyboard. @@ -116445,10 +121260,7 @@ Ext.define('Ext.grid.plugin.Editing', { * An appropriate field type should be chosen to match the data structure that it will be editing. For example, * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor. * - * {@img Ext.grid.plugin.CellEditing/Ext.grid.plugin.CellEditing.png Ext.grid.plugin.CellEditing plugin} - * - * ## Example Usage - * + * @example * Ext.create('Ext.data.Store', { * storeId:'simpsonsStore', * fields:['name', 'email', 'phone'], @@ -116466,16 +121278,16 @@ Ext.define('Ext.grid.plugin.Editing', { * } * } * }); - * + * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: Ext.data.StoreManager.lookup('simpsonsStore'), * columns: [ - * {header: 'Name', dataIndex: 'name', field: 'textfield'}, + * {header: 'Name', dataIndex: 'name', editor: 'textfield'}, * {header: 'Email', dataIndex: 'email', flex:1, * editor: { - * xtype:'textfield', - * allowBlank:false + * xtype: 'textfield', + * allowBlank: false * } * }, * {header: 'Phone', dataIndex: 'phone'} @@ -116499,79 +121311,73 @@ Ext.define('Ext.grid.plugin.CellEditing', { constructor: function() { /** * @event beforeedit - * Fires before cell editing is triggered. The edit event object has the following properties
        - *
          - *
        • grid - The grid
        • - *
        • record - The record being edited
        • - *
        • field - The field name being edited
        • - *
        • value - The value for the field being edited.
        • - *
        • row - The grid table row
        • - *
        • column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited.
        • - *
        • rowIdx - The row index that is being edited
        • - *
        • colIdx - The column index that is being edited
        • - *
        • cancel - Set this to true to cancel the edit or return false from your handler.
        • - *
        - * @param {Ext.grid.plugin.Editing} editor - * @param {Object} e An edit event (see above for description) + * Fires before cell editing is triggered. Return false from event handler to stop the editing. + * + * @param {Object} e An edit event with the following properties: + * + * - grid - The grid + * - record - The record being edited + * - field - The field name being edited + * - value - The value for the field being edited. + * - row - The grid table row + * - column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited. + * - rowIdx - The row index that is being edited + * - colIdx - The column index that is being edited + * - cancel - Set this to true to cancel the edit or return false from your handler. */ /** * @event edit - * Fires after a cell is edited. The edit event object has the following properties
        - *
          - *
        • grid - The grid
        • - *
        • record - The record that was edited
        • - *
        • field - The field name that was edited
        • - *
        • value - The value being set
        • - *
        • originalValue - The original value for the field, before the edit.
        • - *
        • row - The grid table row
        • - *
        • column - The grid {@link Ext.grid.column.Column Column} defining the column that was edited.
        • - *
        • rowIdx - The row index that was edited
        • - *
        • colIdx - The column index that was edited
        • - *
        + * Fires after a cell is edited. Usage example: + * + * grid.on('edit', function(editor, e) { + * // commit the changes right after editing finished + * e.record.commit(); + * }; * - *
        
        -grid.on('edit', onEdit, this);
        -
        -function onEdit(e) {
        -    // execute an XHR to send/commit data to the server, in callback do (if successful):
        -    e.record.commit();
        -};
        -         * 
        * @param {Ext.grid.plugin.Editing} editor - * @param {Object} e An edit event (see above for description) + * @param {Object} e An edit event with the following properties: + * + * - grid - The grid + * - record - The record that was edited + * - field - The field name that was edited + * - value - The value being set + * - originalValue - The original value for the field, before the edit. + * - row - The grid table row + * - column - The grid {@link Ext.grid.column.Column Column} defining the column that was edited. + * - rowIdx - The row index that was edited + * - colIdx - The column index that was edited */ /** * @event validateedit - * Fires after a cell is edited, but before the value is set in the record. Return false - * to cancel the change. The edit event object has the following properties
        - *
          - *
        • grid - The grid
        • - *
        • record - The record being edited
        • - *
        • field - The field name being edited
        • - *
        • value - The value being set
        • - *
        • originalValue - The original value for the field, before the edit.
        • - *
        • row - The grid table row
        • - *
        • column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited.
        • - *
        • rowIdx - The row index that is being edited
        • - *
        • colIdx - The column index that is being edited
        • - *
        • cancel - Set this to true to cancel the edit or return false from your handler.
        • - *
        - * Usage example showing how to remove the red triangle (dirty record indicator) from some - * records (not all). By observing the grid's validateedit event, it can be cancelled if - * the edit occurs on a targeted row (for example) and then setting the field's new value - * in the Record directly: - *
        
        -grid.on('validateedit', function(e) {
        -  var myTargetRow = 6;
        -
        -  if (e.row == myTargetRow) {
        -    e.cancel = true;
        -    e.record.data[e.field] = e.value;
        -  }
        -});
        -         * 
        + * Fires after a cell is edited, but before the value is set in the record. Return false from event handler to + * cancel the change. + * + * Usage example showing how to remove the red triangle (dirty record indicator) from some records (not all). By + * observing the grid's validateedit event, it can be cancelled if the edit occurs on a targeted row (for + * example) and then setting the field's new value in the Record directly: + * + * grid.on('validateedit', function(editor, e) { + * var myTargetRow = 6; + * + * if (e.row == myTargetRow) { + * e.cancel = true; + * e.record.data[e.field] = e.value; + * } + * }); + * * @param {Ext.grid.plugin.Editing} editor - * @param {Object} e An edit event (see above for description) + * @param {Object} e An edit event with the following properties: + * + * - grid - The grid + * - record - The record being edited + * - field - The field name being edited + * - value - The value being set + * - originalValue - The original value for the field, before the edit. + * - row - The grid table row + * - column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited. + * - rowIdx - The row index that is being edited + * - colIdx - The column index that is being edited + * - cancel - Set this to true to cancel the edit or return false from your handler. */ this.callParent(arguments); this.editors = Ext.create('Ext.util.MixedCollection', false, function(editor) { @@ -116625,16 +121431,15 @@ grid.on('validateedit', function(e) { }, /** - * Start editing the specified record, using the specified Column definition to define which field is being edited. - * @param {Model} record The Store data record which backs the row to be edited. - * @param {Model} columnHeader The Column object defining the column to be edited. - * @override + * Starts editing the specified record, using the specified Column definition to define which field is being edited. + * @param {Ext.data.Model} record The Store data record which backs the row to be edited. + * @param {Ext.data.Model} columnHeader The Column object defining the column to be edited. @override */ startEdit: function(record, columnHeader) { var me = this, - ed = me.getEditor(record, columnHeader), value = record.get(columnHeader.dataIndex), - context = me.getEditingContext(record, columnHeader); + context = me.getEditingContext(record, columnHeader), + ed; record = context.record; columnHeader = context.column; @@ -116643,17 +121448,18 @@ grid.on('validateedit', function(e) { // cell DOM element. Completing the edit causes a view refresh. me.completeEdit(); + context.originalValue = context.value = value; + if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) { + return false; + } + // See if the field is editable for the requested record if (columnHeader && !columnHeader.getEditor(record)) { return false; } - + + ed = me.getEditor(record, columnHeader); if (ed) { - context.originalValue = context.value = value; - if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) { - return false; - } - me.context = context; me.setActiveEditor(ed); me.setActiveRecord(record); @@ -116735,11 +121541,19 @@ grid.on('validateedit', function(e) { return editor; } }, + + // inherit docs + setColumnField: function(column, field) { + var ed = this.editors.getByKey(column.getItemId()); + Ext.destroy(ed, column.field); + this.editors.removeAtKey(column.getItemId()); + this.callParent(arguments); + }, /** - * Get the cell (td) for a particular record and column. + * Gets the cell (td) for a particular record and column. * @param {Ext.data.Model} record - * @param {Ext.grid.column.Colunm} column + * @param {Ext.grid.column.Column} column * @private */ getCell: function(record, column) { @@ -116786,13 +121600,11 @@ grid.on('validateedit', function(e) { } me.context.value = value; me.fireEvent('edit', me, me.context); - - } }, /** - * Cancel any active editing. + * Cancels any active editing. */ cancelEdit: function() { var me = this, @@ -116826,26 +121638,65 @@ grid.on('validateedit', function(e) { } }); /** - * @class Ext.grid.plugin.DragDrop - *

        This plugin provides drag and/or drop functionality for a GridView.

        - *

        It creates a specialized instance of {@link Ext.dd.DragZone DragZone} which knows how to drag out of a {@link Ext.grid.View GridView} - * and loads the data object which is passed to a cooperating {@link Ext.dd.DragZone DragZone}'s methods with the following properties:

          - *
        • copy : Boolean - *
          The value of the GridView's copy property, or true if the GridView was configured - * with allowCopy: true and the control key was pressed when the drag operation was begun.
        • - *
        • view : GridView - *
          The source GridView from which the drag originated.
        • - *
        • ddel : HtmlElement - *
          The drag proxy element which moves with the mouse
        • - *
        • item : HtmlElement - *
          The GridView node upon which the mousedown event was registered.
        • - *
        • records : Array - *
          An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView.
        • - *

        - *

        It also creates a specialized instance of {@link Ext.dd.DropZone} which cooperates with other DropZones which are members of the same - * ddGroup which processes such data objects.

        - *

        Adding this plugin to a view means that two new events may be fired from the client GridView, {@link #event-beforedrop beforedrop} and - * {@link #event-drop drop}

        + * This plugin provides drag and/or drop functionality for a GridView. + * + * It creates a specialized instance of {@link Ext.dd.DragZone DragZone} which knows how to drag out of a {@link + * Ext.grid.View GridView} and loads the data object which is passed to a cooperating {@link Ext.dd.DragZone DragZone}'s + * methods with the following properties: + * + * - `copy` : Boolean + * + * The value of the GridView's `copy` property, or `true` if the GridView was configured with `allowCopy: true` _and_ + * the control key was pressed when the drag operation was begun. + * + * - `view` : GridView + * + * The source GridView from which the drag originated. + * + * - `ddel` : HtmlElement + * + * The drag proxy element which moves with the mouse + * + * - `item` : HtmlElement + * + * The GridView node upon which the mousedown event was registered. + * + * - `records` : Array + * + * An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView. + * + * It also creates a specialized instance of {@link Ext.dd.DropZone} which cooperates with other DropZones which are + * members of the same ddGroup which processes such data objects. + * + * Adding this plugin to a view means that two new events may be fired from the client GridView, `{@link #beforedrop + * beforedrop}` and `{@link #drop drop}` + * + * @example + * Ext.create('Ext.data.Store', { + * storeId:'simpsonsStore', + * fields:['name'], + * data: [["Lisa"], ["Bart"], ["Homer"], ["Marge"]], + * proxy: { + * type: 'memory', + * reader: 'array' + * } + * }); + * + * Ext.create('Ext.grid.Panel', { + * store: 'simpsonsStore', + * columns: [ + * {header: 'Name', dataIndex: 'name', flex: true} + * ], + * viewConfig: { + * plugins: { + * ptype: 'gridviewdragdrop', + * dragText: 'Drag and drop to reorganize' + * } + * }, + * height: 200, + * width: 400, + * renderTo: Ext.getBody() + * }); */ Ext.define('Ext.grid.plugin.DragDrop', { extend: 'Ext.AbstractPlugin', @@ -116858,94 +121709,131 @@ Ext.define('Ext.grid.plugin.DragDrop', { /** * @event beforedrop - *

        This event is fired through the GridView. Add listeners to the GridView object

        - *

        Fired when a drop gesture has been triggered by a mouseup event in a valid drop position in the GridView. - * @param {HtmlElement} node The GridView node if any over which the mouse was positioned.

        - *

        Returning false to this event signals that the drop gesture was invalid, and if the drag proxy - * will animate back to the point from which the drag began.

        - *

        Returning 0 To this event signals that the data transfer operation should not take place, but - * that the gesture was valid, and that the repair operation should not take place.

        - *

        Any other return value continues with the data transfer operation.

        - * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s - * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:
          - *
        • copy : Boolean - *
          The value of the GridView's copy property, or true if the GridView was configured - * with allowCopy: true and the control key was pressed when the drag operation was begun
        • - *
        • view : GridView - *
          The source GridView from which the drag originated.
        • - *
        • ddel : HtmlElement - *
          The drag proxy element which moves with the mouse
        • - *
        • item : HtmlElement - *
          The GridView node upon which the mousedown event was registered.
        • - *
        • records : Array - *
          An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView.
        • - *
        + * **This event is fired through the GridView. Add listeners to the GridView object** + * + * Fired when a drop gesture has been triggered by a mouseup event in a valid drop position in the GridView. + * + * @param {HTMLElement} node The GridView node **if any** over which the mouse was positioned. + * + * Returning `false` to this event signals that the drop gesture was invalid, and if the drag proxy will animate + * back to the point from which the drag began. + * + * Returning `0` To this event signals that the data transfer operation should not take place, but that the gesture + * was valid, and that the repair operation should not take place. + * + * Any other return value continues with the data transfer operation. + * + * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone + * DragZone}'s {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties: + * + * - copy : Boolean + * + * The value of the GridView's `copy` property, or `true` if the GridView was configured with `allowCopy: true` and + * the control key was pressed when the drag operation was begun + * + * - view : GridView + * + * The source GridView from which the drag originated. + * + * - ddel : HtmlElement + * + * The drag proxy element which moves with the mouse + * + * - item : HtmlElement + * + * The GridView node upon which the mousedown event was registered. + * + * - records : Array + * + * An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView. + * * @param {Ext.data.Model} overModel The Model over which the drop gesture took place. - * @param {String} dropPosition "before" or "after" depending on whether the mouse is above or below the midline of the node. - * @param {Function} dropFunction

        A function to call to complete the data transfer operation and either move or copy Model instances from the source - * View's Store to the destination View's Store.

        - *

        This is useful when you want to perform some kind of asynchronous processing before confirming - * the drop, such as an {@link Ext.window.MessageBox#confirm confirm} call, or an Ajax request.

        - *

        Return 0 from this event handler, and call the dropFunction at any time to perform the data transfer.

        + * + * @param {String} dropPosition `"before"` or `"after"` depending on whether the mouse is above or below the midline + * of the node. + * + * @param {Function} dropFunction + * + * A function to call to complete the data transfer operation and either move or copy Model instances from the + * source View's Store to the destination View's Store. + * + * This is useful when you want to perform some kind of asynchronous processing before confirming the drop, such as + * an {@link Ext.window.MessageBox#confirm confirm} call, or an Ajax request. + * + * Return `0` from this event handler, and call the `dropFunction` at any time to perform the data transfer. */ /** * @event drop - * This event is fired through the GridView. Add listeners to the GridView object - * Fired when a drop operation has been completed and the data has been moved or copied. - * @param {HtmlElement} node The GridView node if any over which the mouse was positioned. - * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s - * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:
          - *
        • copy : Boolean - *
          The value of the GridView's copy property, or true if the GridView was configured - * with allowCopy: true and the control key was pressed when the drag operation was begun
        • - *
        • view : GridView - *
          The source GridView from which the drag originated.
        • - *
        • ddel : HtmlElement - *
          The drag proxy element which moves with the mouse
        • - *
        • item : HtmlElement - *
          The GridView node upon which the mousedown event was registered.
        • - *
        • records : Array - *
          An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView.
        • - *
        + * **This event is fired through the GridView. Add listeners to the GridView object** Fired when a drop operation + * has been completed and the data has been moved or copied. + * + * @param {HTMLElement} node The GridView node **if any** over which the mouse was positioned. + * + * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone + * DragZone}'s {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties: + * + * - copy : Boolean + * + * The value of the GridView's `copy` property, or `true` if the GridView was configured with `allowCopy: true` and + * the control key was pressed when the drag operation was begun + * + * - view : GridView + * + * The source GridView from which the drag originated. + * + * - ddel : HtmlElement + * + * The drag proxy element which moves with the mouse + * + * - item : HtmlElement + * + * The GridView node upon which the mousedown event was registered. + * + * - records : Array + * + * An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView. + * * @param {Ext.data.Model} overModel The Model over which the drop gesture took place. - * @param {String} dropPosition "before" or "after" depending on whether the mouse is above or below the midline of the node. + * + * @param {String} dropPosition `"before"` or `"after"` depending on whether the mouse is above or below the midline + * of the node. */ dragText : '{0} selected row{1}', /** * @cfg {String} ddGroup - * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and DropZone - * used by this plugin will only interact with other drag drop objects in the same group (defaults to 'TreeDD'). + * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and + * DropZone used by this plugin will only interact with other drag drop objects in the same group. */ ddGroup : "GridDD", /** * @cfg {String} dragGroup - *

        The ddGroup to which the DragZone will belong.

        - *

        This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other Drag/DropZones - * which are members of the same ddGroup.

        + * The ddGroup to which the DragZone will belong. + * + * This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other + * Drag/DropZones which are members of the same ddGroup. */ /** * @cfg {String} dropGroup - *

        The ddGroup to which the DropZone will belong.

        - *

        This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other Drag/DropZones - * which are members of the same ddGroup.

        + * The ddGroup to which the DropZone will belong. + * + * This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other + * Drag/DropZones which are members of the same ddGroup. */ /** * @cfg {Boolean} enableDrop - *

        Defaults to true

        - *

        Set to false to disallow the View from accepting drop gestures

        + * False to disallow the View from accepting drop gestures. */ enableDrop: true, /** * @cfg {Boolean} enableDrag - *

        Defaults to true

        - *

        Set to false to disallow dragging items from the View

        + * False to disallow dragging items from the View. */ enableDrag: true, @@ -116961,6 +121849,28 @@ Ext.define('Ext.grid.plugin.DragDrop', { Ext.destroy(this.dragZone, this.dropZone); }, + enable: function() { + var me = this; + if (me.dragZone) { + me.dragZone.unlock(); + } + if (me.dropZone) { + me.dropZone.unlock(); + } + me.callParent(); + }, + + disable: function() { + var me = this; + if (me.dragZone) { + me.dragZone.lock(); + } + if (me.dropZone) { + me.dropZone.lock(); + } + me.callParent(); + }, + onViewRender : function(view) { var me = this; @@ -117028,18 +121938,15 @@ Ext.define('Ext.grid.plugin.HeaderReorderer', { /** * @class Ext.grid.plugin.HeaderResizer * @extends Ext.util.Observable - * + * * Plugin to add header resizing functionality to a HeaderContainer. * Always resizing header to the left of the splitter you are resizing. - * - * Todo: Consider RTL support, columns would always calculate to the right of - * the splitter instead of to the left. */ Ext.define('Ext.grid.plugin.HeaderResizer', { extend: 'Ext.util.Observable', requires: ['Ext.dd.DragTracker', 'Ext.util.Region'], alias: 'plugin.gridheaderresizer', - + disabled: false, /** @@ -117110,9 +122017,10 @@ Ext.define('Ext.grid.plugin.HeaderResizer', { if (headerEl){ overHeader = Ext.getCmp(headerEl.id); - // On left edge, we are resizing the previous non-hidden, base level column. + // On left edge, go back to the previous non-hidden header. if (overHeader.isOnLeftEdge(e)) { - resizeHeader = overHeader.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])'); + resizeHeader = overHeader.previousNode('gridcolumn:not([hidden])'); + } // Else, if on the right edge, we're resizing the column we are over else if (overHeader.isOnRightEdge(e)) { @@ -117126,14 +122034,15 @@ Ext.define('Ext.grid.plugin.HeaderResizer', { // We *are* resizing if (resizeHeader) { // If we're attempting to resize a group header, that cannot be resized, - // so find its last base level column header; Group headers are sized + // so find its last visible leaf header; Group headers are sized // by the size of their child headers. if (resizeHeader.isGroupHeader) { - resizeHeader = resizeHeader.getVisibleGridColumns(); - resizeHeader = resizeHeader[resizeHeader.length - 1]; + resizeHeader = resizeHeader.down(':not([isGroupHeader]):not([hidden]):last'); } - if (resizeHeader && !(resizeHeader.fixed || this.disabled)) { + // Check if the header is resizable. Continue checking the old "fixed" property, bug also + // check whether the resizablwe property is set to false. + if (resizeHeader && !(resizeHeader.fixed || (resizeHeader.resizable === false) || this.disabled)) { this.activeHd = resizeHeader; overHeader.el.dom.style.cursor = this.eResizeCursor; } @@ -117263,38 +122172,32 @@ Ext.define('Ext.grid.plugin.HeaderResizer', { delete dragHd.flex; } + this.headerCt.suspendLayout = true; + dragHd.setWidth(this.origWidth + offset[0], false); + + // In the case of forceFit, change the following Header width. + // Then apply the two width changes by laying out the owning HeaderContainer // If HeaderContainer is configured forceFit, inhibit upstream layout notification, so that // we can also shrink the following Header by an equal amount, and *then* inform the upstream layout. if (this.headerCt.forceFit) { nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])'); if (nextHd) { - this.headerCt.componentLayout.layoutBusy = true; + delete nextHd.flex; + nextHd.setWidth(nextHd.getWidth() - offset[0], false); } } - - // Non-flexed Headers may never be squeezed in the event of a shortfall so - // always set the minWidth to their current width. - dragHd.minWidth = this.origWidth + offset[0]; - dragHd.setWidth(dragHd.minWidth); - - // In the case of forceFit, change the following Header width. - // Then apply the two width changes by laying out the owning HeaderContainer - if (nextHd) { - delete nextHd.flex; - nextHd.setWidth(nextHd.getWidth() - offset[0]); - this.headerCt.componentLayout.layoutBusy = false; - this.headerCt.doComponentLayout(); - } + this.headerCt.suspendLayout = false; + this.headerCt.doComponentLayout(this.headerCt.getFullWidth()); } }, - + disable: function() { this.disabled = true; if (this.tracker) { this.tracker.disable(); } }, - + enable: function() { this.disabled = false; if (this.tracker) { @@ -117303,53 +122206,40 @@ Ext.define('Ext.grid.plugin.HeaderResizer', { } }); /** - * @class Ext.grid.plugin.RowEditing - * @extends Ext.grid.plugin.Editing - * * The Ext.grid.plugin.RowEditing plugin injects editing at a row level for a Grid. When editing begins, * a small floating dialog will be shown for the appropriate row. Each editable column will show a field * for editing. There is a button to save or cancel all changes for the edit. - * + * * The field that will be used for the editor is defined at the - * {@link Ext.grid.column.Column#field field}. The editor can be a field instance or a field configuration. + * {@link Ext.grid.column.Column#editor editor}. The editor can be a field instance or a field configuration. * If an editor is not specified for a particular column then that column won't be editable and the value of * the column will be displayed. * * The editor may be shared for each column in the grid, or a different one may be specified for each column. * An appropriate field type should be chosen to match the data structure that it will be editing. For example, * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor. - * - * {@img Ext.grid.plugin.RowEditing/Ext.grid.plugin.RowEditing.png Ext.grid.plugin.RowEditing plugin} - * - * ## Example Usage * + * @example * Ext.create('Ext.data.Store', { * storeId:'simpsonsStore', * fields:['name', 'email', 'phone'], - * data:{'items':[ + * data: [ * {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"}, * {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"}, - * {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"}, - * {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"} - * ]}, - * proxy: { - * type: 'memory', - * reader: { - * type: 'json', - * root: 'items' - * } - * } + * {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"}, + * {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"} + * ] * }); - * + * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: Ext.data.StoreManager.lookup('simpsonsStore'), * columns: [ - * {header: 'Name', dataIndex: 'name', field: 'textfield'}, - * {header: 'Email', dataIndex: 'email', flex:1, + * {header: 'Name', dataIndex: 'name', editor: 'textfield'}, + * {header: 'Email', dataIndex: 'email', flex:1, * editor: { - * xtype:'textfield', - * allowBlank:false + * xtype: 'textfield', + * allowBlank: false * } * }, * {header: 'Phone', dataIndex: 'phone'} @@ -117377,9 +122267,8 @@ Ext.define('Ext.grid.plugin.RowEditing', { /** * @cfg {Boolean} autoCancel - * `true` to automatically cancel any pending changes when the row editor begins editing a new row. - * `false` to force the user to explicitly cancel the pending changes. Defaults to `true`. - * @markdown + * True to automatically cancel any pending changes when the row editor begins editing a new row. + * False to force the user to explicitly cancel the pending changes. Defaults to true. */ autoCancel: true, @@ -117387,90 +122276,90 @@ Ext.define('Ext.grid.plugin.RowEditing', { * @cfg {Number} clicksToMoveEditor * The number of clicks to move the row editor to a new row while it is visible and actively editing another row. * This will default to the same value as {@link Ext.grid.plugin.Editing#clicksToEdit clicksToEdit}. - * @markdown */ /** * @cfg {Boolean} errorSummary - * `true` to show a {@link Ext.tip.ToolTip tooltip} that summarizes all validation errors present - * in the row editor. Set to `false` to prevent the tooltip from showing. Defaults to `true`. - * @markdown + * True to show a {@link Ext.tip.ToolTip tooltip} that summarizes all validation errors present + * in the row editor. Set to false to prevent the tooltip from showing. Defaults to true. */ errorSummary: true, /** * @event beforeedit - * Fires before row editing is triggered. The edit event object has the following properties
        - *
          - *
        • grid - The grid this editor is on
        • - *
        • view - The grid view
        • - *
        • store - The grid store
        • - *
        • record - The record being edited
        • - *
        • row - The grid table row
        • - *
        • column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit
        • - *
        • rowIdx - The row index that is being edited
        • - *
        • colIdx - The column index that initiated the edit
        • - *
        • cancel - Set this to true to cancel the edit or return false from your handler.
        • - *
        + * Fires before row editing is triggered. + * * @param {Ext.grid.plugin.Editing} editor - * @param {Object} e An edit event (see above for description) + * @param {Object} e An edit event with the following properties: + * + * - grid - The grid this editor is on + * - view - The grid view + * - store - The grid store + * - record - The record being edited + * - row - The grid table row + * - column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit + * - rowIdx - The row index that is being edited + * - colIdx - The column index that initiated the edit + * - cancel - Set this to true to cancel the edit or return false from your handler. */ + + /** + * @event canceledit + * Fires when the user has started editing a row but then cancelled the edit + * @param {Object} grid The grid + */ + /** * @event edit - * Fires after a row is edited. The edit event object has the following properties
        - *
          - *
        • grid - The grid this editor is on
        • - *
        • view - The grid view
        • - *
        • store - The grid store
        • - *
        • record - The record being edited
        • - *
        • row - The grid table row
        • - *
        • column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit
        • - *
        • rowIdx - The row index that is being edited
        • - *
        • colIdx - The column index that initiated the edit
        • - *
        + * Fires after a row is edited. Usage example: + * + * grid.on('edit', function(editor, e) { + * // commit the changes right after editing finished + * e.record.commit(); + * }; * - *
        
        -grid.on('edit', onEdit, this);
        -
        -function onEdit(e) {
        -    // execute an XHR to send/commit data to the server, in callback do (if successful):
        -    e.record.commit();
        -};
        -     * 
        * @param {Ext.grid.plugin.Editing} editor - * @param {Object} e An edit event (see above for description) + * @param {Object} e An edit event with the following properties: + * + * - grid - The grid this editor is on + * - view - The grid view + * - store - The grid store + * - record - The record being edited + * - row - The grid table row + * - column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit + * - rowIdx - The row index that is being edited + * - colIdx - The column index that initiated the edit */ /** * @event validateedit - * Fires after a cell is edited, but before the value is set in the record. Return false - * to cancel the change. The edit event object has the following properties
        - *
          - *
        • grid - The grid this editor is on
        • - *
        • view - The grid view
        • - *
        • store - The grid store
        • - *
        • record - The record being edited
        • - *
        • row - The grid table row
        • - *
        • column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit
        • - *
        • rowIdx - The row index that is being edited
        • - *
        • colIdx - The column index that initiated the edit
        • - *
        • cancel - Set this to true to cancel the edit or return false from your handler.
        • - *
        - * Usage example showing how to remove the red triangle (dirty record indicator) from some - * records (not all). By observing the grid's validateedit event, it can be cancelled if - * the edit occurs on a targeted row (for example) and then setting the field's new value - * in the Record directly: - *
        
        -grid.on('validateedit', function(e) {
        -  var myTargetRow = 6;
        -
        -  if (e.rowIdx == myTargetRow) {
        -    e.cancel = true;
        -    e.record.data[e.field] = e.value;
        -  }
        -});
        -     * 
        + * Fires after a cell is edited, but before the value is set in the record. Return false to cancel the change. The + * edit event object has the following properties + * + * Usage example showing how to remove the red triangle (dirty record indicator) from some records (not all). By + * observing the grid's validateedit event, it can be cancelled if the edit occurs on a targeted row (for example) + * and then setting the field's new value in the Record directly: + * + * grid.on('validateedit', function(editor, e) { + * var myTargetRow = 6; + * + * if (e.rowIdx == myTargetRow) { + * e.cancel = true; + * e.record.data[e.field] = e.value; + * } + * }); + * * @param {Ext.grid.plugin.Editing} editor - * @param {Object} e An edit event (see above for description) + * @param {Object} e An edit event with the following properties: + * + * - grid - The grid this editor is on + * - view - The grid view + * - store - The grid store + * - record - The record being edited + * - row - The grid table row + * - column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit + * - rowIdx - The row index that is being edited + * - colIdx - The column index that initiated the edit + * - cancel - Set this to true to cancel the edit or return false from your handler. */ constructor: function() { @@ -117495,10 +122384,9 @@ grid.on('validateedit', function(e) { }, /** - * Start editing the specified record, using the specified Column definition to define which field is being edited. - * @param {Model} record The Store data record which backs the row to be edited. - * @param {Model} columnHeader The Column object defining the column to be edited. - * @override + * Starts editing the specified record, using the specified Column definition to define which field is being edited. + * @param {Ext.data.Model} record The Store data record which backs the row to be edited. + * @param {Ext.data.Model} columnHeader The Column object defining the column to be edited. @override */ startEdit: function(record, columnHeader) { var me = this, @@ -117521,6 +122409,8 @@ grid.on('validateedit', function(e) { if (me.editing) { me.getEditor().cancelEdit(); me.callParent(arguments); + + me.fireEvent('canceledit', me.context); } }, @@ -117536,7 +122426,26 @@ grid.on('validateedit', function(e) { // private validateEdit: function() { - var me = this; + var me = this, + editor = me.editor, + context = me.context, + record = context.record, + newValues = {}, + originalValues = {}, + name; + + editor.items.each(function(item) { + name = item.name; + + newValues[name] = item.getValue(); + originalValues[name] = record.get(name); + }); + + Ext.apply(context, { + newValues : newValues, + originalValues : originalValues + }); + return me.callParent(arguments) && me.getEditor().completeEdit(); }, @@ -117616,10 +122525,10 @@ grid.on('validateedit', function(e) { if (column.isHeader) { var me = this, editor; - + me.initFieldAccessors(column); editor = me.getEditor(); - + if (editor && editor.onColumnAdd) { editor.onColumnAdd(column); } @@ -117631,11 +122540,11 @@ grid.on('validateedit', function(e) { if (column.isHeader) { var me = this, editor = me.getEditor(); - + if (editor && editor.onColumnRemove) { editor.onColumnRemove(column); } - me.removeFieldAccessors(column); + me.removeFieldAccessors(column); } }, @@ -117644,7 +122553,7 @@ grid.on('validateedit', function(e) { if (column.isHeader) { var me = this, editor = me.getEditor(); - + if (editor && editor.onColumnResize) { editor.onColumnResize(column, width); } @@ -117691,26 +122600,28 @@ grid.on('validateedit', function(e) { me.getEditor().setField(column.field, column); } }); + /** * @class Ext.grid.property.Grid * @extends Ext.grid.Panel + * * A specialized grid implementation intended to mimic the traditional property grid as typically seen in * development IDEs. Each row in the grid represents a property of some object, and the data is stored * as a set of name/value pairs in {@link Ext.grid.property.Property Properties}. Example usage: - *
        
        -var grid = new Ext.grid.property.Grid({
        -    title: 'Properties Grid',
        -    width: 300,
        -    renderTo: 'grid-ct',
        -    source: {
        -        "(name)": "My Object",
        -        "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),
        -        "Available": false,
        -        "Version": .01,
        -        "Description": "A test object"
        -    }
        -});
        -
        + * + * @example + * Ext.create('Ext.grid.property.Grid', { + * title: 'Properties Grid', + * width: 300, + * renderTo: Ext.getBody(), + * source: { + * "(name)": "My Object", + * "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'), + * "Available": false, + * "Version": .01, + * "Description": "A test object" + * } + * }); */ Ext.define('Ext.grid.property.Grid', { @@ -117868,8 +122779,8 @@ var grid = Ext.create('Ext.grid.property.Grid', { * @param {Object} source The source data object for the grid (corresponds to the same object passed in * as the {@link #source} config property). * @param {String} recordId The record's id in the data store - * @param {Mixed} value The current edited property value - * @param {Mixed} oldValue The original property value prior to editing + * @param {Object} value The current edited property value + * @param {Object} oldValue The original property value prior to editing */ 'beforepropertychange', /** @@ -117878,8 +122789,8 @@ var grid = Ext.create('Ext.grid.property.Grid', { * @param {Object} source The source data object for the grid (corresponds to the same object passed in * as the {@link #source} config property). * @param {String} recordId The record's id in the data store - * @param {Mixed} value The current edited property value - * @param {Mixed} oldValue The original property value prior to editing + * @param {Object} value The current edited property value + * @param {Object} oldValue The original property value prior to editing */ 'propertychange' ); @@ -118019,7 +122930,7 @@ grid.setSource({ /** * Sets the value of a property. * @param {String} prop The name of the property to set - * @param {Mixed} value The value to test + * @param {Object} value The value to test * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to false. */ setProperty: function(prop, value, create) { @@ -118204,7 +123115,7 @@ Ext.define('Ext.grid.property.Store', { /** * Creates new property store. - * @param {Ext.grid.Grid} grid The grid this store will be bound to + * @param {Ext.grid.Panel} grid The grid this store will be bound to * @param {Object} source The source data config object */ constructor : function(grid, source){ @@ -118519,63 +123430,61 @@ Ext.define('Ext.layout.component.field.Slider', { /** * @class Ext.layout.container.Absolute * @extends Ext.layout.container.Anchor - *

        This is a layout that inherits the anchoring of {@link Ext.layout.container.Anchor} and adds the - * ability for x/y positioning using the standard x and y component config options.

        - *

        This class is intended to be extended or created via the {@link Ext.container.Container#layout layout} - * configuration property. See {@link Ext.container.Container#layout} for additional details.

        - * {@img Ext.layout.container.Absolute/Ext.layout.container.Absolute.png Ext.layout.container.Absolute container layout} - *

        Example usage:

        - *
        
        -Ext.create('Ext.form.Panel', {
        -    title: 'Absolute Layout',
        -    width: 300,
        -    height: 275,
        -    layout:'absolute',
        -    layoutConfig: {
        -        // layout-specific configs go here
        -        //itemCls: 'x-abs-layout-item',
        -    },
        -    url:'save-form.php',
        -    defaultType: 'textfield',
        -    items: [{
        -        x: 10,
        -        y: 10,
        -        xtype:'label',
        -        text: 'Send To:'
        -    },{
        -        x: 80,
        -        y: 10,
        -        name: 'to',
        -        anchor:'90%'  // anchor width by percentage
        -    },{
        -        x: 10,
        -        y: 40,
        -        xtype:'label',
        -        text: 'Subject:'
        -    },{
        -        x: 80,
        -        y: 40,
        -        name: 'subject',
        -        anchor: '90%'  // anchor width by percentage
        -    },{
        -        x:0,
        -        y: 80,
        -        xtype: 'textareafield',
        -        name: 'msg',
        -        anchor: '100% 100%'  // anchor width and height
        -    }],
        -    renderTo: Ext.getBody()
        -});
        -
        + * + * This is a layout that inherits the anchoring of {@link Ext.layout.container.Anchor} and adds the + * ability for x/y positioning using the standard x and y component config options. + * + * This class is intended to be extended or created via the {@link Ext.container.Container#layout layout} + * configuration property. See {@link Ext.container.Container#layout} for additional details. + * + * @example + * Ext.create('Ext.form.Panel', { + * title: 'Absolute Layout', + * width: 300, + * height: 275, + * layout:'absolute', + * layoutConfig: { + * // layout-specific configs go here + * //itemCls: 'x-abs-layout-item', + * }, + * url:'save-form.php', + * defaultType: 'textfield', + * items: [{ + * x: 10, + * y: 10, + * xtype:'label', + * text: 'Send To:' + * },{ + * x: 80, + * y: 10, + * name: 'to', + * anchor:'90%' // anchor width by percentage + * },{ + * x: 10, + * y: 40, + * xtype:'label', + * text: 'Subject:' + * },{ + * x: 80, + * y: 40, + * name: 'subject', + * anchor: '90%' // anchor width by percentage + * },{ + * x:0, + * y: 80, + * xtype: 'textareafield', + * name: 'msg', + * anchor: '100% 100%' // anchor width and height + * }], + * renderTo: Ext.getBody() + * }); */ - Ext.define('Ext.layout.container.Absolute', { /* Begin Definitions */ alias: 'layout.absolute', extend: 'Ext.layout.container.Anchor', - requires: ['Ext.chart.axis.Axis', 'Ext.fx.Anim'], alternateClassName: 'Ext.layout.AbsoluteLayout', /* End Definitions */ @@ -118613,98 +123522,104 @@ Ext.define('Ext.layout.container.Absolute', { /** * @class Ext.layout.container.Accordion * @extends Ext.layout.container.VBox - *

        This is a layout that manages multiple Panels in an expandable accordion style such that only - * one Panel can be expanded at any given time. Each Panel has built-in support for expanding and collapsing.

        - *

        Note: Only Ext.Panels and all subclasses of Ext.panel.Panel may be used in an accordion layout Container.

        - * {@img Ext.layout.container.Accordion/Ext.layout.container.Accordion.png Ext.layout.container.Accordion container layout} - *

        Example usage:

        - *
        
        -Ext.create('Ext.panel.Panel', {
        -    title: 'Accordion Layout',
        -    width: 300,
        -    height: 300,
        -    layout:'accordion',
        -    defaults: {
        -        // applied to each contained panel
        -        bodyStyle: 'padding:15px'
        -    },
        -    layoutConfig: {
        -        // layout-specific configs go here
        -        titleCollapse: false,
        -        animate: true,
        -        activeOnTop: true
        -    },
        -    items: [{
        -        title: 'Panel 1',
        -        html: 'Panel content!'
        -    },{
        -        title: 'Panel 2',
        -        html: 'Panel content!'
        -    },{
        -        title: 'Panel 3',
        -        html: 'Panel content!'
        -    }],
        -    renderTo: Ext.getBody()
        -});
        -
        + * + * This is a layout that manages multiple Panels in an expandable accordion style such that only + * **one Panel can be expanded at any given time**. Each Panel has built-in support for expanding and collapsing. + * + * Note: Only Ext Panels and all subclasses of Ext.panel.Panel may be used in an accordion layout Container. + * + * @example + * Ext.create('Ext.panel.Panel', { + * title: 'Accordion Layout', + * width: 300, + * height: 300, + * layout:'accordion', + * defaults: { + * // applied to each contained panel + * bodyStyle: 'padding:15px' + * }, + * layoutConfig: { + * // layout-specific configs go here + * titleCollapse: false, + * animate: true, + * activeOnTop: true + * }, + * items: [{ + * title: 'Panel 1', + * html: 'Panel content!' + * },{ + * title: 'Panel 2', + * html: 'Panel content!' + * },{ + * title: 'Panel 3', + * html: 'Panel content!' + * }], + * renderTo: Ext.getBody() + * }); */ Ext.define('Ext.layout.container.Accordion', { extend: 'Ext.layout.container.VBox', alias: ['layout.accordion'], alternateClassName: 'Ext.layout.AccordionLayout', + itemCls: Ext.baseCSSPrefix + 'box-item ' + Ext.baseCSSPrefix + 'accordion-item', + align: 'stretch', /** * @cfg {Boolean} fill * True to adjust the active item's height to fill the available space in the container, false to use the - * item's current height, or auto height if not explicitly set (defaults to true). + * item's current height, or auto height if not explicitly set. */ fill : true, + /** * @cfg {Boolean} autoWidth - *

        This config is ignored in ExtJS 4.x.

        * Child Panels have their width actively managed to fit within the accordion's width. + * @deprecated This config is ignored in ExtJS 4 */ autoWidth : true, + /** * @cfg {Boolean} titleCollapse - *

        Not implemented in PR2.

        * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow - * expand/collapse only when the toggle tool button is clicked (defaults to true). When set to false, + * expand/collapse only when the toggle tool button is clicked. When set to false, * {@link #hideCollapseTool} should be false also. */ titleCollapse : true, + /** * @cfg {Boolean} hideCollapseTool - * True to hide the contained Panels' collapse/expand toggle buttons, false to display them (defaults to false). + * True to hide the contained Panels' collapse/expand toggle buttons, false to display them. * When set to true, {@link #titleCollapse} is automatically set to true. */ hideCollapseTool : false, + /** * @cfg {Boolean} collapseFirst * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools - * in the contained Panels' title bars, false to render it last (defaults to false). + * in the contained Panels' title bars, false to render it last. */ collapseFirst : false, + /** * @cfg {Boolean} animate * True to slide the contained panels open and closed during expand/collapse using animation, false to open and - * close directly with no animation (defaults to true). Note: The layout performs animated collapsing + * close directly with no animation. Note: The layout performs animated collapsing * and expanding, not the child Panels. */ animate : true, /** * @cfg {Boolean} activeOnTop - *

        Not implemented in PR4.

        - *

        Only valid when {@link #multi" is false.

        + * Only valid when {@link #multi} is `false` and {@link #animate} is `false`. + * * True to swap the position of each panel as it is expanded so that it becomes the first item in the container, - * false to keep the panels in the rendered order. This is NOT compatible with "animate:true" (defaults to false). + * false to keep the panels in the rendered order. */ activeOnTop : false, /** * @cfg {Boolean} multi - * Defaults to false. Set to true to enable multiple accordion items to be open at once. + * Set to true to enable multiple accordion items to be open at once. */ multi: false, @@ -118730,7 +123645,7 @@ Ext.define('Ext.layout.container.Accordion', { me.callParent(arguments); if (me.fill) { - if (!me.owner.el.dom.style.height || !me.getLayoutTargetSize().height) { + if (!(me.owner.el.dom.style.height || me.getLayoutTargetSize().height)) { return false; } } else { @@ -118746,8 +123661,7 @@ Ext.define('Ext.layout.container.Accordion', { i = 0, comp, targetSize = me.getLayoutTargetSize(), - renderedPanels = [], - border; + renderedPanels = []; for (; i < ln; i++) { comp = items[i]; @@ -118800,6 +123714,7 @@ Ext.define('Ext.layout.container.Accordion', { comp.autoHeight = true; comp.autoScroll = false; } + comp.border = comp.collapsed; } } @@ -118807,7 +123722,7 @@ Ext.define('Ext.layout.container.Accordion', { if (ln && me.expandedItem === undefined) { me.expandedItem = 0; comp = items[0]; - comp.collapsed = false; + comp.collapsed = comp.border = false; if (me.fill) { comp.flex = 1; } @@ -118864,6 +123779,12 @@ Ext.define('Ext.layout.container.Accordion', { for (i = 0; i < ln; i++) { child = children[i]; + // Fix for EXTJSIV-3724. Windows only. + // Collapsing the Psnel's el to a size which only allows a single hesder to be visible, scrolls the header out of view. + if (Ext.isWindows) { + child.el.dom.scrollTop = 0; + } + if (siblingCollapsed) { child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded'); } @@ -118880,6 +123801,21 @@ Ext.define('Ext.layout.container.Accordion', { siblingCollapsed = child.collapsed; } }, + + animCallback: function(){ + Ext.Array.forEach(this.toCollapse, function(comp){ + comp.fireEvent('collapse', comp); + }); + + Ext.Array.forEach(this.toExpand, function(comp){ + comp.fireEvent('expand', comp); + }); + }, + + setupEvents: function(){ + this.toCollapse = []; + this.toExpand = []; + }, // When a Component expands, adjust the heights of the other Components to be just enough to accommodate // their headers. @@ -118891,6 +123827,7 @@ Ext.define('Ext.layout.container.Accordion', { i = 0, comp; + me.setupEvents(); for (; i < len; i++) { comp = it[i]; if (comp === toExpand && comp.collapsed) { @@ -118901,7 +123838,12 @@ Ext.define('Ext.layout.container.Accordion', { } me.animate = me.initialAnimate; - me.layout(); + if (me.activeOnTop) { + // insert will trigger a layout + me.owner.insert(0, toExpand); + } else { + me.layout(); + } me.animate = false; return false; }, @@ -118911,6 +123853,7 @@ Ext.define('Ext.layout.container.Accordion', { toExpand = comp.next() || comp.prev(), expanded = me.multi ? me.owner.query('>panel:not([collapsed])') : []; + me.setupEvents(); // If we are allowing multi, and the "toCollapse" component is NOT the only expanded Component, // then ask the box layout to collapse it to its header. if (me.multi) { @@ -118959,7 +123902,11 @@ Ext.define('Ext.layout.container.Accordion', { comp.el.setHeight(comp.height); comp.collapsed = true; delete comp.flex; - comp.fireEvent('collapse', comp); + if (this.initialAnimate) { + this.toCollapse.push(comp); + } else { + comp.fireEvent('collapse', comp); + } if (comp.collapseTool) { comp.collapseTool.setType('expand-' + comp.getOppositeDirection(comp.collapseDirection)); } @@ -118986,7 +123933,11 @@ Ext.define('Ext.layout.container.Accordion', { comp.flex = 1; comp.removeCls(comp.collapsedCls); comp.header.removeCls(comp.collapsedHeaderCls); - comp.fireEvent('expand', comp); + if (this.initialAnimate) { + this.toExpand.push(comp); + } else { + comp.fireEvent('expand', comp); + } if (comp.collapseTool) { comp.collapseTool.setType('collapse-' + comp.collapseDirection); } @@ -118994,17 +123945,17 @@ Ext.define('Ext.layout.container.Accordion', { } }); /** - * @class Ext.resizer.Splitter - * @extends Ext.Component - *

        This class functions between siblings of a {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox} - * layout to resize both immediate siblings.

        - *

        By default it will set the size of both siblings. One of the siblings may be configured with - * {@link Ext.Component#maintainFlex maintainFlex}: true which will cause it not to receive a new size explicitly, but to be resized - * by the layout.

        - *

        A Splitter may be configured to show a centered mini-collapse tool orientated to collapse the {@link #collapseTarget}. + * This class functions between siblings of a {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox} + * layout to resize both immediate siblings. + * + * By default it will set the size of both siblings. One of the siblings may be configured with + * `{@link Ext.Component#maintainFlex maintainFlex}: true` which will cause it not to receive a new size explicitly, but to be resized + * by the layout. + * + * A Splitter may be configured to show a centered mini-collapse tool orientated to collapse the {@link #collapseTarget}. * The Splitter will then call that sibling Panel's {@link Ext.panel.Panel#collapse collapse} or {@link Ext.panel.Panel#expand expand} method * to perform the appropriate operation (depending on the sibling collapse state). To create the mini-collapse tool but take care - * of collapsing yourself, configure the splitter with {@link #performCollapse} false.

        + * of collapsing yourself, configure the splitter with {@link #performCollapse} false. */ Ext.define('Ext.resizer.Splitter', { extend: 'Ext.Component', @@ -119013,7 +123964,10 @@ Ext.define('Ext.resizer.Splitter', { alias: 'widget.splitter', renderTpl: [ - '
         
        ' + '', + '
         
        ', + '
        ' ], baseCls: Ext.baseCSSPrefix + 'splitter', @@ -119051,7 +124005,7 @@ Ext.define('Ext.resizer.Splitter', { * that the splitter is between. */ defaultSplitMax: 1000, - + /** * @cfg {String} collapsedCls * A class to add to the splitter when it is collapsed. See {@link #collapsible}. @@ -119061,7 +124015,7 @@ Ext.define('Ext.resizer.Splitter', { height: 5, /** - * @cfg {Mixed} collapseTarget + * @cfg {String/Ext.panel.Panel} collapseTarget *

        A string describing the relative position of the immediate sibling Panel to collapse. May be 'prev' or 'next' (Defaults to 'next')

        *

        Or the immediate sibling Panel to collapse.

        *

        The orientation of the mini-collapse tool will be inferred from this setting.

        @@ -119085,9 +124039,8 @@ Ext.define('Ext.resizer.Splitter', { collapseDir: collapseDir, collapsible: me.collapsible || target.collapsible }); - Ext.applyIf(me.renderSelectors, { - collapseEl: '.' + Ext.baseCSSPrefix + 'collapse-el' - }); + + me.addChildEls('collapseEl'); this.callParent(arguments); @@ -119141,7 +124094,7 @@ Ext.define('Ext.resizer.Splitter', { getCollapseTarget: function() { var me = this; - + return me.collapseTarget.isComponent ? me.collapseTarget : me.collapseTarget == 'prev' ? me.previousSibling() : me.nextSibling(); }, @@ -119180,72 +124133,76 @@ Ext.define('Ext.resizer.Splitter', { }); /** - * @class Ext.layout.container.Border - * @extends Ext.layout.container.Container - *

        This is a multi-pane, application-oriented UI layout style that supports multiple - * nested panels, automatic bars between regions and built-in - * {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.

        - *

        This class is intended to be extended or created via the layout:'border' - * {@link Ext.container.Container#layout} config, and should generally not need to be created directly - * via the new keyword.

        - * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout} - *

        Example usage:

        - *
        
        -     Ext.create('Ext.panel.Panel', {
        -        width: 500,
        -        height: 400,
        -        title: 'Border Layout',
        -        layout: 'border',
        -        items: [{
        -            title: 'South Region is resizable',
        -            region: 'south',     // position for region
        -            xtype: 'panel',
        -            height: 100,
        -            split: true,         // enable resizing
        -            margins: '0 5 5 5'
        -        },{
        -            // xtype: 'panel' implied by default
        -            title: 'West Region is collapsible',
        -            region:'west',
        -            xtype: 'panel',
        -            margins: '5 0 0 5',
        -            width: 200,
        -            collapsible: true,   // make collapsible
        -            id: 'west-region-container',
        -            layout: 'fit'
        -        },{
        -            title: 'Center Region',
        -            region: 'center',     // center region is required, no width/height specified
        -            xtype: 'panel',
        -            layout: 'fit',
        -            margins: '5 5 0 0'
        -        }],
        -        renderTo: Ext.getBody()
        -    });
        -
        - *

        Notes:

          - *
        • Any Container using the Border layout must have a child item with region:'center'. - * The child item in the center region will always be resized to fill the remaining space not used by - * the other regions in the layout.
        • - *
        • Any child items with a region of west or east may be configured with either - * an initial width, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width string (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of 1.
        • - *
        • Any child items with a region of north or south may be configured with either - * an initial height, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height string (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of 1.
        • - *
        • The regions of a BorderLayout are fixed at render time and thereafter, its child Components may not be removed or added.To add/remove - * Components within a BorderLayout, have them wrapped by an additional Container which is directly - * managed by the BorderLayout. If the region is to be collapsible, the Container used directly - * by the BorderLayout manager should be a Panel. In the following example a Container (an Ext.panel.Panel) - * is added to the west region:
          
          -wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
          -wrc.{@link Ext.container.Container#removeAll removeAll}();
          -wrc.{@link Ext.container.Container#add add}({
          -    title: 'Added Panel',
          -    html: 'Some content'
          -});
          - * 
          - *
        • - *
        • There is no BorderLayout.Region class in ExtJS 4.0+
        • - *
        + * This is a multi-pane, application-oriented UI layout style that supports multiple nested panels, automatic bars + * between regions and built-in {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions. + * + * This class is intended to be extended or created via the `layout:'border'` {@link Ext.container.Container#layout} + * config, and should generally not need to be created directly via the new keyword. + * + * @example + * Ext.create('Ext.panel.Panel', { + * width: 500, + * height: 400, + * title: 'Border Layout', + * layout: 'border', + * items: [{ + * title: 'South Region is resizable', + * region: 'south', // position for region + * xtype: 'panel', + * height: 100, + * split: true, // enable resizing + * margins: '0 5 5 5' + * },{ + * // xtype: 'panel' implied by default + * title: 'West Region is collapsible', + * region:'west', + * xtype: 'panel', + * margins: '5 0 0 5', + * width: 200, + * collapsible: true, // make collapsible + * id: 'west-region-container', + * layout: 'fit' + * },{ + * title: 'Center Region', + * region: 'center', // center region is required, no width/height specified + * xtype: 'panel', + * layout: 'fit', + * margins: '5 5 0 0' + * }], + * renderTo: Ext.getBody() + * }); + * + * # Notes + * + * - Any Container using the Border layout **must** have a child item with `region:'center'`. + * The child item in the center region will always be resized to fill the remaining space + * not used by the other regions in the layout. + * + * - Any child items with a region of `west` or `east` may be configured with either an initial + * `width`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width + * **string** (Which is simply divided by 100 and used as a flex value). + * The 'center' region has a flex value of `1`. + * + * - Any child items with a region of `north` or `south` may be configured with either an initial + * `height`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height + * **string** (Which is simply divided by 100 and used as a flex value). + * The 'center' region has a flex value of `1`. + * + * - The regions of a BorderLayout are **fixed at render time** and thereafter, its child + * Components may not be removed or added**. To add/remove Components within a BorderLayout, + * have them wrapped by an additional Container which is directly managed by the BorderLayout. + * If the region is to be collapsible, the Container used directly by the BorderLayout manager + * should be a Panel. In the following example a Container (an Ext.panel.Panel) is added to + * the west region: + * + * wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container'); + * wrc.{@link Ext.container.Container#removeAll removeAll}(); + * wrc.{@link Ext.container.Container#add add}({ + * title: 'Added Panel', + * html: 'Some content' + * }); + * + * - **There is no BorderLayout.Region class in ExtJS 4.0+** */ Ext.define('Ext.layout.container.Border', { @@ -119315,6 +124272,8 @@ Ext.define('Ext.layout.container.Border', { // Delegate this operation to the shadow "V" or "H" box layout. this.shadowLayout.beforeLayout(); + + // note: don't call base because that does a renderItems again }, renderItems: function(items, target) { @@ -119325,6 +124284,14 @@ Ext.define('Ext.layout.container.Border', { Ext.Error.raise('This should not be called'); }, + renderChildren: function() { + if (!this.borderLayoutInitialized) { + this.initializeBorderLayout(); + } + + this.shadowLayout.renderChildren(); + }, + /* * Gathers items for a layout operation. Injected into child Box layouts through configuration. * We must not include child items which are floated over the layout (are primed with a slide out animation) @@ -119358,7 +124325,7 @@ Ext.define('Ext.layout.container.Border', { // This layout intercepts any initial collapsed state. Panel must not do this itself. comp.borderCollapse = comp.collapsed; - delete comp.collapsed; + comp.collapsed = false; comp.on({ beforecollapse: me.onBeforeRegionCollapse, @@ -119678,13 +124645,16 @@ Ext.define('Ext.layout.container.Border', { }, /** - *

        Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the layout will collapse. - * By default, this will be a {@link Ext.panel.Header Header} component (Docked to the appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}. - * config to customize this.

        - *

        Note that this will be a fully instantiated Component, but will only be rendered when the Panel is first collapsed.

        - * @param {Panel} panel The child Panel of the layout for which to return the {@link Ext.panel.Panel#placeholder placeholder}. - * @returns {Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link Ext.panel.Panel#collapseMode collapseMode} is - * 'header', in which case undefined is returned. + * Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the + * layout will collapse. By default, this will be a {@link Ext.panel.Header Header} component (Docked to the + * appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}. config to customize this. + * + * **Note that this will be a fully instantiated Component, but will only be _rendered_ when the Panel is first + * collapsed.** + * @param {Ext.panel.Panel} panel The child Panel of the layout for which to return the {@link + * Ext.panel.Panel#placeholder placeholder}. + * @return {Ext.Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link + * Ext.panel.Panel#collapseMode collapseMode} is `'header'`, in which case _undefined_ is returned. */ getPlaceholder: function(comp) { var me = this, @@ -119787,7 +124757,7 @@ Ext.define('Ext.layout.container.Border', { * @private * Calculates the size and positioning of the passed child item. Must be present because Panel's expand, * when configured with a flex, calls this method on its ownerCt's layout. - * @param {Component} child The child Component to calculate the box for + * @param {Ext.Component} child The child Component to calculate the box for * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height. */ calculateChildBox: function(comp) { @@ -119810,6 +124780,13 @@ Ext.define('Ext.layout.container.Border', { * @returns {Boolean} false to inhibit the Panel from performing its own collapse. */ onBeforeRegionCollapse: function(comp, direction, animate) { + if (comp.collapsedChangingLayout) { + if (Ext.global.console && Ext.global.console.warn) { + Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing'); + } + return false; + } + comp.collapsedChangingLayout = true; var me = this, compEl = comp.el, width, @@ -119884,6 +124861,7 @@ Ext.define('Ext.layout.container.Border', { delete me.shadowContainer.layout.layoutBusy; delete me.layoutBusy; delete me.owner.componentLayout.layoutBusy; + delete comp.collapsedChangingLayout; // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component comp.collapsed = true; @@ -119932,7 +124910,8 @@ Ext.define('Ext.layout.container.Border', { // Hijack the expand operation to remove the placeholder and slide the region back in. onBeforeRegionExpand: function(comp, animate) { - this.onPlaceHolderToolClick(null, null, null, {client: comp}); + // We don't check for comp.collapsedChangingLayout here because onPlaceHolderToolClick does it + this.onPlaceHolderToolClick(null, null, null, {client: comp, shouldFireBeforeexpand: false}); return false; }, @@ -119954,6 +124933,16 @@ Ext.define('Ext.layout.container.Border', { scsl = shadowContainer.suspendLayout, isFloating; + if (comp.collapsedChangingLayout) { + if (Ext.global.console && Ext.global.console.warn) { + Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing'); + } + return false; + } + if (tool.shouldFireBeforeexpand !== false && comp.fireEvent('beforeexpand', comp, true) === false) { + return false; + } + comp.collapsedChangingLayout = true; // If the slide in is still going, stop it. // This will either leave the Component in its fully floated state (which is processed below) // or in its collapsed state. Either way, we expand it.. @@ -120029,6 +125018,7 @@ Ext.define('Ext.layout.container.Border', { delete me.shadowContainer.layout.layoutBusy; delete me.layoutBusy; delete me.owner.componentLayout.layoutBusy; + delete comp.collapsedChangingLayout; // In case it was floated out and they clicked the re-expand tool comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in'); @@ -120244,29 +125234,36 @@ Ext.define('Ext.layout.container.Border', { }); /** - * @class Ext.layout.container.Card - * @extends Ext.layout.container.AbstractCard - * * This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be * visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc. * This class is intended to be extended or created via the layout:'card' {@link Ext.container.Container#layout} config, * and should generally not need to be created directly via the new keyword. * * The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time, - * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of - * the next panel to display. The layout itself does not provide a user interface for handling this navigation, + * the only way to move from one Component to the next is by calling setActiveItem, passing the next panel to display + * (or its id or index). The layout itself does not provide a user interface for handling this navigation, * so that functionality must be provided by the developer. * + * To change the active card of a container, call the setActiveItem method of its layout: + * + * Ext.create('Ext.panel.Panel', { + * layout: 'card', + * items: [ + * { html: 'Card 1' }, + * { html: 'Card 2' } + * ], + * renderTo: Ext.getBody() + * }); + * + * p.getLayout().setActiveItem(1); + * * In the following example, a simplistic wizard setup is demonstrated. A button bar is added * to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a * common navigation routine. Note that other uses of a CardLayout (like a tab control) would require a * completely different implementation. For serious implementations, a better approach would be to extend * CardLayout to provide the custom functionality needed. * - * {@img Ext.layout.container.Card/Ext.layout.container.Card.png Ext.layout.container.Card container layout} - * - * Example usage: - * + * @example * var navigate = function(panel, direction){ * // This routine could contain business logic required to manage the navigation steps. * // It would call setActiveItem as needed, manage navigation button state, handle any @@ -120279,13 +125276,12 @@ Ext.define('Ext.layout.container.Border', { * Ext.getCmp('move-prev').setDisabled(!layout.getPrev()); * Ext.getCmp('move-next').setDisabled(!layout.getNext()); * }; - * + * * Ext.create('Ext.panel.Panel', { * title: 'Example Wizard', * width: 300, * height: 200, * layout: 'card', - * activeItem: 0, // make sure the active item is set on the container config! * bodyStyle: 'padding:15px', * defaults: { * // applied to each contained panel @@ -120322,7 +125318,7 @@ Ext.define('Ext.layout.container.Border', { * html: '

        Congratulations!

        Step 3 of 3 - Complete

        ' * }], * renderTo: Ext.getBody() - * }); + * }); */ Ext.define('Ext.layout.container.Card', { @@ -120337,7 +125333,7 @@ Ext.define('Ext.layout.container.Card', { /** * Makes the given card active. - * + * * var card1 = Ext.create('Ext.panel.Panel', {itemId: 'card-1'}); * var card2 = Ext.create('Ext.panel.Panel', {itemId: 'card-2'}); * var panel = Ext.create('Ext.panel.Panel', { @@ -120349,7 +125345,7 @@ Ext.define('Ext.layout.container.Card', { * panel.getLayout().setActiveItem(card2); * panel.getLayout().setActiveItem('card-2'); * panel.getLayout().setActiveItem(1); - * + * * @param {Ext.Component/Number/String} newCard The component, component {@link Ext.Component#id id}, * {@link Ext.Component#itemId itemId}, or index of component. * @return {Ext.Component} the activated component or false when nothing activated. @@ -120422,7 +125418,6 @@ Ext.define('Ext.layout.container.Card', { }, configureItem: function(item) { - // Card layout only controls dimensions which IT has controlled. // That calculation has to be determined at run time by examining the ownerCt's isFixedWidth()/isFixedHeight() methods item.layoutManagedHeight = 0; @@ -120431,72 +125426,70 @@ Ext.define('Ext.layout.container.Card', { this.callParent(arguments); }}); /** - * @class Ext.layout.container.Column - * @extends Ext.layout.container.Auto - *

        This is the layout style of choice for creating structural layouts in a multi-column format where the width of - * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content. - * This class is intended to be extended or created via the layout:'column' {@link Ext.container.Container#layout} config, - * and should generally not need to be created directly via the new keyword.

        - *

        ColumnLayout does not have any direct config options (other than inherited ones), but it does support a - * specific config property of columnWidth that can be included in the config of any panel added to it. The - * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel. - * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).

        - *

        The width property is always evaluated as pixels, and must be a number greater than or equal to 1. - * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and - * less than 1 (e.g., .25).

        - *

        The basic rules for specifying column widths are pretty simple. The logic makes two passes through the - * set of contained panels. During the first layout pass, all panels that either have a fixed width or none - * specified (auto) are skipped, but their widths are subtracted from the overall container width. During the second - * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on - * the total remaining container width. In other words, percentage width panels are designed to fill the space - * left over by all the fixed-width and/or auto-width panels. Because of this, while you can specify any number of columns - * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your - * layout may not render as expected. - * {@img Ext.layout.container.Column/Ext.layout.container.Column1.png Ext.layout.container.Column container layout} - * Example usage:

        - *
        
        -    // All columns are percentages -- they must add up to 1
        -    Ext.create('Ext.panel.Panel', {
        -        title: 'Column Layout - Percentage Only',
        -        width: 350,
        -        height: 250,
        -        layout:'column',
        -        items: [{
        -            title: 'Column 1',
        -            columnWidth: .25
        -        },{
        -            title: 'Column 2',
        -            columnWidth: .55
        -        },{
        -            title: 'Column 3',
        -            columnWidth: .20
        -        }],
        -        renderTo: Ext.getBody()
        -    }); 
        -
        -// {@img Ext.layout.container.Column/Ext.layout.container.Column2.png Ext.layout.container.Column container layout}
        -// Mix of width and columnWidth -- all columnWidth values must add up
        -// to 1. The first column will take up exactly 120px, and the last two
        -// columns will fill the remaining container width.
        -
        -    Ext.create('Ext.Panel', {
        -        title: 'Column Layout - Mixed',
        -        width: 350,
        -        height: 250,
        -        layout:'column',
        -        items: [{
        -            title: 'Column 1',
        -            width: 120
        -        },{
        -            title: 'Column 2',
        -            columnWidth: .7
        -        },{
        -            title: 'Column 3',
        -            columnWidth: .3
        -        }],
        -        renderTo: Ext.getBody()
        -    }); 
        -
        + * This is the layout style of choice for creating structural layouts in a multi-column format where the width of each + * column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content. This + * class is intended to be extended or created via the layout:'column' {@link Ext.container.Container#layout} config, + * and should generally not need to be created directly via the new keyword. + * + * ColumnLayout does not have any direct config options (other than inherited ones), but it does support a specific + * config property of `columnWidth` that can be included in the config of any panel added to it. The layout will use + * the columnWidth (if present) or width of each panel during layout to determine how to size each panel. If width or + * columnWidth is not specified for a given panel, its width will default to the panel's width (or auto). + * + * The width property is always evaluated as pixels, and must be a number greater than or equal to 1. The columnWidth + * property is always evaluated as a percentage, and must be a decimal value greater than 0 and less than 1 (e.g., .25). + * + * The basic rules for specifying column widths are pretty simple. The logic makes two passes through the set of + * contained panels. During the first layout pass, all panels that either have a fixed width or none specified (auto) + * are skipped, but their widths are subtracted from the overall container width. + * + * During the second pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages + * based on the total **remaining** container width. In other words, percentage width panels are designed to fill + * the space left over by all the fixed-width and/or auto-width panels. Because of this, while you can specify any + * number of columns with different percentages, the columnWidths must always add up to 1 (or 100%) when added + * together, otherwise your layout may not render as expected. + * + * @example + * // All columns are percentages -- they must add up to 1 + * Ext.create('Ext.panel.Panel', { + * title: 'Column Layout - Percentage Only', + * width: 350, + * height: 250, + * layout:'column', + * items: [{ + * title: 'Column 1', + * columnWidth: .25 + * },{ + * title: 'Column 2', + * columnWidth: .55 + * },{ + * title: 'Column 3', + * columnWidth: .20 + * }], + * renderTo: Ext.getBody() + * }); + * + * // Mix of width and columnWidth -- all columnWidth values must add up + * // to 1. The first column will take up exactly 120px, and the last two + * // columns will fill the remaining container width. + * + * Ext.create('Ext.Panel', { + * title: 'Column Layout - Mixed', + * width: 350, + * height: 250, + * layout:'column', + * items: [{ + * title: 'Column 1', + * width: 120 + * },{ + * title: 'Column 2', + * columnWidth: .7 + * },{ + * title: 'Column 3', + * columnWidth: .3 + * }], + * renderTo: Ext.getBody() + * }); */ Ext.define('Ext.layout.container.Column', { @@ -120589,9 +125582,9 @@ Ext.define('Ext.layout.container.Column', { item = items[i]; if (item.columnWidth) { columnWidth = Math.floor(item.columnWidth * availableWidth) - parallelMargins[i]; - if (item.getWidth() != columnWidth) { - me.setItemSize(item, columnWidth, item.height); - } + me.setItemSize(item, columnWidth, item.height); + } else { + me.layoutItem(item); } } @@ -120616,79 +125609,67 @@ Ext.define('Ext.layout.container.Column', { }, configureItem: function(item) { + this.callParent(arguments); + if (item.columnWidth) { item.layoutManagedWidth = 1; - } else { - item.layoutManagedWidth = 2; } - item.layoutManagedHeight = 2; - this.callParent(arguments); } }); /** - * @class Ext.layout.container.Table - * @extends Ext.layout.container.Auto - *

        This layout allows you to easily render content into an HTML table. The total number of columns can be - * specified, and rowspan and colspan can be used to create complex layouts within the table. - * This class is intended to be extended or created via the layout: {type: 'table'} - * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword.

        - *

        Note that when creating a layout via config, the layout-specific config properties must be passed in via - * the {@link Ext.container.Container#layout} object which will then be applied internally to the layout. In the - * case of TableLayout, the only valid layout config properties are {@link #columns} and {@link #tableAttrs}. - * However, the items added to a TableLayout can supply the following table-specific config properties:

        - *
          - *
        • rowspan Applied to the table cell containing the item.
        • - *
        • colspan Applied to the table cell containing the item.
        • - *
        • cellId An id applied to the table cell containing the item.
        • - *
        • cellCls A CSS class name added to the table cell containing the item.
        • - *
        - *

        The basic concept of building up a TableLayout is conceptually very similar to building up a standard - * HTML table. You simply add each panel (or "cell") that you want to include along with any span attributes - * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts. - * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the - * total column count in the layoutConfig and start adding panels in their natural order from left to right, - * top to bottom. The layout will automatically figure out, based on the column count, rowspans and colspans, - * how to position each panel within the table. Just like with HTML tables, your rowspans and colspans must add - * up correctly in your overall layout or you'll end up with missing and/or extra cells! Example usage:

        - * {@img Ext.layout.container.Table/Ext.layout.container.Table.png Ext.layout.container.Table container layout} - *
        
        -// This code will generate a layout table that is 3 columns by 2 rows
        -// with some spanning included.  The basic layout will be:
        -// +--------+-----------------+
        -// |   A    |   B             |
        -// |        |--------+--------|
        -// |        |   C    |   D    |
        -// +--------+--------+--------+
        -    Ext.create('Ext.panel.Panel', {
        -        title: 'Table Layout',
        -        width: 300,
        -        height: 150,
        -        layout: {
        -            type: 'table',
        -            // The total column count must be specified here
        -            columns: 3
        -        },
        -        defaults: {
        -            // applied to each contained panel
        -            bodyStyle:'padding:20px'
        -        },
        -        items: [{
        -            html: 'Cell A content',
        -            rowspan: 2
        -        },{
        -            html: 'Cell B content',
        -            colspan: 2
        -        },{
        -            html: 'Cell C content',
        -            cellCls: 'highlight'
        -        },{
        -            html: 'Cell D content'
        -        }],
        -        renderTo: Ext.getBody()
        -    });
        -
        + * This layout allows you to easily render content into an HTML table. The total number of columns can be specified, and + * rowspan and colspan can be used to create complex layouts within the table. This class is intended to be extended or + * created via the `layout: {type: 'table'}` {@link Ext.container.Container#layout} config, and should generally not + * need to be created directly via the new keyword. + * + * Note that when creating a layout via config, the layout-specific config properties must be passed in via the {@link + * Ext.container.Container#layout} object which will then be applied internally to the layout. In the case of + * TableLayout, the only valid layout config properties are {@link #columns} and {@link #tableAttrs}. However, the items + * added to a TableLayout can supply the following table-specific config properties: + * + * - **rowspan** Applied to the table cell containing the item. + * - **colspan** Applied to the table cell containing the item. + * - **cellId** An id applied to the table cell containing the item. + * - **cellCls** A CSS class name added to the table cell containing the item. + * + * The basic concept of building up a TableLayout is conceptually very similar to building up a standard HTML table. You + * simply add each panel (or "cell") that you want to include along with any span attributes specified as the special + * config properties of rowspan and colspan which work exactly like their HTML counterparts. Rather than explicitly + * creating and nesting rows and columns as you would in HTML, you simply specify the total column count in the + * layoutConfig and start adding panels in their natural order from left to right, top to bottom. The layout will + * automatically figure out, based on the column count, rowspans and colspans, how to position each panel within the + * table. Just like with HTML tables, your rowspans and colspans must add up correctly in your overall layout or you'll + * end up with missing and/or extra cells! Example usage: + * + * @example + * Ext.create('Ext.panel.Panel', { + * title: 'Table Layout', + * width: 300, + * height: 150, + * layout: { + * type: 'table', + * // The total column count must be specified here + * columns: 3 + * }, + * defaults: { + * // applied to each contained panel + * bodyStyle: 'padding:20px' + * }, + * items: [{ + * html: 'Cell A content', + * rowspan: 2 + * },{ + * html: 'Cell B content', + * colspan: 2 + * },{ + * html: 'Cell C content', + * cellCls: 'highlight' + * },{ + * html: 'Cell D content' + * }], + * renderTo: Ext.getBody() + * }); */ - Ext.define('Ext.layout.container.Table', { /* Begin Definitions */ @@ -120701,7 +125682,7 @@ Ext.define('Ext.layout.container.Table', { /** * @cfg {Number} columns - * The total number of columns to create in the table for this layout. If not specified, all Components added to + * The total number of columns to create in the table for this layout. If not specified, all Components added to * this layout will be rendered into a single row using one column per Component. */ @@ -120722,33 +125703,34 @@ Ext.define('Ext.layout.container.Table', { /** * @cfg {Object} tableAttrs - *

        An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification - * used to create the layout's <table> element. Example:

        
        -{
        -    xtype: 'panel',
        -    layout: {
        -        type: 'table',
        -        columns: 3,
        -        tableAttrs: {
        -            style: {
        -                width: '100%'
        -            }
        -        }
        -    }
        -}
        + * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification used to + * create the layout's `
        ` element. Example: + * + * { + * xtype: 'panel', + * layout: { + * type: 'table', + * columns: 3, + * tableAttrs: { + * style: { + * width: '100%' + * } + * } + * } + * } */ tableAttrs:null, /** * @cfg {Object} trAttrs - *

        An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification - * used to create the layout's <tr> elements. + * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification used to + * create the layout's

        elements. */ /** * @cfg {Object} tdAttrs - *

        An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification - * used to create the layout's <td> elements. + * An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification used to + * create the layout's

        elements. */ /** @@ -120842,7 +125824,7 @@ Ext.define('Ext.layout.container.Table', { * Determine the row and cell indexes for each component, taking into consideration * the number of columns and each item's configured colspan/rowspan values. * @param {Array} items The layout components - * @return {Array} List of row and cell indexes for each of the components + * @return {Object[]} List of row and cell indexes for each of the components */ calculateCells: function(items) { var cells = [], @@ -120929,16 +125911,10 @@ Ext.define('Ext.layout.container.Table', { } }); /** - * @class Ext.menu.Item - * @extends Ext.Component - * * A base class for all menu items that require menu-related functionality such as click handling, * sub-menus, icons, etc. * - * {@img Ext.menu.Menu/Ext.menu.Menu.png Ext.menu.Menu component} - * - * __Example Usage:__ - * + * @example * Ext.create('Ext.menu.Menu', { * width: 100, * height: 100, @@ -120948,13 +125924,12 @@ Ext.define('Ext.layout.container.Table', { * text: 'icon item', * iconCls: 'add16' * },{ - * text: 'text item', + * text: 'text item' * },{ * text: 'plain item', * plain: true * }] * }); - * */ Ext.define('Ext.menu.Item', { extend: 'Ext.Component', @@ -120966,11 +125941,15 @@ Ext.define('Ext.menu.Item', { * Whether or not this item is currently activated */ + /** + * @property {Ext.menu.Menu} parentMenu + * The parent Menu of this item. + */ + /** * @cfg {String} activeCls * The CSS class added to the menu item when the item is activated (focused/mouseover). * Defaults to `Ext.baseCSSPrefix + 'menu-item-active'`. - * @markdown */ activeCls: Ext.baseCSSPrefix + 'menu-item-active', @@ -120982,7 +125961,6 @@ Ext.define('Ext.menu.Item', { /** * @cfg {Boolean} canActivate * Whether or not this menu item can be activated when focused/mouseovered. Defaults to `true`. - * @markdown */ canActivate: true, @@ -120990,7 +125968,6 @@ Ext.define('Ext.menu.Item', { * @cfg {Number} clickHideDelay * The delay in milliseconds to wait before hiding the menu after clicking the menu item. * This only has an effect when `hideOnClick: true`. Defaults to `1`. - * @markdown */ clickHideDelay: 1, @@ -121004,7 +125981,6 @@ Ext.define('Ext.menu.Item', { * @cfg {String} disabledCls * The CSS class added to the menu item when the item is disabled. * Defaults to `Ext.baseCSSPrefix + 'menu-item-disabled'`. - * @markdown */ disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled', @@ -121051,7 +126027,7 @@ Ext.define('Ext.menu.Item', { /** * @cfg {String} menuAlign - * The default {@link Ext.core.Element#getAlignToXY Ext.Element.getAlignToXY} anchor position value for this + * The default {@link Ext.Element#getAlignToXY Ext.Element.getAlignToXY} anchor position value for this * item's sub-menu relative to this item's position. Defaults to `'tl-tr?'`. * @markdown */ @@ -121082,11 +126058,11 @@ Ext.define('Ext.menu.Item', { '{text}', '', '', - 'target="{hrefTarget}" hidefocus="true" unselectable="on">', - '', - 'style="margin-right: 17px;" >{text}', + 'target="{hrefTarget}" hidefocus="true" unselectable="on">', + '', + 'style="margin-right: 17px;" >{text}', '', - '', + '', '', '', '' @@ -121276,24 +126252,20 @@ Ext.define('Ext.menu.Item', { onRender: function(ct, pos) { var me = this, - prefix = '.' + Ext.baseCSSPrefix; + blank = Ext.BLANK_IMAGE_URL; Ext.applyIf(me.renderData, { href: me.href || '#', hrefTarget: me.hrefTarget, - icon: me.icon || Ext.BLANK_IMAGE_URL, + icon: me.icon || blank, iconCls: me.iconCls + (me.checkChangeDisabled ? ' ' + me.disabledCls : ''), menu: Ext.isDefined(me.menu), plain: me.plain, - text: me.text + text: me.text, + blank: blank }); - Ext.applyIf(me.renderSelectors, { - itemEl: prefix + 'menu-item-link', - iconEl: prefix + 'menu-item-icon', - textEl: prefix + 'menu-item-text', - arrowEl: prefix + 'menu-item-arrow' - }); + me.addChildEls('itemEl', 'iconEl', 'textEl', 'arrowEl'); me.callParent(arguments); }, @@ -121347,15 +126319,9 @@ Ext.define('Ext.menu.Item', { }); /** - * @class Ext.menu.CheckItem - * @extends Ext.menu.Item - * * A menu item that contains a togglable checkbox by default, but that can also be a part of a radio group. * - * {@img Ext.menu.CheckItem/Ext.menu.CheckItem.png Ext.menu.CheckItem component} - * - * __Example Usage__ - * + * @example * Ext.create('Ext.menu.Menu', { * width: 100, * height: 110, @@ -121373,8 +126339,7 @@ Ext.define('Ext.menu.Item', { * },{ * text: 'regular item' * }] - * }); - * + * }); */ Ext.define('Ext.menu.CheckItem', { extend: 'Ext.menu.Item', @@ -121384,14 +126349,12 @@ Ext.define('Ext.menu.CheckItem', { * @cfg {String} checkedCls * The CSS class used by {@link #cls} to show the checked state. * Defaults to `Ext.baseCSSPrefix + 'menu-item-checked'`. - * @markdown */ checkedCls: Ext.baseCSSPrefix + 'menu-item-checked', /** * @cfg {String} uncheckedCls * The CSS class used by {@link #cls} to show the unchecked state. * Defaults to `Ext.baseCSSPrefix + 'menu-item-unchecked'`. - * @markdown */ uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked', /** @@ -121399,7 +126362,6 @@ Ext.define('Ext.menu.CheckItem', { * The CSS class applied to this item's icon image to denote being a part of a radio group. * Defaults to `Ext.baseCSSClass + 'menu-group-icon'`. * Any specified {@link #iconCls} overrides this. - * @markdown */ groupCls: Ext.baseCSSPrefix + 'menu-group-icon', @@ -121407,7 +126369,6 @@ Ext.define('Ext.menu.CheckItem', { * @cfg {Boolean} hideOnClick * Whether to not to hide the owning menu when this item is clicked. * Defaults to `false` for checkbox items, and to `true` for radio group items. - * @markdown */ hideOnClick: false, @@ -121492,7 +126453,6 @@ Ext.define('Ext.menu.CheckItem', { * Sets the checked state of the item * @param {Boolean} checked True to check, false to uncheck * @param {Boolean} suppressEvents (optional) True to prevent firing the checkchange events. Defaults to `false`. - * @markdown */ setChecked: function(checked, suppressEvents) { var me = this; @@ -121546,10 +126506,14 @@ Ext.define('Ext.menu.KeyNav', { }, enter: function(e) { - var menu = this.menu; - + var menu = this.menu, + focused = menu.focusedItem; + if (menu.activeItem) { menu.onClick(e); + } else if (focused && focused.isFormField) { + // prevent stopEvent being called + return true; } }, @@ -121641,16 +126605,10 @@ Ext.define('Ext.menu.KeyNav', { } }); /** - * @class Ext.menu.Separator - * @extends Ext.menu.Item - * * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will * add one of these by using "-" in your call to add() or in your items config rather than creating one directly. * - * {@img Ext.menu.Separator/Ext.menu.Separator.png Ext.menu.Separator component} - * - * ## Code - * + * @example * Ext.create('Ext.menu.Menu', { * width: 100, * height: 100, @@ -121666,156 +126624,141 @@ Ext.define('Ext.menu.KeyNav', { * },{ * text: 'regular item', * }] - * }); - * - * @markdown + * }); */ Ext.define('Ext.menu.Separator', { extend: 'Ext.menu.Item', alias: 'widget.menuseparator', - + /** * @cfg {String} activeCls @hide */ - + /** * @cfg {Boolean} canActivate @hide */ canActivate: false, - + /** * @cfg {Boolean} clickHideDelay @hide */ - + /** * @cfg {Boolean} destroyMenu @hide */ - + /** * @cfg {Boolean} disabledCls @hide */ - + focusable: false, - + /** * @cfg {String} href @hide */ - + /** * @cfg {String} hrefTarget @hide */ - + /** * @cfg {Boolean} hideOnClick @hide */ hideOnClick: false, - + /** * @cfg {String} icon @hide */ - + /** * @cfg {String} iconCls @hide */ - + /** - * @cfg {Mixed} menu @hide + * @cfg {Object} menu @hide */ - + /** * @cfg {String} menuAlign @hide */ - + /** * @cfg {Number} menuExpandDelay @hide */ - + /** * @cfg {Number} menuHideDelay @hide */ - + /** * @cfg {Boolean} plain @hide */ plain: true, - + /** * @cfg {String} separatorCls * The CSS class used by the separator item to show the incised line. * Defaults to `Ext.baseCSSPrefix + 'menu-item-separator'`. - * @markdown */ separatorCls: Ext.baseCSSPrefix + 'menu-item-separator', - + /** * @cfg {String} text @hide */ text: ' ', - + onRender: function(ct, pos) { var me = this, sepCls = me.separatorCls; - + me.cls += ' ' + sepCls; - - Ext.applyIf(me.renderSelectors, { - itemSepEl: '.' + sepCls - }); - + me.callParent(arguments); } }); /** - * @class Ext.menu.Menu - * @extends Ext.panel.Panel - * * A menu object. This is the container to which you may add {@link Ext.menu.Item menu items}. * * Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Components}. * Menus may also contain {@link Ext.panel.AbstractPanel#dockedItems docked items} because it extends {@link Ext.panel.Panel}. * * To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}, - * specify `{@link Ext.menu.Item#iconCls iconCls}: 'no-icon'` _or_ `{@link Ext.menu.Item#indent indent}: true`. - * This reserves a space for an icon, and indents the Component in line with the other menu items. - * See {@link Ext.form.field.ComboBox}.{@link Ext.form.field.ComboBox#getListParent getListParent} for an example. + * specify `{@link Ext.menu.Item#plain plain}: true`. This reserves a space for an icon, and indents the Component + * in line with the other menu items. * - * By default, Menus are absolutely positioned, floating Components. By configuring a Menu with `{@link #floating}:false`, + * By default, Menus are absolutely positioned, floating Components. By configuring a Menu with `{@link #floating}: false`, * a Menu may be used as a child of a {@link Ext.container.Container Container}. * - * {@img Ext.menu.Item/Ext.menu.Item.png Ext.menu.Item component} - * - *__Example Usage__ - * + * @example * Ext.create('Ext.menu.Menu', { * width: 100, * height: 100, * margin: '0 0 10 0', * floating: false, // usually you want this set to True (default) * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * text: 'regular item 1' + * items: [{ + * text: 'regular item 1' * },{ * text: 'regular item 2' * },{ - * text: 'regular item 3' + * text: 'regular item 3' * }] - * }); - * + * }); + * * Ext.create('Ext.menu.Menu', { * width: 100, * height: 100, * plain: true, * floating: false, // usually you want this set to True (default) * renderTo: Ext.getBody(), // usually rendered by it's containing component - * items: [{ - * text: 'plain item 1' + * items: [{ + * text: 'plain item 1' * },{ * text: 'plain item 2' * },{ * text: 'plain item 3' * }] - * }); - * + * }); */ Ext.define('Ext.menu.Menu', { extend: 'Ext.panel.Panel', @@ -121830,10 +126773,14 @@ Ext.define('Ext.menu.Menu', { 'Ext.menu.Separator' ], + /** + * @property {Ext.menu.Menu} parentMenu + * The parent Menu of this Menu. + */ + /** * @cfg {Boolean} allowOtherMenus - * True to allow multiple menus to be displayed at the same time. Defaults to `false`. - * @markdown + * True to allow multiple menus to be displayed at the same time. */ allowOtherMenus: false, @@ -121849,9 +126796,8 @@ Ext.define('Ext.menu.Menu', { /** * @cfg {String} defaultAlign - * The default {@link Ext.core.Element#getAlignToXY Ext.core.Element#getAlignToXY} anchor position value for this menu - * relative to its element of origin. Defaults to `'tl-bl?'`. - * @markdown + * The default {@link Ext.Element#getAlignToXY Ext.Element#getAlignToXY} anchor position value for this menu + * relative to its element of origin. */ defaultAlign: 'tl-bl?', @@ -121860,7 +126806,6 @@ Ext.define('Ext.menu.Menu', { * A Menu configured as `floating: true` (the default) will be rendered as an absolutely positioned, * {@link Ext.Component#floating floating} {@link Ext.Component Component}. If configured as `floating: false`, the Menu may be * used as a child item of another {@link Ext.container.Container Container}. - * @markdown */ floating: true, @@ -121871,10 +126816,10 @@ Ext.define('Ext.menu.Menu', { constrain: true, /** - * @cfg {Boolean} hidden + * @cfg {Boolean} [hidden=undefined] * True to initially render the Menu as hidden, requiring to be shown manually. + * * Defaults to `true` when `floating: true`, and defaults to `false` when `floating: false`. - * @markdown */ hidden: true, @@ -121883,8 +126828,7 @@ Ext.define('Ext.menu.Menu', { /** * @cfg {Boolean} ignoreParentClicks * True to ignore clicks on any item in this menu that is a parent item (displays a submenu) - * so that the submenu is not dismissed when clicking the parent item. Defaults to `false`. - * @markdown + * so that the submenu is not dismissed when clicking the parent item. */ ignoreParentClicks: false, @@ -121895,22 +126839,20 @@ Ext.define('Ext.menu.Menu', { */ /** - * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true). + * @cfg {Boolean} showSeparator + * True to show the icon separator. */ showSeparator : true, /** * @cfg {Number} minWidth - * The minimum width of the Menu. Defaults to `120`. - * @markdown + * The minimum width of the Menu. */ minWidth: 120, /** - * @cfg {Boolean} plain - * True to remove the incised line down the left side of the menu and to not - * indent general Component items. Defaults to `false`. - * @markdown + * @cfg {Boolean} [plain=false] + * True to remove the incised line down the left side of the menu and to not indent general Component items. */ initComponent: function() { @@ -121926,7 +126868,6 @@ Ext.define('Ext.menu.Menu', { * @param {Ext.menu.Menu} menu The menu which has been clicked * @param {Ext.Component} item The menu item that was clicked. `undefined` if not applicable. * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}. - * @markdown */ 'click', @@ -121935,7 +126876,6 @@ Ext.define('Ext.menu.Menu', { * Fires when the mouse enters this menu * @param {Ext.menu.Menu} menu The menu * @param {Ext.EventObject} e The underlying {@link Ext.EventObject} - * @markdown */ 'mouseenter', @@ -121944,7 +126884,6 @@ Ext.define('Ext.menu.Menu', { * Fires when the mouse leaves this menu * @param {Ext.menu.Menu} menu The menu * @param {Ext.EventObject} e The underlying {@link Ext.EventObject} - * @markdown */ 'mouseleave', @@ -122044,7 +126983,7 @@ Ext.define('Ext.menu.Menu', { // floating elements inherit their parent's width, making them the width of // document.body instead of the width of their contents. // This includes left/right dock items. - if ((!Ext.iStrict && Ext.isIE) || Ext.isIE6) { + if ((!Ext.isStrict && Ext.isIE) || Ext.isIE6) { var innerCt = me.layout.getRenderTarget(), innerCtWidth = 0, dis = me.dockedItems, @@ -122068,6 +127007,10 @@ Ext.define('Ext.menu.Menu', { me.el.setWidth(newWidth); } }, + + getBubbleTarget: function(){ + return this.parentMenu || this.callParent(); + }, /** * Returns whether a menu item can be activated or not. @@ -122089,7 +127032,9 @@ Ext.define('Ext.menu.Menu', { delete me.activeItem; } } - if (me.focusedItem) { + + // only blur if focusedItem is not a filter + if (me.focusedItem && !me.filtered) { me.focusedItem.blur(); if (!me.focusedItem.$focused) { delete me.focusedItem; @@ -122326,12 +127271,12 @@ Ext.define('Ext.menu.Menu', { }, /** - * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.core.Element Element}. - * @param {Mixed component} The {@link Ext.Component} or {@link Ext.core.Element} to show the menu by. - * @param {String} position (optional) Alignment position as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `{@link #defaultAlign}`. - * @param {Array} offsets (optional) Alignment offsets as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `undefined`. - * @return {Menu} This Menu. - * @markdown + * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.Element Element}. + * @param {Ext.Component/Ext.Element} component The {@link Ext.Component} or {@link Ext.Element} to show the menu by. + * @param {String} position (optional) Alignment position as used by {@link Ext.Element#getAlignToXY}. + * Defaults to `{@link #defaultAlign}`. + * @param {Number[]} offsets (optional) Alignment offsets as used by {@link Ext.Element#getAlignToXY}. Defaults to `undefined`. + * @return {Ext.menu.Menu} This Menu. */ showBy: function(cmp, pos, off) { var me = this, @@ -122343,6 +127288,7 @@ Ext.define('Ext.menu.Menu', { // show off-screen first so that we can calc position without causing a visual jump me.doAutoRender(); + delete me.needsLayout; // Component or Element cmp = cmp.el || cmp; @@ -122358,14 +127304,6 @@ Ext.define('Ext.menu.Menu', { } return me; }, - - // inherit docs - showAt: function(){ - this.callParent(arguments); - if (this.floating) { - this.doConstrain(); - } - }, doConstrain : function() { var me = this, @@ -122378,7 +127316,8 @@ Ext.define('Ext.menu.Menu', { me.setSize(); full = me.getHeight(); if (me.floating) { - parentEl = Ext.fly(me.el.dom.parentNode); + //if our reset css is scoped, there will be a x-reset wrapper on this menu which we need to skip + parentEl = Ext.fly(me.el.getScopeParent()); scrollTop = parentEl.getScroll().top; viewHeight = parentEl.getViewSize().height; //Normalize y by the scroll position for the parent element. Need to move it into the coordinate space @@ -122407,34 +127346,33 @@ Ext.define('Ext.menu.Menu', { me.iconSepEl.setHeight(me.layout.getRenderTarget().dom.scrollHeight); } } - vector = me.getConstrainVector(me.el.dom.parentNode); + vector = me.getConstrainVector(me.el.getScopeParent()); if (vector) { me.setPosition(me.getPosition()[0] + vector[0]); } me.el.setY(returnY); } }); + /** - * @class Ext.menu.ColorPicker - * @extends Ext.menu.Menu - *

        A menu containing a {@link Ext.picker.Color} Component.

        - *

        Notes:

          - *
        • Although not listed here, the constructor for this class - * accepts all of the configuration options of {@link Ext.picker.Color}.
        • - *
        • If subclassing ColorMenu, any configuration options for the ColorPicker must be - * applied to the initialConfig property of the ColorMenu. - * Applying {@link Ext.picker.Color ColorPicker} configuration settings to - * this will not affect the ColorPicker's configuration.
        • - *
        + * A menu containing a Ext.picker.Color Component. + * + * Notes: * - * {@img Ext.menu.ColorPicker/Ext.menu.ColorPicker.png Ext.menu.ColorPicker component} + * - Although not listed here, the **constructor** for this class accepts all of the + * configuration options of {@link Ext.picker.Color}. + * - If subclassing ColorMenu, any configuration options for the ColorPicker must be + * applied to the **initialConfig** property of the ColorMenu. Applying + * {@link Ext.picker.Color ColorPicker} configuration settings to `this` will **not** + * affect the ColorPicker's configuration. * - * __Example Usage__ + * Example: * + * @example * var colorPicker = Ext.create('Ext.menu.ColorPicker', { * value: '000000' * }); - * + * * Ext.create('Ext.menu.Menu', { * width: 100, * height: 90, @@ -122450,8 +127388,6 @@ Ext.define('Ext.menu.Menu', { * text: 'regular item' * }] * }); - * - * @author Nicolas Ferrero */ Ext.define('Ext.menu.ColorPicker', { extend: 'Ext.menu.Menu', @@ -122464,13 +127400,13 @@ Ext.define('Ext.menu.Menu', { /** * @cfg {Boolean} hideOnClick - * False to continue showing the menu after a date is selected, defaults to true. + * False to continue showing the menu after a date is selected. */ hideOnClick : true, /** * @cfg {String} pickerId - * An id to assign to the underlying color picker. Defaults to null. + * An id to assign to the underlying color picker. */ pickerId : null, @@ -122480,9 +127416,8 @@ Ext.define('Ext.menu.Menu', { */ /** + * @property {Ext.picker.Color} picker * The {@link Ext.picker.Color} instance for this ColorMenu - * @property picker - * @type ColorPicker */ /** @@ -122496,8 +127431,11 @@ Ext.define('Ext.menu.Menu', { */ initComponent : function(){ - var me = this; + var me = this, + cfg = Ext.apply({}, me.initialConfig); + // Ensure we don't get duplicate listeners + delete cfg.listeners; Ext.apply(me, { plain: true, showSeparator: false, @@ -122505,7 +127443,7 @@ Ext.define('Ext.menu.Menu', { cls: Ext.baseCSSPrefix + 'menu-color-item', id: me.pickerId, xtype: 'colorpicker' - }, me.initialConfig) + }, cfg) }); me.callParent(arguments); @@ -122514,9 +127452,7 @@ Ext.define('Ext.menu.Menu', { /** * @event select - * Fires when a date is selected from the {@link #picker Ext.picker.Color} - * @param {Ext.picker.Color} picker The {@link #picker Ext.picker.Color} - * @param {String} color The 6-digit color hex code (without the # symbol) + * @alias Ext.picker.Color#select */ me.relayEvents(me.picker, ['select']); @@ -122534,28 +127470,25 @@ Ext.define('Ext.menu.Menu', { } }); /** - * @class Ext.menu.DatePicker - * @extends Ext.menu.Menu - *

        A menu containing an {@link Ext.picker.Date} Component.

        - *

        Notes:

          - *
        • Although not listed here, the constructor for this class - * accepts all of the configuration options of {@link Ext.picker.Date}.
        • - *
        • If subclassing DateMenu, any configuration options for the DatePicker must be - * applied to the initialConfig property of the DateMenu. - * Applying {@link Ext.picker.Date DatePicker} configuration settings to - * this will not affect the DatePicker's configuration.
        • - *
        + * A menu containing an Ext.picker.Date Component. * - * {@img Ext.menu.DatePicker/Ext.menu.DatePicker.png Ext.menu.DatePicker component} + * Notes: * - * __Example Usage__ + * - Although not listed here, the **constructor** for this class accepts all of the + * configuration options of **{@link Ext.picker.Date}**. + * - If subclassing DateMenu, any configuration options for the DatePicker must be applied + * to the **initialConfig** property of the DateMenu. Applying {@link Ext.picker.Date Date Picker} + * configuration settings to **this** will **not** affect the Date Picker's configuration. * + * Example: + * + * @example * var dateMenu = Ext.create('Ext.menu.DatePicker', { * handler: function(dp, date){ - * Ext.Msg.alert('Date Selected', 'You choose {0}.', Ext.Date.format(date, 'M j, Y')); + * Ext.Msg.alert('Date Selected', 'You selected ' + Ext.Date.format(date, 'M j, Y')); * } * }); - * + * * Ext.create('Ext.menu.Menu', { * width: 100, * height: 90, @@ -122571,8 +127504,6 @@ Ext.define('Ext.menu.Menu', { * text: 'regular item' * }] * }); - * - * @author Nicolas Ferrero */ Ext.define('Ext.menu.DatePicker', { extend: 'Ext.menu.Menu', @@ -122585,13 +127516,13 @@ Ext.define('Ext.menu.Menu', { /** * @cfg {Boolean} hideOnClick - * False to continue showing the menu after a date is selected, defaults to true. + * False to continue showing the menu after a date is selected. */ hideOnClick : true, /** * @cfg {String} pickerId - * An id to assign to the underlying date picker. Defaults to null. + * An id to assign to the underlying date picker. */ pickerId : null, @@ -122601,9 +127532,8 @@ Ext.define('Ext.menu.Menu', { */ /** + * @property {Ext.picker.Date} picker * The {@link Ext.picker.Date} instance for this DateMenu - * @property picker - * @type Ext.picker.Date */ /** @@ -122636,9 +127566,7 @@ Ext.define('Ext.menu.Menu', { me.picker = me.down('datepicker'); /** * @event select - * Fires when a date is selected from the {@link #picker Ext.picker.Date} - * @param {Ext.picker.Date} picker The {@link #picker Ext.picker.Date} - * @param {Date} date The selected date + * @alias Ext.picker.Date#select */ me.relayEvents(me.picker, ['select']); @@ -122652,44 +127580,38 @@ Ext.define('Ext.menu.Menu', { } }); /** - * @class Ext.panel.Tool - * @extends Ext.Component - -This class is used to display small visual icons in the header of a panel. There are a set of -25 icons that can be specified by using the {@link #type} config. The {@link #handler} config -can be used to provide a function that will respond to any click events. In general, this class -will not be instantiated directly, rather it will be created by specifying the {@link Ext.panel.Panel#tools} -configuration on the Panel itself. - -__Example Usage__ - - Ext.create('Ext.panel.Panel', { - width: 200, - height: 200, - renderTo: document.body, - title: 'A Panel', - tools: [{ - type: 'help', - handler: function(){ - // show help here - } - }, { - itemId: 'refresh', - type: 'refresh', - hidden: true, - handler: function(){ - // do refresh - } - }, { - type: 'search', - handler: function(event, target, owner, tool){ - // do search - owner.child('#refresh').show(); - } - }] - }); - - * @markdown + * This class is used to display small visual icons in the header of a panel. There are a set of + * 25 icons that can be specified by using the {@link #type} config. The {@link #handler} config + * can be used to provide a function that will respond to any click events. In general, this class + * will not be instantiated directly, rather it will be created by specifying the {@link Ext.panel.Panel#tools} + * configuration on the Panel itself. + * + * @example + * Ext.create('Ext.panel.Panel', { + * width: 200, + * height: 200, + * renderTo: document.body, + * title: 'A Panel', + * tools: [{ + * type: 'help', + * handler: function(){ + * // show help here + * } + * }, { + * itemId: 'refresh', + * type: 'refresh', + * hidden: true, + * handler: function(){ + * // do refresh + * } + * }, { + * type: 'search', + * handler: function(event, target, owner, tool){ + * // do search + * owner.child('#refresh').show(); + * } + * }] + * }); */ Ext.define('Ext.panel.Tool', { extend: 'Ext.Component', @@ -122701,65 +127623,67 @@ Ext.define('Ext.panel.Tool', { toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed', toolOverCls: Ext.baseCSSPrefix + 'tool-over', ariaRole: 'button', - renderTpl: [''], - + renderTpl: [''], + /** * @cfg {Function} handler - * A function to execute when the tool is clicked. - * Arguments passed are: - *
          - *
        • event : Ext.EventObject
          The click event.
        • - *
        • toolEl : Ext.core.Element
          The tool Element.
        • - *
        • panel : Ext.panel.Panel
          The host Panel
        • - *
        • tool : Ext.panel.Tool
          The tool object
        • - *
        + * A function to execute when the tool is clicked. Arguments passed are: + * + * - **event** : Ext.EventObject - The click event. + * - **toolEl** : Ext.Element - The tool Element. + * - **owner** : Ext.panel.Header - The host panel header. + * - **tool** : Ext.panel.Tool - The tool object */ - + /** * @cfg {Object} scope * The scope to execute the {@link #handler} function. Defaults to the tool. */ - + /** * @cfg {String} type * The type of tool to render. The following types are available: - *
          - *
        • close
        • - *
        • collapse
        • - *
        • down
        • - *
        • expand
        • - *
        • gear
        • - *
        • help
        • - *
        • left
        • - *
        • maximize
        • - *
        • minimize
        • - *
        • minus
        • - *
        • move
        • - *
        • next
        • - *
        • pin
        • - *
        • plus
        • - *
        • prev
        • - *
        • print
        • - *
        • refresh
        • - *
        • resize
        • - *
        • restore
        • - *
        • right
        • - *
        • save
        • - *
        • search
        • - *
        • toggle
        • - *
        • unpin
        • - *
        • up
        • - *
        + * + * - close + * - minimize + * - maximize + * - restore + * - toggle + * - gear + * - prev + * - next + * - pin + * - unpin + * - right + * - left + * - down + * - up + * - refresh + * - plus + * - minus + * - search + * - save + * - help + * - print + * - expand + * - collapse + */ + + /** + * @cfg {String/Object} tooltip + * The tooltip for the tool - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config + * object */ - - /** - * @cfg {String/Object} tooltip - * The tooltip for the tool - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object + + /** + * @cfg {String} tooltipType + * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute. */ - + tooltipType: 'qtip', + /** * @cfg {Boolean} stopEvent - * Defaults to true. Specify as false to allow click event to propagate. + * Specify as false to allow click event to propagate. */ stopEvent: true, @@ -122774,39 +127698,39 @@ Ext.define('Ext.panel.Tool', { */ 'click' ); - + var types = [ - 'close', - 'collapse', - 'down', - 'expand', - 'gear', - 'help', - 'left', - 'maximize', - 'minimize', - 'minus', - 'move', - 'next', - 'pin', - 'plus', - 'prev', - 'print', - 'refresh', - 'resize', - 'restore', - 'right', - 'save', - 'search', + 'close', + 'collapse', + 'down', + 'expand', + 'gear', + 'help', + 'left', + 'maximize', + 'minimize', + 'minus', + 'move', + 'next', + 'pin', + 'plus', + 'prev', + 'print', + 'refresh', + 'resize', + 'restore', + 'right', + 'save', + 'search', 'toggle', - 'unpin', + 'unpin', 'up' ]; - + if (me.id && Ext.Array.indexOf(types, me.id) > -1 && Ext.global.console) { Ext.global.console.warn('When specifying a tool you should use the type option, the id can conflict now that tool is a Component'); } - + me.type = me.type || me.id; Ext.applyIf(me.renderData, { @@ -122814,22 +127738,29 @@ Ext.define('Ext.panel.Tool', { blank: Ext.BLANK_IMAGE_URL, type: me.type }); - me.renderSelectors.toolEl = '.' + me.baseCls + '-' + me.type; + + me.addChildEls('toolEl'); + + // alias qtip, should use tooltip since it's what we have in the docs + me.tooltip = me.tooltip || me.qtip; me.callParent(); }, // inherit docs afterRender: function() { - var me = this; + var me = this, + attr; + me.callParent(arguments); - if (me.qtip) { - if (Ext.isObject(me.qtip)) { + if (me.tooltip) { + if (Ext.isObject(me.tooltip)) { Ext.tip.QuickTipManager.register(Ext.apply({ target: me.id - }, me.qtip)); + }, me.tooltip)); } else { - me.toolEl.dom.qtip = me.qtip; + attr = me.tooltipType == 'qtip' ? 'data-qtip' : 'title'; + me.toolEl.dom.setAttribute(attr, me.tooltip); } } @@ -122843,13 +127774,13 @@ Ext.define('Ext.panel.Tool', { }, /** - * Set the type of the tool. Allows the icon to be changed. + * Sets the type of the tool. Allows the icon to be changed. * @param {String} type The new type. See the {@link #type} config. * @return {Ext.panel.Tool} this */ setType: function(type) { var me = this; - + me.type = type; if (me.rendered) { me.toolEl.dom.className = me.baseCls + '-' + type; @@ -122867,7 +127798,7 @@ Ext.define('Ext.panel.Tool', { }, /** - * Fired when the tool element is clicked + * Called when the tool element is clicked * @private * @param {Ext.EventObject} e * @param {HTMLElement} target The target element @@ -122875,7 +127806,7 @@ Ext.define('Ext.panel.Tool', { onClick: function(e, target) { var me = this, owner; - + if (me.disabled) { return false; } @@ -122893,17 +127824,17 @@ Ext.define('Ext.panel.Tool', { me.fireEvent('click', me, e); return true; }, - + // inherit docs onDestroy: function(){ if (Ext.isObject(this.tooltip)) { Ext.tip.QuickTipManager.unregister(this.id); - } + } this.callParent(); }, /** - * Called then the user pressing their mouse button down on a tool + * Called when the user presses their mouse button down on a tool * Adds the press class ({@link #toolPressedCls}) * @private */ @@ -122962,45 +127893,40 @@ Ext.define('Ext.resizer.Handle', { }); /** - * @class Ext.resizer.Resizer - *

        Applies drag handles to an element or component to make it resizable. The - * drag handles are inserted into the element (or component's element) and - * positioned absolute.

        - * - *

        Textarea and img elements will be wrapped with an additional div because - * these elements do not support child nodes. The original element can be accessed - * through the originalTarget property.

        - * - *

        Here is the list of valid resize handles:

        - *
        -Value   Description
        -------  -------------------
        - 'n'     north
        - 's'     south
        - 'e'     east
        - 'w'     west
        - 'nw'    northwest
        - 'sw'    southwest
        - 'se'    southeast
        - 'ne'    northeast
        - 'all'   all
        -
        + * Applies drag handles to an element or component to make it resizable. The drag handles are inserted into the element + * (or component's element) and positioned absolute. + * + * Textarea and img elements will be wrapped with an additional div because these elements do not support child nodes. + * The original element can be accessed through the originalTarget property. + * + * Here is the list of valid resize handles: + * + * Value Description + * ------ ------------------- + * 'n' north + * 's' south + * 'e' east + * 'w' west + * 'nw' northwest + * 'sw' southwest + * 'se' southeast + * 'ne' northeast + * 'all' all + * * {@img Ext.resizer.Resizer/Ext.resizer.Resizer.png Ext.resizer.Resizer component} - *

        Here's an example showing the creation of a typical Resizer:

        - *
        
        -    
        - - Ext.create('Ext.resizer.Resizer', { - el: 'elToResize', - handles: 'all', - minWidth: 200, - minHeight: 100, - maxWidth: 500, - maxHeight: 400, - pinned: true - }); -
        -*/ + * + * Here's an example showing the creation of a typical Resizer: + * + * Ext.create('Ext.resizer.Resizer', { + * el: 'elToResize', + * handles: 'all', + * minWidth: 200, + * minHeight: 100, + * maxWidth: 500, + * maxHeight: 400, + * pinned: true + * }); + */ Ext.define('Ext.resizer.Resizer', { mixins: { observable: 'Ext.util.Observable' @@ -123012,88 +127938,96 @@ Ext.define('Ext.resizer.Resizer', { handleCls: Ext.baseCSSPrefix + 'resizable-handle', pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned', overCls: Ext.baseCSSPrefix + 'resizable-over', - proxyCls: Ext.baseCSSPrefix + 'resizable-proxy', wrapCls: Ext.baseCSSPrefix + 'resizable-wrap', /** * @cfg {Boolean} dynamic - *

        Specify as true to update the {@link #target} (Element or {@link Ext.Component Component}) dynamically during dragging. - * This is true by default, but the {@link Ext.Component Component} class passes false when it - * is configured as {@link Ext.Component#resizable}.

        - *

        If specified as false, a proxy element is displayed during the resize operation, and the {@link #target} - * is updated on mouseup.

        + * Specify as true to update the {@link #target} (Element or {@link Ext.Component Component}) dynamically during + * dragging. This is `true` by default, but the {@link Ext.Component Component} class passes `false` when it is + * configured as {@link Ext.Component#resizable}. + * + * If specified as `false`, a proxy element is displayed during the resize operation, and the {@link #target} is + * updated on mouseup. */ dynamic: true, /** - * @cfg {String} handles String consisting of the resize handles to display. Defaults to 's e se' for - * Elements and fixed position Components. Defaults to 8 point resizing for floating Components (such as Windows). - * Specify either 'all' or any of 'n s e w ne nw se sw'. + * @cfg {String} handles + * String consisting of the resize handles to display. Defaults to 's e se' for Elements and fixed position + * Components. Defaults to 8 point resizing for floating Components (such as Windows). Specify either `'all'` or any + * of `'n s e w ne nw se sw'`. */ handles: 's e se', /** - * @cfg {Number} height Optional. The height to set target to in pixels (defaults to null) + * @cfg {Number} height + * Optional. The height to set target to in pixels */ height : null, /** - * @cfg {Number} width Optional. The width to set the target to in pixels (defaults to null) + * @cfg {Number} width + * Optional. The width to set the target to in pixels */ width : null, /** - * @cfg {Number} heightIncrement The increment to snap the height resize in pixels. - * Defaults to 0. + * @cfg {Number} heightIncrement + * The increment to snap the height resize in pixels. */ heightIncrement : 0, /** - * @cfg {Number} widthIncrement The increment to snap the width resize in pixels - * Defaults to 0. + * @cfg {Number} widthIncrement + * The increment to snap the width resize in pixels. */ widthIncrement : 0, /** - * @cfg {Number} minHeight The minimum height for the element (defaults to 20) + * @cfg {Number} minHeight + * The minimum height for the element */ minHeight : 20, /** - * @cfg {Number} minWidth The minimum width for the element (defaults to 20) + * @cfg {Number} minWidth + * The minimum width for the element */ minWidth : 20, /** - * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000) + * @cfg {Number} maxHeight + * The maximum height for the element */ maxHeight : 10000, /** - * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000) + * @cfg {Number} maxWidth + * The maximum width for the element */ maxWidth : 10000, /** - * @cfg {Boolean} pinned True to ensure that the resize handles are always - * visible, false indicates resizing by cursor changes only (defaults to false) + * @cfg {Boolean} pinned + * True to ensure that the resize handles are always visible, false indicates resizing by cursor changes only */ pinned: false, /** - * @cfg {Boolean} preserveRatio True to preserve the original ratio between height - * and width during resize (defaults to false) + * @cfg {Boolean} preserveRatio + * True to preserve the original ratio between height and width during resize */ preserveRatio: false, /** - * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false) + * @cfg {Boolean} transparent + * True for transparent handles. This is only applied at config time. */ transparent: false, /** - * @cfg {Mixed} constrainTo Optional. An element, or a {@link Ext.util.Region} into which the resize operation - * must be constrained. + * @cfg {Ext.Element/Ext.util.Region} constrainTo + * An element, or a {@link Ext.util.Region Region} into which the resize operation must be constrained. */ possiblePositions: { @@ -123108,13 +128042,13 @@ Ext.define('Ext.resizer.Resizer', { }, /** - * @cfg {Mixed} target The Element or Component to resize. + * @cfg {Ext.Element/Ext.Component} target + * The Element or Component to resize. */ /** + * @property {Ext.Element} el * Outer element for resizing behavior. - * @type Ext.core.Element - * @property el */ constructor: function(config) { @@ -123204,10 +128138,9 @@ Ext.define('Ext.resizer.Resizer', { tag = me.el.dom.tagName; if (tag == 'TEXTAREA' || tag == 'IMG') { /** - * Reference to the original resize target if the element of the original - * resize target was an IMG or a TEXTAREA which must be wrapped in a DIV. - * @type Mixed - * @property originalTarget + * @property {Ext.Element/Ext.Component} originalTarget + * Reference to the original resize target if the element of the original resize target was an IMG or a + * TEXTAREA which must be wrapped in a DIV. */ me.originalTarget = me.target; me.target = me.el = me.el.wrap({ @@ -123230,8 +128163,7 @@ Ext.define('Ext.resizer.Resizer', { } /** - * @type Ext.resizer.ResizeTracker - * @property resizeTracker + * @property {Ext.resizer.ResizeTracker} resizeTracker */ me.resizeTracker = Ext.create('Ext.resizer.ResizeTracker', { disabled: me.disabled, @@ -123357,24 +128289,23 @@ Ext.define('Ext.resizer.Resizer', { }, /** - *

        Returns the element that was configured with the el or target config property. - * If a component was configured with the target property then this will return the - * element of this component.

        - *

        Textarea and img elements will be wrapped with an additional div because - * these elements do not support child nodes. The original element can be accessed - * through the originalTarget property.

        - * @return {Element} element + * Returns the element that was configured with the el or target config property. If a component was configured with + * the target property then this will return the element of this component. + * + * Textarea and img elements will be wrapped with an additional div because these elements do not support child + * nodes. The original element can be accessed through the originalTarget property. + * @return {Ext.Element} element */ getEl : function() { return this.el; }, /** - *

        Returns the element or component that was configured with the target config property.

        - *

        Textarea and img elements will be wrapped with an additional div because - * these elements do not support child nodes. The original element can be accessed - * through the originalTarget property.

        - * @return {Element/Component} + * Returns the element or component that was configured with the target config property. + * + * Textarea and img elements will be wrapped with an additional div because these elements do not support child + * nodes. The original element can be accessed through the originalTarget property. + * @return {Ext.Element/Ext.Component} */ getTarget: function() { return this.target; @@ -123423,6 +128354,8 @@ Ext.define('Ext.resizer.ResizeTracker', { // Default to no constraint constrainTo: null, + + proxyCls: Ext.baseCSSPrefix + 'resizable-proxy', constructor: function(config) { var me = this; @@ -123479,15 +128412,45 @@ Ext.define('Ext.resizer.ResizeTracker', { * If dynamic is false, this will be a proxy, otherwise it will be our actual target. */ getDynamicTarget: function() { - var d = this.target; - if (this.dynamic) { - return d; - } else if (!this.proxy) { - this.proxy = d.isComponent ? d.getProxy().addCls(Ext.baseCSSPrefix + 'resizable-proxy') : d.createProxy({tag: 'div', cls: Ext.baseCSSPrefix + 'resizable-proxy', id: d.id + '-rzproxy'}, Ext.getBody()); - this.proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el'); + var me = this, + target = me.target; + + if (me.dynamic) { + return target; + } else if (!me.proxy) { + me.proxy = me.createProxy(target); } - this.proxy.show(); - return this.proxy; + me.proxy.show(); + return me.proxy; + }, + + /** + * Create a proxy for this resizer + * @param {Ext.Component/Ext.Element} target The target + * @return {Ext.Element} A proxy element + */ + createProxy: function(target){ + var proxy, + cls = this.proxyCls, + renderTo; + + if (target.isComponent) { + proxy = target.getProxy().addCls(cls); + } else { + renderTo = Ext.getBody(); + if (Ext.scopeResetCSS) { + renderTo = Ext.getBody().createChild({ + cls: Ext.baseCSSPrefix + 'reset' + }); + } + proxy = target.createProxy({ + tag: 'div', + cls: cls, + id: target.id + '-rzproxy' + }, renderTo); + } + proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el'); + return proxy; }, onStart: function(e) { @@ -123763,7 +128726,7 @@ Ext.define('Ext.resizer.SplitterTracker', { html: ' ' }); overlay.unselectable(); - overlay.setSize(Ext.core.Element.getViewWidth(true), Ext.core.Element.getViewHeight(true)); + overlay.setSize(Ext.Element.getViewWidth(true), Ext.Element.getViewHeight(true)); overlay.show(); // store boxes of previous and next @@ -123933,23 +128896,22 @@ Ext.define('Ext.resizer.SplitterTracker', { /** * @class Ext.selection.CellModel * @extends Ext.selection.Model - * @private */ Ext.define('Ext.selection.CellModel', { extend: 'Ext.selection.Model', alias: 'selection.cellmodel', requires: ['Ext.util.KeyNav'], - + /** * @cfg {Boolean} enableKeyNav - * Turns on/off keyboard navigation within the grid. Defaults to true. + * Turns on/off keyboard navigation within the grid. */ enableKeyNav: true, - + /** * @cfg {Boolean} preventWrap * Set this configuration to true to prevent wrapping around of selection as - * a user navigates to the first or last column. Defaults to false. + * a user navigates to the first or last column. */ preventWrap: false, @@ -123964,7 +128926,7 @@ Ext.define('Ext.selection.CellModel', { * @param {Number} column The column index deselected */ 'deselect', - + /** * @event select * Fired after a cell is selected @@ -123975,7 +128937,7 @@ Ext.define('Ext.selection.CellModel', { */ 'select' ); - this.callParent(arguments); + this.callParent(arguments); }, bindComponent: function(view) { @@ -123998,7 +128960,7 @@ Ext.define('Ext.selection.CellModel', { initKeyNav: function(view) { var me = this; - + if (!view.rendered) { view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true}); return; @@ -124019,7 +128981,7 @@ Ext.define('Ext.selection.CellModel', { scope: me }); }, - + getHeaderCt: function() { return this.primaryView.headerCt; }, @@ -124035,11 +128997,11 @@ Ext.define('Ext.selection.CellModel', { onKeyLeft: function(e, t) { this.move('left', e); }, - + onKeyRight: function(e, t) { this.move('right', e); }, - + move: function(dir, e) { var me = this, pos = me.primaryView.walkCells(me.getCurrentPosition(), dir, e, me.preventWrap); @@ -124055,14 +129017,14 @@ Ext.define('Ext.selection.CellModel', { getCurrentPosition: function() { return this.position; }, - + /** * Sets the current position * @param {Object} position The position to set. */ setCurrentPosition: function(pos) { var me = this; - + if (me.position) { me.onCellDeselect(me.position); } @@ -124154,10 +129116,6 @@ Ext.define('Ext.selection.CellModel', { /** * @class Ext.selection.RowModel * @extends Ext.selection.Model - * - * Implement row based navigation via keyboard. - * - * Must synchronize across grid sections */ Ext.define('Ext.selection.RowModel', { extend: 'Ext.selection.Model', @@ -124174,9 +129132,17 @@ Ext.define('Ext.selection.RowModel', { /** * @cfg {Boolean} enableKeyNav * - * Turns on/off keyboard navigation within the grid. Defaults to true. + * Turns on/off keyboard navigation within the grid. */ enableKeyNav: true, + + /** + * @cfg {Boolean} [ignoreRightMouseSelection=true] + * True to ignore selections that are made when using the right mouse button if there are + * records that are already selected. If no records are selected, selection will continue + * as normal + */ + ignoreRightMouseSelection: true, constructor: function(){ this.addEvents( @@ -124184,7 +129150,7 @@ Ext.define('Ext.selection.RowModel', { * @event beforedeselect * Fired before a record is deselected. If any listener returns false, the * deselection is cancelled. - * @param {Ext.selection.RowSelectionModel} this + * @param {Ext.selection.RowModel} this * @param {Ext.data.Model} record The deselected record * @param {Number} index The row index deselected */ @@ -124194,7 +129160,7 @@ Ext.define('Ext.selection.RowModel', { * @event beforeselect * Fired before a record is selected. If any listener returns false, the * selection is cancelled. - * @param {Ext.selection.RowSelectionModel} this + * @param {Ext.selection.RowModel} this * @param {Ext.data.Model} record The selected record * @param {Number} index The row index selected */ @@ -124203,7 +129169,7 @@ Ext.define('Ext.selection.RowModel', { /** * @event deselect * Fired after a record is deselected - * @param {Ext.selection.RowSelectionModel} this + * @param {Ext.selection.RowModel} this * @param {Ext.data.Model} record The deselected record * @param {Number} index The row index deselected */ @@ -124212,7 +129178,7 @@ Ext.define('Ext.selection.RowModel', { /** * @event select * Fired after a record is selected - * @param {Ext.selection.RowSelectionModel} this + * @param {Ext.selection.RowModel} this * @param {Ext.data.Model} record The selected record * @param {Number} index The row index selected */ @@ -124493,8 +129459,26 @@ Ext.define('Ext.selection.RowModel', { // we can take into account ctrlKey, shiftKey, etc onRowMouseDown: function(view, record, item, index, e) { view.el.focus(); + if (!this.allowRightMouseSelection(e)) { + return; + } this.selectWithEvent(record, e); }, + + /** + * Checks whether a selection should proceed based on the ignoreRightMouseSelection + * option. + * @private + * @param {Ext.EventObject} e The event + * @return {Boolean} False if the selection should not proceed + */ + allowRightMouseSelection: function(e) { + var disallow = this.ignoreRightMouseSelection && e.button !== 0; + if (disallow) { + disallow = this.hasSelection(); + } + return !disallow; + }, // Allow the GridView to update the UI by // adding/removing a CSS class from the row. @@ -124593,40 +129577,72 @@ Ext.define('Ext.selection.CheckboxModel', { mode: 'MULTI', /** - * @cfg {Mixed} injectCheckbox + * @cfg {Number/Boolean/String} injectCheckbox * Instructs the SelectionModel whether or not to inject the checkbox header * automatically or not. (Note: By not placing the checkbox in manually, the * grid view will need to be rendered 2x on initial render.) * Supported values are a Number index, false and the strings 'first' and 'last'. - * Default is 0. */ injectCheckbox: 0, /** * @cfg {Boolean} checkOnly true if rows can only be selected by clicking on the - * checkbox column (defaults to false). + * checkbox column. */ checkOnly: false, + headerWidth: 24, + // private checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on', - bindComponent: function() { - this.sortable = false; - this.callParent(arguments); + bindComponent: function(view) { + var me = this; - var view = this.views[0], + me.sortable = false; + me.callParent(arguments); + if (!me.hasLockedHeader() || view.headerCt.lockedCt) { + // if we have a locked header, only hook up to the first + view.headerCt.on('headerclick', me.onHeaderClick, me); + me.addCheckbox(true); + me.mon(view.ownerCt, 'reconfigure', me.addCheckbox, me); + } + }, + + hasLockedHeader: function(){ + var hasLocked = false; + Ext.each(this.views, function(view){ + if (view.headerCt.lockedCt) { + hasLocked = true; + return false; + } + }); + return hasLocked; + }, + + /** + * Add the header checkbox to the header row + * @private + * @param {Boolean} initial True if we're binding for the first time. + */ + addCheckbox: function(initial){ + var me = this, + checkbox = me.injectCheckbox, + view = me.views[0], headerCt = view.headerCt; - if (this.injectCheckbox !== false) { - if (this.injectCheckbox == 'first') { - this.injectCheckbox = 0; - } else if (this.injectCheckbox == 'last') { - this.injectCheckbox = headerCt.getColumnCount(); + if (checkbox !== false) { + if (checkbox == 'first') { + checkbox = 0; + } else if (checkbox == 'last') { + checkbox = headerCt.getColumnCount(); } - headerCt.add(this.injectCheckbox, this.getHeaderConfig()); + headerCt.add(checkbox, me.getHeaderConfig()); + } + + if (initial !== true) { + view.refresh(); } - headerCt.on('headerclick', this.onHeaderClick, this); }, /** @@ -124671,17 +129687,21 @@ Ext.define('Ext.selection.CheckboxModel', { * This should be used when injectCheckbox is set to false. */ getHeaderConfig: function() { + var me = this; + return { isCheckerHd: true, text : ' ', - width: 24, + width: me.headerWidth, sortable: false, - fixed: true, + draggable: false, + resizable: false, hideable: false, menuDisabled: true, dataIndex: '', cls: Ext.baseCSSPrefix + 'column-header-checkbox ', - renderer: Ext.Function.bind(this.renderer, this) + renderer: Ext.Function.bind(me.renderer, me), + locked: me.hasLockedHeader() }; }, @@ -124700,6 +129720,10 @@ Ext.define('Ext.selection.CheckboxModel', { view.el.focus(); var me = this, checker = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-checker'); + + if (!me.allowRightMouseSelection(e)) { + return; + } // checkOnly set, but we didn't click on a checker. if (me.checkOnly && !checker) { @@ -124799,16 +129823,15 @@ Ext.define('Ext.selection.TreeModel', { }, onKeyPress: function(e, t) { - var selected, checked; + var key = e.getKey(), + selected, + checked; - if (e.getKey() === e.SPACE || e.getKey() === e.ENTER) { + if (key === e.SPACE || key === e.ENTER) { e.stopEvent(); selected = this.getLastSelected(); - if (selected && selected.isLeaf()) { - checked = selected.get('checked'); - if (Ext.isBoolean(checked)) { - selected.set('checked', !checked); - } + if (selected) { + this.view.onCheckChange(selected); } } else { this.callParent(arguments); @@ -124821,30 +129844,31 @@ Ext.define('Ext.selection.TreeModel', { * @extends Ext.Base * @private * Represents a single thumb element on a Slider. This would not usually be created manually and would instead - * be created internally by an {@link Ext.slider.Multi Ext.Slider}. + * be created internally by an {@link Ext.slider.Multi Multi slider}. */ Ext.define('Ext.slider.Thumb', { requires: ['Ext.dd.DragTracker', 'Ext.util.Format'], /** * @private - * @property topThumbZIndex - * @type Number + * @property {Number} topThumbZIndex * The number used internally to set the z index of the top thumb (see promoteThumb for details) */ topZIndex: 10000, + /** - * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required) + * @cfg {Ext.slider.MultiSlider} slider (required) + * The Slider to render to. */ + /** * Creates new slider thumb. * @param {Object} config (optional) Config object. */ constructor: function(config) { var me = this; - + /** - * @property slider - * @type Ext.slider.MultiSlider + * @property {Ext.slider.MultiSlider} slider * The slider this thumb is contained within */ Ext.apply(me, config || {}, { @@ -124867,14 +129891,14 @@ Ext.define('Ext.slider.Thumb', { */ render: function() { var me = this; - + me.el = me.slider.innerEl.insertFirst({cls: me.cls}); if (me.disabled) { me.disable(); } me.initEvents(); }, - + /** * @private * move the thumb @@ -124900,7 +129924,7 @@ Ext.define('Ext.slider.Thumb', { bringToFront: function() { this.el.setStyle('zIndex', this.topZIndex); }, - + /** * @private * Send thumb dom element to back. @@ -124908,13 +129932,13 @@ Ext.define('Ext.slider.Thumb', { sendToBack: function() { this.el.setStyle('zIndex', ''); }, - + /** * Enables the thumb if it is currently disabled */ enable: function() { var me = this; - + me.disabled = false; if (me.el) { me.el.removeCls(me.slider.disabledCls); @@ -124926,7 +129950,7 @@ Ext.define('Ext.slider.Thumb', { */ disable: function() { var me = this; - + me.disabled = true; if (me.el) { me.el.addCls(me.slider.disabledCls); @@ -124975,7 +129999,7 @@ Ext.define('Ext.slider.Thumb', { */ onDragStart: function(e){ var me = this; - + me.el.addCls(Ext.baseCSSPrefix + 'slider-thumb-drag'); me.dragging = true; me.dragStartValue = me.value; @@ -125003,7 +130027,7 @@ Ext.define('Ext.slider.Thumb', { if (below !== undefined && newValue <= below.value) { newValue = below.value; } - + if (above !== undefined && newValue >= above.value) { newValue = above.value; } @@ -125072,46 +130096,44 @@ Ext.define('Ext.slider.Thumb', { }); /** - * @class Ext.slider.Tip - * @extends Ext.tip.Tip - * Simple plugin for using an Ext.tip.Tip with a slider to show the slider value. In general this - * class is not created directly, instead pass the {@link Ext.slider.Multi#useTips} and - * {@link Ext.slider.Multi#tipText} configuration options to the slider directly. - * {@img Ext.slider.Tip/Ext.slider.Tip1.png Ext.slider.Tip component} - * Example usage: -
        -    Ext.create('Ext.slider.Single', {
        -        width: 214,
        -        minValue: 0,
        -        maxValue: 100,
        -        useTips: true,
        -        renderTo: Ext.getBody()
        -    });   
        -
        + * Simple plugin for using an Ext.tip.Tip with a slider to show the slider value. In general this class is not created + * directly, instead pass the {@link Ext.slider.Multi#useTips} and {@link Ext.slider.Multi#tipText} configuration + * options to the slider directly. + * + * @example + * Ext.create('Ext.slider.Single', { + * width: 214, + * minValue: 0, + * maxValue: 100, + * useTips: true, + * renderTo: Ext.getBody() + * }); + * * Optionally provide your own tip text by passing tipText: -
        - new Ext.slider.Single({
        -     width: 214,
        -     minValue: 0,
        -     maxValue: 100,
        -     useTips: true,
        -     tipText: function(thumb){
        -         return Ext.String.format('{0}% complete', thumb.value);
        -     }
        - });
        - 
        + * + * @example + * Ext.create('Ext.slider.Single', { + * width: 214, + * minValue: 0, + * maxValue: 100, + * useTips: true, + * tipText: function(thumb){ + * return Ext.String.format('**{0}% complete**', thumb.value); + * }, + * renderTo: Ext.getBody() + * }); */ Ext.define('Ext.slider.Tip', { extend: 'Ext.tip.Tip', minWidth: 10, alias: 'widget.slidertip', offsets : [0, -10], - + isSliderTip: true, init: function(slider) { var me = this; - + slider.on({ scope : me, dragstart: me.onSlide, @@ -125122,7 +130144,7 @@ Ext.define('Ext.slider.Tip', { }, /** * @private - * Called whenever a dragstart or drag event is received on the associated Thumb. + * Called whenever a dragstart or drag event is received on the associated Thumb. * Aligns the Tip with the Thumb's new position. * @param {Ext.slider.MultiSlider} slider The slider * @param {Ext.EventObject} e The Event object @@ -125137,8 +130159,8 @@ Ext.define('Ext.slider.Tip', { }, /** - * Used to create the text that appears in the Tip's body. By default this just returns - * the value of the Slider Thumb that the Tip is attached to. Override to customize. + * Used to create the text that appears in the Tip's body. By default this just returns the value of the Slider + * Thumb that the Tip is attached to. Override to customize. * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to * @return {String} The text to display in the tip */ @@ -125147,26 +130169,23 @@ Ext.define('Ext.slider.Tip', { } }); /** - * @class Ext.slider.Multi - * @extends Ext.form.field.Base - *

        Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis - * clicking and animation. Can be added as an item to any container. In addition, - * {@img Ext.slider.Multi/Ext.slider.Multi.png Ext.slider.Multi component} - *

        Example usage:

        + * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking + * and animation. Can be added as an item to any container. + * * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one: -
        -    Ext.create('Ext.slider.Multi', {
        -        width: 200,
        -        values: [25, 50, 75],
        -        increment: 5,
        -        minValue: 0,
        -        maxValue: 100,
        -
        -        //this defaults to true, setting to false allows the thumbs to pass each other
        -        {@link #constrainThumbs}: false,
        -        renderTo: Ext.getBody()
        -    });
        -
        + * + * @example + * Ext.create('Ext.slider.Multi', { + * width: 200, + * values: [25, 50, 75], + * increment: 5, + * minValue: 0, + * maxValue: 100, + * + * // this defaults to true, setting to false allows the thumbs to pass each other + * constrainThumbs: false, + * renderTo: Ext.getBody() + * }); */ Ext.define('Ext.slider.Multi', { extend: 'Ext.form.field.Base', @@ -125182,11 +130201,12 @@ Ext.define('Ext.slider.Multi', { 'Ext.layout.component.field.Slider' ], + // note: {id} here is really {inputId}, but {cmpId} is available fieldSubTpl: [ - '
        ', - '
        ', - '
        ', + '
        ', '', thHtml, '
        ', @@ -127502,6 +132598,36 @@ Ext.define('Ext.tree.View', { } }, + beginBulkUpdate: function(){ + this.bulkUpdate = true; + this.ownerCt.changingScrollbars = true; + }, + + endBulkUpdate: function(){ + var me = this, + ownerCt = me.ownerCt; + + me.bulkUpdate = false; + me.ownerCt.changingScrollbars = true; + me.resetScrollers(); + }, + + onRemove : function(ds, record, index) { + var me = this, + bulk = me.bulkUpdate; + + me.doRemove(record, index); + if (!bulk) { + me.updateIndexes(index); + } + if (me.store.getCount() === 0){ + me.refresh(); + } + if (!bulk) { + me.fireEvent('itemremove', record, index); + } + }, + doRemove: function(record, index) { // If we are adding records which have a parent that is currently expanding // lets add them to the animation wrap @@ -127589,10 +132715,12 @@ Ext.define('Ext.tree.View', { }, resetScrollers: function(){ - var panel = this.panel; - - panel.determineScrollbars(); - panel.invalidateScroller(); + if (!this.bulkUpdate) { + var panel = this.panel; + + panel.determineScrollbars(); + panel.invalidateScroller(); + } }, onBeforeCollapse: function(parent, records, index) { @@ -127690,7 +132818,7 @@ Ext.define('Ext.tree.View', { }, /** - * Expand a record that is loaded in the view. + * Expands a record that is loaded in the view. * @param {Ext.data.Model} record The record to expand * @param {Boolean} deep (optional) True to expand nodes all the way down the tree hierarchy. * @param {Function} callback (optional) The function to run after the expand is completed @@ -127701,7 +132829,7 @@ Ext.define('Ext.tree.View', { }, /** - * Collapse a record that is loaded in the view. + * Collapses a record that is loaded in the view. * @param {Ext.data.Model} record The record to collapse * @param {Boolean} deep (optional) True to collapse nodes all the way up the tree hierarchy. * @param {Function} callback (optional) The function to run after the collapse is completed @@ -127712,8 +132840,8 @@ Ext.define('Ext.tree.View', { }, /** - * Toggle a record between expanded and collapsed. - * @param {Ext.data.Record} recordInstance + * Toggles a record between expanded and collapsed. + * @param {Ext.data.Model} recordInstance */ toggle: function(record) { this[record.isExpanded() ? 'collapse' : 'expand'](record); @@ -127770,17 +132898,14 @@ Ext.define('Ext.tree.View', { /** * The TreePanel provides tree-structured UI representation of tree-structured data. * A TreePanel must be bound to a {@link Ext.data.TreeStore}. TreePanel's support - * multiple columns through the {@link #columns} configuration. - * - * Simple TreePanel using inline data. + * multiple columns through the {@link #columns} configuration. * - * {@img Ext.tree.Panel/Ext.tree.Panel1.png Ext.tree.Panel component} - * - * Code: + * Simple TreePanel using inline data: * + * @example * var store = Ext.create('Ext.data.TreeStore', { * root: { - * expanded: true, + * expanded: true, * children: [ * { text: "detention", leaf: true }, * { text: "homework", expanded: true, children: [ @@ -127790,16 +132915,19 @@ Ext.define('Ext.tree.View', { * { text: "buy lottery tickets", leaf: true } * ] * } - * }); + * }); * * Ext.create('Ext.tree.Panel', { * title: 'Simple Tree', * width: 200, * height: 150, * store: store, - * rootVisible: false, + * rootVisible: false, * renderTo: Ext.getBody() * }); + * + * For the tree node config options (like `text`, `leaf`, `expanded`), see the documentation of + * {@link Ext.data.NodeInterface NodeInterface} config options. */ Ext.define('Ext.tree.Panel', { extend: 'Ext.panel.Table', @@ -127808,46 +132936,46 @@ Ext.define('Ext.tree.Panel', { requires: ['Ext.tree.View', 'Ext.selection.TreeModel', 'Ext.tree.Column'], viewType: 'treeview', selType: 'treemodel', - + treeCls: Ext.baseCSSPrefix + 'tree-panel', deferRowRender: false, /** - * @cfg {Boolean} lines False to disable tree lines. Defaults to true. + * @cfg {Boolean} lines False to disable tree lines. */ lines: true, - + /** - * @cfg {Boolean} useArrows True to use Vista-style arrows in the tree. Defaults to false. + * @cfg {Boolean} useArrows True to use Vista-style arrows in the tree. */ useArrows: false, - + /** - * @cfg {Boolean} singleExpand True if only 1 node per branch may be expanded. Defaults to false. + * @cfg {Boolean} singleExpand True if only 1 node per branch may be expanded. */ singleExpand: false, - + ddConfig: { enableDrag: true, enableDrop: true }, - - /** + + /** * @cfg {Boolean} animate True to enable animated expand/collapse. Defaults to the value of {@link Ext#enableFx}. */ - - /** - * @cfg {Boolean} rootVisible False to hide the root node. Defaults to true. + + /** + * @cfg {Boolean} rootVisible False to hide the root node. */ rootVisible: true, - - /** - * @cfg {Boolean} displayField The field inside the model that will be used as the node's text. Defaults to 'text'. - */ + + /** + * @cfg {Boolean} displayField The field inside the model that will be used as the node's text. + */ displayField: 'text', - /** + /** * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root * Allows you to not specify a store on this TreePanel. This is useful for creating a simple tree with preloaded * data without having to specify a TreeStore and Model. A store and model will be created and root will be passed @@ -127867,7 +132995,7 @@ Ext.define('Ext.tree.Panel', { * }); */ root: null, - + // Required for the Lockable Mixin. These are the configurations which will be copied to the // normal and locked sub tablepanels normalCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible', 'scroll'], @@ -127876,11 +133004,11 @@ Ext.define('Ext.tree.Panel', { /** * @cfg {Boolean} hideHeaders True to hide the headers. Defaults to `undefined`. */ - + /** * @cfg {Boolean} folderSort True to automatically prepend a leaf sorter to the store. Defaults to `undefined`. - */ - + */ + constructor: function(config) { config = config || {}; if (config.animate === undefined) { @@ -127888,10 +133016,10 @@ Ext.define('Ext.tree.Panel', { } this.enableAnimations = config.animate; delete config.animate; - + this.callParent([config]); }, - + initComponent: function() { var me = this, cls = [me.treeCls]; @@ -127900,13 +133028,13 @@ Ext.define('Ext.tree.Panel', { cls.push(Ext.baseCSSPrefix + 'tree-arrows'); me.lines = false; } - + if (me.lines) { cls.push(Ext.baseCSSPrefix + 'tree-lines'); } else if (!me.useArrows) { cls.push(Ext.baseCSSPrefix + 'tree-no-lines'); } - + if (Ext.isString(me.store)) { me.store = Ext.StoreMgr.lookup(me.store); } else if (!me.store || Ext.isObject(me.store) && !me.store.isStore) { @@ -127922,14 +133050,14 @@ Ext.define('Ext.tree.Panel', { if (me.folderSort !== undefined) { me.store.folderSort = me.folderSort; me.store.sort(); - } + } } - + // I'm not sure if we want to this. It might be confusing // if (me.initialConfig.rootVisible === undefined && !me.getRootNode()) { // me.rootVisible = false; // } - + me.viewConfig = Ext.applyIf(me.viewConfig || {}, { rootVisible: me.rootVisible, animate: me.enableAnimations, @@ -127937,141 +133065,101 @@ Ext.define('Ext.tree.Panel', { node: me.store.getRootNode(), hideHeaders: me.hideHeaders }); - + me.mon(me.store, { scope: me, rootchange: me.onRootChange, clear: me.onClear }); - + me.relayEvents(me.store, [ /** * @event beforeload - * Event description - * @param {Ext.data.Store} store This Store - * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to load the Store + * @alias Ext.data.Store#beforeload */ 'beforeload', /** * @event load - * Fires whenever the store reads data from a remote data source. - * @param {Ext.data.store} this - * @param {Array} records An array of records - * @param {Boolean} successful True if the operation was successful. + * @alias Ext.data.Store#load */ - 'load' + 'load' ]); - + me.store.on({ /** * @event itemappend - * Fires when a new child node is appended to a node in the tree. - * @param {Tree} tree The owner tree - * @param {Node} parent The parent node - * @param {Node} node The newly appended node - * @param {Number} index The index of the newly appended node + * @alias Ext.data.TreeStore#append */ append: me.createRelayer('itemappend'), - + /** * @event itemremove - * Fires when a child node is removed from a node in the tree - * @param {Tree} tree The owner tree - * @param {Node} parent The parent node - * @param {Node} node The child node removed + * @alias Ext.data.TreeStore#remove */ remove: me.createRelayer('itemremove'), - + /** * @event itemmove - * Fires when a node is moved to a new location in the tree - * @param {Tree} tree The owner tree - * @param {Node} node The node moved - * @param {Node} oldParent The old parent of this node - * @param {Node} newParent The new parent of this node - * @param {Number} index The index it was moved to + * @alias Ext.data.TreeStore#move */ move: me.createRelayer('itemmove'), - + /** * @event iteminsert - * Fires when a new child node is inserted in a node in tree - * @param {Tree} tree The owner tree - * @param {Node} parent The parent node - * @param {Node} node The child node inserted - * @param {Node} refNode The child node the node was inserted before + * @alias Ext.data.TreeStore#insert */ insert: me.createRelayer('iteminsert'), - + /** * @event beforeitemappend - * Fires before a new child is appended to a node in this tree, return false to cancel the append. - * @param {Tree} tree The owner tree - * @param {Node} parent The parent node - * @param {Node} node The child node to be appended + * @alias Ext.data.TreeStore#beforeappend */ beforeappend: me.createRelayer('beforeitemappend'), - + /** * @event beforeitemremove - * Fires before a child is removed from a node in this tree, return false to cancel the remove. - * @param {Tree} tree The owner tree - * @param {Node} parent The parent node - * @param {Node} node The child node to be removed + * @alias Ext.data.TreeStore#beforeremove */ beforeremove: me.createRelayer('beforeitemremove'), - + /** * @event beforeitemmove - * Fires before a node is moved to a new location in the tree. Return false to cancel the move. - * @param {Tree} tree The owner tree - * @param {Node} node The node being moved - * @param {Node} oldParent The parent of the node - * @param {Node} newParent The new parent the node is moving to - * @param {Number} index The index it is being moved to + * @alias Ext.data.TreeStore#beforemove */ beforemove: me.createRelayer('beforeitemmove'), - + /** * @event beforeiteminsert - * Fires before a new child is inserted in a node in this tree, return false to cancel the insert. - * @param {Tree} tree The owner tree - * @param {Node} parent The parent node - * @param {Node} node The child node to be inserted - * @param {Node} refNode The child node the node is being inserted before + * @alias Ext.data.TreeStore#beforeinsert */ beforeinsert: me.createRelayer('beforeiteminsert'), - + /** * @event itemexpand - * Fires when a node is expanded. - * @param {Node} this The expanding node + * @alias Ext.data.TreeStore#expand */ expand: me.createRelayer('itemexpand'), - + /** * @event itemcollapse - * Fires when a node is collapsed. - * @param {Node} this The collapsing node + * @alias Ext.data.TreeStore#collapse */ collapse: me.createRelayer('itemcollapse'), - + /** * @event beforeitemexpand - * Fires before a node is expanded. - * @param {Node} this The expanding node + * @alias Ext.data.TreeStore#beforeexpand */ beforeexpand: me.createRelayer('beforeitemexpand'), - + /** * @event beforeitemcollapse - * Fires before a node is collapsed. - * @param {Node} this The collapsing node + * @alias Ext.data.TreeStore#beforecollapse */ beforecollapse: me.createRelayer('beforeitemcollapse') }); - + // If the user specifies the headers collection manually then dont inject our own if (!me.columns) { if (me.initialConfig.hideHeaders === undefined) { @@ -128081,16 +133169,16 @@ Ext.define('Ext.tree.Panel', { xtype : 'treecolumn', text : 'Name', flex : 1, - dataIndex: me.displayField + dataIndex: me.displayField }]; } - + if (me.cls) { cls.push(me.cls); } me.cls = cls.join(' '); me.callParent(); - + me.relayEvents(me.getView(), [ /** * @event checkchange @@ -128100,7 +133188,7 @@ Ext.define('Ext.tree.Panel', { */ 'checkchange' ]); - + // If the root is not visible and there is no rootnode defined, then just lets load the store if (!me.getView().rootVisible && !me.getRootNode()) { me.setRootNode({ @@ -128108,44 +133196,61 @@ Ext.define('Ext.tree.Panel', { }); } }, - + onClear: function(){ this.view.onClear(); }, - + + /** + * Sets root node of this tree. + * @param {Ext.data.Model/Ext.data.NodeInterface/Object} root + * @return {Ext.data.NodeInterface} The new root + */ setRootNode: function() { return this.store.setRootNode.apply(this.store, arguments); }, - + + /** + * Returns the root node for this tree. + * @return {Ext.data.NodeInterface} + */ getRootNode: function() { return this.store.getRootNode(); }, - + onRootChange: function(root) { this.view.setRootNode(root); }, /** * Retrieve an array of checked records. - * @return {Array} An array containing the checked records + * @return {Ext.data.Model[]} An array containing the checked records */ getChecked: function() { return this.getView().getChecked(); }, - + isItemChecked: function(rec) { return rec.get('checked'); }, - + /** * Expand all nodes * @param {Function} callback (optional) A function to execute when the expand finishes. * @param {Object} scope (optional) The scope of the callback function */ expandAll : function(callback, scope) { - var root = this.getRootNode(); + var root = this.getRootNode(), + animate = this.enableAnimations, + view = this.getView(); if (root) { + if (!animate) { + view.beginBulkUpdate(); + } root.expand(true, callback, scope); + if (!animate) { + view.endBulkUpdate(); + } } }, @@ -128155,14 +133260,22 @@ Ext.define('Ext.tree.Panel', { * @param {Object} scope (optional) The scope of the callback function */ collapseAll : function(callback, scope) { - var root = this.getRootNode(); + var root = this.getRootNode(), + animate = this.enableAnimations, + view = this.getView(); + if (root) { - if (this.getView().rootVisible) { - root.collapse(true, callback, scope); + if (!animate) { + view.beginBulkUpdate(); } - else { + if (view.rootVisible) { + root.collapse(true, callback, scope); + } else { root.collapseChildren(true, callback, scope); } + if (!animate) { + view.endBulkUpdate(); + } } }, @@ -128182,22 +133295,22 @@ Ext.define('Ext.tree.Panel', { view = me.getView(), keys, expander; - + field = field || me.getRootNode().idProperty; separator = separator || '/'; - + if (Ext.isEmpty(path)) { Ext.callback(callback, scope || me, [false, null]); return; } - + keys = path.split(separator); if (current.get(field) != keys[1]) { // invalid root Ext.callback(callback, scope || me, [false, current]); return; } - + expander = function(){ if (++index === keys.length) { Ext.callback(callback, scope || me, [true, current]); @@ -128213,9 +133326,9 @@ Ext.define('Ext.tree.Panel', { }; current.expand(false, expander); }, - + /** - * Expand the tree to the path of a particular node, then selecti t. + * Expand the tree to the path of a particular node, then select it. * @param {String} path The path to select. The path should include a leading separator. * @param {String} field (optional) The field to get the data from. Defaults to the model idProperty. * @param {String} separator (optional) A separator to use. Defaults to `'/'`. @@ -128227,13 +133340,13 @@ Ext.define('Ext.tree.Panel', { var me = this, keys, last; - + field = field || me.getRootNode().idProperty; separator = separator || '/'; - + keys = path.split(separator); last = keys.pop(); - + me.expandPath(keys.join(separator), field, separator, function(success, node){ var doSuccess = false; if (success && node) { @@ -128416,26 +133529,26 @@ Ext.define('Ext.tree.ViewDropZone', { /** * @cfg {Boolean} allowParentInsert * Allow inserting a dragged node between an expanded parent node and its first child that will become a - * sibling of the parent when dropped (defaults to false) + * sibling of the parent when dropped. */ allowParentInserts: false, /** * @cfg {String} allowContainerDrop - * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false) + * True if drops on the tree container (outside of a specific tree node) are allowed. */ allowContainerDrops: false, /** * @cfg {String} appendOnly - * True if the tree should only allow append drops (use for trees which are sorted, defaults to false) + * True if the tree should only allow append drops (use for trees which are sorted). */ appendOnly: false, /** * @cfg {String} expandDelay * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node - * over the target (defaults to 500) + * over the target. */ expandDelay : 500, @@ -128689,27 +133802,44 @@ Ext.define('Ext.tree.ViewDropZone', { } }); /** - * @class Ext.tree.ViewDDPlugin - * @extends Ext.AbstractPlugin - *

        This plugin provides drag and/or drop functionality for a TreeView.

        - *

        It creates a specialized instance of {@link Ext.dd.DragZone DragZone} which knows how to drag out of a {@link Ext.tree.View TreeView} - * and loads the data object which is passed to a cooperating {@link Ext.dd.DragZone DragZone}'s methods with the following properties:

          - *
        • copy : Boolean - *
          The value of the TreeView's copy property, or true if the TreeView was configured - * with allowCopy: true and the control key was pressed when the drag operation was begun.
        • - *
        • view : TreeView - *
          The source TreeView from which the drag originated.
        • - *
        • ddel : HtmlElement - *
          The drag proxy element which moves with the mouse
        • - *
        • item : HtmlElement - *
          The TreeView node upon which the mousedown event was registered.
        • - *
        • records : Array - *
          An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source TreeView.
        • - *

        - *

        It also creates a specialized instance of {@link Ext.dd.DropZone} which cooperates with other DropZones which are members of the same - * ddGroup which processes such data objects.

        - *

        Adding this plugin to a view means that two new events may be fired from the client TreeView, {@link #event-beforedrop beforedrop} and - * {@link #event-drop drop}

        + * This plugin provides drag and/or drop functionality for a TreeView. + * + * It creates a specialized instance of {@link Ext.dd.DragZone DragZone} which knows how to drag out of a + * {@link Ext.tree.View TreeView} and loads the data object which is passed to a cooperating + * {@link Ext.dd.DragZone DragZone}'s methods with the following properties: + * + * - copy : Boolean + * + * The value of the TreeView's `copy` property, or `true` if the TreeView was configured with `allowCopy: true` *and* + * the control key was pressed when the drag operation was begun. + * + * - view : TreeView + * + * The source TreeView from which the drag originated. + * + * - ddel : HtmlElement + * + * The drag proxy element which moves with the mouse + * + * - item : HtmlElement + * + * The TreeView node upon which the mousedown event was registered. + * + * - records : Array + * + * An Array of {@link Ext.data.Model Models} representing the selected data being dragged from the source TreeView. + * + * It also creates a specialized instance of {@link Ext.dd.DropZone} which cooperates with other DropZones which are + * members of the same ddGroup which processes such data objects. + * + * Adding this plugin to a view means that two new events may be fired from the client TreeView, {@link #beforedrop} and + * {@link #drop}. + * + * Note that the plugin must be added to the tree view, not to the tree panel. For example using viewConfig: + * + * viewConfig: { + * plugins: { ptype: 'treeviewdragdrop' } + * } */ Ext.define('Ext.tree.plugin.TreeViewDragDrop', { extend: 'Ext.AbstractPlugin', @@ -128722,145 +133852,155 @@ Ext.define('Ext.tree.plugin.TreeViewDragDrop', { /** * @event beforedrop - *

        This event is fired through the TreeView. Add listeners to the TreeView object

        - *

        Fired when a drop gesture has been triggered by a mouseup event in a valid drop position in the TreeView. - * @param {HtmlElement} node The TreeView node if any over which the mouse was positioned.

        - *

        Returning false to this event signals that the drop gesture was invalid, and if the drag proxy - * will animate back to the point from which the drag began.

        - *

        Returning 0 To this event signals that the data transfer operation should not take place, but - * that the gesture was valid, and that the repair operation should not take place.

        - *

        Any other return value continues with the data transfer operation.

        - * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s - * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:
          - *
        • copy : Boolean - *
          The value of the TreeView's copy property, or true if the TreeView was configured - * with allowCopy: true and the control key was pressed when the drag operation was begun
        • - *
        • view : TreeView - *
          The source TreeView from which the drag originated.
        • - *
        • ddel : HtmlElement - *
          The drag proxy element which moves with the mouse
        • - *
        • item : HtmlElement - *
          The TreeView node upon which the mousedown event was registered.
        • - *
        • records : Array - *
          An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source TreeView.
        • - *
        + * + * **This event is fired through the TreeView. Add listeners to the TreeView object** + * + * Fired when a drop gesture has been triggered by a mouseup event in a valid drop position in the TreeView. + * + * @param {HTMLElement} node The TreeView node **if any** over which the mouse was positioned. + * + * Returning `false` to this event signals that the drop gesture was invalid, and if the drag proxy will animate + * back to the point from which the drag began. + * + * Returning `0` To this event signals that the data transfer operation should not take place, but that the gesture + * was valid, and that the repair operation should not take place. + * + * Any other return value continues with the data transfer operation. + * + * @param {Object} data The data object gathered at mousedown time by the cooperating + * {@link Ext.dd.DragZone DragZone}'s {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following + * properties: + * @param {Boolean} data.copy The value of the TreeView's `copy` property, or `true` if the TreeView was configured with + * `allowCopy: true` and the control key was pressed when the drag operation was begun + * @param {Ext.tree.View} data.view The source TreeView from which the drag originated. + * @param {HTMLElement} data.ddel The drag proxy element which moves with the mouse + * @param {HTMLElement} data.item The TreeView node upon which the mousedown event was registered. + * @param {Ext.data.Model[]} data.records An Array of {@link Ext.data.Model Model}s representing the selected data being + * dragged from the source TreeView. + * * @param {Ext.data.Model} overModel The Model over which the drop gesture took place. - * @param {String} dropPosition "before", "after" or "append" depending on whether the mouse is above or below the midline of the node, - * or the node is a branch node which accepts new child nodes. - * @param {Function} dropFunction

        A function to call to complete the data transfer operation and either move or copy Model instances from the source - * View's Store to the destination View's Store.

        - *

        This is useful when you want to perform some kind of asynchronous processing before confirming - * the drop, such as an {@link Ext.window.MessageBox#confirm confirm} call, or an Ajax request.

        - *

        Return 0 from this event handler, and call the dropFunction at any time to perform the data transfer.

        + * + * @param {String} dropPosition `"before"`, `"after"` or `"append"` depending on whether the mouse is above or below + * the midline of the node, or the node is a branch node which accepts new child nodes. + * + * @param {Function} dropFunction A function to call to complete the data transfer operation and either move or copy + * Model instances from the source View's Store to the destination View's Store. + * + * This is useful when you want to perform some kind of asynchronous processing before confirming the drop, such as + * an {@link Ext.window.MessageBox#confirm confirm} call, or an Ajax request. + * + * Return `0` from this event handler, and call the `dropFunction` at any time to perform the data transfer. */ /** * @event drop - * This event is fired through the TreeView. Add listeners to the TreeView object - * Fired when a drop operation has been completed and the data has been moved or copied. - * @param {HtmlElement} node The TreeView node if any over which the mouse was positioned. - * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s - * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:
          - *
        • copy : Boolean - *
          The value of the TreeView's copy property, or true if the TreeView was configured - * with allowCopy: true and the control key was pressed when the drag operation was begun
        • - *
        • view : TreeView - *
          The source TreeView from which the drag originated.
        • - *
        • ddel : HtmlElement - *
          The drag proxy element which moves with the mouse
        • - *
        • item : HtmlElement - *
          The TreeView node upon which the mousedown event was registered.
        • - *
        • records : Array - *
          An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source TreeView.
        • - *
        + * + * **This event is fired through the TreeView. Add listeners to the TreeView object** Fired when a drop operation + * has been completed and the data has been moved or copied. + * + * @param {HTMLElement} node The TreeView node **if any** over which the mouse was positioned. + * + * @param {Object} data The data object gathered at mousedown time by the cooperating + * {@link Ext.dd.DragZone DragZone}'s {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following + * properties: + * @param {Boolean} data.copy The value of the TreeView's `copy` property, or `true` if the TreeView was configured with + * `allowCopy: true` and the control key was pressed when the drag operation was begun + * @param {Ext.tree.View} data.view The source TreeView from which the drag originated. + * @param {HTMLElement} data.ddel The drag proxy element which moves with the mouse + * @param {HTMLElement} data.item The TreeView node upon which the mousedown event was registered. + * @param {Ext.data.Model[]} data.records An Array of {@link Ext.data.Model Model}s representing the selected data being + * dragged from the source TreeView. + * * @param {Ext.data.Model} overModel The Model over which the drop gesture took place. - * @param {String} dropPosition "before", "after" or "append" depending on whether the mouse is above or below the midline of the node, - * or the node is a branch node which accepts new child nodes. + * + * @param {String} dropPosition `"before"`, `"after"` or `"append"` depending on whether the mouse is above or below + * the midline of the node, or the node is a branch node which accepts new child nodes. */ dragText : '{0} selected node{1}', /** * @cfg {Boolean} allowParentInsert - * Allow inserting a dragged node between an expanded parent node and its first child that will become a - * sibling of the parent when dropped (defaults to false) + * Allow inserting a dragged node between an expanded parent node and its first child that will become a sibling of + * the parent when dropped. */ allowParentInserts: false, /** * @cfg {String} allowContainerDrop - * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false) + * True if drops on the tree container (outside of a specific tree node) are allowed. */ allowContainerDrops: false, /** * @cfg {String} appendOnly - * True if the tree should only allow append drops (use for trees which are sorted, defaults to false) + * True if the tree should only allow append drops (use for trees which are sorted). */ appendOnly: false, /** * @cfg {String} ddGroup - * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and DropZone - * used by this plugin will only interact with other drag drop objects in the same group (defaults to 'TreeDD'). + * A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and + * DropZone used by this plugin will only interact with other drag drop objects in the same group. */ ddGroup : "TreeDD", /** * @cfg {String} dragGroup - *

        The ddGroup to which the DragZone will belong.

        - *

        This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other Drag/DropZones - * which are members of the same ddGroup.

        + * The ddGroup to which the DragZone will belong. + * + * This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other + * Drag/DropZones which are members of the same ddGroup. */ /** * @cfg {String} dropGroup - *

        The ddGroup to which the DropZone will belong.

        - *

        This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other Drag/DropZones - * which are members of the same ddGroup.

        + * The ddGroup to which the DropZone will belong. + * + * This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other + * Drag/DropZones which are members of the same ddGroup. */ /** * @cfg {String} expandDelay - * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node - * over the target (defaults to 1000) + * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node over the + * target. */ expandDelay : 1000, /** * @cfg {Boolean} enableDrop - *

        Defaults to true

        - *

        Set to false to disallow the View from accepting drop gestures

        + * Set to `false` to disallow the View from accepting drop gestures. */ enableDrop: true, /** * @cfg {Boolean} enableDrag - *

        Defaults to true

        - *

        Set to false to disallow dragging items from the View

        + * Set to `false` to disallow dragging items from the View. */ enableDrag: true, - + /** - * @cfg {String} nodeHighlightColor The color to use when visually highlighting the dragged - * or dropped node (defaults to 'c3daf9' - light blue). The color must be a 6 digit hex value, without - * a preceding '#'. See also {@link #nodeHighlightOnDrop} and {@link #nodeHighlightOnRepair}. + * @cfg {String} nodeHighlightColor + * The color to use when visually highlighting the dragged or dropped node (default value is light blue). + * The color must be a 6 digit hex value, without a preceding '#'. See also {@link #nodeHighlightOnDrop} and + * {@link #nodeHighlightOnRepair}. */ nodeHighlightColor: 'c3daf9', - + /** - * @cfg {Boolean} nodeHighlightOnDrop Whether or not to highlight any nodes after they are + * @cfg {Boolean} nodeHighlightOnDrop + * Whether or not to highlight any nodes after they are * successfully dropped on their target. Defaults to the value of `Ext.enableFx`. * See also {@link #nodeHighlightColor} and {@link #nodeHighlightOnRepair}. - * @markdown */ nodeHighlightOnDrop: Ext.enableFx, - + /** - * @cfg {Boolean} nodeHighlightOnRepair Whether or not to highlight any nodes after they are + * @cfg {Boolean} nodeHighlightOnRepair + * Whether or not to highlight any nodes after they are * repaired from an unsuccessful drag/drop. Defaults to the value of `Ext.enableFx`. * See also {@link #nodeHighlightColor} and {@link #nodeHighlightOnDrop}. - * @markdown */ nodeHighlightOnRepair: Ext.enableFx, @@ -128922,7 +134062,7 @@ Ext.define('Ext.util.Cookies', { * for the cookie may be optionally specified (for example: expiration, * access restriction, SSL). * @param {String} name The name of the cookie to set. - * @param {Mixed} value The value to set for the cookie. + * @param {Object} value The value to set for the cookie. * @param {Object} expires (Optional) Specify an expiration date the * cookie is to persist until. Note that the specified Date object will * be converted to Greenwich Mean Time (GMT). @@ -128959,7 +134099,7 @@ Ext.define('Ext.util.Cookies', { * var validStatus = Ext.util.Cookies.get("valid"); * * @param {String} name The name of the cookie to get - * @return {Mixed} Returns the cookie value for the specified name; + * @return {Object} Returns the cookie value for the specified name; * null if the cookie name does not exist. */ get : function(name){ @@ -129027,13 +134167,13 @@ Ext.define('Ext.util.CSS', function() { this.rules = {}; this.initialized = false; }, - + /** * Creates a stylesheet from a text blob of rules. * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document. * @param {String} cssText The text containing the css rules * @param {String} id An id to add to the stylesheet for later removal - * @return {StyleSheet} + * @return {CSSStyleSheet} */ createStyleSheet : function(cssText, id) { var ss, @@ -129112,7 +134252,7 @@ Ext.define('Ext.util.CSS', function() { for (; i >= 0; --i) { selectorText = ssRules[i].selectorText; if (selectorText) { - + // Split in case there are multiple, comma-delimited selectors selectorText = selectorText.split(','); selectors = selectorText.length; @@ -129141,7 +134281,7 @@ Ext.define('Ext.util.CSS', function() { if (!ds[i].disabled) { this.cacheStyleSheet(ds[i]); } - } catch(e) {} + } catch(e) {} } } return rules; @@ -129149,9 +134289,9 @@ Ext.define('Ext.util.CSS', function() { /** * Gets an an individual CSS rule by selector(s) - * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned. + * @param {String/String[]} selector The CSS selector or an array of selectors to try. The first selector that is found is returned. * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically - * @return {CSSRule} The CSS rule or null if one is not found + * @return {CSSStyleRule} The CSS rule or null if one is not found */ getRule: function(selector, refreshCache) { var rs = this.getRules(refreshCache); @@ -129168,7 +134308,7 @@ Ext.define('Ext.util.CSS', function() { /** * Updates a rule property - * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found. + * @param {String/String[]} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found. * @param {String} property The css property * @param {String} value The new value for the property * @return {Boolean} true If a rule was found and updated @@ -129193,28 +134333,27 @@ Ext.define('Ext.util.CSS', function() { }()); /** * @class Ext.util.History - -History management component that allows you to register arbitrary tokens that signify application -history state on navigation actions. You can then handle the history {@link #change} event in order -to reset your application UI to the appropriate state when the user navigates forward or backward through -the browser history stack. - -__Initializing__ -The {@link #init} method of the History object must be called before using History. This sets up the internal -state and must be the first thing called before using History. - -__Setup__ -The History objects requires elements on the page to keep track of the browser history. For older versions of IE, -an IFrame is required to do the tracking. For other browsers, a hidden field can be used. The history objects expects -these to be on the page before the {@link #init} method is called. The following markup is suggested in order -to support all browsers: - - - - - - - * @markdown + * + * History management component that allows you to register arbitrary tokens that signify application + * history state on navigation actions. You can then handle the history {@link #change} event in order + * to reset your application UI to the appropriate state when the user navigates forward or backward through + * the browser history stack. + * + * ## Initializing + * The {@link #init} method of the History object must be called before using History. This sets up the internal + * state and must be the first thing called before using History. + * + * ## Setup + * The History objects requires elements on the page to keep track of the browser history. For older versions of IE, + * an IFrame is required to do the tracking. For other browsers, a hidden field can be used. The history objects expects + * these to be on the page before the {@link #init} method is called. The following markup is suggested in order + * to support all browsers: + * + *
        + * + * + *
        + * * @singleton */ Ext.define('Ext.util.History', { @@ -129223,7 +134362,7 @@ Ext.define('Ext.util.History', { mixins: { observable: 'Ext.util.Observable' }, - + constructor: function() { var me = this; me.oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict && Ext.isIE8; @@ -129232,18 +134371,18 @@ Ext.define('Ext.util.History', { me.ready = false; me.currentToken = null; }, - + getHash: function() { var href = window.location.href, i = href.indexOf("#"); - + return i >= 0 ? href.substr(i + 1) : null; }, doSave: function() { this.hiddenField.value = this.currentToken; }, - + handleStateChange: function(token) { this.currentToken = token; @@ -129251,8 +134390,8 @@ Ext.define('Ext.util.History', { }, updateIFrame: function(token) { - var html = '
        ' + - Ext.util.Format.htmlEncode(token) + + var html = '
        ' + + Ext.util.Format.htmlEncode(token) + '
        '; try { @@ -129269,17 +134408,17 @@ Ext.define('Ext.util.History', { checkIFrame: function () { var me = this, contentWindow = me.iframe.contentWindow; - + if (!contentWindow || !contentWindow.document) { Ext.Function.defer(this.checkIFrame, 10, this); return; } - + var doc = contentWindow.document, elem = doc.getElementById("state"), oldToken = elem ? elem.innerText : null, oldHash = me.getHash(); - + Ext.TaskManager.start({ run: function () { var doc = contentWindow.document, @@ -129297,17 +134436,17 @@ Ext.define('Ext.util.History', { oldHash = newHash; me.updateIFrame(newHash); } - }, + }, interval: 50, scope: me }); me.ready = true; - me.fireEvent('ready', me); + me.fireEvent('ready', me); }, startUp: function () { var me = this; - + me.currentToken = me.hiddenField.value || this.getHash(); if (me.oldIEMode) { @@ -129329,7 +134468,7 @@ Ext.define('Ext.util.History', { me.ready = true; me.fireEvent('ready', me); } - + }, /** @@ -129349,29 +134488,29 @@ Ext.define('Ext.util.History', { * Initialize the global History instance. * @param {Boolean} onReady (optional) A callback function that will be called once the history * component is fully initialized. - * @param {Object} scope (optional) The scope (this reference) in which the callback is executed. Defaults to the browser window. + * @param {Object} scope (optional) The scope (`this` reference) in which the callback is executed. Defaults to the browser window. */ init: function (onReady, scope) { var me = this; - + if (me.ready) { Ext.callback(onReady, scope, [me]); return; } - + if (!Ext.isReady) { Ext.onReady(function() { me.init(onReady, scope); }); return; } - + me.hiddenField = Ext.getDom(me.fieldId); - + if (me.oldIEMode) { me.iframe = Ext.getDom(me.iframeId); } - + me.addEvents( /** * @event ready @@ -129386,7 +134525,7 @@ Ext.define('Ext.util.History', { */ 'change' ); - + if (onReady) { me.on('ready', onReady, scope, {single: true}); } @@ -129395,28 +134534,28 @@ Ext.define('Ext.util.History', { /** * Add a new token to the history stack. This can be any arbitrary value, although it would - * commonly be the concatenation of a component id and another id marking the specifc history - * state of that component. Example usage: - *
        
        -// Handle tab changes on a TabPanel
        -tabPanel.on('tabchange', function(tabPanel, tab){
        -Ext.History.add(tabPanel.id + ':' + tab.id);
        -});
        -
        + * commonly be the concatenation of a component id and another id marking the specific history + * state of that component. Example usage: + * + * // Handle tab changes on a TabPanel + * tabPanel.on('tabchange', function(tabPanel, tab){ + * Ext.History.add(tabPanel.id + ':' + tab.id); + * }); + * * @param {String} token The value that defines a particular application-specific history state - * @param {Boolean} preventDuplicates When true, if the passed token matches the current token + * @param {Boolean} [preventDuplicates=true] When true, if the passed token matches the current token * it will not save a new history step. Set to false if the same state can be saved more than once - * at the same history stack location (defaults to true). + * at the same history stack location. */ add: function (token, preventDup) { var me = this; - + if (preventDup !== false) { if (me.getToken() === token) { return true; } } - + if (me.oldIEMode) { return me.updateIFrame(token); } else {