Created
May 27, 2014 22:07
-
-
Save Rob--W/8b5adedd84c0d36aba64 to your computer and use it in GitHub Desktop.
FormData polyfill for Web Workers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* FormData for XMLHttpRequest 2 - Polyfill for Web Worker | |
* (c) 2014 Rob Wu <[email protected]> | |
* License: MIT | |
* - append(name, value[, filename]) | |
* - XMLHttpRequest.prototype.send(object FormData) | |
* | |
* Specification: http://www.w3.org/TR/XMLHttpRequest/#formdata | |
* http://www.w3.org/TR/XMLHttpRequest/#the-send-method | |
* The .append() implementation also accepts Uint8Array and ArrayBuffer objects | |
* Web Workers do not natively support FormData: | |
* http://dev.w3.org/html5/workers/#apis-available-to-workers | |
* Originally released in 2012 as a part of http://stackoverflow.com/a/10002486. | |
* Updates since initial release: | |
* - Forward-compatibility by testing whether FormData exists before defining it. | |
* - Increased robustness of .append. | |
* - Allow any typed array in .append. | |
* - Remove use of String.prototype.toString to work around a Firefox bug. | |
* - Use typed array in xhr.send instead of arraybuffer to get rid of deprecation | |
* warnings. | |
**/ | |
(function(exports) { | |
if (exports.FormData) { | |
// Don't replace FormData if it already exists | |
return; | |
} | |
// Export variable to the global scope | |
exports.FormData = FormData; | |
var ___send$rw = XMLHttpRequest.prototype.send; | |
XMLHttpRequest.prototype.send = function(data) { | |
if (data instanceof FormData) { | |
if (!data.__endedMultipart) data.__append('--' + data.boundary + '--\r\n'); | |
data.__endedMultipart = true; | |
this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + data.boundary); | |
data = new Uint8Array(data.data); | |
} | |
// Invoke original XHR.send | |
return ___send$rw.call(this, data); | |
}; | |
function FormData() { | |
// Force a Constructor | |
if (!(this instanceof FormData)) return new FormData(); | |
// Generate a random boundary - This must be unique with respect to the form's contents. | |
this.boundary = '------RWWorkerFormDataBoundary' + Math.random().toString(36); | |
var internal_data = this.data = []; | |
/** | |
* Internal method. | |
* @param inp String | ArrayBuffer | Uint8Array Input | |
*/ | |
this.__append = function(inp) { | |
var i = 0, len; | |
if (typeof inp == 'string') { | |
for (len = inp.length; i < len; ++i) | |
internal_data.push(inp.charCodeAt(i) & 0xff); | |
} else if (inp && inp.byteLength) {/*If ArrayBuffer or typed array */ | |
if (!('byteOffset' in inp)) /* If ArrayBuffer, wrap in view */ | |
inp = new Uint8Array(inp); | |
for (len = inp.byteLength; i < len; ++i) | |
internal_data.push(inp[i] & 0xff); | |
} | |
}; | |
} | |
/** | |
* @param name String Key name | |
* @param value String|Blob|File|typed array|ArrayBuffer Value | |
* @param filename String Optional File name (when value is not a string). | |
**/ | |
FormData.prototype.append = function(name, value, filename) { | |
if (this.__endedMultipart) { | |
// Truncate the closing boundary | |
this.data.length -= this.boundary.length + 6; | |
this.__endedMultipart = false; | |
} | |
if (arguments.length < 2) { | |
throw new SyntaxError('Not enough arguments'); | |
} | |
var part = '--' + this.boundary + '\r\n' + | |
'Content-Disposition: form-data; name="' + name + '"'; | |
if (value instanceof File || value instanceof Blob) { | |
return this.append(name, | |
new Uint8Array(new FileReaderSync().readAsArrayBuffer(value)), | |
filename || value.name); | |
} else if (typeof value.byteLength == 'number') { | |
// Duck-typed typed array or array buffer | |
part += '; filename="'+ (filename || 'blob').replace(/"/g,'%22') +'"\r\n'; | |
part += 'Content-Type: application/octet-stream\r\n\r\n'; | |
this.__append(part); | |
this.__append(value); | |
part = '\r\n'; | |
} else { | |
part += '\r\n\r\n' + value + '\r\n'; | |
} | |
this.__append(part); | |
}; | |
})(this || self); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@349989153 This polyfill only works in web workers (WebWorker, SharedWorker), not service workers or regular web pages. But in the latter case (regular web pages), the FormData API should already be available, and the polyfill should return early without any changes.