"use strict"; //TextEncoder polyfil from MDN (https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder): if (typeof TextEncoder === "undefined") { TextEncoder=function TextEncoder(){}; TextEncoder.prototype.encode = function encode(str) { "use strict"; var Len = str.length, resPos = -1; var resArr = typeof Uint8Array === "undefined" ? new Array(Len * 2) : new Uint8Array(Len * 3); for (var point=0, nextcode=0, i = 0; i !== Len; ) { point = str.charCodeAt(i), i += 1; if (point >= 0xD800 && point <= 0xDBFF) { if (i === Len) { resArr[resPos += 1] = 0xef/*0b11101111*/; resArr[resPos += 1] = 0xbf/*0b10111111*/; resArr[resPos += 1] = 0xbd/*0b10111101*/; break; } // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae nextcode = str.charCodeAt(i); if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) { point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000; i += 1; if (point > 0xffff) { resArr[resPos += 1] = (0x1e/*0b11110*/<<3) | (point>>>18); resArr[resPos += 1] = (0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/); resArr[resPos += 1] = (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/); resArr[resPos += 1] = (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/); continue; } } else { resArr[resPos += 1] = 0xef/*0b11101111*/; resArr[resPos += 1] = 0xbf/*0b10111111*/; resArr[resPos += 1] = 0xbd/*0b10111101*/; continue; } } if (point <= 0x007f) { resArr[resPos += 1] = (0x0/*0b0*/<<7) | point; } else if (point <= 0x07ff) { resArr[resPos += 1] = (0x6/*0b110*/<<5) | (point>>>6); resArr[resPos += 1] = (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/); } else { resArr[resPos += 1] = (0xe/*0b1110*/<<4) | (point>>>12); resArr[resPos += 1] = (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/); resArr[resPos += 1] = (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/); } } if (typeof Uint8Array!=="undefined") return new Uint8Array(resArr.buffer.slice(0, resPos+1)); else return resArr.length === resPos+1 ? resArr : resArr.slice(0, resPos+1); // IE 6-9 }; TextEncoder.prototype.toString = function(){return "[object TextEncoder]"}; try { // Object.defineProperty only works on DOM prototypes in IE8 Object.defineProperty(TextEncoder.prototype,"encoding",{ get:function(){if(TextEncoder.prototype.isPrototypeOf(this)) return"utf-8"; else throw TypeError("Illegal invocation");} }); } catch(e) { /*IE6-8 fallback*/ TextEncoder.prototype.encoding = "utf-8"; } if(typeof Symbol!=="undefined")TextEncoder.prototype[Symbol.toStringTag]="TextEncoder"; } //---------------------------- function EntryForm() { //Connect to document: this.div = document.getElementById("EntryForm"); this.name = document.getElementById("EntryForm-name"), this.author = document.getElementById("EntryForm-author"), this.url = document.getElementById("EntryForm-url"), this.file = document.getElementById("EntryForm-file") this.button = document.getElementById("EntryForm-button"); this.status = document.getElementById("EntryForm-status"); //Event handling for submit button: let me = this; this.button.addEventListener('click', function(){ me.submit(); }); } EntryForm.prototype.message = function EntryForm_message(message) { this.status.classList.remove('error'); this.status.innerText = message; console.log(message); }; EntryForm.prototype.error = function EntryForm_error(message) { if (!this.status.classList.contains("error")) this.status.classList.add('error'); this.status.innerText = message; console.error(message); }; EntryForm.prototype.submit = function EntryForm_submit() { this.button.disabled = true; this.message(""); this.read(); }; EntryForm.prototype.read = function EntryForm_read() { //read file: if (this.file.files.length === 0) { this.error("Missing file!"); this.button.disabled = false; return; } else if (this.file.files.length !== 1) { this.error("Too many files!"); this.button.disabled = false; return; } this.message("Loading file..."); let me = this; this.reader = new FileReader(); this.reader.onerror = function(){ me.error("File loading failed."); me.button.disabled = false; delete me.reader; }; this.reader.onload = function(){ //build+send message: me.send(); }; this.reader.readAsArrayBuffer(this.file.files[0]); }; EntryForm.prototype.send = function EntryForm_send() { this.message("Sending entry..."); //Build data to send: const encoder = new TextEncoder("utf-8"); let nameBytes = encoder.encode(this.name.value); let authorBytes = encoder.encode(this.author.value); let urlBytes = encoder.encode(this.url.value); let fileBytes = new Uint8Array(this.reader.result); let data = new Uint8Array(nameBytes.length + 1 + authorBytes.length + 1 + urlBytes.length + 1 + fileBytes.length); let offset = 0; data.set(nameBytes, offset); offset += nameBytes.length; offset += 1; data.set(authorBytes, offset); offset += authorBytes.length; offset += 1; data.set(urlBytes, offset); offset += urlBytes.length; offset += 1; data.set(fileBytes, offset); offset += fileBytes.length; console.assert(offset === data.length, "filled data perfectly"); //Build request to send data: this.xhr = new XMLHttpRequest(); let me = this; this.xhr.upload.addEventListener('error', function() { me.error("Upload failed."); me.button.disabled = false; delete me.reader; delete me.xhr; }); this.xhr.upload.addEventListener('abort', function() { me.error("Upload aborted."); me.button.disabled = false; delete me.reader; delete me.xhr; }); this.xhr.upload.addEventListener('load', function() { me.message("Awaiting response..."); }); this.xhr.addEventListener('error', function() { me.error("Read failed."); me.button.disabled = false; delete me.reader; delete me.xhr; }); this.xhr.addEventListener('abort', function() { me.error("Read aborted"); me.button.disabled = false; delete me.reader; delete me.xhr; }); this.xhr.addEventListener('load', function() { me.finish(); }); this.xhr.open("POST", "/" + this.div.dataset.contest + "/submit"); this.xhr.send(data); }; EntryForm.prototype.finish = function EntryForm_finish() { //return should be status URL for submission if (this.xhr.status === 200) { this.message("Success, redirecting to " + this.xhr.responseText); window.location.href = this.xhr.responseText; } else { this.error("Error: '" + this.xhr.responseText + "'"); } delete this.reader; delete this.xhr; this.button.disabled = false; }; window.EF = new EntryForm();