PageSourceSearch

https://www.springerpub.com/static/version1789146322/base/Magento/base/default/mage/utils/objects.js

js springerpub.com collected 2026-09-24 08:47:42 UTC 13,112 bytes, 451 lines download raw bytes

1/**
2 * Copyright © Magento, Inc. All rights reserved.
3 * See COPYING.txt for license details.
4 */
5define([
6    'ko',
7    'jquery',
8    'underscore',
9    'mage/utils/strings'
10], function (ko, $, _, stringUtils) {
11    'use strict';
12
13    var primitives = [
14        'undefined',
15        'boolean',
16        'number',
17        'string'
18    ];
19
20    /**
21     * Sets nested property of a specified object.
22     * @private
23     *
24     * @param {Object} parent - Object to look inside for the properties.
25     * @param {Array} path - Splitted path the property.
26     * @param {*} value - Value of the last property in 'path' array.
27     * returns {*} New value for the property.
28     */
29    function setNested(parent, path, value) {
30        var last = path.pop(),
31            len = path.length,
32            pi = 0,
33            part = path[pi];
34
35        for (; pi < len; part = path[++pi]) {
36            if (!_.isObject(parent[part])) {
37                parent[part] = {};
38            }
39
40            parent = parent[part];
41        }
42
43        if (typeof parent[last] === 'function') {
44            parent[last](value);
45        } else {
46            parent[last] = value;
47        }
48
49        return value;
50    }
51
52    /**
53     * Retrieves value of a nested property.
54     * @private
55     *
56     * @param {Object} parent - Object to look inside for the properties.
57     * @param {Array} path - Splitted path the property.
58     * @returns {*} Value of the property.
59     */
60    function getNested(parent, path) {
61        var exists = true,
62            len = path.length,
63            pi = 0;
64
65        for (; pi < len && exists; pi++) {
66            parent = parent[path[pi]];
67
68            if (typeof parent === 'undefined') {
69                exists = false;
70            }
71        }
72
73        if (exists) {
74            if (ko.isObservable(parent)) {
75                parent = parent();
76            }
77
78            return parent;
79        }
80    }
81
82    /**
83     * Removes property from a specified object.
84     * @private
85     *
86     * @param {Object} parent - Object from which to remove property.
87     * @param {Array} path - Splitted path to the property.
88     */
89    function removeNested(parent, path) {
90        var field = path.pop();
91
92        parent = getNested(parent, path);
93
94        if (_.isObject(parent)) {
95            delete parent[field];
96        }
97    }
98
99    return {
100
101        /**
102         * Retrieves or defines objects' property by a composite path.
103         *
104         * @param {Object} data - Container for the properties specified in path.
105         * @param {String} path - Objects' properties divided by dots.
106         * @param {*} [value] - New value for the last property.
107         * @returns {*} Returns value of the last property in chain.
108         *
109         * @example
110         *      utils.nested({}, 'one.two', 3);
111         *      => { one: {two: 3} }
112         */
113        nested: function (data, path, value) {
114            var action = arguments.length > 2 ? setNested : getNested;
115
116            path = path ? path.split('.') : [];
117
118            return action(data, path, value);
119        },
120
121        /**
122         * Removes nested property from an object.
123         *
124         * @param {Object} data - Data source.
125         * @param {String} path - Path to the property e.g. 'one.two.three'
126         */
127        nestedRemove: function (data, path) {
128            path = path.split('.');
129
130            removeNested(data, path);
131        },
132
133        /**
134         * Flattens objects' nested properties.
135         *
136         * @param {Object} data - Object to flatten.
137         * @param {String} [separator='.'] - Objects' keys separator.
138         * @returns {Object} Flattened object.
139         *
140         * @example Example with a default separator.
141         *      utils.flatten({one: { two: { three: 'value'} }});
142         *      => { 'one.two.three': 'value' };
143         *
144         * @example Example with a custom separator.
145         *      utils.flatten({one: { two: { three: 'value'} }}, '=>');
146         *      => {'one=>two=>three': 'value'};
147         */
148        flatten: function (data, separator, parent, result) {
149            separator = separator || '.';
150            result = result || {};
151
152            if (!data) {
153                return result;
154            }
155
156            // UnderscoreJS each breaks when an object has a length property so we use Object.keys
157            _.each(Object.keys(data), function (name) {
158                var node = data[name];
159
160                if ({}.toString.call(node) === '[object Function]') {
161                    return;
162                }
163
164                if (parent) {
165                    name = parent + separator + name;
166                }
167
168                typeof node === 'object' ?
169                    this.flatten(node, separator, name, result) :
170                    result[name] = node;
171
172            }, this);
173
174            return result;
175        },
176
177        /**
178         * Opposite operation of the 'flatten' method.
179         *
180         * @param {Object} data - Previously flattened object.
181         * @param {String} [separator='.'] - Keys separator.
182         * @returns {Object} Object with nested properties.
183         *
184         * @example Example using custom separator.
185         *      utils.unflatten({'one=>two': 'value'}, '=>');
186         *      => {
187         *          one: { two: 'value' }
188         *      };
189         */
190        unflatten: function (data, separator) {
191            var result = {};
192
193            separator = separator || '.';
194
195            _.each(data, function (value, nodes) {
196                nodes = nodes.split(separator);
197
198                setNested(result, nodes, value);
199            });
200
201            return result;
202        },
203
204        /**
205         * Same operation as 'flatten' method,
206         * but returns objects' keys wrapped in '[]'.
207         *
208         * @param {Object} data - Object that should be serialized.
209         * @returns {Object} Serialized data.
210         *
211         * @example
212         *      utils.serialize({one: { two: { three: 'value'} }});
213         *      => { 'one[two][three]': 'value' }
214         */
215        serialize: function (data) {
216            var result = {};
217
218            data = this.flatten(data);
219
220            _.each(data, function (value, keys) {
221                keys = stringUtils.serializeName(keys);
222                value = _.isUndefined(value) ? '' : value;
223
224                result[keys] = value;
225            }, this);
226
227            return result;
228        },
229
230        /**
231         * Performs deep extend of specified objects.
232         *
233         * @returns {Object|Array} Extended object.
234         */
235        extend: function () {
236            var args = _.toArray(arguments);
237
238            args.unshift(true);
239
240            return $.extend.apply($, args);
241        },
242
243        /**
244         * Performs a deep clone of a specified object.
245         *
246         * @param {(Object|Array)} data - Data that should be copied.
247         * @returns {Object|Array} Cloned object.
248         */
249        copy: function (data) {
250            var result = data,
251                isArray = Array.isArray(data),
252                placeholder;
253
254            if (this.isObject(data) || isArray) {
255                placeholder = isArray ? [] : {};
256                result = this.extend(placeholder, data);
257            }
258
259            return result;
260        },
261
262        /**
263         * Performs a deep clone of a specified object.
264         * Doesn't save links to original object.
265         *
266         * @param {*} original - Object to clone
267         * @returns {*}
268         */
269        hardCopy: function (original) {
270            if (original === null || typeof original !== 'object') {
271                return original;
272            }
273
274            return JSON.parse(JSON.stringify(original));
275        },
276
277        /**
278         * Removes specified nested properties from the target object.
279         *
280         * @param {Object} target - Object whose properties should be removed.
281         * @param {(...String|Array|Object)} list - List that specifies properties to be removed.
282         * @returns {Object} Modified object.
283         *
284         * @example Basic usage
285         *      var obj = {a: {b: 2}, c: 'a'};
286         *
287         *      omit(obj, 'a.b');
288         *      => {'a.b': 2};
289         *      obj => {a: {}, c: 'a'};
290         *
291         * @example Various syntaxes that would return same result
292         *      omit(obj, ['a.b', 'c']);
293         *      omit(obj, 'a.b', 'c');
294         *      omit(obj, {'a.b': true, 'c': true});
295         */
296        omit: function (target, list) {
297            var removed = {},
298                ignored = list;
299
300            if (this.isObject(list)) {
301                ignored = [];
302
303                _.each(list, function (value, key) {
304                    if (value) {
305                        ignored.push(key);
306                    }
307                });
308            } else if (_.isString(list)) {
309                ignored = _.toArray(arguments).slice(1);
310            }
311
312            _.each(ignored, function (path) {
313                var value = this.nested(target, path);
314
315                if (!_.isUndefined(value)) {
316                    removed[path] = value;
317
318                    this.nestedRemove(target, path);
319                }
320            }, this);
321
322            return removed;
323        },
324
325        /**
326         * Checks if provided value is a plain object.
327         *
328         * @param {*} value - Value to be checked.
329         * @returns {Boolean}
330         */
331        isObject: function (value) {
332            var objProto = Object.prototype;
333
334            return typeof value == 'object' ?
335            objProto.toString.call(value) === '[object Object]' :
336                false;
337        },
338
339        /**
340         *
341         * @param {*} value
342         * @returns {Boolean}
343         */
344        isPrimitive: function (value) {
345            return value === null || ~primitives.indexOf(typeof value);
346        },
347
348        /**
349         * Iterates over obj props/array elems recursively, applying action to each one
350         *
351         * @param {Object|Array} data - Data to be iterated.
352         * @param {Function} action - Callback to be called with each item as an argument.
353         * @param {Number} [maxDepth=7] - Max recursion depth.
354         */
355        forEachRecursive: function (data, action, maxDepth) {
356            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;
357
358            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {
359                return;
360            }
361
362            if (!_.isObject(data)) {
363                action(data);
364
365                return;
366            }
367
368            _.each(data, function (value) {
369                this.forEachRecursive(value, action, maxDepth);
370            }, this);
371
372            action(data);
373        },
374
375        /**
376         * Maps obj props/array elems recursively
377         *
378         * @param {Object|Array} data - Data to be iterated.
379         * @param {Function} action - Callback to transform each item.
380         * @param {Number} [maxDepth=7] - Max recursion depth.
381         *
382         * @returns {Object|Array}
383         */
384        mapRecursive: function (data, action, maxDepth) {
385            var newData;
386
387            maxDepth = typeof maxDepth === 'number' && !isNaN(maxDepth) ? maxDepth - 1 : 7;
388
389            if (!_.isFunction(action) || _.isFunction(data) || maxDepth < 0) {
390                return data;
391            }
392
393            if (!_.isObject(data)) {
394                return action(data);
395            }
396
397            if (_.isArray(data)) {
398                newData = _.map(data, function (item) {
399                    return this.mapRecursive(item, action, maxDepth);
400                }, this);
401
402                return action(newData);
403            }
404
405            newData = _.mapObject(data, function (val, key) {
406                if (data.hasOwnProperty(key)) {
407                    return this.mapRecursive(val, action, maxDepth);
408                }
409
410                return val;
411            }, this);
412
413            return action(newData);
414        },
415
416        /**
417         * Removes empty(in common sence) obj props/array elems
418         *
419         * @param {*} data - Data to be cleaned.
420         * @returns {*}
421         */
422        removeEmptyValues: function (data) {
423            if (!_.isObject(data)) {
424                return data;
425            }
426
427            if (_.isArray(data)) {
428                return data.filter(function (item) {
429                    return !this.isEmptyObj(item);
430                }, this);
431            }
432
433            return _.omit(data, this.isEmptyObj.bind(this));
434        },
435
436        /**
437         * Checks that argument of any type is empty in common sence:
438         * empty string, string with spaces only, object without own props, empty array, null or undefined
439         *
440         * @param {*} val - Value to be checked.
441         * @returns {Boolean}
442         */
443        isEmptyObj: function (val) {
444
445            return _.isObject(val) && _.isEmpty(val) ||
446            this.isEmpty(val) ||
447            val && val.trim && this.isEmpty(val.trim());
448        }
449    };
450});
451

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.