3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @author Jacky Nguyen <jacky@sencha.com>
17 * @docauthor Jacky Nguyen <jacky@sencha.com>
20 * A collection of useful static methods to deal with objects.
27 var ExtObject = Ext.Object = {
30 * Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
31 * query strings. For example:
33 * var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
35 * // objects then equals:
37 * { name: 'hobbies', value: 'reading' },
38 * { name: 'hobbies', value: 'cooking' },
39 * { name: 'hobbies', value: 'swimming' },
42 * var objects = Ext.Object.toQueryObjects('dateOfBirth', {
50 * }, true); // Recursive
52 * // objects then equals:
54 * { name: 'dateOfBirth[day]', value: 3 },
55 * { name: 'dateOfBirth[month]', value: 8 },
56 * { name: 'dateOfBirth[year]', value: 1987 },
57 * { name: 'dateOfBirth[extra][hour]', value: 4 },
58 * { name: 'dateOfBirth[extra][minute]', value: 30 },
61 * @param {String} name
62 * @param {Object/Array} value
63 * @param {Boolean} [recursive=false] True to traverse object recursively
66 toQueryObjects: function(name, value, recursive) {
67 var self = ExtObject.toQueryObjects,
71 if (Ext.isArray(value)) {
72 for (i = 0, ln = value.length; i < ln; i++) {
74 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
84 else if (Ext.isObject(value)) {
86 if (value.hasOwnProperty(i)) {
88 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
110 * Takes an object and converts it to an encoded query string.
114 * Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
115 * Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
116 * Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
117 * Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
118 * Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
122 * Ext.Object.toQueryString({
129 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
130 * }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
132 * // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
133 * // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
135 * @param {Object} object The object to encode
136 * @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
137 * (PHP / Ruby on Rails servers and similar).
138 * @return {String} queryString
140 toQueryString: function(object, recursive) {
141 var paramObjects = [],
143 i, j, ln, paramObject, value;
146 if (object.hasOwnProperty(i)) {
147 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
151 for (j = 0, ln = paramObjects.length; j < ln; j++) {
152 paramObject = paramObjects[j];
153 value = paramObject.value;
155 if (Ext.isEmpty(value)) {
158 else if (Ext.isDate(value)) {
159 value = Ext.Date.toString(value);
162 params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
165 return params.join('&');
169 * Converts a query string back into an object.
173 * Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
174 * Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
175 * Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
176 * Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
180 * 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);
189 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
192 * @param {String} queryString The query string to decode
193 * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
194 * PHP / Ruby on Rails servers and similar.
197 fromQueryString: function(queryString, recursive) {
198 var parts = queryString.replace(/^\?/, '').split('&'),
200 temp, components, name, value, i, ln,
201 part, j, subLn, matchedKeys, matchedName,
204 for (i = 0, ln = parts.length; i < ln; i++) {
207 if (part.length > 0) {
208 components = part.split('=');
209 name = decodeURIComponent(components[0]);
210 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
213 if (object.hasOwnProperty(name)) {
214 if (!Ext.isArray(object[name])) {
215 object[name] = [object[name]];
218 object[name].push(value);
221 object[name] = value;
225 matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
226 matchedName = name.match(/^([^\[]+)/);
231 sourceClass: "Ext.Object",
232 sourceMethod: "fromQueryString",
233 queryString: queryString,
234 recursive: recursive,
235 msg: 'Malformed query string given, failed parsing name from "' + part + '"'
240 name = matchedName[0];
243 if (matchedKeys === null) {
244 object[name] = value;
248 for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
249 key = matchedKeys[j];
250 key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
258 for (j = 0, subLn = keys.length; j < subLn; j++) {
261 if (j === subLn - 1) {
262 if (Ext.isArray(temp) && key === '') {
270 if (temp[key] === undefined || typeof temp[key] === 'string') {
273 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
287 * Iterates through an object and invokes the given callback function for each iteration.
288 * The iteration can be stopped by returning `false` in the callback function. For example:
293 * loves: ['food', 'sleeping', 'wife']
296 * Ext.Object.each(person, function(key, value, myself) {
297 * console.log(key + ":" + value);
299 * if (key === 'hairColor') {
300 * return false; // stop the iteration
304 * @param {Object} object The object to iterate
305 * @param {Function} fn The callback function.
306 * @param {String} fn.key
307 * @param {Object} fn.value
308 * @param {Object} fn.object The object itself
309 * @param {Object} [scope] The execution scope (`this`) of the callback function
311 each: function(object, fn, scope) {
312 for (var property in object) {
313 if (object.hasOwnProperty(property)) {
314 if (fn.call(scope || object, property, object[property], object) === false) {
322 * Merges any number of objects recursively without referencing them or their children.
325 * companyName: 'Ext JS',
326 * products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
330 * location: 'Palo Alto',
336 * companyName: 'Sencha Inc.',
337 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
340 * location: 'Redwood City'
344 * var sencha = Ext.Object.merge(extjs, newStuff);
346 * // extjs and sencha then equals to
348 * companyName: 'Sencha Inc.',
349 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
353 * location: 'Redwood City'
358 * @param {Object...} object Any number of objects to merge.
359 * @return {Object} merged The object that is created as a result of merging all the objects passed in.
361 merge: function(source, key, value) {
362 if (typeof key === 'string') {
363 if (value && value.constructor === Object) {
364 if (source[key] && source[key].constructor === Object) {
365 ExtObject.merge(source[key], value);
368 source[key] = Ext.clone(value);
379 ln = arguments.length,
382 for (; i < ln; i++) {
383 object = arguments[i];
385 for (property in object) {
386 if (object.hasOwnProperty(property)) {
387 ExtObject.merge(source, property, object[property]);
396 * Returns the first matching key corresponding to the given value.
397 * If no matching value is found, null is returned.
404 * alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
406 * @param {Object} object
407 * @param {Object} value The value to find
409 getKey: function(object, value) {
410 for (var property in object) {
411 if (object.hasOwnProperty(property) && object[property] === value) {
420 * Gets all values of the given object as an array.
422 * var values = Ext.Object.getValues({
425 * }); // ['Jacky', 'food']
427 * @param {Object} object
428 * @return {Array} An array of values from the object
430 getValues: function(object) {
434 for (property in object) {
435 if (object.hasOwnProperty(property)) {
436 values.push(object[property]);
444 * Gets all keys of the given object as an array.
446 * var values = Ext.Object.getKeys({
449 * }); // ['name', 'loves']
451 * @param {Object} object
452 * @return {String[]} An array of keys from the object
455 getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
459 for (property in object) {
460 if (object.hasOwnProperty(property)) {
469 * Gets the total number of this object's own properties
471 * var size = Ext.Object.getSize({
474 * }); // size equals 2
476 * @param {Object} object
477 * @return {Number} size
479 getSize: function(object) {
483 for (property in object) {
484 if (object.hasOwnProperty(property)) {
495 * A convenient alias method for {@link Ext.Object#merge}.
499 * @alias Ext.Object#merge
501 Ext.merge = Ext.Object.merge;
504 * Alias for {@link Ext.Object#toQueryString}.
508 * @alias Ext.Object#toQueryString
509 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
511 Ext.urlEncode = function() {
512 var args = Ext.Array.from(arguments),
515 // Support for the old `pre` argument
516 if ((typeof args[1] === 'string')) {
517 prefix = args[1] + '&';
521 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
525 * Alias for {@link Ext.Object#fromQueryString}.
529 * @alias Ext.Object#fromQueryString
530 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
532 Ext.urlDecode = function() {
533 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);