From acc903b85036e890caa3311755d3fbc0ab33a06f Mon Sep 17 00:00:00 2001 From: Marco 'Lubber' Wienkoop Date: Wed, 23 Mar 2022 10:45:13 +0100 Subject: [PATCH] feat(api): serializeObject, formdata, files This PR implements an optimized version of the original jquery-serializeobject dependency of SUI when serializeForm:true was used. https://github.com/macek/jquery-serialize-object/blob/master/jquery.serialize-object.js I enhanced the original logic to also support file inputs, convert pure number values and support same name keys (without trailing []) I also added support to use The FormData Api when serializeForm is set to 'formdata'. This way the old code stays backward compatible. --- src/definitions/behaviors/api.js | 81 +++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/src/definitions/behaviors/api.js b/src/definitions/behaviors/api.js index 229476442f..ee7e610dd8 100644 --- a/src/definitions/behaviors/api.js +++ b/src/definitions/behaviors/api.js @@ -428,28 +428,76 @@ $.api = $.fn.api = function(parameters) { formData: function(data) { var formData = {}, - hasOtherData + hasOtherData, + useFormDataApi = settings.serializeForm === 'formdata' ; data = data || originalData || settings.data; hasOtherData = $.isPlainObject(data); - $.each($form.serializeArray(), function (i, element) { - var node = formData[element.name]; - - if ('undefined' !== typeof node && node !== null) { - if (Array.isArray(node)) { - node.push(element.value); + if (useFormDataApi) { + formData = new FormData($form[0]); + settings.processData = typeof settings.processData !== 'undefined' ? settings.processData : false; + settings.contentType = typeof settings.contentType !== 'undefined' ? settings.contentType : false; + } else { + var formArray = $form.serializeArray(), + pushes = {}, + pushValues= {}, + build = function(base, key, value) { + base[key] = value; + return base; + } + ; + // add files + $.each($('input[type="file"]',$form), function(i, tag) { + $.each($(tag)[0].files, function(j, file) { + formArray.push({name:tag.name, value: file}); + }); + }); + $.each(formArray, function(i, el) { + if (!settings.regExp.validate.test(el.name)) return; + var isCheckbox = $('[name="' + el.name + '"]', $form).attr('type') === 'checkbox', + floatValue = parseFloat(el.value), + value = (isCheckbox && el.value === 'on') || el.value === 'true' || (String(floatValue) === el.value ? floatValue : (el.value === 'false' ? false : el.value)), + nameKeys = el.name.match(settings.regExp.key) || [], k, pushKey= el.name.replace(/\[\]$/,'') + ; + if(!(pushKey in pushes)) { + pushes[pushKey] = 0; + pushValues[pushKey] = value; + } else if (Array.isArray(pushValues[pushKey])) { + pushValues[pushKey].push(value); } else { - formData[element.name] = [node, element.value]; + pushValues[pushKey] = [pushValues[pushKey] , value]; } - } else { - formData[element.name] = element.value; - } - }); + value = pushValues[pushKey]; + + while ((k = nameKeys.pop()) !== undefined) { + // foo[] + if (k == '' && !Array.isArray(value)){ + value = build([], pushes[pushKey]++, value); + } + // foo[n] + else if (settings.regExp.fixed.test(k)) { + value = build([], k, value); + } + // foo; foo[bar] + else if (settings.regExp.named.test(k)) { + value = build({}, k, value); + } + } + formData = $.extend(true, formData, value); + }); + } if(hasOtherData) { module.debug('Extending existing data with form data', data, formData); - data = $.extend(true, {}, data, formData); + if(useFormDataApi) { + $.each(Object.keys(data),function(i, el){ + formData.append(el, data[el]); + }); + data = formData; + } else { + data = $.extend(true, {}, data, formData); + } } else { module.debug('Adding form data', formData); @@ -1088,6 +1136,8 @@ $.api.settings = { defaultData : true, // whether to serialize closest form + // use true to convert complex named keys like a[b][1][c][] into a nested object + // use 'formdata' for formdata web api serializeForm : false, // how long to wait before request should occur @@ -1158,6 +1208,11 @@ $.api.settings = { regExp : { required : /\{\$*[A-z0-9]+\}/g, optional : /\{\/\$*[A-z0-9]+\}/g, + validate: /^[a-z_][a-z0-9_-]*(?:\[(?:\d*|[a-z0-9_-]+)\])*$/i, + key: /[a-z0-9_-]+|(?=\[\])/gi, + push: /^$/, + fixed: /^\d+$/, + named: /^[a-z0-9_-]+$/i }, className: {