-
Notifications
You must be signed in to change notification settings - Fork 1
/
weave.js
213 lines (192 loc) · 6.7 KB
/
weave.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/**
* @license MIT http://troopjs.mit-license.org/
*/
define([
"./config",
"troopjs-core/component/signal/start",
"require",
"when/when",
"jquery",
"mu-getargs/main"
], function (config, start, parentRequire, when, $, getargs) {
"use strict";
/**
* @class widget.weave
* @mixin widget.config
* @mixin Function
* @static
*/
var UNDEFINED;
var NULL = null;
var ARRAY_PROTO = Array.prototype;
var ARRAY_MAP = ARRAY_PROTO.map;
var ARRAY_PUSH = ARRAY_PROTO.push;
var DEFERRED = "deferred";
var MODULE = "module";
var LENGTH = "length";
var $WEFT = config.widget.$weft;
var ATTR_WEAVE = config.widget.weave;
var ATTR_WOVEN = config.widget.woven;
var RE_SEPARATOR = /[\s,]+/;
/**
* Weaves `$element`
* @param {String} weave_attr
* @param {Array} constructor_args
* @return {Promise}
* @ignore
*/
function $weave (weave_attr, constructor_args) {
/*eslint consistent-this:0*/
// Let `$element` be `this`
var $element = this;
// Get all data from `$element`
var $data = $element.data();
// Let `$weft` be `$data[$WEFT]` or `$data[$WEFT] = []`
var $weft = $data.hasOwnProperty($WEFT)
? $data[$WEFT]
: $data[$WEFT] = [];
// Scope `weave_re` locally since we use the `g` flag
var weave_re = /[\s,]*(((?:\w+!)?([\w\/\.\-]+)(?:#[^(\s]+)?)(?:\((.*?)\))?)/g;
// Let `weave_args` be `[]`
var weave_args = [];
var weave_arg;
var args;
var matches;
/**
* Maps `value` to `$data[value]`
* @param {*} value
* @return {*}
* @private
*/
function $map (value) {
return $data.hasOwnProperty(value)
? $data[value]
: value;
}
// Iterate while `weave_re` matches
// matches[1] : max widget name with args - "mv!widget/name#1.x(1, 'string', false)"
// matches[2] : max widget name - "mv!widget/name#1.x"
// matches[3] : min widget name - "widget/name"
// matches[4] : widget arguments - "1, 'string', false"
while ((matches = weave_re.exec(weave_attr)) !== NULL) {
// Let `weave_arg` be [ $element, widget display name ].
// Push `weave_arg` on `weave_args`
ARRAY_PUSH.call(weave_args, weave_arg = [ $element, matches[3] ]);
// Let `weave_arg[MODULE]` be `matches[2]`
weave_arg[MODULE] = matches[2];
// If there were additional arguments ...
if ((args = matches[4]) !== UNDEFINED) {
// .. parse them using `getargs`, `.map` the values with `$map` and push to `weave_arg`
ARRAY_PUSH.apply(weave_arg, getargs.call(args).map($map));
}
// Let `weave_arg[DEFERRED]` be `when.defer()`
// Push `weave_arg[DEFERRED].promise` on `$weft`
ARRAY_PUSH.call($weft, (weave_arg[DEFERRED] = when.defer()).promise);
// Push `constructor_args` on `weave_arg`
ARRAY_PUSH.apply(weave_arg, constructor_args);
}
// Start async promise chain
return when
// Require, instantiate and start
.map(weave_args, function (widget_args) {
// Let `deferred` be `widget_args[DEFERRED]`
var deferred = widget_args[DEFERRED];
// Extract `resolve`, `reject` and `promise` from `deferred`
var resolve = deferred.resolve;
var reject = deferred.reject;
// Require `weave_arg[MODULE]`
parentRequire([ widget_args[MODULE] ], function (Widget) {
var $deferred;
// Create widget instance
var widget = Widget.apply(Widget, widget_args);
// TroopJS <= 1.x (detect presence of ComposeJS)
if (widget.constructor._getBases) {
// Let `$deferred` be `$.Deferred()`
$deferred = $.Deferred();
// Get trusted promise
when($deferred)
// Yield
.yield(widget)
// Link
.then(resolve, reject);
// Start widget
widget.start($deferred);
}
// TroopJS >= 2.x
else {
// Start widget
start.call(widget)
// Yield
.yield(widget)
// Link
.then(resolve, reject);
}
}, reject);
// Return `deferred.promise`
return deferred.promise;
})
// Update `ATTR_WOVEN`
.tap(function (widgets) {
// Bail fast if no widgets were woven
if (widgets[LENGTH] === 0) {
return;
}
// Map `Widget[]` to `String[]`
var woven = widgets.map(function (widget) {
return widget.toString();
});
// Update `$element` attribute `ATTR_WOVEN`
$element.attr(ATTR_WOVEN, function (index, attr) {
// Split `attr` and concat with `woven`
var values = (attr === UNDEFINED ? ARRAY_PROTO : attr.split(RE_SEPARATOR)).concat(woven);
// If `values[LENGTH]` is not `0` ...
return values[LENGTH] !== 0
// ... return `values.join(" ")`
? values.join(" ")
// ... otherwise return `NULL` to remove the attribute
: NULL;
});
});
}
/**
* Instantiate all {@link widget.component widgets} specified in the `data-weave` attribute
* of this element, and to signal the widget for start with the arguments.
*
* The weaving will result in:
*
* - Updates the `data-woven` attribute with the created widget instances names.
* - The `$weft` data property will reference the widget instances.
*
* @localdoc
*
* It also lives as a jquery plugin as {@link $#method-weave}.
*
* **Note:** It's not commonly to use this method directly, use instead {@link $#method-weave jQuery.fn.weave}.
*
* // Create element for weaving
* var $el = $('<div data-weave="my/widget(option)"></div>')
* // Populate `data`
* .data("option",{"foo":"bar"})
* // Instantiate the widget defined in "my/widget" module, with one param read from the element's custom data.
* .weave();
*
* @method constructor
* @param {...*} [args] Arguments that will be passed to the {@link core.component.signal.start start} signal
* @return {Promise} Promise for the completion of weaving all widgets.
*/
return function () {
// Let `constructor_args` be `arguments`
var constructor_args = arguments;
// Wait for map (sync) and weave (async)
return when.all(ARRAY_MAP.call(this, function (element) {
// Bless `$element` with `$`
var $element = $(element);
// Get ATTR_WEAVE attribute or default to `""`
var weave_attr = $element.attr(ATTR_WEAVE) || "";
// Make sure to remove ATTR_WEAVE asap in case someone else tries to `weave` again
$element.removeAttr(ATTR_WEAVE);
// Attempt weave
return $weave.call($element, weave_attr, constructor_args);
}));
};
});