Closed tremby closed 6 years ago
There has been a couple of requests to patch xhr and fetch to transparently calling its _blob
method. will see if i can take a look at it later. Need to patch both xhr and fetch.
Ah, I was hoping it'd be as simple as prototype.toString = prototype._blob
or something.
Ah, I was hoping it'd be as simple as
prototype.toString = prototype._blob
or something.
If only it was that simple... Reason I have held back is cuz I wanted it to be focus to just be about a FormData and not so much about anything else regarding of how it's consumed by other API's such as fetch and/or XHR. But there has been a couple of people asking for transparencies now
Yeah, I think the fact is that anyone using FormData is very likely to be then sending it via either XHR or fetch. And speaking for myself at least, the reason I use a polyfill at all is that I want to use the APIs the standards describe, with no code changes at all except loading the polyfill if I can get away with it. If that's not possible, the polyfill sadly isn't quite fulfilling its purpose, as I see it.
Incidentally, if you do proceed with patching fetch, please make sure it's compatible with the whatwg-fetch
polyfill. (Presumably that one would have to be loaded before the FormData polyfill, if using both.)
Will take it into consideration.
fetch polyfill is based on xhr, So don't need to worry so much about that
Would you do me the honor of testing this for me?
var f,l="function"==typeof Object.defineProperties?Object.defineProperty:function(b,a,d){b!=Array.prototype&&b!=Object.prototype&&(b[a]=d.value)},m="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function n(){n=function(){};m.Symbol||(m.Symbol=p)}var p=function(){var b=0;return function(a){return"jscomp_symbol_"+(a||"")+b++}}();
function q(){n();var b=m.Symbol.iterator;b||(b=m.Symbol.iterator=m.Symbol("iterator"));"function"!=typeof Array.prototype[b]&&l(Array.prototype,b,{configurable:!0,writable:!0,value:function(){return t(this)}});q=function(){}}function t(b){var a=0;return u(function(){return a<b.length?{done:!1,value:b[a++]}:{done:!0}})}function u(b){q();b={next:b};b[m.Symbol.iterator]=function(){return this};return b}function v(b){q();n();q();var a=b[Symbol.iterator];return a?a.call(b):t(b)}
function w(b){for(var a,d=[];!(a=b.next()).done;)d.push(a.value);return d}
if(!window.FormData||!window.FormData.prototype.get){var y=function(b,a,d){if(2>arguments.length)throw new TypeError("2 arguments required, but only "+arguments.length+" present.");return a instanceof Blob?[b+"",a,void 0!==d?d+"":"File"===Object.prototype.toString.call(a).slice(8,-1)?a.name:"Blob"]:[b+"",a+""]},A=function(b){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");return[b+""]},B=function(b){var a=v(b);b=a.next().value;a=a.next().value;b instanceof Blob&&
(b=new File([b],a,{type:b.type,lastModified:b.lastModified}));return b},C=window.FormData,D=window.XMLHttpRequest.prototype.send,E=window.Request&&window.fetch;n();var F=window.Symbol&&Symbol.toStringTag,G=new WeakMap;F&&(Blob.prototype[F]||(Blob.prototype[F]="Blob"),"File"in window&&!File.prototype[F]&&(File.prototype[F]="File"));try{new File([],"")}catch(b){window.File=function(a,d,c){a=new Blob(a,c);c=c&&void 0!==c.lastModified?new Date(c.lastModified):new Date;Object.defineProperties(a,{name:{value:d},
lastModifiedDate:{value:c},lastModified:{value:+c},toString:{value:function(){return"[object File]"}}});F&&Object.defineProperty(a,F,{value:"File"});return a}}var H=function(b){G.set(this,Object.create(null));if(!b)return this;b=v(Array.from(b.elements));for(var a=b.next();!a.done;a=b.next()){var d=a.value;a=d.name;var c=d.type,e=d.value,g=d.files,k=d.checked;d=d.selectedOptions;if(a)if("file"===c)for(c=v(g),e=c.next();!e.done;e=c.next())this.append(a,e.value);else if("select-multiple"===c||"select-one"===
c)for(c=v(Array.from(d)),e=c.next();!e.done;e=c.next())this.append(a,e.value.value);else"checkbox"===c||"radio"===c?k&&this.append(a,e):this.append(a,e)}};f=H.prototype;f.append=function(b,a,d){var c=G.get(this);c[b]||(c[b]=[]);c[b].push([a,d])};f["delete"]=function(b){delete G.get(this)[b]};f.entries=function(){function b(b,x){for(;;)switch(a){case 0:z=G.get(N);h=[];k=z;for(g in k)h.push(g);r=0;case 1:if(!(r<h.length)){a=3;break}g=h[r];if(g in k){a=4;break}a=2;break;case 4:e=v(z[g]),c=e.next();case 5:if(c.done){a=
7;break}d=c.value;a=8;return{value:[g,B(d)],done:!1};case 8:if(1!=b){a=9;break}a=-1;throw x;case 9:case 6:c=e.next();a=5;break;case 7:case 2:r++;a=1;break;case 3:a=-1;default:return{value:void 0,done:!0}}}var a=0,d,c,e,g,k,r,h,z,N=this,x={next:function(){return b(0,void 0)},"throw":function(a){return b(1,a)},"return":function(){throw Error("Not yet implemented");}};q();x[Symbol.iterator]=function(){return this};return x};f.forEach=function(b,a){for(var d=v(this),c=d.next();!c.done;c=d.next()){var e=
v(c.value);c=e.next().value;e=e.next().value;b.call(a,e,c,this)}};f.get=function(b){var a=G.get(this);return a[b]?B(a[b][0]):null};f.getAll=function(b){return(G.get(this)[b]||[]).map(B)};f.has=function(b){return b in G.get(this)};f.keys=function(){function b(b,h){for(;;)switch(a){case 0:k=v(r),g=k.next();case 1:if(g.done){a=3;break}e=g.value;c=v(e);d=c.next().value;a=4;return{value:d,done:!1};case 4:if(1!=b){a=5;break}a=-1;throw h;case 5:case 2:g=k.next();a=1;break;case 3:a=-1;default:return{value:void 0,
done:!0}}}var a=0,d,c,e,g,k,r=this,h={next:function(){return b(0,void 0)},"throw":function(a){return b(1,a)},"return":function(){throw Error("Not yet implemented");}};q();h[Symbol.iterator]=function(){return this};return h};f.set=function(b,a,d){G.get(this)[b]=[[a,d]]};f.values=function(){function b(b,h){for(;;)switch(a){case 0:k=v(r),g=k.next();case 1:if(g.done){a=3;break}e=g.value;c=v(e);c.next();d=c.next().value;a=4;return{value:d,done:!1};case 4:if(1!=b){a=5;break}a=-1;throw h;case 5:case 2:g=
k.next();a=1;break;case 3:a=-1;default:return{value:void 0,done:!0}}}var a=0,d,c,e,g,k,r=this,h={next:function(){return b(0,void 0)},"throw":function(a){return b(1,a)},"return":function(){throw Error("Not yet implemented");}};q();h[Symbol.iterator]=function(){return this};return h};f.stream=function(){try{return this._blob().stream()}catch(b){throw Error("Include https://github.com/jimmywarting/Screw-FileReader for streaming support");}};H.prototype._asNative=function(){for(var b=new C,a=v(this),
d=a.next();!d.done;d=a.next()){var c=v(d.value);d=c.next().value;c=c.next().value;b.append(d,c)}return b};H.prototype._blob=function(){for(var b="----formdata-polyfill-"+Math.random(),a=[],d=v(this),c=d.next();!c.done;c=d.next()){var e=v(c.value);c=e.next().value;e=e.next().value;a.push("--"+b+"\r\n");e instanceof Blob?a.push('Content-Disposition: form-data; name="'+c+'"; filename="'+e.name+'"\r\n',"Content-Type: "+(e.type||"application/octet-stream")+"\r\n\r\n",e,"\r\n"):a.push('Content-Disposition: form-data; name="'+
c+'"\r\n\r\n'+e+"\r\n")}a.push("--"+b+"--");return new Blob(a,{type:"multipart/form-data; boundary="+b})};n();q();H.prototype[Symbol.iterator]=function(){return this.entries()};H.prototype.toString=function(){return"[object FormData]"};F&&(H.prototype[F]="FormData");for(var I={},J=v([["append",y],["delete",A],["get",A],["getAll",A],["has",A],["set",y]]),K=J.next();!K.done;I={b:I.b,a:I.a},K=J.next()){var L=v(K.value),M=L.next().value;I.a=L.next().value;I.b=H.prototype[M];H.prototype[M]=function(b){return function(){return b.b.apply(this,
b.a.apply(null,[].concat(arguments instanceof Array?arguments:w(v(arguments)))))}}(I)}XMLHttpRequest.prototype.send=function(b){D.call(this,b instanceof H?b.c():b)};if(E){var O=window.fetch;window.fetch=function(b,a){a&&a.body&&a.body instanceof H&&(a.body=a.body.c());return O(b,a)}}window.FormData=H};
It's minified with Closure Compiler adv. mode
The differences in this is that it don't have a module.export
it only has to be required or loaded in just as it's (result in easier hosting options such as cdnjs)
I'm patching fetch and xhr and also adding the polyfill to the global scope.
Only thing i didn't do is patching Request constructor. so this don't work:
req = new Request(url, {method: 'post', body: formdata}) // must call _blob here
fetch(request)
I'm testing with this: https://jsfiddle.net/sofj9c3h/3/
This is only testing without the whatwg-fetch polyfill so far.
In Firefox FormData is not overridden (that's fine with me -- I want it overridden only when its append or delete or entries or other such methods don't exist). It then deletes and adds and loops through entries as expected, and starts the fetch OK (which then fails, as expected, since I'm not posting to any real endpoint).
In Safari on desktop on a Mac, it's overridden (correct), deletes and adds and loops through entries as expected, but fails to start the fetch:
a.body.c is not a function. (In 'a.body.c()', 'a.body.c' is undefined)
Was only one tiny mistake, Closure Compiler mangle properties starting with _blob()
had to wrap it into body['_blob']()
Tested it myself and seems to work okey, probably going to make a new release later today, need to update the readme also.
just one tip: if you are testing http request https://httpbin.org is a good choice Allows cors request and echo everything back that you send to it
Good to know; thanks. But in this case I'd know whether the test passed or failed whether or not the actual request succeeded.
The test/request passed, just have to publish it to npm first, will do that later... Meanwhile I would appreciate if you could test it and also edit the readme.md for grammar, before i publish it.
Thanks in advance and for using the formdata polyfill
The test/request passed, just have to publish it to npm first, will do that later...
You misunderstood me here. I was just saying that while httpbin is useful to know about, I didn't need it for my purposes here -- the jsfiddle test case is conclusive even with a failing request. Anyway, this doesn't matter.
Meanwhile I would appreciate if you could test
Seems to work fine for me.
also edit the readme.md for grammar, before i publish it.
Sure. I'll open a pull request. (Done: #30)
I'm using fetch (no polyfill, haven't "fixed" it -- don't know how and don't see any docs on that), and sending
body: formData
. This was working natively in Chrome and Firefox and Safari, but I wanted the FormData polyfill so that I could use formData.delete and the other methods. After installing the polyfill, the fetch no longer worked in Safari: it was sending the string[object FormData]
rather than the actual data. I realized after reading the docs again that I needed to usebody: formData._blob()
.This is a shame, since it means I can't selectively activate the polyfill, only when needed, while leaving my source code alone.
_blob
method, so that no code change is needed?body: formData._blob ? formData._blob() : formData
?