X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/core/src/lang/Object.js?ds=sidebyside diff --git a/src/core/src/lang/Object.js b/src/core/src/lang/Object.js new file mode 100644 index 00000000..febe5ffb --- /dev/null +++ b/src/core/src/lang/Object.js @@ -0,0 +1,528 @@ +/** + * @author Jacky Nguyen + * @docauthor Jacky Nguyen + * @class Ext.Object + * + * A collection of useful static methods to deal with objects + * + * @singleton + */ + +(function() { + +var ExtObject = Ext.Object = { + + /** + * Convert 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 }, + ]; + + * @param {String} name + * @param {Mixed} value + * @param {Boolean} recursive + * @markdown + */ + toQueryObjects: function(name, value, recursive) { + var self = ExtObject.toQueryObjects, + objects = [], + i, ln; + + if (Ext.isArray(value)) { + for (i = 0, ln = value.length; i < ln; i++) { + if (recursive) { + objects = objects.concat(self(name + '[' + i + ']', value[i], true)); + } + else { + objects.push({ + name: name, + value: value[i] + }); + } + } + } + else if (Ext.isObject(value)) { + for (i in value) { + if (value.hasOwnProperty(i)) { + if (recursive) { + objects = objects.concat(self(name + '[' + i + ']', value[i], true)); + } + else { + objects.push({ + name: name, + value: value[i] + }); + } + } + } + } + else { + objects.push({ + name: name, + value: value + }); + } + + return objects; + }, + + /** + * 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 + * @return {String} queryString + * @markdown + */ + toQueryString: function(object, recursive) { + var paramObjects = [], + params = [], + i, j, ln, paramObject, value; + + for (i in object) { + if (object.hasOwnProperty(i)) { + paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive)); + } + } + + for (j = 0, ln = paramObjects.length; j < ln; j++) { + paramObject = paramObjects[j]; + value = paramObject.value; + + if (Ext.isEmpty(value)) { + value = ''; + } + else if (Ext.isDate(value)) { + value = Ext.Date.toString(value); + } + + params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value))); + } + + return params.join('&'); + }, + + /** + * Converts a query string back into an object. + * +- Non-recursive: + + Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2} + Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2} + Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'} + Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']} + +- Recursive: + + Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true); + + // returns + { + username: 'Jacky', + dateOfBirth: { + day: '1', + month: '2', + year: '1911' + }, + hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']] + } + + * @param {String} queryString The query string to decode + * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by + * PHP / Ruby on Rails servers and similar. Defaults to false + * @return {Object} + */ + fromQueryString: function(queryString, recursive) { + var parts = queryString.replace(/^\?/, '').split('&'), + object = {}, + temp, components, name, value, i, ln, + part, j, subLn, matchedKeys, matchedName, + keys, key, nextKey; + + for (i = 0, ln = parts.length; i < ln; i++) { + part = parts[i]; + + if (part.length > 0) { + components = part.split('='); + name = decodeURIComponent(components[0]); + value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : ''; + + if (!recursive) { + if (object.hasOwnProperty(name)) { + if (!Ext.isArray(object[name])) { + object[name] = [object[name]]; + } + + object[name].push(value); + } + else { + object[name] = value; + } + } + else { + matchedKeys = name.match(/(\[):?([^\]]*)\]/g); + matchedName = name.match(/^([^\[]+)/); + + // + if (!matchedName) { + Ext.Error.raise({ + sourceClass: "Ext.Object", + sourceMethod: "fromQueryString", + queryString: queryString, + recursive: recursive, + msg: 'Malformed query string given, failed parsing name from "' + part + '"' + }); + } + // + + name = matchedName[0]; + keys = []; + + if (matchedKeys === null) { + object[name] = value; + continue; + } + + for (j = 0, subLn = matchedKeys.length; j < subLn; j++) { + key = matchedKeys[j]; + key = (key.length === 2) ? '' : key.substring(1, key.length - 1); + keys.push(key); + } + + keys.unshift(name); + + temp = object; + + for (j = 0, subLn = keys.length; j < subLn; j++) { + key = keys[j]; + + if (j === subLn - 1) { + if (Ext.isArray(temp) && key === '') { + temp.push(value); + } + else { + temp[key] = value; + } + } + else { + if (temp[key] === undefined || typeof temp[key] === 'string') { + nextKey = keys[j+1]; + + temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {}; + } + + temp = temp[key]; + } + } + } + } + } + + return 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 + } + }); + + * @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 + */ + each: function(object, fn, scope) { + for (var property in object) { + if (object.hasOwnProperty(property)) { + if (fn.call(scope || object, property, object[property], object) === false) { + return; + } + } + } + }, + + /** + * 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,... + * @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') { + if (value && value.constructor === Object) { + if (source[key] && source[key].constructor === Object) { + ExtObject.merge(source[key], value); + } + else { + source[key] = Ext.clone(value); + } + } + else { + source[key] = value; + } + + return source; + } + + var i = 1, + ln = arguments.length, + object, property; + + for (; i < ln; i++) { + object = arguments[i]; + + for (property in object) { + if (object.hasOwnProperty(property)) { + ExtObject.merge(source, property, object[property]); + } + } + } + + return source; + }, + + /** + * 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' + + * @param {Object} object + * @param {Object} value The value to find + * @markdown + */ + getKey: function(object, value) { + for (var property in object) { + if (object.hasOwnProperty(property) && object[property] === value) { + return property; + } + } + + return null; + }, + + /** + * Gets all values of the given object as an array. + + 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 = [], + property; + + for (property in object) { + if (object.hasOwnProperty(property)) { + values.push(object[property]); + } + } + + return values; + }, + + /** + * Gets all keys of the given object as an array. + + var values = Ext.Object.getKeys({ + name: 'Jacky', + loves: 'food' + }); // ['name', 'loves'] + + * @param {Object} object + * @return {Array} An array of keys from the object + */ + getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) { + var keys = [], + property; + + for (property in object) { + if (object.hasOwnProperty(property)) { + keys.push(property); + } + } + + return keys; + }, + + /** + * Gets the total number of this object's own properties + + 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, + property; + + for (property in object) { + if (object.hasOwnProperty(property)) { + size++; + } + } + + return size; + } +}; + + +/** + * A convenient alias method for {@link Ext.Object#merge} + * + * @member Ext + * @method merge + */ +Ext.merge = Ext.Object.merge; + +/** + * A convenient alias method for {@link Ext.Object#toQueryString} + * + * @member Ext + * @method urlEncode + * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead + */ +Ext.urlEncode = function() { + var args = Ext.Array.from(arguments), + prefix = ''; + + // Support for the old `pre` argument + if ((typeof args[1] === 'string')) { + prefix = args[1] + '&'; + args[1] = false; + } + + return prefix + Ext.Object.toQueryString.apply(Ext.Object, args); +}; + +/** + * A convenient alias method for {@link Ext.Object#fromQueryString} + * + * @member Ext + * @method urlDecode + * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead + */ +Ext.urlDecode = function() { + return Ext.Object.fromQueryString.apply(Ext.Object, arguments); +}; + +})();