UNPKG

9.26 kBJavaScriptView Raw
1import Utility from './utility.js';
2import Batcher from './batcher.js';
3import Piper from './piper.js';
4
5import Class from './binder/class.js';
6import Default from './binder/default.js';
7import Disable from './binder/disable.js';
8import Each from './binder/each.js';
9import Enable from './binder/enable.js';
10import Hide from './binder/hide.js';
11import Href from './binder/href.js';
12import Html from './binder/html.js';
13import Label from './binder/label.js';
14import On from './binder/on.js';
15import Read from './binder/read.js';
16import Require from './binder/require.js';
17import Show from './binder/show.js';
18import Style from './binder/style.js';
19import Text from './binder/text.js';
20import Value from './binder/value.js';
21import Write from './binder/write.js';
22
23const DATA = new Map();
24
25const BINDERS = {
26 class: Class,
27 css: Style,
28 default: Default,
29 disable: Disable,
30 disabled: Disable,
31 each: Each,
32 enable: Enable,
33 enabled: Enable,
34 hide: Hide,
35 hidden: Hide,
36 href: Href,
37 html: Html,
38 label: Label,
39 on: On,
40 read: Read,
41 require: Require,
42 required: Require,
43 show: Show,
44 showed: Show,
45 style: Style,
46 text: Text,
47 value: Value,
48 write: Write
49};
50
51export default {
52
53 get data () { return DATA; },
54 get binders () { return BINDERS; },
55
56 async setup (options) {
57 options = options || {};
58
59 this.data.set('location', new Map());
60 this.data.set('attribute', new Map());
61
62 for (const name in this.binders) {
63 this.binders[name] = this.binders[name].bind(this);
64 }
65
66 if (options.binders) {
67
68 for (const name in options.binders) {
69
70 if (name in this.binders === false) {
71 this.binders[name] = options.binders[name].bind(this);
72 }
73
74 }
75
76 }
77
78 },
79
80 get (type) {
81
82 if (!type) throw new Error('Oxe.binder.get - type argument required');
83
84 let result = this.data.get(type);
85
86 if (!result) return result;
87
88 for (let i = 1, l = arguments.length; i < l; i++) {
89 const argument = arguments[i];
90 result = result.get(argument);
91
92 if (!result) {
93 return result;
94 }
95
96 }
97
98 return result;
99 },
100
101 create (data) {
102
103 if (data.name === undefined) throw new Error('Oxe.binder.create - missing name');
104 if (data.value === undefined) throw new Error('Oxe.binder.create - missing value');
105 if (data.target === undefined) throw new Error('Oxe.binder.create - missing target');
106 if (data.container === undefined) throw new Error('Oxe.binder.create - missing container');
107
108 const originalValue = data.value;
109
110 if (data.value.slice(0, 2) === '{{' && data.value.slice(-2) === '}}') {
111 data.value = data.value.slice(2, -2);
112 }
113
114 if (data.value.indexOf('$') !== -1 && data.context && data.context.variable && data.context.path && data.context.key) {
115 const pattern = new RegExp(`\\$${data.context.variable}(,|\\s+|\\.|\\|)?(.*)?$`, 'ig');
116 data.value = data.value.replace(pattern, `${data.context.path}.${data.context.key}$1$2`);
117 }
118
119 const scope = data.container.scope;
120 const names = data.names || Utility.binderNames(data.name);
121 const pipes = data.pipes || Utility.binderPipes(data.value);
122 const values = data.values || Utility.binderValues(data.value);
123
124 const type = names[0];
125 const path = values.join('.');
126 const keys = [ scope ].concat(values);
127 const location = keys.join('.');
128
129 const meta = data.meta || {};
130 const context = data.context || {};
131 const source = type === 'on' || type === 'submit' ? data.container.methods : data.container.model;
132
133 return {
134 get location () { return location; },
135
136 get type () { return type; },
137 get path () { return path; },
138 get scope () { return scope; },
139
140 get name () { return data.name; },
141 get value () { return data.value; },
142 get target () { return data.target; },
143 get container () { return data.container; },
144 get model () { return data.container.model; },
145 get methods () { return data.container.methods; },
146
147 get keys () { return keys; },
148 get names () { return names; },
149 get pipes () { return pipes; },
150 get values () { return values; },
151
152 get meta () { return meta; },
153 get context () { return context; },
154
155 get originalValue () { return originalValue; },
156
157 get data () {
158 const data = Utility.getByPath(source, values);
159 return Piper(this, data);
160 },
161
162 set data (value) {
163 const data = Piper(this, value);
164 return Utility.setByPath(source, values, data);
165 }
166
167 };
168 },
169
170 render (binder, caller) {
171
172 if (binder.type === 'submit') return;
173
174 const type = binder.type in this.binders ? binder.type : 'default';
175 const render = this.binders[type](binder, caller);
176
177 Batcher.batch(render);
178 },
179
180 unbind (node) {
181
182 this.data.get('location').forEach(function (scopes) {
183 scopes.forEach(function (binders) {
184 binders.forEach(function (binder, index) {
185 if (binder.target === node) {
186 binders.splice(index, 1);
187 }
188 });
189 });
190 });
191
192 this.data.get('attribute').delete(node);
193 },
194
195 bind (node, name, value, context) {
196
197 if (value === `$${context.variable}.$key` || value === `{{$${context.variable}.$key}}`) {
198 return Batcher.batch({ write () { node.textContent = context.key; } });
199 }
200
201 if (value === `$${context.variable}.$index` || value === `{{$${context.variable}.$index}}`) {
202 return Batcher.batch({ write () { node.textContent = context.index; } });
203 }
204
205 const binder = this.create({
206 name: name,
207 value: value,
208 target: node,
209 context: context,
210 container: context.container,
211 scope: context.container.scope
212 });
213
214 if (!this.data.get('attribute').has(binder.target)) {
215 this.data.get('attribute').set(binder.target, new Map());
216 }
217
218 if (!this.data.get('location').has(binder.scope)) {
219 this.data.get('location').set(binder.scope, new Map());
220 }
221
222 if (!this.data.get('location').get(binder.scope).has(binder.path)) {
223 this.data.get('location').get(binder.scope).set(binder.path, []);
224 }
225
226 this.data.get('attribute').get(binder.target).set(binder.name, binder);
227 this.data.get('location').get(binder.scope).get(binder.path).push(binder);
228
229 this.render(binder);
230 // this.render(binder, 'view');
231 },
232
233
234 remove (node) {
235
236 this.unbind(node);
237
238 for (let i = 0; i < node.childNodes.length; i++) {
239 this.remove(node.childNodes[i]);
240 }
241
242 },
243
244 add (node, context) {
245 if (node.nodeType === Node.TEXT_NODE) {
246
247 if (node.textContent.indexOf('{{') === -1 || node.textContent.indexOf('}}') === -1) {
248 return;
249 }
250
251 const start = node.textContent.indexOf('{{');
252 if (start !== -1 && start !== 0) {
253 node = node.splitText(start);
254 }
255
256 const end = node.textContent.indexOf('}}');
257 const length = node.textContent.length;
258 if (end !== -1 && end !== length - 2) {
259 const split = node.splitText(end + 2);
260 this.add(split, context);
261 }
262
263 this.bind(node, 'o-text', node.textContent, context);
264
265 } else if (node.nodeType === Node.ELEMENT_NODE) {
266 let skipChildren = false;
267
268 const attributes = node.attributes;
269 for (let i = 0, l = attributes.length; i < l; i++) {
270 const attribute = attributes[i];
271
272 if (
273 attribute.name === 'o-html' ||
274 attribute.name === 'o-scope' ||
275 attribute.name.indexOf('o-each') === 0
276 ) {
277 skipChildren = true;
278 }
279
280 if (
281 attribute.name === 'o-value' ||
282 attribute.name === 'o-scope' ||
283 attribute.name === 'o-reset' ||
284 attribute.name === 'o-action' ||
285 attribute.name === 'o-method' ||
286 attribute.name === 'o-enctype' ||
287 attribute.name.indexOf('o-') !== 0
288 ) {
289 continue;
290 }
291
292 this.bind(node, attribute.name, attribute.value, context);
293 }
294
295 // priorities o-each
296 if ('o-value' in attributes) {
297 this.bind(node, 'o-value', attributes['o-value'].value, context);
298 }
299
300 if (skipChildren) return;
301
302 for (let i = 0; i < node.childNodes.length; i++) {
303 this.add(node.childNodes[i], context);
304 }
305
306 }
307 }
308
309};