diff --git a/FormData.js b/FormData.js index c3111fe..4beefcb 100644 --- a/FormData.js +++ b/FormData.js @@ -103,6 +103,15 @@ if (typeof FormData === 'undefined' || !FormData.prototype.keys) { : [name + '', value + ''] } + // normalize linefeeds for textareas + // https://html.spec.whatwg.org/multipage/form-elements.html#textarea-line-break-normalisation-transformation + function normalizeLinefeeds(value) { + if (typeof value === "string") { + value = value.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n") + } + return value + } + function each (arr, cb) { for (let i = 0; i < arr.length; i++) { cb(arr[i]) @@ -141,7 +150,8 @@ if (typeof FormData === 'undefined' || !FormData.prototype.keys) { } else if (elm.type === 'checkbox' || elm.type === 'radio') { if (elm.checked) self.append(elm.name, elm.value) } else { - self.append(elm.name, elm.value) + const value = elm.type === 'textarea' ? normalizeLinefeeds(elm.value) : elm.value + self.append(elm.name, value) } }) } diff --git a/formdata.min.js b/formdata.min.js index ac012fa..81c8e07 100644 --- a/formdata.min.js +++ b/formdata.min.js @@ -5,13 +5,13 @@ D.prototype.i=function(a){this.f={u:a,v:!0};this.b=this.s||this.l};D.prototype[" function K(a,b){E(a.a);var d=a.a.c;if(d)return I(a,"return"in d?d["return"]:function(a){return{value:a,done:!0}},b,a.a["return"]);a.a["return"](b);return J(a)}H.prototype.i=function(a){E(this.a);if(this.a.c)return I(this,this.a.c["throw"],a,this.a.h);this.a.i(a);return J(this)}; function I(a,b,d,c){try{var e=b.call(a.a.c,d);if(!(e instanceof Object))throw new TypeError("Iterator result "+e+" is not an object");if(!e.done)return a.a.g=!1,e;var f=e.value}catch(g){return a.a.c=null,a.a.i(g),J(a)}a.a.c=null;c.call(a.a,f);return J(a)}function J(a){for(;a.a.b;)try{var b=a.A(a.a);if(b)return a.a.g=!1,{value:b.value,done:!1}}catch(d){a.a.m=void 0,a.a.i(d)}a.a.g=!1;if(a.a.f){b=a.a.f;a.a.f=null;if(b.v)throw b.u;return{value:b["return"],done:!0}}return{value:void 0,done:!0}} function L(a){this.next=function(b){return a.h(b)};this["throw"]=function(b){return a.i(b)};this["return"]=function(b){return K(a,b)};u();this[Symbol.iterator]=function(){return this}}function M(a,b){var d=new L(new H(b));C&&C(d,a.prototype);return d} -if("undefined"===typeof FormData||!FormData.prototype.keys){var N=function(a,b){for(var d=0;darguments.length)throw new TypeError("2 arguments required, but only "+arguments.length+" present.");return b instanceof Blob?[a+"",b,void 0!==d?d+"":"string"===typeof b.name?b.name:"blob"]:[a+"",b+""]},P=function(a){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");return[a+""]},Q=function(a){var b=x(a);a=b.next().value;b=b.next().value; -a instanceof Blob&&(a=new File([a],b,{type:a.type,lastModified:a.lastModified}));return a},R="object"===typeof window?window:"object"===typeof self?self:this,S=R.FormData,T=R.XMLHttpRequest&&R.XMLHttpRequest.prototype.send,U=R.Request&&R.fetch;p();var V=R.Symbol&&Symbol.toStringTag,W=new WeakMap,X=Array.from||function(a){return[].slice.call(a)};V&&(Blob.prototype[V]||(Blob.prototype[V]="Blob"),"File"in R&&!File.prototype[V]&&(File.prototype[V]="File"));try{new File([],"")}catch(a){R.File=function(b, -d,c){b=new Blob(b,c);c=c&&void 0!==c.lastModified?new Date(c.lastModified):new Date;Object.defineProperties(b,{name:{value:d},lastModifiedDate:{value:c},lastModified:{value:+c},toString:{value:function(){return"[object File]"}}});V&&Object.defineProperty(b,V,{value:"File"});return b}}p();u();var Y=function(a){W.set(this,Object.create(null));if(!a)return this;var b=this;N(a.elements,function(a){a.name&&!a.disabled&&"submit"!==a.type&&"button"!==a.type&&("file"===a.type?N(a.files||[],function(c){b.append(a.name, -c)}):"select-multiple"===a.type||"select-one"===a.type?N(a.options,function(c){!c.disabled&&c.selected&&b.append(a.name,c.value)}):"checkbox"===a.type||"radio"===a.type?a.checked&&b.append(a.name,a.value):b.append(a.name,a.value))})};k=Y.prototype;k.append=function(a,b,d){var c=W.get(this);c[a]||(c[a]=[]);c[a].push([b,d])};k["delete"]=function(a){delete W.get(this)[a]};k.entries=function b(){var d=this,c,e,f,g,h,q;return M(b,function(b){switch(b.b){case 1:c=W.get(d),f=new G(c);case 2:var t;a:{for(t= -f;0arguments.length)throw new TypeError("2 arguments required, but only "+arguments.length+" present.");return b instanceof Blob?[a+"",b,void 0!==d?d+"":"string"===typeof b.name?b.name:"blob"]:[a+"",b+""]},Q=function(a){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present."); +return[a+""]},R=function(a){var b=x(a);a=b.next().value;b=b.next().value;a instanceof Blob&&(a=new File([a],b,{type:a.type,lastModified:a.lastModified}));return a},S="object"===typeof window?window:"object"===typeof self?self:this,T=S.FormData,U=S.XMLHttpRequest&&S.XMLHttpRequest.prototype.send,V=S.Request&&S.fetch;p();var W=S.Symbol&&Symbol.toStringTag,X=new WeakMap,Y=Array.from||function(a){return[].slice.call(a)};W&&(Blob.prototype[W]||(Blob.prototype[W]="Blob"),"File"in S&&!File.prototype[W]&& +(File.prototype[W]="File"));try{new File([],"")}catch(a){S.File=function(b,d,c){b=new Blob(b,c);c=c&&void 0!==c.lastModified?new Date(c.lastModified):new Date;Object.defineProperties(b,{name:{value:d},lastModifiedDate:{value:c},lastModified:{value:+c},toString:{value:function(){return"[object File]"}}});W&&Object.defineProperty(b,W,{value:"File"});return b}}p();u();var Z=function(a){X.set(this,Object.create(null));if(!a)return this;var b=this;N(a.elements,function(a){if(a.name&&!a.disabled&&"submit"!== +a.type&&"button"!==a.type)if("file"===a.type)N(a.files||[],function(c){b.append(a.name,c)});else if("select-multiple"===a.type||"select-one"===a.type)N(a.options,function(c){!c.disabled&&c.selected&&b.append(a.name,c.value)});else if("checkbox"===a.type||"radio"===a.type)a.checked&&b.append(a.name,a.value);else{var c="textarea"===a.type?O(a.value):a.value;b.append(a.name,c)}})};k=Z.prototype;k.append=function(a,b,d){var c=X.get(this);c[a]||(c[a]=[]);c[a].push([b,d])};k["delete"]=function(a){delete X.get(this)[a]}; +k.entries=function b(){var d=this,c,e,f,g,h,q;return M(b,function(b){switch(b.b){case 1:c=X.get(d),f=new G(c);case 2:var t;a:{for(t=f;0 { + // Native FormData normalizes linefeeds in textareas to CRLF + // In order to be consistent the polyfill should do the same + it('Should convert LF to CRLF for textareas', () => { + // This can not be tested with 'create_form' as the function ignores \n + const form = document.createElement('form') + const textarea = document.createElement('textarea') + textarea.name = 'key' + textarea.value = '\n' + form.appendChild(textarea) + const fd = new FormData(form) + const value = fd.get('key') + assert.equal('\r\n', value) + }) + + it('Should convert CR to CRLF for textareas', () => { + const form = document.createElement('form') + const textarea = document.createElement('textarea') + textarea.name = 'key' + textarea.value = '\r' + form.appendChild(textarea) + const fd = new FormData(form) + const value = fd.get('key') + assert.equal('\r\n', value) + }) + + it('Should normalize mixed linefeeds to CRLF for textareas', () => { + const form = document.createElement('form') + const textarea = document.createElement('textarea') + textarea.name = 'key' + textarea.value = 'a\n\ra\r\na\n\r\n\r\n\r\n\na\r\r' + form.appendChild(textarea) + const fd = new FormData(form) + const value = fd.get('key') + assert.equal('a\r\n\r\na\r\na\r\n\r\n\r\n\r\n\r\na\r\n\r\n', value) + }) + + it('Should not convert LF to CRLF when provided by append', () => { + const fd = create_formdata(['key', '\n']) + const value = fd.get('key') + assert.equal('\n', value) + }) + + it('Should not convert CR to CRLF when provided by append', () => { + const fd = create_formdata(['key', '\r']) + const value = fd.get('key') + assert.equal('\r', value) + }) + }) + describe('disabled', () => { it('Shold not include disabled fields', () => { const fd = create_form(