PageSourceSearch

https://www.springerpub.com/static/version1789146322/base/Magento/…/Magento_Ui/js/core/renderer/layout.js

js springerpub.com collected 2026-09-24 08:47:42 UTC 17,895 bytes, 637 lines download raw bytes

1/**
2 * Copyright © Magento, Inc. All rights reserved.
3 * See COPYING.txt for license details.
4 */
5
6define([
7    'underscore',
8    'jquery',
9    'mageUtils',
10    'uiRegistry',
11    './types',
12    '../../lib/logger/console-logger'
13], function (_, $, utils, registry, types, consoleLogger) {
14    'use strict';
15
16    var templates = registry.create(),
17        layout = {},
18        cachedConfig = {};
19
20    /**
21     * Build name from parent name and node name
22     *
23     * @param {Object} parent
24     * @param {Object} node
25     * @param {String} [name]
26     * @returns {String}
27     */
28    function getNodeName(parent, node, name) {
29        var parentName = parent && parent.name;
30
31        if (typeof name !== 'string') {
32            name = node.name || name;
33        }
34
35        return utils.fullPath(parentName, name);
36    }
37
38    /**
39     * Get node type from node or parent.
40     *
41     * @param {Object} parent
42     * @param {Object} node
43     * @returns {String}
44     */
45    function getNodeType(parent, node) {
46        return node.type || parent && parent.childType;
47    }
48
49    /**
50     * Get data scope based on parent data scope and node data scope.
51     *
52     * @param {Object} parent
53     * @param {Object} node
54     * @returns {String}
55     */
56    function getDataScope(parent, node) {
57        var dataScope = node.dataScope,
58            parentScope = parent && parent.dataScope;
59
60        return !utils.isEmpty(parentScope) ?
61            !utils.isEmpty(dataScope) ?
62                parentScope + '.' + dataScope :
63                parentScope :
64            dataScope || '';
65    }
66
67    /**
68     * Load node dependencies on other instances.
69     *
70     * @param {Object} node
71     * @returns {jQueryPromise}
72     */
73    function loadDeps(node) {
74        var loaded = $.Deferred(),
75            loggerUtils = consoleLogger.utils;
76
77        if (node.deps) {
78            consoleLogger.utils.asyncLog(
79                loaded,
80                {
81                    data: {
82                        component: node.name,
83                        deps: node.deps
84                    },
85                    messages: loggerUtils.createMessages(
86                        'depsStartRequesting',
87                        'depsFinishRequesting',
88                        'depsLoadingFail'
89                    )
90                }
91            );
92        }
93
94        registry.get(node.deps, function (deps) {
95            node.provider = node.extendProvider ? deps && deps.name : node.provider;
96            loaded.resolve(node);
97        });
98
99        return loaded.promise();
100    }
101
102    /**
103     * Load node component file via requirejs.
104     *
105     * @param {Object} node
106     * @returns {jQueryPromise}
107     */
108    function loadSource(node) {
109        var loaded = $.Deferred(),
110            source = node.component;
111
112        consoleLogger.info('componentStartLoading', {
113            component: node.component
114        });
115
116        require([source], function (constr) {
117            consoleLogger.info('componentFinishLoading', {
118                component: node.component
119            });
120            loaded.resolve(node, constr);
121        }, function (err) {
122            consoleLogger.error('componentLoadingFail', {
123                component: node.component,
124                errorMsg: err
125            });
126        });
127
128        return loaded.promise();
129    }
130
131    /**
132     * Create a new component instance and set it to the registry.
133     *
134     * @param {Object} node
135     * @param {Function} Constr
136     */
137    function initComponent(node, Constr) {
138        var component = new Constr(_.omit(node, 'children'));
139
140        consoleLogger.info('componentStartInitialization', {
141            component: node.component,
142            componentName: node.name
143        });
144
145        registry.set(node.name, component);
146    }
147
148    /**
149     * Application entry point.
150     *
151     * @param {Object} nodes
152     * @param {Object} parent
153     * @param {Boolean} cached
154     * @param {Boolean} merge
155     * @returns {Boolean|undefined}
156     */
157    function run(nodes, parent, cached, merge) {
158        if (_.isBoolean(merge) && merge) {
159            layout.merge(nodes);
160
161            return false;
162        }
163
164        if (cached) {
165            cachedConfig[_.keys(nodes)[0]] = JSON.parse(JSON.stringify(nodes));
166        }
167
168        _.each(nodes || [], layout.iterator.bind(layout, parent));
169    }
170
171    _.extend(layout, {
172        /**
173         * Determines if node ready to be added or process it.
174         *
175         * @param {Object} parent
176         * @param {Object|String} node
177         */
178        iterator: function (parent, node) {
179            var action = _.isString(node) ?
180                this.addChild :
181                this.process;
182
183            action.apply(this, arguments);
184        },
185
186        /**
187         * Prepare component.
188         *
189         * @param {Object} parent
190         * @param {Object} node
191         * @param {String} name
192         * @returns {Object}
193         */
194        process: function (parent, node, name) {
195            if (!parent && node.parent) {
196                return this.waitParent(node, name);
197            }
198
199            if (node.nodeTemplate) {
200                return this.waitTemplate.apply(this, arguments);
201            }
202
203            node = this.build.apply(this, arguments);
204
205            if (!registry.has(node.name)) {
206                this.addChild(parent, node)
207                    .manipulate(node)
208                    .initComponent(node);
209            }
210
211            if (node) {
212                run(node.children, node);
213            }
214
215            return this;
216        },
217
218        /**
219         * Detailed processing of component config.
220         *
221         * @param {Object} parent
222         * @param {Object} node
223         * @param {String} name
224         * @returns {Boolean|Object}
225         */
226        build: function (parent, node, name) {
227            var defaults    = parent && parent.childDefaults || {},
228                children    = this.filterDisabledChildren(node.children),
229                type        = getNodeType(parent, node),
230                dataScope   = getDataScope(parent, node),
231                component,
232                extendDeps  = true,
233                nodeName;
234
235            node.children = false;
236            node.extendProvider = true;
237
238            if (node.config && node.config.provider || node.provider) {
239                node.extendProvider = false;
240            }
241
242            if (node.config && node.config.deps || node.deps) {
243                extendDeps = false;
244            }
245
246            node = utils.extend({
247            }, types.get(type), defaults, node);
248
249            nodeName = getNodeName(parent, node, name);
250
251            if (registry.has(nodeName)) {
252                component = registry.get(nodeName);
253                component.children = children;
254
255                return component;
256            }
257
258            if (extendDeps && parent && parent.deps && type) {
259                node.deps = parent.deps;
260            }
261
262            _.extend(node, node.config || {}, {
263                index: node.name || name,
264                name: nodeName,
265                dataScope: dataScope,
266                parentName: utils.getPart(nodeName, -2),
267                parentScope: utils.getPart(dataScope, -2)
268            });
269
270            node.children = children;
271            node.componentType = node.type;
272
273            delete node.type;
274            delete node.config;
275
276            if (children) {
277                node.initChildCount = _.size(children);
278            }
279
280            if (node.isTemplate) {
281                node.isTemplate = false;
282
283                templates.set(node.name, node);
284                registry.get(node.parentName, function (parentComp) {
285                    parentComp.childTemplate = node;
286                });
287
288                return false;
289            }
290
291            if (node.componentDisabled === true) {
292                return false;
293            }
294
295            return node;
296        },
297
298        /**
299         * Filter out all disabled components.
300         *
301         * @param {Object} children
302         * @returns {*}
303         */
304        filterDisabledChildren: function (children) {
305            var cIds;
306
307            //cleanup children config.componentDisabled = true
308            if (children && typeof children === 'object') {
309                cIds = Object.keys(children);
310
311                if (cIds) {
312                    _.each(cIds, function (cId) {
313                        if (typeof children[cId] === 'object' &&
314                            children[cId].hasOwnProperty('config') &&
315                            typeof children[cId].config === 'object' &&
316                            children[cId].config.hasOwnProperty('componentDisabled') &&
317                            children[cId].config.componentDisabled === true) {
318                            delete children[cId];
319                        }
320                    });
321                }
322            }
323
324            return children;
325        },
326
327        /**
328         * Init component.
329         *
330         * @param {Object} node
331         * @returns {Object}
332         */
333        initComponent: function (node) {
334            if (!node.component) {
335                return this;
336            }
337
338            loadDeps(node)
339                .then(loadSource)
340                .done(initComponent);
341
342            return this;
343        }
344    });
345
346    _.extend(layout, {
347        /**
348         * Loading component marked as isTemplate.
349         *
350         * @param {Object} parent
351         * @param {Object} node
352         * @returns {Object}
353         */
354        waitTemplate: function (parent, node) {
355            var args = _.toArray(arguments);
356
357            templates.get(node.nodeTemplate, function () {
358                this.applyTemplate.apply(this, args);
359            }.bind(this));
360
361            return this;
362        },
363
364        /**
365         * Waiting for parent component and process provided component.
366         *
367         * @param {Object} node
368         * @param {String} name
369         * @returns {Object}
370         */
371        waitParent: function (node, name) {
372            var process = this.process.bind(this);
373
374            registry.get(node.parent, function (parent) {
375                process(parent, node, name);
376            });
377
378            return this;
379        },
380
381        /**
382         * Processing component marked as isTemplate.
383         *
384         * @param {Object} parent
385         * @param {Object} node
386         * @param {String} name
387         */
388        applyTemplate: function (parent, node, name) {
389            var template = templates.get(node.nodeTemplate);
390
391            node = utils.extend({}, template, node);
392
393            delete node.nodeTemplate;
394
395            this.process(parent, node, name);
396        }
397    });
398
399    _.extend(layout, {
400        /**
401         * Determines inserting strategy.
402         *
403         * @param {Object} node
404         * @returns {Object}
405         */
406        manipulate: function (node) {
407            var name = node.name;
408
409            if (node.appendTo) {
410                this.insert(name, node.appendTo, -1);
411            }
412
413            if (node.prependTo) {
414                this.insert(name, node.prependTo, 0);
415            }
416
417            if (node.insertTo) {
418                this.insertTo(name, node.insertTo);
419            }
420
421            return this;
422        },
423
424        /**
425         * Insert component to provide target and position.
426         *
427         * @param {Object|String} item
428         * @param {Object} target
429         * @param {Number} position
430         * @returns {Object}
431         */
432        insert: function (item, target, position) {
433            registry.get(target, function (container) {
434                container.insertChild(item, position);
435            });
436
437            return this;
438        },
439
440        /**
441         * Insert component into multiple targets.
442         *
443         * @param {Object} item
444         * @param {Array} targets
445         * @returns {Object}
446         */
447        insertTo: function (item, targets) {
448            _.each(targets, function (info, target) {
449                this.insert(item, target, info.position);
450            }, this);
451
452            return this;
453        },
454
455        /**
456         * Add provided child to parent.
457         *
458         * @param {Object} parent
459         * @param {Object|String} child
460         * @returns {Object}
461         */
462        addChild: function (parent, child) {
463            var name;
464
465            if (parent && parent.component) {
466                name = child.name || child;
467
468                this.insert(name, parent.name, child.sortOrder);
469            }
470
471            return this;
472        },
473
474        /**
475         * Merge components configuration with cached configuration.
476         *
477         * @param {Array} components
478         */
479        merge: function (components) {
480            var cachedKey = _.keys(components)[0],
481                compared = utils.compare(cachedConfig[cachedKey], components),
482                remove = this.filterComponents(this.getByProperty(compared.changes, 'type', 'remove'), true),
483                update = this.getByProperty(compared.changes, 'type', 'update'),
484                dataSources = this.getDataSources(components),
485                names, index, name, component;
486
487            _.each(dataSources, function (val, key) {
488                name = key.replace(/\.children|\.config/g, '');
489                component = registry.get(name);
490
491                component.cacheData();
492                component.updateConfig(
493                    true,
494                    this.getFullConfig(key, components),
495                    this.getFullConfig(key, cachedConfig[cachedKey])
496                );
497            }, this);
498
499            _.each(remove, function (val) {
500                component = registry.get(val.path);
501
502                if (component) {
503                    component.destroy();
504                }
505            });
506
507            update = _.compact(_.filter(update, function (val) {
508                return !_.isEqual(val.oldValue, val.value);
509            }));
510
511            _.each(update, function (val) {
512                names = val.path.split('.');
513                index = Math.max(_.lastIndexOf(names, 'config'), _.lastIndexOf(names, 'children') + 2);
514                name = _.without(names.splice(0, index), 'children', 'config').join('.');
515                component = registry.get(name);
516
517                if (val.name === 'sortOrder' && component) {
518                    registry.get(component.parentName).insertChild(component, val.value);
519                } else if (component) {
520                    component.updateConfig(
521                        val.oldValue,
522                        val.value,
523                        val.path
524                    );
525                }
526            }, this);
527
528            run(components, undefined, true);
529        },
530
531        /**
532         * Recursive dataSource assignment.
533         *
534         * @param {Object} config
535         * @param {String} parentPath
536         * @returns {Object}
537         */
538        getDataSources: function (config, parentPath) {
539            var dataSources = {},
540                key, obj;
541
542            /* eslint-disable no-loop-func, max-depth */
543            for (key in config) {
544                if (config.hasOwnProperty(key)) {
545                    if (
546                        key === 'type' &&
547                        config[key] === 'dataSource' &&
548                        config.hasOwnProperty('config')
549                    ) {
550                        dataSources[parentPath + '.config'] = config.config;
551                    } else if (_.isObject(config[key])) {
552                        obj = this.getDataSources(config[key], utils.fullPath(parentPath, key));
553
554                        _.each(obj, function (value, path) {
555                            dataSources[path] = value;
556                        });
557                    }
558                }
559            }
560
561            /* eslint-enable no-loop-func, max-depth */
562
563            return dataSources;
564        },
565
566        /**
567         * Configuration getter.
568         *
569         * @param {String} path
570         * @param {Object} config
571         * @returns {Boolean|Object}
572         */
573        getFullConfig: function (path, config) {
574            var index;
575
576            path = path.split('.');
577            index = _.lastIndexOf(path, 'config');
578
579            if (!~index) {
580                return false;
581            }
582            path = path.splice(0, index);
583
584            _.each(path, function (val) {
585                config = config[val];
586            });
587
588            return config.config;
589        },
590
591        /**
592         * Filter data by property and value.
593         *
594         * @param {Object} data
595         * @param {String} prop
596         * @param {*} propValue
597         */
598        getByProperty: function (data, prop, propValue) {
599            return _.filter(data, function (value) {
600                return value[prop] === propValue;
601            });
602        },
603
604        /**
605         * Filter components.
606         *
607         * @param {Array} data
608         * @param {Boolean} splitPath
609         * @param {Number} index
610         * @param {String} separator
611         * @param {String} keyName
612         * @returns {Array}
613         */
614        filterComponents: function (data, splitPath, index, separator, keyName) {
615            var result = [],
616                names, length;
617
618            index = -2;
619            separator = '.' || separator;
620            keyName = 'children' || keyName;
621
622            _.each(data, function (val) {
623                names = val.path.split(separator);
624                length  = names.length;
625
626                if (names[length + index] === keyName) {
627                    val.path = splitPath ? _.without(names, keyName).join(separator) : val.path;
628                    result.push(val);
629                }
630            });
631
632            return result;
633        }
634    });
635
636    return run;
637});

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.