1 |
|
2 | export default {
|
3 |
|
4 | PIPE: /\s?\|\s?/,
|
5 | PIPES: /\s?,\s?|\s+/,
|
6 |
|
7 | value (element, model) {
|
8 |
|
9 | if (!model) throw new Error('Utility.value - requires model argument');
|
10 | if (!element) throw new Error('Utility.value - requires element argument');
|
11 |
|
12 | const type = this.type(element);
|
13 |
|
14 | if (type === 'radio' || type === 'checkbox') {
|
15 | const name = this.name(element);
|
16 | const query = 'input[type="' + type + '"][name="' + name + '"]';
|
17 | const form = this.form(element);
|
18 | const elements = form ? this.form(element).querySelectorAll(query) : [ element ];
|
19 | const multiple = elements.length > 1;
|
20 |
|
21 | let result = multiple ? [] : undefined;
|
22 |
|
23 | for (let i = 0, l = elements.length; i < l; i++) {
|
24 | const child = elements[i];
|
25 | const checked = this.checked(child);
|
26 |
|
27 | if (!checked) continue;
|
28 | const value = this.value(child, model);
|
29 |
|
30 | if (multiple) {
|
31 | result.push(value);
|
32 | } else {
|
33 | result = value;
|
34 | break;
|
35 | }
|
36 |
|
37 | }
|
38 |
|
39 | return result;
|
40 | } else if (type === 'select-one' || type === 'select-multiple') {
|
41 | const multiple = this.multiple(element);
|
42 | const options = element.options;
|
43 | let result = multiple ? [] : undefined;
|
44 |
|
45 | for (let i = 0, l = options.length; i < l; i++) {
|
46 | const option = options[i];
|
47 | const selected = option.selected;
|
48 | const value = this.value(option, model);
|
49 | const match = this[multiple ? 'includes' : 'compare'](this.data, value);
|
50 |
|
51 |
|
52 |
|
53 |
|
54 | if (selected && !match) {
|
55 | if (this.multiple) {
|
56 | result.push(value);
|
57 | } else {
|
58 | result = value;
|
59 | }
|
60 | } else if (!selected && match) {
|
61 | option.selected = true;
|
62 | }
|
63 |
|
64 | }
|
65 |
|
66 | return result;
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | } else {
|
73 | const attribute = element.attributes['o-value'];
|
74 | if (attribute) {
|
75 | const values = this.binderValues(attribute.value);
|
76 | const value = this.getByPath(model, values);
|
77 | return value || element.value;
|
78 | } else {
|
79 | return element.value;
|
80 | }
|
81 | }
|
82 | },
|
83 |
|
84 | form (element) {
|
85 | if (element.form) {
|
86 | return element.form;
|
87 | } else {
|
88 | while (element = element.parentElement) {
|
89 | if (element.nodeName === 'FORM' || element.nodeName.indexOf('-FORM') !== -1) {
|
90 | return element;
|
91 | }
|
92 | }
|
93 | }
|
94 | },
|
95 |
|
96 | type (element) {
|
97 | if (typeof element.type === 'string') {
|
98 | return element.type;
|
99 | } else {
|
100 | return element.getAttribute('type');
|
101 | }
|
102 | },
|
103 |
|
104 | name (element) {
|
105 | if (typeof element.name === 'string') {
|
106 | return element.name;
|
107 | } else {
|
108 | return element.getAttribute('name');
|
109 | }
|
110 | },
|
111 |
|
112 | checked (element) {
|
113 | if (typeof element.checked === 'boolean') {
|
114 | return element.checked;
|
115 | } else {
|
116 | switch (element.getAttribute('checked')) {
|
117 | case undefined: return false;
|
118 | case 'true': return true;
|
119 | case null: return false;
|
120 | case '': return true;
|
121 | default: return false;
|
122 | }
|
123 | }
|
124 | },
|
125 |
|
126 | multiple (element) {
|
127 | if (typeof element.multiple === 'boolean') {
|
128 | return element.multiple;
|
129 | } else {
|
130 | switch (element.getAttribute('multiple')) {
|
131 | case undefined: return false;
|
132 | case 'true': return true;
|
133 | case null: return false;
|
134 | case '': return true;
|
135 | default: return false;
|
136 | }
|
137 | }
|
138 | },
|
139 |
|
140 | disabled (element) {
|
141 | if (typeof element.disabled === 'boolean') {
|
142 | return element.disabled;
|
143 | } else {
|
144 | switch (element.getAttribute('disabled')) {
|
145 | case undefined: return false;
|
146 | case 'true': return true;
|
147 | case null: return false;
|
148 | case '': return true;
|
149 | default: return false;
|
150 | }
|
151 | }
|
152 | },
|
153 |
|
154 | index (items, item) {
|
155 |
|
156 | for (let i = 0, l = items.length; i < l; i++) {
|
157 | if (this.match(items[i], item)) {
|
158 | return i;
|
159 | }
|
160 | }
|
161 |
|
162 | return -1;
|
163 | },
|
164 |
|
165 | includes (items, item) {
|
166 |
|
167 | for (let i = 0, l = items.length; i < l; i++) {
|
168 | if (this.match(items[i], item)) {
|
169 | return true;
|
170 | }
|
171 | }
|
172 |
|
173 | return false;
|
174 | },
|
175 |
|
176 | match (source, target) {
|
177 |
|
178 | if (source === target) {
|
179 | return true;
|
180 | }
|
181 |
|
182 | if (typeof source !== typeof target) {
|
183 | return false;
|
184 | }
|
185 |
|
186 | if (source.constructor !== target.constructor) {
|
187 | return false;
|
188 | }
|
189 |
|
190 | if (typeof source !== 'object' || typeof target !== 'object') {
|
191 | return source === target;
|
192 | }
|
193 |
|
194 | const sourceKeys = Object.keys(source);
|
195 | const targetKeys = Object.keys(target);
|
196 |
|
197 | if (sourceKeys.length !== targetKeys.length) {
|
198 | return false;
|
199 | }
|
200 |
|
201 | for (let i = 0, l = sourceKeys.length; i < l; i++) {
|
202 | const name = sourceKeys[i];
|
203 |
|
204 | if (!this.match(source[name], target[name])) {
|
205 | return false;
|
206 | }
|
207 |
|
208 | }
|
209 |
|
210 | return true;
|
211 | },
|
212 |
|
213 | binderNames (data) {
|
214 | data = data.split('o-')[1];
|
215 | return data ? data.split('-') : [];
|
216 | },
|
217 |
|
218 | binderValues (data) {
|
219 | data = data.split(this.PIPE)[0];
|
220 | return data ? data.split('.') : [];
|
221 | },
|
222 |
|
223 | binderPipes (data) {
|
224 | data = data.split(this.PIPE)[1];
|
225 | return data ? data.split(this.PIPES) : [];
|
226 | },
|
227 |
|
228 | ensureElement (data) {
|
229 | data.query = data.query || '';
|
230 | data.scope = data.scope || document.body;
|
231 | data.position = data.position || 'beforeend';
|
232 |
|
233 | let element = data.scope.querySelector(`${data.name}${data.query}`);
|
234 |
|
235 | if (!element) {
|
236 | element = document.createElement(data.name);
|
237 | data.scope.insertAdjacentElement(data.position, element);
|
238 | }
|
239 |
|
240 | for (let i = 0, l = data.attributes.length; i < l; i++) {
|
241 | const { name, value } = data.attributes[i];
|
242 | element.setAttribute(name, value);
|
243 | }
|
244 |
|
245 | return element;
|
246 | },
|
247 |
|
248 | setByPath (data, path, value) {
|
249 | const keys = typeof path === 'string' ? path.split('.') : path;
|
250 | const last = keys.length - 1;
|
251 |
|
252 | for (let i = 0; i < last; i++) {
|
253 | const key = keys[i];
|
254 |
|
255 | if (!(key in data)) {
|
256 |
|
257 | if (isNaN(keys[i + 1])) {
|
258 | data[key] = {};
|
259 | } else {
|
260 | data[key] = [];
|
261 | }
|
262 |
|
263 | }
|
264 |
|
265 | data = data[key];
|
266 | }
|
267 |
|
268 | return data[keys[last]] = value;
|
269 | },
|
270 |
|
271 | getByPath (data, path) {
|
272 | const keys = typeof path === 'string' ? path.split('.') : path;
|
273 | const last = keys.length - 1;
|
274 |
|
275 | if (keys[last] === '$key' || keys[last] === '$index') {
|
276 | return keys[last - 1];
|
277 | }
|
278 |
|
279 | for (let i = 0; i < last; i++) {
|
280 | const key = keys[i];
|
281 |
|
282 | if (key in data === false) {
|
283 | return undefined;
|
284 | } else {
|
285 | data = data[key];
|
286 | }
|
287 |
|
288 | }
|
289 |
|
290 | return data[keys[last]];
|
291 | },
|
292 |
|
293 | clone (source) {
|
294 |
|
295 | if (
|
296 | source === null ||
|
297 | source === undefined ||
|
298 | source.constructor !== Array &&
|
299 | source.constructor !== Object
|
300 | ) {
|
301 | return source;
|
302 | }
|
303 |
|
304 | var target = source.constructor();
|
305 |
|
306 | for (const name in source) {
|
307 | const descriptor = Object.getOwnPropertyDescriptor(source, name);
|
308 |
|
309 | if (descriptor) {
|
310 |
|
311 | if ('value' in descriptor) {
|
312 | descriptor.value = this.clone(descriptor.value);
|
313 | }
|
314 |
|
315 | Object.defineProperty(target, name, descriptor);
|
316 | }
|
317 |
|
318 | }
|
319 |
|
320 | return target;
|
321 | }
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 | };
|