1 |
|
2 | import BunnyFormData from '../polyfills/BunnyFormData';
|
3 |
|
4 | /**
|
5 | * BunnyJS Form component
|
6 | * Wraps native FormData API to allow working with same form from multiple closures
|
7 | * and adds custom methods to make form processing, including AJAX submit, file uploads and date/times, easier
|
8 | * works only with real forms and elements in DOM
|
9 | * Whenever new input is added or removed from DOM - Form data is updated
|
10 | *
|
11 | * IE11+
|
12 | *
|
13 | * It should:
|
14 | * 1. RadioNodeList
|
15 | * 1.1. Setting .value of RadioNodeList for radio buttons is allowed only to one of values from all radio buttons within this RadioNodeList,
|
16 | * 1.2. If invalid value passed Error should be thrown
|
17 | * 1.3. If invalid value passed old value should be returned by .value getter, new value should not be saved
|
18 | * 1.4. Setting .value of RadioNodeList should redraw (update) radio buttons setting old unchecked and new checked
|
19 | * 1.5. Setting .value of RadioNodeList should save new value and getting .value should return same new value
|
20 | * 1.6. Setting .value of RadioNodeList should call 'change' event on related radio button
|
21 | *
|
22 | * 2. File input
|
23 | * 2.1. .value of file input should return first File object if file uploaded by user from native UI
|
24 | * 2.2. .value of file input should return Blob object if file was set by .value = blob
|
25 | * 2.3. value attribute can contain a string - URL to selected object, .value should return this file's Blob
|
26 | * 2.4. Only blob should be allowed to be assigned to setter .value
|
27 | * 2.5. URL (including rel path) can be assigned to .value (todo)
|
28 | * 2.6. File processing should be allowed to be delegated to a custom method, for example, to send file to cropper before storing it (todo)
|
29 | * 2.7. Setting .value should call 'change' event on file input
|
30 | * 2.8. After refresh if there is file uploaded by user via native UI file should be stored in .value (todo)
|
31 | *
|
32 | * 3. Common inputs
|
33 | * 3.1. Setting .value to any input should call 'change' event
|
34 | * 3.2. Setting .value to any input should redraw changes
|
35 | * 3.3.
|
36 | *
|
37 | * 4. DOM mutations, new inputs added to DOM or removed from DOM
|
38 | * 4.1. Whenever new input added to DOM inside form, it should be initiated and work as common input
|
39 | * 4.2. Whenever input is removed from DOM inside form, it should be also removed with all events from Form collection
|
40 | */
|
41 | export default Form = {
|
42 |
|
43 | /*
|
44 | properties
|
45 | */
|
46 |
|
47 | /**
|
48 | * Collection of FormData
|
49 | * @private
|
50 | */
|
51 | _collection: {},
|
52 |
|
53 | /**
|
54 | * Collection of mirrored elements
|
55 | * See Form.mirror() for detailed description
|
56 | */
|
57 | _mirrorCollection: {},
|
58 |
|
59 | _valueSetFromEvent: false,
|
60 |
|
61 |
|
62 | //_calcMirrorCollection: {},
|
63 |
|
64 |
|
65 | /*
|
66 | init methods
|
67 | */
|
68 |
|
69 | /**
|
70 | * Init form
|
71 | * Must be called after DOMContentLoaded (ready)
|
72 | *
|
73 | * @param {string} form_id
|
74 | *
|
75 | * @throws Error
|
76 | */
|
77 | init(form_id) {
|
78 | const form = document.forms[form_id];
|
79 | if (form === undefined) {
|
80 | throw new Error(`Form with ID ${form_id} not found in DOM!`);
|
81 | }
|
82 | if (this._collection[form_id] !== undefined) {
|
83 | throw new Error(`Form with ID ${form_id} already initiated!`);
|
84 | }
|
85 | this._collection[form_id] = new BunnyFormData(form);
|
86 | this._attachChangeAndDefaultFileEvent(form_id);
|
87 | this._attachRadioListChangeEvent(form_id);
|
88 | this._attachDOMChangeEvent(form_id);
|
89 | },
|
90 |
|
91 | isInitiated(form_id) {
|
92 | return this._collection[form_id] !== undefined;
|
93 | },
|
94 |
|
95 | /**
|
96 | * Update FormData when user changed input's value
|
97 | * or when value changed from script
|
98 | *
|
99 | * Also init default value for File inputs
|
100 | *
|
101 | * @param {string} form_id
|
102 | *
|
103 | * @private
|
104 | */
|
105 | _attachChangeAndDefaultFileEvent(form_id) {
|
106 | const elements = this._collection[form_id].getInputs();
|
107 | [].forEach.call(elements, (form_control) => {
|
108 |
|
109 | this.__attachSingleChangeEvent(form_id, form_control);
|
110 | this.__observeSingleValueChange(form_id, form_control);
|
111 |
|
112 | // set default file input value
|
113 | if (form_control.type === 'file' && form_control.hasAttribute('value')) {
|
114 | const url = form_control.getAttribute('value');
|
115 | if (url !== '') {
|
116 | this.setFileFromUrl(form_id, form_control.name, url);
|
117 | }
|
118 | }
|
119 | });
|
120 | },
|
121 |
|
122 | _attachRadioListChangeEvent(form_id) {
|
123 | const radio_lists = this._collection[form_id].getRadioLists();
|
124 | for (let radio_group_name in radio_lists) {
|
125 | let single_radio_list = radio_lists[radio_group_name];
|
126 | this.__observeSingleValueChange(form_id, single_radio_list);
|
127 | }
|
128 | },
|
129 |
|
130 | __attachSingleChangeEvent(form_id, form_control) {
|
131 | form_control.addEventListener('change', (e) => {
|
132 | if (form_control.type === 'file' && e.isTrusted) {
|
133 | // file selected by user
|
134 | //this._collection[form_id].set(form_control.name, form_control.files[0]);
|
135 | this._valueSetFromEvent = true;
|
136 | form_control.value = form_control.files[0];
|
137 | this._valueSetFromEvent = false;
|
138 | } else {
|
139 | this._parseFormControl(form_id, form_control, form_control.value);
|
140 | }
|
141 |
|
142 | // update mirror if mirrored
|
143 | if (this._mirrorCollection[form_id] !== undefined) {
|
144 | if (this._mirrorCollection[form_id][form_control.name] === true) {
|
145 | this.setMirrors(form_id, form_control.name);
|
146 | }
|
147 | }
|
148 | });
|
149 | },
|
150 |
|
151 | // handlers for different input types
|
152 | // with 4th argument - setter
|
153 | // without 4th argument - getter
|
154 | // called from .value property observer
|
155 | _parseFormControl(form_id, form_control, value = undefined) {
|
156 | const type = this._collection[form_id].getInputType(form_control);
|
157 |
|
158 | console.log(type);
|
159 |
|
160 | // check if parser for specific input type exists and call it instead
|
161 | let method = type.toLowerCase();
|
162 | method = method.charAt(0).toUpperCase() + method.slice(1); // upper case first char
|
163 | method = '_parseFormControl' + method;
|
164 |
|
165 | if (value === undefined) {
|
166 | method = method + 'Getter';
|
167 | }
|
168 |
|
169 | if (this[method] !== undefined) {
|
170 | return this[method](form_id, form_control, value);
|
171 | } else {
|
172 | // call default parser
|
173 | // if input with same name exists - override
|
174 | if (value === undefined) {
|
175 | return this._parseFormControlDefaultGetter(form_id, form_control);
|
176 | } else {
|
177 | this._parseFormControlDefault(form_id, form_control, value);
|
178 | }
|
179 | }
|
180 | },
|
181 |
|
182 | _parseFormControlDefault(form_id, form_control, value) {
|
183 | this._collection[form_id].set(form_control.name, value);
|
184 | },
|
185 |
|
186 | _parseFormControlDefaultGetter(form_id, form_control) {
|
187 | return Object.getOwnPropertyDescriptor(form_control.constructor.prototype, 'value').get.call(form_control);
|
188 | //return this._collection[form_id].get(form_control.name);
|
189 | },
|
190 |
|
191 | _parseFormControlRadiolist(form_id, form_control, value) {
|
192 | let found = false;
|
193 | const radio_list = form_control;
|
194 | for (let k = 0; k < radio_list.length; k++) {
|
195 | let radio_input = radio_list[k];
|
196 | if (radio_input.value === value) {
|
197 | this._collection[form_id].set(radio_input.name, value);
|
198 | found = true;
|
199 | break;
|
200 | }
|
201 | }
|
202 |
|
203 | if (!found) {
|
204 | throw new TypeError('Trying to Form.set() on radio with unexisted value="'+value+'"');
|
205 | }
|
206 | },
|
207 |
|
208 | _parseFormControlCheckbox(form_id, form_control, value) {
|
209 | const fd = this._collection[form_id];
|
210 | fd.setCheckbox(form_control.name, value, form_control.checked);
|
211 | },
|
212 |
|
213 | _parseFormControlFile(form_id, form_control, value) {
|
214 | if (value !== '' && !(value instanceof Blob) && !(value instanceof File)) {
|
215 | throw new TypeError('Only empty string, Blob or File object is allowed to be assigned to .value property of file input using Bunny Form');
|
216 | } else {
|
217 | if (value.name === undefined) {
|
218 | value.name = 'blob';
|
219 | }
|
220 | this._collection[form_id].set(form_control.name, value);
|
221 | }
|
222 | },
|
223 |
|
224 | _parseFormControlFileGetter(form_id, form_control) {
|
225 | // Override native file input .value logic
|
226 | // return Blob or File object or empty string if no file set
|
227 | return this.get(form_id, form_control.name);
|
228 | },
|
229 |
|
230 | __observeSingleValueChange(form_id, form_control) {
|
231 | Object.defineProperty(form_control, 'value', {
|
232 | configurable: true,
|
233 | get: () => {
|
234 | return this._parseFormControl(form_id, form_control);
|
235 | },
|
236 | set: (value) => {
|
237 | console.log('setting to');
|
238 | console.log(value);
|
239 | console.log(form_control);
|
240 | // call parent setter to redraw changes in UI, update checked etc.
|
241 | if (form_control.type !== 'file') {
|
242 | Object.getOwnPropertyDescriptor(form_control.constructor.prototype, 'value').set.call(form_control, value);
|
243 | }
|
244 |
|
245 |
|
246 |
|
247 | //this._parseFormControl(form_id, form_control, value);
|
248 | if (!(this._collection[form_id].isNodeList(form_control))) {
|
249 | if (!this._valueSetFromEvent) {
|
250 | console.log('firing event');
|
251 | const event = new CustomEvent('change');
|
252 | form_control.dispatchEvent(event);
|
253 |
|
254 | }
|
255 | } else {
|
256 |
|
257 | // For radio - call change event on changed input
|
258 | for (let k = 0; k < form_control.length; k++) {
|
259 | let radio_input = form_control[k];
|
260 | if (radio_input.getAttribute('value') === value) {
|
261 | if (!this._valueSetFromEvent) {
|
262 | console.log('firing radio event');
|
263 | const event = new CustomEvent('change');
|
264 | radio_input.dispatchEvent(event);
|
265 | break;
|
266 | }
|
267 | }
|
268 | }
|
269 | }
|
270 | }
|
271 | });
|
272 | },
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | _initNewInput(form_id, input) {
|
280 | this._checkInit(form_id);
|
281 | this._collection[form_id]._initSingleInput(input);
|
282 | this.__attachSingleChangeEvent(form_id, input);
|
283 | this.__observeSingleValueChange(form_id, input, input.name);
|
284 | },
|
285 |
|
286 | _attachDOMChangeEvent(form_id) {
|
287 | const target = document.forms[form_id];
|
288 | const observer_config = { childList: true, subtree: true };
|
289 | const observer = new MutationObserver( (mutations) => {
|
290 | mutations.forEach( (mutation) => {
|
291 | if (mutation.addedNodes.length > 0) {
|
292 | // probably new input added, update form data
|
293 | this.__handleAddedNodes(form_id, mutation.addedNodes);
|
294 | } else if (mutation.removedNodes.length > 0) {
|
295 | // probably input removed, update form data
|
296 | this.__handleRemovedNodes(form_id, mutation.removedNodes);
|
297 | }
|
298 | });
|
299 | });
|
300 |
|
301 | observer.observe(target, observer_config);
|
302 | },
|
303 |
|
304 | __handleAddedNodes(form_id, added_nodes) {
|
305 | for (let k = 0; k < added_nodes.length; k++) {
|
306 | let node = added_nodes[k];
|
307 | if (node.tagName === 'INPUT') {
|
308 | this._initNewInput(form_id, node);
|
309 | } else if (node.getElementsByTagName !== undefined) {
|
310 | // to make sure node is not text node or any other type of node without full Element API
|
311 | let inputs = node.getElementsByTagName('input');
|
312 | if (inputs.length > 0) {
|
313 | for (let k2 = 0; k2 < inputs.length; k2++) {
|
314 | this._initNewInput(form_id, inputs[k2]);
|
315 | }
|
316 | }
|
317 | }
|
318 | }
|
319 | },
|
320 |
|
321 | __handleRemovedNodes(form_id, removed_nodes) {
|
322 | for (let k = 0; k < removed_nodes.length; k++) {
|
323 | let node = removed_nodes[k];
|
324 | if (node.tagName === 'INPUT') {
|
325 | let input = node;
|
326 | this._collection[form_id].remove(input.name, input.value);
|
327 | } else if (node.getElementsByTagName !== undefined) {
|
328 | // to make sure node is not text node or any other type of node without full Element API
|
329 | let inputs = node.getElementsByTagName('input');
|
330 | if (inputs.length > 0) {
|
331 | for (let k2 = 0; k2 < inputs.length; k2++) {
|
332 | let input = inputs[k2];
|
333 | this._collection[form_id].remove(input.name, input.value);
|
334 | }
|
335 | }
|
336 | }
|
337 | }
|
338 | },
|
339 |
|
340 | /**
|
341 | * Init all forms in DOM
|
342 | * Must be called after DOMContentLoaded (ready)
|
343 | */
|
344 | initAll() {
|
345 | [].forEach.call(document.forms, (form) => {
|
346 | this.init(form.id)
|
347 | });
|
348 | },
|
349 |
|
350 | /**
|
351 | * Check if form is initiated
|
352 | *
|
353 | * @param {string} form_id
|
354 | *
|
355 | * @throws Error
|
356 | * @private
|
357 | */
|
358 | _checkInit(form_id) {
|
359 | if (this._collection[form_id] === undefined) {
|
360 | throw new Error(`Form with ID ${form_id} is not initiated! Init form with Form.init(form_id) first.`);
|
361 | }
|
362 | },
|
363 |
|
364 |
|
365 | /*
|
366 | Get and set form data methods
|
367 | */
|
368 |
|
369 | /**
|
370 | * Set new value of real DOM input or virtual input
|
371 | * Actually fires change event and values are set in _attachChangeAndDefaultFileEvent()
|
372 | *
|
373 | * @param {string} form_id
|
374 | * @param {string} input_name
|
375 | * @param {string|Blob|Object} input_value
|
376 | */
|
377 | set(form_id, input_name, input_value) {
|
378 | this._checkInit(form_id);
|
379 | const input = this._collection[form_id].getInput(input_name);
|
380 | input.value = input_value;
|
381 | },
|
382 |
|
383 |
|
384 | /**
|
385 | * Fill form data with object values. Object property name/value => form input name/value
|
386 | * @param form_id
|
387 | * @param data
|
388 | */
|
389 | fill(form_id, data) {
|
390 | this._checkInit(form_id);
|
391 | for (let input_name in data) {
|
392 | if (this._collection[form_id].has(input_name)) {
|
393 | this.set(form_id, input_name, data[input_name]);
|
394 | }
|
395 | }
|
396 | },
|
397 |
|
398 | fillOrAppend(form_id, data) {
|
399 | this._checkInit(form_id);
|
400 | for (let input_name in data) {
|
401 | if (this._collection[form_id].has(input_name)) {
|
402 | this.set(form_id, input_name, data[input_name]);
|
403 | } else {
|
404 | this.append(form_id, input_name, data[input_name]);
|
405 | }
|
406 | }
|
407 | },
|
408 |
|
409 | observe(form_id, data_object) {
|
410 | let new_data_object = Object.create(data_object);
|
411 | for (let input_name in new_data_object) {
|
412 | Object.defineProperty(new_data_object, input_name, {
|
413 | set: (value) => {
|
414 | this.set(form_id, input_name, value);
|
415 | },
|
416 | get: () => this.get(form_id, input_name)
|
417 | });
|
418 | }
|
419 | return new_data_object;
|
420 | },
|
421 |
|
422 | fillAndObserve(form_id, data_object) {
|
423 | this.fill(form_id, data_object);
|
424 | this.observe(form_id, data_object);
|
425 | },
|
426 |
|
427 | /**
|
428 | * Get value of real DOM input or virtual input
|
429 | *
|
430 | * @param {string} form_id
|
431 | * @param {string} input_name
|
432 | *
|
433 | * @returns {string|File|Blob}
|
434 | */
|
435 | get(form_id, input_name) {
|
436 | this._checkInit(form_id);
|
437 | return this._collection[form_id].get(input_name);
|
438 | },
|
439 |
|
440 | getObservableModel(form_id) {
|
441 | return this.observe(form_id, this.getAll(form_id));
|
442 | },
|
443 |
|
444 | /**
|
445 | * Get all form input values as key - value object
|
446 | * @param form_id
|
447 | * @returns {object}
|
448 | */
|
449 | getAll(form_id) {
|
450 | this._checkInit(form_id);
|
451 | /*const data = {};
|
452 | const items = this._collection[form_id].entries();
|
453 | for (let item of items) {
|
454 | data[item[0]] = item[1];
|
455 | }
|
456 | return data;*/
|
457 | return this._collection[form_id].getAllElements();
|
458 | },
|
459 |
|
460 | /**
|
461 | * Get native FormData object
|
462 | * For example, to submit form with custom handler
|
463 | * @param {string} form_id
|
464 | * @returns {FormData}
|
465 | */
|
466 | getFormDataObject(form_id) {
|
467 | this._checkInit(form_id);
|
468 | return this._collection[form_id].buildFormDataObject();
|
469 | },
|
470 |
|
471 | getInput(form_id, input_name) {
|
472 | return this._collection[form_id].getInput(input_name);
|
473 | },
|
474 |
|
475 |
|
476 | /*
|
477 | virtual checkbox, item list, tag list, etc methods
|
478 | */
|
479 | append(form_id, array_name, value) {
|
480 | this._checkInit(form_id);
|
481 | const formData = this._collection[form_id];
|
482 | formData.append(array_name, value);
|
483 | },
|
484 |
|
485 | remove(form_id, array_name, value = undefined) {
|
486 | this._checkInit(form_id);
|
487 | /*const formData = this._collection[form_id];
|
488 | formData.delete(array_name);
|
489 | const collection = formData.getAll(array_name);
|
490 | collection.forEach( (item) => {
|
491 | if (item !== value) {
|
492 | formData.append(array_name, item);
|
493 | }
|
494 | });*/
|
495 | this._collection[form_id].remove(array_name, value);
|
496 | },
|
497 |
|
498 |
|
499 | /*
|
500 | binding (mirror) methods
|
501 | */
|
502 |
|
503 | /**
|
504 | * Mirrors real DOM input's value with any DOM element (two-way data binding)
|
505 | * All DOM elements with attribute data-mirror="form_id.input_name" are always updated when input value changed
|
506 | * @param {string} form_id
|
507 | * @param {string} input_name
|
508 | */
|
509 | mirror(form_id, input_name) {
|
510 | this._checkInit(form_id);
|
511 | const input = this._collection[form_id].getInput(input_name);
|
512 | if (!(input instanceof HTMLInputElement)) {
|
513 | // make sure it is normal input and not RadioNodeList or other interfaces which don't have addEventListener
|
514 | throw new Error('Cannot mirror radio buttons or checkboxes.')
|
515 | }
|
516 | if (this._mirrorCollection[form_id] === undefined) {
|
517 | this._mirrorCollection[form_id] = {};
|
518 | }
|
519 | this._mirrorCollection[form_id][input_name] = true;
|
520 |
|
521 | //const input = document.forms[form_id].elements[input_name];
|
522 | this.setMirrors(form_id, input_name);
|
523 | input.addEventListener('change', () => {
|
524 | this.setMirrors(form_id, input_name);
|
525 | });
|
526 | },
|
527 |
|
528 | /**
|
529 | * Mirrors all inputs of form
|
530 | * Does not mirror radio buttons and checkboxes
|
531 | * See Form.mirror() for detailed description
|
532 | * @param form_id
|
533 | */
|
534 | mirrorAll(form_id) {
|
535 | this._checkInit(form_id);
|
536 | const inputs = this._collection[form_id].getInputs();
|
537 | [].forEach.call(inputs, (input) => {
|
538 | if (input instanceof HTMLInputElement && input.type !== 'checkbox' && input.type !== 'radio') {
|
539 | // make sure it is normal input and not RadioNodeList or other interfaces which don't have addEventListener
|
540 | this.mirror(form_id, input.name);
|
541 | }
|
542 | });
|
543 | },
|
544 |
|
545 | getMirrors(form_id, input_name) {
|
546 | this._checkInit(form_id);
|
547 | return document.querySelectorAll(`[data-mirror="${form_id}.${input_name}"]`);
|
548 | },
|
549 |
|
550 | setMirrors(form_id, input_name) {
|
551 | this._checkInit(form_id);
|
552 | const mirrors = this.getMirrors(form_id, input_name);
|
553 | const input = this._collection[form_id].getInput(input_name);
|
554 | //const input = document.forms[form_id].elements[input_name];
|
555 | [].forEach.call(mirrors, (mirror) => {
|
556 | if (mirror.tagName === 'IMG') {
|
557 | let data = this.get(form_id, input_name);
|
558 | if (data === '') {
|
559 | mirror.src = '';
|
560 | } else if (data.size !== 0) {
|
561 | mirror.src = URL.createObjectURL(this.get(form_id, input_name));
|
562 | }
|
563 | } else {
|
564 | mirror.textContent = input.value;
|
565 | }
|
566 | });
|
567 | },
|
568 |
|
569 |
|
570 | /*
|
571 | Calc methods
|
572 | */
|
573 | /*_getCalcMirrors(form_id) {
|
574 | this._checkInit(form_id);
|
575 | return document.querySelectorAll(`[data-mirror="${form_id}"]`);
|
576 | },
|
577 |
|
578 | _getCalcMirrorFunction(calc_mirror_el) {
|
579 | return calc_mirror_el.getAttribute('data-mirror-function');
|
580 | },
|
581 |
|
582 | _calcMirror(form_id, calc_mirror, calc_mirror_function) {
|
583 | // parse function
|
584 | const input_names = calc_mirror_function.split('*');
|
585 | console.log(input_names);
|
586 | // get arguments (inputs)
|
587 | const input1 = document.forms[form_id].elements[input_names[0]];
|
588 | const input2 = document.forms[form_id].elements[input_names[1]];
|
589 |
|
590 | const value1 = (input1.value === '') ? 0 : input1.value;
|
591 | const value2 = (input2.value === '') ? 0 : input2.value;
|
592 |
|
593 | // update collection
|
594 | if (this._calcMirrorCollection[form_id] === undefined) {
|
595 | this._calcMirrorCollection[form_id] = {};
|
596 | }
|
597 | if (this._calcMirrorCollection[form_id][input1.name] === undefined) {
|
598 | this._calcMirrorCollection[form_id][input1.name] = {}
|
599 | }
|
600 | if (this._calcMirrorCollection[form_id][input2.name] === undefined) {
|
601 | this._calcMirrorCollection[form_id][input2.name] = {}
|
602 | }
|
603 | this._calcMirrorCollection[form_id][input1.name][input2.name] = calc_mirror;
|
604 | this._calcMirrorCollection[form_id][input2.name][input1.name] = calc_mirror;
|
605 |
|
606 | // set initial value
|
607 | calc_mirror.textContent = value1 * value2;
|
608 |
|
609 | // set new value when input value changed
|
610 | input1.addEventListener('change', () => {
|
611 | calc_mirror.textContent = input1.value * document.forms[form_id].elements[input2.name].value;
|
612 | });
|
613 | input2.addEventListener('change', () => {
|
614 | calc_mirror.textContent = input2.value * document.forms[form_id].elements[input1.name].value;
|
615 | });
|
616 | },
|
617 |
|
618 | calcMirrorAll(form_id) {
|
619 | this._checkInit(form_id);
|
620 | const calc_mirrors = this._getCalcMirrors(form_id);
|
621 | for(let calc_mirror of calc_mirrors) {
|
622 | let f = this._getCalcMirrorFunction(calc_mirror);
|
623 | if (f === undefined) {
|
624 | console.trace();
|
625 | throw new Error('Calc mirror element with attribute data-mirror does not have attribute data-mirror-function')
|
626 | } else {
|
627 | this._calcMirror(form_id, calc_mirror, f);
|
628 | }
|
629 | }
|
630 | },*/
|
631 |
|
632 | /*
|
633 | file methods
|
634 | */
|
635 | setFileFromUrl(form_id, input_name, url) {
|
636 | var request = new XMLHttpRequest();
|
637 | const p = new Promise( (success, fail) => {
|
638 | request.onload = () => {
|
639 | if (request.status === 200) {
|
640 | const blob = request.response;
|
641 | this.set(form_id, input_name, blob);
|
642 | success(blob);
|
643 | } else {
|
644 | fail(request);
|
645 | }
|
646 | };
|
647 | });
|
648 |
|
649 | request.open('GET', url, true);
|
650 | request.responseType = 'blob';
|
651 | request.send();
|
652 | return p;
|
653 | },
|
654 |
|
655 |
|
656 | /*
|
657 | submit methods
|
658 | */
|
659 |
|
660 | submit(form_id, url = null, method = 'POST', headers = {'X-Requested-With': 'XMLHttpRequest'}) {
|
661 | this._checkInit(form_id);
|
662 | const request = new XMLHttpRequest();
|
663 | if (url === null) {
|
664 | if (document.forms[form_id].hasAttribute('action')) {
|
665 | url = document.forms[form_id].getAttribute('action');
|
666 | } else {
|
667 | //throw new Error('Form.submit() is missing 2nd URL argument');
|
668 | url = '';
|
669 | }
|
670 | }
|
671 | request.open(method, url);
|
672 | const p = new Promise( (success, fail) => {
|
673 | request.onload = () => {
|
674 | if (request.status === 200) {
|
675 | success(request.responseText);
|
676 | } else {
|
677 | fail(request);
|
678 | }
|
679 | };
|
680 | });
|
681 | for (let header in headers) {
|
682 | request.setRequestHeader(header, headers[header]);
|
683 | }
|
684 | this._collection[form_id].set('categories', [2, 3]);
|
685 | request.send(this.getFormDataObject(form_id));
|
686 | return p;
|
687 | }
|
688 |
|
689 | };
|