Skip to content

Commit cd0ccc0

Browse files
author
Denys Rul
committed
MAGETWO-33932: Add ability to initialize modules by selector
- Support initialization from a custom script tag
1 parent baadf05 commit cd0ccc0

File tree

4 files changed

+171
-132
lines changed

4 files changed

+171
-132
lines changed

lib/web/mage/apply/main.js

Lines changed: 34 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,29 @@
33
* See COPYING.txt for license details.
44
*/
55
define([
6-
'jquery',
76
'underscore',
8-
'./registry',
9-
'require'
10-
], function ($, _, registry, require) {
7+
'jquery',
8+
'./scripts'
9+
], function (_, $, scripts) {
1110
'use strict';
1211

13-
var dataAttr = 'data-mage-init',
14-
nodeSelector = 'script[type="mage/config"]';
12+
var dataAttr = 'data-mage-init';
1513

1614
/**
17-
* Initializes components assigned to a specied element via data-* attribute.
15+
* Initializes components assigned to a specfied element via data-* attribute.
16+
*
1817
* @param {HTMLElement} el - Element to initialize components with.
1918
* @param {Object|String} config - Initial components' config.
2019
* @param {String} component - Components' path.
2120
*/
2221
function init(el, config, component) {
23-
if (registry.has(el, component)) {
24-
return;
25-
}
26-
27-
registry.add(el, component);
28-
29-
require([component], function (callback) {
30-
config = getConfig(el, config);
31-
32-
if (typeof callback === 'object') {
33-
callback = callback[component];
22+
require([component], function (fn) {
23+
if (typeof fn === 'object') {
24+
fn = fn[component];
3425
}
3526

36-
if (_.isFunction(callback)) {
37-
callback(config, el);
27+
if (_.isFunction(fn)) {
28+
fn(config, el);
3829
} else if ($(el)[component]) {
3930
$(el)[component](config);
4031
}
@@ -43,83 +34,63 @@ define([
4334

4435
/**
4536
* Searches for elements which has 'data-mage-init' attribute.
37+
*
4638
* @param {HTMLElement} [parent=document.body] - Optional node inside of which to perform search.
4739
* @returns {Array} An array of elements with 'data-mage-init' attribute.
4840
*/
49-
function getElements(parent) {
41+
function getElems(parent) {
5042
var elems;
5143

5244
parent = parent || document.body;
5345

5446
elems = parent.querySelectorAll('[' + dataAttr + ']');
55-
elems = Array.prototype.slice.call(elems);
47+
elems = _.toArray(elems);
5648

5749
if (parent.hasAttribute(dataAttr)) {
58-
elems.push(parent);
50+
elems.unshift(parent);
5951
}
6052

6153
return elems;
6254
}
6355

6456
/**
6557
* Parses elements 'data-mage-init' attribute as a valid JSON data.
58+
* Note: data-mage-init attribute will be removed.
59+
*
6660
* @param {HTMLElement} el - Element whose attribute should be parsed.
6761
* @returns {Object}
6862
*/
6963
function getData(el) {
7064
var data = el.getAttribute(dataAttr);
7165

72-
return JSON.parse(data);
73-
}
74-
75-
/**
76-
* Searches for a components' configurational node.
77-
* @param {HTMLElement} el - Will be used as a parentNode for configurational element.
78-
* @param {Object|String} [config] - Initial config.
79-
* @returns {Object} Components' config.
80-
*/
81-
function getConfig(el, config) {
82-
var node,
83-
content;
66+
el.removeAttribute(dataAttr);
8467

85-
if (el.tagName === 'SCRIPT') {
86-
node = el;
87-
} else {
88-
node = el.querySelector(nodeSelector);
89-
90-
if (node && node.parentNode !== el) {
91-
node = false;
92-
}
93-
}
94-
95-
config = config || {};
96-
content = node ? JSON.parse(node.textContent) : {};
97-
98-
return _.extend(config, content);
68+
return {
69+
el: el,
70+
data: JSON.parse(data)
71+
};
9972
}
10073

10174
return {
10275
/**
10376
* Initializes components assigned to HTML elements via [data-mage-init].
77+
*
10478
* @param {HTMLElement} [ctx=document.body] - Optional node to search inside.
79+
*
80+
* @example Sample 'data-mage-init' declaration.
81+
* data-mage-init='{"path/to/component": {"foo": "bar"}}'
10582
*/
10683
apply: function (ctx) {
107-
var elems = getElements(ctx),
108-
data;
109-
110-
elems.forEach(function (el) {
111-
data = getData(el);
112-
113-
_.each(data, init.bind(this, el));
114-
});
84+
var virtual = scripts.process();
85+
86+
getElems(ctx)
87+
.map(getData)
88+
.concat(virtual)
89+
.forEach(function (item) {
90+
_.each(item.data, init.bind(null, item.el));
91+
});
11592
},
11693

117-
/**
118-
* Intializes single element with a specified component and config.
119-
* @param {HTMLElement} el - Element to initialize component on.
120-
* @param {Object} config - Components' configuration.
121-
* @param {String} component - Components' path.
122-
*/
12394
applyFor: init
12495
};
12596
});

lib/web/mage/apply/registry.js

Lines changed: 0 additions & 63 deletions
This file was deleted.

lib/web/mage/apply/scripts.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Copyright © 2015 Magento. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
define([
6+
'underscore',
7+
'jquery'
8+
], function (_, $) {
9+
'use strict';
10+
11+
var scriptSelecotr = 'script[type="text/x-magento-init"]',
12+
virtual = [];
13+
14+
/**
15+
* Adds components to the virtula list.
16+
*
17+
* @param {Object} components
18+
*/
19+
function addVirtual(components) {
20+
virtual.push({
21+
el: false,
22+
data: components
23+
});
24+
}
25+
26+
/**
27+
* Merges provided data with a current data
28+
* of a elements' "data-mage-init" attribute.
29+
*
30+
* @param {Object} components - Object with compoenets and theirs configuration.
31+
* @param {HTMLElement} elem - Element whose data should be modified.
32+
*/
33+
function setData(components, elem) {
34+
var data = elem.getAttribute('data-mage-init');
35+
36+
data = !!data ? JSON.parse(data) : {};
37+
data = $.extend(true, data, components);
38+
data = JSON.stringify(data);
39+
40+
elem.setAttribute('data-mage-init', data);
41+
}
42+
43+
/**
44+
* Search for the elements by privded selector and extends theirs data.
45+
*
46+
* @param {Object} components - Object with compoenets and theirs configuration.
47+
* @param {String} selector - Selector for the elements.
48+
*/
49+
function processElems(components, selector) {
50+
var setFn,
51+
elems;
52+
53+
if (selector === '*') {
54+
addVirtual(components);
55+
} else {
56+
elems = document.querySelectorAll(selector);
57+
setFn = setData.bind(null, components);
58+
59+
_.toArray(elems).forEach(setFn);
60+
}
61+
}
62+
63+
/**
64+
* Searches for script tags whith a 'text/x-mage-init' type.
65+
*
66+
* @param {HTMLElement} [parent=document.body] - Optional node inside of which to perform search.
67+
* @returns {Array} An array of elements script nodes.
68+
*/
69+
function getNodes(parent) {
70+
var elems;
71+
72+
parent = parent || document.body;
73+
74+
elems = document.querySelectorAll(scriptSelecotr);
75+
76+
return _.toArray(elems);
77+
}
78+
79+
/**
80+
* Parses content of a provided script node.
81+
* Note: node will be removed from DOM.
82+
*
83+
* @param {HTMLScriptElement} node - Node to be processed.
84+
* @returns {Object}
85+
*/
86+
function getNodeData(node) {
87+
var data = node.textContent;
88+
89+
node.parentNode.removeChild(node);
90+
91+
return JSON.parse(data);
92+
}
93+
94+
return {
95+
/**
96+
* Parses 'script' tags with a custom type attribute and moves it's data
97+
* to a 'data-mage-init' attribute of an elemennt found by provided selector.
98+
* Note: All found script nodes would be removed from DOM.
99+
*
100+
* @param {HTMLElement} [ctx=document.body] - Optional node to search inside.
101+
* @returns {Array} An array of components not assigned to the specific element.
102+
*
103+
* @example Sample declaration.
104+
* <script type="text/x-mage-init">
105+
* {
106+
* "body": {
107+
* "path/to/component": {"foo": "bar"}
108+
* }
109+
* }
110+
* </script>
111+
*
112+
* @example Providing data without selector.
113+
* {
114+
* "*": {
115+
* "path/to/component": {"bar": "baz"}
116+
* }
117+
* }
118+
*/
119+
process: function (ctx) {
120+
getNodes(ctx)
121+
.map(getNodeData)
122+
.forEach(function (item) {
123+
_.each(item, processElems);
124+
});
125+
126+
return virtual.splice(0, virtual.length);
127+
}
128+
};
129+
});

lib/web/mage/mage.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
* Copyright © 2015 Magento. All rights reserved.
33
* See COPYING.txt for license details.
44
*/
5-
/*jshint eqnull:true browser:true jquery:true expr:true */
5+
/*jshint eqnull:true browser:true expr:true */
66
/*global require:true console:true*/
7-
(function (factory) {
7+
(function (root, factory) {
8+
'use strict';
9+
810
if (typeof define === 'function' && define.amd) {
911
define([
1012
'jquery',
1113
'mage/apply/main'
1214
], factory);
1315
} else {
14-
factory(jQuery);
16+
factory(root.jQuery);
1517
}
16-
}(function ($, mage) {
18+
}(this, function ($, mage) {
1719
'use strict';
1820

1921
/**
@@ -61,7 +63,7 @@
6163

6264
forced = !!forced;
6365
timeout = timeout || 0;
64-
type = type || "assign";
66+
type = type || 'assign';
6567

6668
_redirect = function () {
6769
window.location[type](type === 'reload' ? forced : url);
@@ -96,4 +98,4 @@
9698
});
9799

98100
return $.mage;
99-
}));
101+
}));

0 commit comments

Comments
 (0)