UNPKG

10.8 kBJavaScriptView Raw
1
2import nonEnumKeys from '../utils/object/nonEnumKeys';
3
4/**
5 * FormData wrapper for browsers supporting only FormData constructor and append()
6 * Instead of keys(), entries(), values() have getAllElements()
7 * Instead of delete() -> remove()
8 * To get real FormData object use buildFormDataObject()
9 * Also adds custom methods
10 */
11
12function BunnyFormData(form) {
13 if (!(form instanceof HTMLFormElement)) {
14 throw new Error('Form passed to BunnyFormData constructor is not an instance of HTMLFormElement');
15 }
16
17 // form
18 this._form = form;
19
20 // form control collection;
21 this._collection = {};
22
23 // build collection from form elements
24 var elements = this.getInputs();
25 for (var k = 0; k < elements.length; k++) {
26 var input = elements[k];
27 this._initSingleInput(input);
28 }
29}
30
31BunnyFormData.prototype._initSingleInput = function _initSingleInput(input) {
32 const type = this.getInputType(input);
33
34 // check if parser for specific input type exists and call it instead
35 let method = type.toLowerCase();
36 method = method.charAt(0).toUpperCase() + method.slice(1); // upper case first char
37 method = '_formControlParser' + method;
38 if (this[method] !== undefined) {
39 this[method](input);
40 } else {
41 // call default parser
42 // if input with same name exists - override
43 this._formControlParserDefault(input);
44 }
45};
46
47
48
49
50
51
52BunnyFormData.prototype._formControlParserDefault = function _formControlParserDefault(input) {
53 if (this._collection[input.name] !== undefined) {
54 // element with same name already exists in collection
55 if (!Array.isArray(this._collection[input.name])) {
56 // is not array, convert to array first
57 this._collection[input.name] = [this._collection[input.name]];
58 }
59 this._collection[input.name].push(input.value);
60 } else {
61 this._collection[input.name] = input.value;
62 }
63};
64
65BunnyFormData.prototype._formControlParserRadio = function _formControlParserRadio(input) {
66 // radio buttons must have same name and only one can be checked
67 // exactly one radio must have checked attribute
68 // radio buttons must have value attribute
69 if (input.checked) {
70 this._collection[input.name] = input.value;
71 }
72};
73
74BunnyFormData.prototype._formControlParserCheckbox = function _formControlParserCheckbox(input) {
75 // checkboxes may have different names or same name if checkboxes should be an array
76 // each checkbox may have checked attribute
77 // checkboxes must have value attribute
78 if (this._collection[input.name] === undefined) {
79 // first checkbox with this name found
80 if (input.checked) {
81 this._collection[input.name] = input.value;
82 } else {
83 this._collection[input.name] = '';
84 }
85 } else {
86 if (input.checked) {
87 // checkbox with same name already exists in collection
88 if (!Array.isArray(this._collection[input.name])) {
89 // is not array, convert to array first
90 this._collection[input.name] = [this._collection[input.name]];
91 }
92 this._collection[input.name].push(input.value);
93 }
94 }
95};
96
97BunnyFormData.prototype._formControlParserFile = function _formControlParserFile(input) {
98 this._collection[input.name] = (input.files[0] === undefined || input.files[0] === null) ? '' : input.files[0];
99};
100
101
102
103
104
105
106// since form inputs can be accessed via form.input_name and if input_name = elements
107// then form.elements will return input not FormControlsCollection
108// make sure to get real FormControlsCollection from prototype
109BunnyFormData.prototype.getInputs = function getInputs() {
110 return Object.getOwnPropertyDescriptor(this._form.constructor.prototype, 'elements').get.call(this._form);
111};
112
113BunnyFormData.prototype.getNamedInputs = function getNamedInputs() {
114 const elements = this.getInputs();
115 // IE does not return correct enum keys, get keys manually
116 // non numbered keys will be named keys
117 //const keys = nonEnumKeys(elements);
118 //console.log(keys);
119 const keys = Object.getOwnPropertyNames(elements).filter(key => isNaN(key));
120
121 let named_inputs = {};
122 for (let k = 0; k < keys.length; k++) {
123 let input_name = keys[k];
124 named_inputs[input_name] = elements[input_name];
125 }
126 return named_inputs;
127};
128
129BunnyFormData.prototype.getNodeLists = function getNodeLists() {
130 const elements = this.getNamedInputs();
131 let node_lists = {};
132 for (let input_name in elements) {
133 if (this.isNodeList(input_name)) {
134 node_lists[input_name] = elements[input_name];
135 }
136 }
137 return node_lists;
138};
139
140BunnyFormData.prototype.getRadioLists = function getRadioLists() {
141 const node_lists = this.getNodeLists();
142 let radio_lists = {};
143 for (let input_name in node_lists) {
144 if (node_lists[input_name][0].type === 'radio') {
145 radio_lists[input_name] = node_lists[input_name];
146 }
147 }
148 return radio_lists;
149};
150
151BunnyFormData.prototype.getInputType = function getInputType(name_or_el) {
152 let input = null;
153 if (typeof name_or_el === 'object') {
154 input = name_or_el;
155 } else {
156 input = this.getInput(name_or_el);
157 }
158
159 if (input.type !== undefined && input.type !== null && input.type !== '') {
160 return input.type;
161 }
162 if (input.tagName === 'TEXTAREA') {
163 return 'textarea';
164 } else if (this.isNodeList(input)) {
165 return 'radiolist';
166 } else {
167 return undefined;
168 }
169};
170
171BunnyFormData.prototype.getInput = function getInput(name) {
172 return Object.getOwnPropertyDescriptor(this._form.constructor.prototype, 'elements').get.call(this._form)[name];
173};
174
175BunnyFormData.prototype.get = function get(input_name) {
176 if (this._collection[input_name] === undefined) {
177 return null;
178 } else {
179 return this._collection[input_name];
180 }
181};
182
183// value can also be a Blob/File but this get() polyfill does not include 3rd argument filename
184BunnyFormData.prototype.set = function get(input_name, value) {
185 this._collection[input_name] = value;
186};
187
188BunnyFormData.prototype.setCheckbox = function setArrayKey(input_name, value, checked) {
189 if (this.has(input_name) && !this.empty(input_name)) {
190 if (checked) {
191 // add element to array if not exists
192 if (!this.isArray(input_name)) {
193 // convert to array first
194 if (this._collection[input_name] !== value) {
195 this._collection[input_name] = [this._collection[input_name]];
196 this._collection[input_name].push(value);
197 }
198 } else {
199 if (this._collection[input_name].indexOf(value) === -1) {
200 this._collection[input_name].push(value);
201 }
202 }
203 } else {
204 // remove element from array if exists
205 if (!this.isArray(input_name)) {
206 // convert to array first
207 if (this._collection[input_name] === value) {
208 this._collection[input_name] = '';
209 }
210 } else {
211 let pos = this._collection[input_name].indexOf(value)
212 if (pos !== -1) {
213 if (this._collection[input_name].length === 1) {
214 this._collection[input_name] = '';
215 } else {
216 this._collection[input_name].splice(pos, 1);
217 }
218 }
219 }
220 }
221 } else {
222 this._collection[input_name] = value;
223 }
224};
225
226BunnyFormData.prototype.has = function has(input_name) {
227 return this._collection[input_name] !== undefined;
228};
229
230BunnyFormData.prototype.empty = function has(input_name) {
231 return this._collection[input_name].length === 0;
232};
233
234BunnyFormData.prototype.isArray = function isArray(input_name) {
235 return Array.isArray(this._collection[input_name]);
236};
237
238BunnyFormData.prototype.isNodeList = function isNodeList(input_name_or_el) {
239 const input = (typeof input_name_or_el === 'object') ? input_name_or_el : this.getInput(input_name_or_el);
240 // RadioNodeList is undefined in IE, Edge, it uses HTMLCollection instead
241 return input instanceof (typeof RadioNodeList !== 'undefined' ? RadioNodeList : HTMLCollection);
242};
243
244BunnyFormData.prototype.append = function append(input_name, value) {
245 if (this._collection[input_name] === undefined) {
246 this._collection[input_name] = value;
247 } else if (Array.isArray(this._collection[input_name])) {
248 this._collection[input_name].push(value);
249 } else {
250 // convert single element into array and append new item
251 const item = this._collection[input_name];
252 this._collection[input_name] = [item, value];
253 }
254};
255
256BunnyFormData.prototype.getAll = function getAll(input_name) {
257 if (this._collection[input_name] === undefined) {
258 return [];
259 } else if (Array.isArray(this._collection[input_name])) {
260 return this._collection[input_name];
261 } else {
262 return [this._collection[input_name]];
263 }
264};
265
266// since entries(), keys(), values() return Iterator which is not supported in many browsers
267// there is only one element to simply get object of key => value pairs of all form elements
268// use this method instead of entries(), keys() or values()
269BunnyFormData.prototype.getAllElements = function getAllElements() {
270 return this._collection;
271};
272
273BunnyFormData.prototype.buildFormDataObject = function buildFormDataObject() {
274 const formData = new FormData();
275 for (let key in this._collection) {
276 if (Array.isArray(this._collection[key])) {
277 this._collection[key].forEach((item) => {
278 formData.append(key, item);
279 });
280 } else {
281 formData.append(key, this._collection[key]);
282 }
283 }
284 return formData;
285};
286
287// remove instead of delete(). Also can remove element from array
288BunnyFormData.prototype.remove = function remove(input_name, array_value = undefined) {
289 if (array_value !== undefined) {
290 // remove element from array
291 if (Array.isArray(this._collection[input_name])) {
292 let new_array = [];
293 this._collection[input_name].forEach((item) => {
294 if (item !== array_value) {
295 new_array.push(item);
296 }
297 });
298 this._collection[input_name] = new_array;
299 } else {
300 // not an array, just remove single element
301 delete this._collection[input_name];
302 }
303 } else {
304 delete this._collection[input_name];
305 }
306};
307
308export default BunnyFormData;