Skip to content

Instantly share code, notes, and snippets.

@youmad
Created January 11, 2016 19:28
Show Gist options
  • Save youmad/aa6fc267ccd9550a8291 to your computer and use it in GitHub Desktop.
Save youmad/aa6fc267ccd9550a8291 to your computer and use it in GitHub Desktop.
Send form with file as binary
"use strict";
var Utf8Encode = function (string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
/*\
|*| :: XMLHttpRequest.prototype.sendAsBinary() Polyfill ::
|*|
|*| https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()
\*/
if (!XMLHttpRequest.prototype.sendAsBinary) {
XMLHttpRequest.prototype.sendAsBinary = function (sData) {
var nBytes = sData.length, ui8Data = new Uint8Array(nBytes);
for (var nIdx = 0; nIdx < nBytes; nIdx++) {
ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff;
}
/* send as ArrayBufferView...: */
this.send(ui8Data);
/* ...or as ArrayBuffer (legacy)...: this.send(ui8Data.buffer); */
};
}
/*\
|*| Немного изменёный вариант:
|*| 1. добавлен коллбэк.
|*| 2. имя поля и значение (кроме [type="file"]) кодируется в UTF-8.
|*|
|*| :: AJAX Form Submit Framework ::
|*|
|*| https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest
|*|
|*| This framework is released under the GNU Public License, version 3 or later.
|*| http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
|*| Syntax:
|*|
|*| AJAXSubmit(HTMLFormElement, callback);
\*/
var AJAXSubmit = (function () {
function submitData(oData) {
/* the AJAX request... */
var oAjaxReq = new XMLHttpRequest();
oAjaxReq.submittedData = oData;
oAjaxReq.onload = oData.callback;
if (oData.technique === 0) {
/* method is GET */
oAjaxReq.open("get", oData.receiver.replace(/(?:\?.*)?$/, oData.segments.length > 0 ? "?" + oData.segments.join("&") : ""), true);
oAjaxReq.send(null);
} else {
/* method is POST */
oAjaxReq.open("post", oData.receiver, true);
if (oData.technique === 3) {
/* enctype is multipart/form-data */
var sBoundary = "---------------------------" + Date.now().toString(16);
oAjaxReq.setRequestHeader("Content-Type", "multipart\/form-data; boundary=" + sBoundary);
//Utf8Encode
oAjaxReq.sendAsBinary("--" + sBoundary + "\r\n" + oData.segments.join("--" + sBoundary + "\r\n") + "--" + sBoundary + "--\r\n");
} else {
/* enctype is application/x-www-form-urlencoded or text/plain */
oAjaxReq.setRequestHeader("Content-Type", oData.contentType);
oAjaxReq.send(oData.segments.join(oData.technique === 2 ? "\r\n" : "&"));
}
}
}
function processStatus(oData) {
if (oData.status > 0) {
return;
}
/* the form is now totally serialized! do something before sending it to the server... */
/* doSomething(oData); */
/* console.log("AJAXSubmit - The form is now serialized. Submitting..."); */
submitData(oData);
}
function pushSegment(oFREvt) {
this.owner.segments[this.segmentIdx] += oFREvt.target.result + "\r\n";
this.owner.status--;
processStatus(this.owner);
}
function plainEscape(sText) {
/* how should I treat a text/plain form encoding? what characters are not allowed? this is what I suppose...: */
/* "4\3\7 - Einstein said E=mc2" ----> "4\\3\\7\ -\ Einstein\ said\ E\=mc2" */
return sText.replace(/[\s\=\\]/g, "\\$&");
}
function SubmitRequest(oTarget, callback) {
var nFile, sFieldType, oField, oSegmReq, oFile, bIsPost = oTarget.method.toLowerCase() === "post";
/* console.log("AJAXSubmit - Serializing form..."); */
this.contentType = bIsPost && oTarget.enctype ? oTarget.enctype : "application\/x-www-form-urlencoded";
this.technique = bIsPost ? this.contentType === "multipart\/form-data" ? 3 : this.contentType === "text\/plain" ? 2 : 1 : 0;
this.receiver = oTarget.action.match(/\?/) ? oTarget.action + '&ajax=1' : oTarget.action + '?ajax=1';
this.status = 0;
this.segments = [];
this.callback = callback;
var fFilter = this.technique === 2 ? plainEscape : escape;
for (var nItem = 0; nItem < oTarget.elements.length; nItem++) {
oField = oTarget.elements[nItem];
if (!oField.hasAttribute("name")) {
continue;
}
var oFName = Utf8Encode(oField.name);
var oFValue = Utf8Encode(oField.value);
sFieldType = oField.nodeName.toUpperCase() === "INPUT" ? oField.getAttribute("type").toUpperCase() : "TEXT";
if (sFieldType === "FILE" && oField.files.length > 0) {
if (this.technique === 3) {
/* enctype is multipart/form-data */
for (nFile = 0; nFile < oField.files.length; nFile++) {
oFile = oField.files[nFile];
oSegmReq = new FileReader();
/* (custom properties:) */
oSegmReq.segmentIdx = this.segments.length;
oSegmReq.owner = this;
/* (end of custom properties) */
oSegmReq.onload = pushSegment;
this.segments.push("Content-Disposition: form-data; name=\"" + oFName + "\"; filename=\"" + oFName + "\"\r\nContent-Type: " + oFile.type + "\r\n\r\n");
this.status++;
oSegmReq.readAsBinaryString(oFile);
}
} else {
/* enctype is application/x-www-form-urlencoded or text/plain or method is GET: files will not be sent! */
for (nFile = 0; nFile < oField.files.length; this.segments.push(fFilter(oFName) + "=" + fFilter(oField.files[nFile++].name)))
;
}
} else if ((sFieldType !== "RADIO" && sFieldType !== "CHECKBOX") || oField.checked) {
/* field type is not FILE or is FILE but is empty */
this.segments.push(
this.technique === 3 ? /* enctype is multipart/form-data */
"Content-Disposition: form-data; name=\"" + oFName + "\"\r\n\r\n" + oFValue + "\r\n"
: /* enctype is application/x-www-form-urlencoded or text/plain or method is GET */
fFilter(oFName) + "=" + fFilter(oFValue)
);
}
}
processStatus(this);
}
return function (oFormElement, callback) {
if (!oFormElement.action) {
return;
}
new SubmitRequest(oFormElement, callback);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment