diff --git a/actiontext/CHANGELOG.md b/actiontext/CHANGELOG.md index 34ab27646fd26..13011bff237ae 100644 --- a/actiontext/CHANGELOG.md +++ b/actiontext/CHANGELOG.md @@ -1,3 +1,11 @@ +* Compile ESM package that can be used directly in the browser as actiontext.esm.js + + *Matias Grunberg* + +* Fix using actiontext.js with Sprockets + + *Matias Grunberg* + * Upgrade Trix to 2.0.7 *Hartley McGuire* diff --git a/actiontext/app/assets/javascripts/.gitattributes b/actiontext/app/assets/javascripts/.gitattributes index d8f019e9e21b9..65a3ad3f8bcdc 100644 --- a/actiontext/app/assets/javascripts/.gitattributes +++ b/actiontext/app/assets/javascripts/.gitattributes @@ -1,2 +1,3 @@ actiontext.js linguist-generated +actiontext.esm.js linguist-generated trix.js linguist-vendored diff --git a/actiontext/app/assets/javascripts/actiontext.esm.js b/actiontext/app/assets/javascripts/actiontext.esm.js new file mode 100644 index 0000000000000..2a3fad67a376b --- /dev/null +++ b/actiontext/app/assets/javascripts/actiontext.esm.js @@ -0,0 +1,889 @@ +var sparkMd5 = { + exports: {} +}; + +(function(module, exports) { + (function(factory) { + { + module.exports = factory(); + } + })((function(undefined$1) { + var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ]; + function md5cycle(x, k) { + var a = x[0], b = x[1], c = x[2], d = x[3]; + a += (b & c | ~b & d) + k[0] - 680876936 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[1] - 389564586 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[2] + 606105819 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[3] - 1044525330 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[4] - 176418897 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[5] + 1200080426 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[6] - 1473231341 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[7] - 45705983 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[8] + 1770035416 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[9] - 1958414417 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[10] - 42063 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[11] - 1990404162 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[12] + 1804603682 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[13] - 40341101 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[14] - 1502002290 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[15] + 1236535329 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & d | c & ~d) + k[1] - 165796510 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[6] - 1069501632 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[11] + 643717713 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[0] - 373897302 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[5] - 701558691 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[10] + 38016083 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[15] - 660478335 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[4] - 405537848 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[9] + 568446438 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[14] - 1019803690 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[3] - 187363961 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[8] + 1163531501 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[13] - 1444681467 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[2] - 51403784 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[7] + 1735328473 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[12] - 1926607734 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b ^ c ^ d) + k[5] - 378558 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[8] - 2022574463 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[11] + 1839030562 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[14] - 35309556 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[1] - 1530992060 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[4] + 1272893353 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[7] - 155497632 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[10] - 1094730640 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[13] + 681279174 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[0] - 358537222 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[3] - 722521979 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[6] + 76029189 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[9] - 640364487 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[12] - 421815835 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[15] + 530742520 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[2] - 995338651 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; + b = (b << 21 | b >>> 11) + c | 0; + x[0] = a + x[0] | 0; + x[1] = b + x[1] | 0; + x[2] = c + x[2] | 0; + x[3] = d + x[3] | 0; + } + function md5blk(s) { + var md5blks = [], i; + for (i = 0; i < 64; i += 4) { + md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); + } + return md5blks; + } + function md5blk_array(a) { + var md5blks = [], i; + for (i = 0; i < 64; i += 4) { + md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); + } + return md5blks; + } + function md51(s) { + var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; + for (i = 64; i <= n; i += 64) { + md5cycle(state, md5blk(s.substring(i - 64, i))); + } + s = s.substring(i - 64); + length = s.length; + tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3); + } + tail[i >> 2] |= 128 << (i % 4 << 3); + if (i > 55) { + md5cycle(state, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + tmp = n * 8; + tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); + lo = parseInt(tmp[2], 16); + hi = parseInt(tmp[1], 16) || 0; + tail[14] = lo; + tail[15] = hi; + md5cycle(state, tail); + return state; + } + function md51_array(a) { + var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; + for (i = 64; i <= n; i += 64) { + md5cycle(state, md5blk_array(a.subarray(i - 64, i))); + } + a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0); + length = a.length; + tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= a[i] << (i % 4 << 3); + } + tail[i >> 2] |= 128 << (i % 4 << 3); + if (i > 55) { + md5cycle(state, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + tmp = n * 8; + tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); + lo = parseInt(tmp[2], 16); + hi = parseInt(tmp[1], 16) || 0; + tail[14] = lo; + tail[15] = hi; + md5cycle(state, tail); + return state; + } + function rhex(n) { + var s = "", j; + for (j = 0; j < 4; j += 1) { + s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15]; + } + return s; + } + function hex(x) { + var i; + for (i = 0; i < x.length; i += 1) { + x[i] = rhex(x[i]); + } + return x.join(""); + } + if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ; + if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) { + (function() { + function clamp(val, length) { + val = val | 0 || 0; + if (val < 0) { + return Math.max(val + length, 0); + } + return Math.min(val, length); + } + ArrayBuffer.prototype.slice = function(from, to) { + var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray; + if (to !== undefined$1) { + end = clamp(to, length); + } + if (begin > end) { + return new ArrayBuffer(0); + } + num = end - begin; + target = new ArrayBuffer(num); + targetArray = new Uint8Array(target); + sourceArray = new Uint8Array(this, begin, num); + targetArray.set(sourceArray); + return target; + }; + })(); + } + function toUtf8(str) { + if (/[\u0080-\uFFFF]/.test(str)) { + str = unescape(encodeURIComponent(str)); + } + return str; + } + function utf8Str2ArrayBuffer(str, returnUInt8Array) { + var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i; + for (i = 0; i < length; i += 1) { + arr[i] = str.charCodeAt(i); + } + return returnUInt8Array ? arr : buff; + } + function arrayBuffer2Utf8Str(buff) { + return String.fromCharCode.apply(null, new Uint8Array(buff)); + } + function concatenateArrayBuffers(first, second, returnUInt8Array) { + var result = new Uint8Array(first.byteLength + second.byteLength); + result.set(new Uint8Array(first)); + result.set(new Uint8Array(second), first.byteLength); + return returnUInt8Array ? result : result.buffer; + } + function hexToBinaryString(hex) { + var bytes = [], length = hex.length, x; + for (x = 0; x < length - 1; x += 2) { + bytes.push(parseInt(hex.substr(x, 2), 16)); + } + return String.fromCharCode.apply(String, bytes); + } + function SparkMD5() { + this.reset(); + } + SparkMD5.prototype.append = function(str) { + this.appendBinary(toUtf8(str)); + return this; + }; + SparkMD5.prototype.appendBinary = function(contents) { + this._buff += contents; + this._length += contents.length; + var length = this._buff.length, i; + for (i = 64; i <= length; i += 64) { + md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i))); + } + this._buff = this._buff.substring(i - 64); + return this; + }; + SparkMD5.prototype.end = function(raw) { + var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3); + } + this._finish(tail, length); + ret = hex(this._hash); + if (raw) { + ret = hexToBinaryString(ret); + } + this.reset(); + return ret; + }; + SparkMD5.prototype.reset = function() { + this._buff = ""; + this._length = 0; + this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; + return this; + }; + SparkMD5.prototype.getState = function() { + return { + buff: this._buff, + length: this._length, + hash: this._hash.slice() + }; + }; + SparkMD5.prototype.setState = function(state) { + this._buff = state.buff; + this._length = state.length; + this._hash = state.hash; + return this; + }; + SparkMD5.prototype.destroy = function() { + delete this._hash; + delete this._buff; + delete this._length; + }; + SparkMD5.prototype._finish = function(tail, length) { + var i = length, tmp, lo, hi; + tail[i >> 2] |= 128 << (i % 4 << 3); + if (i > 55) { + md5cycle(this._hash, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + tmp = this._length * 8; + tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); + lo = parseInt(tmp[2], 16); + hi = parseInt(tmp[1], 16) || 0; + tail[14] = lo; + tail[15] = hi; + md5cycle(this._hash, tail); + }; + SparkMD5.hash = function(str, raw) { + return SparkMD5.hashBinary(toUtf8(str), raw); + }; + SparkMD5.hashBinary = function(content, raw) { + var hash = md51(content), ret = hex(hash); + return raw ? hexToBinaryString(ret) : ret; + }; + SparkMD5.ArrayBuffer = function() { + this.reset(); + }; + SparkMD5.ArrayBuffer.prototype.append = function(arr) { + var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i; + this._length += arr.byteLength; + for (i = 64; i <= length; i += 64) { + md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i))); + } + this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0); + return this; + }; + SparkMD5.ArrayBuffer.prototype.end = function(raw) { + var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= buff[i] << (i % 4 << 3); + } + this._finish(tail, length); + ret = hex(this._hash); + if (raw) { + ret = hexToBinaryString(ret); + } + this.reset(); + return ret; + }; + SparkMD5.ArrayBuffer.prototype.reset = function() { + this._buff = new Uint8Array(0); + this._length = 0; + this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; + return this; + }; + SparkMD5.ArrayBuffer.prototype.getState = function() { + var state = SparkMD5.prototype.getState.call(this); + state.buff = arrayBuffer2Utf8Str(state.buff); + return state; + }; + SparkMD5.ArrayBuffer.prototype.setState = function(state) { + state.buff = utf8Str2ArrayBuffer(state.buff, true); + return SparkMD5.prototype.setState.call(this, state); + }; + SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; + SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; + SparkMD5.ArrayBuffer.hash = function(arr, raw) { + var hash = md51_array(new Uint8Array(arr)), ret = hex(hash); + return raw ? hexToBinaryString(ret) : ret; + }; + return SparkMD5; + })); +})(sparkMd5); + +var SparkMD5 = sparkMd5.exports; + +const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; + +class FileChecksum { + static create(file, callback) { + const instance = new FileChecksum(file); + instance.create(callback); + } + constructor(file) { + this.file = file; + this.chunkSize = 2097152; + this.chunkCount = Math.ceil(this.file.size / this.chunkSize); + this.chunkIndex = 0; + } + create(callback) { + this.callback = callback; + this.md5Buffer = new SparkMD5.ArrayBuffer; + this.fileReader = new FileReader; + this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event))); + this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event))); + this.readNextChunk(); + } + fileReaderDidLoad(event) { + this.md5Buffer.append(event.target.result); + if (!this.readNextChunk()) { + const binaryDigest = this.md5Buffer.end(true); + const base64digest = btoa(binaryDigest); + this.callback(null, base64digest); + } + } + fileReaderDidError(event) { + this.callback(`Error reading ${this.file.name}`); + } + readNextChunk() { + if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) { + const start = this.chunkIndex * this.chunkSize; + const end = Math.min(start + this.chunkSize, this.file.size); + const bytes = fileSlice.call(this.file, start, end); + this.fileReader.readAsArrayBuffer(bytes); + this.chunkIndex++; + return true; + } else { + return false; + } + } +} + +function getMetaValue(name) { + const element = findElement(document.head, `meta[name="${name}"]`); + if (element) { + return element.getAttribute("content"); + } +} + +function findElements(root, selector) { + if (typeof root == "string") { + selector = root; + root = document; + } + const elements = root.querySelectorAll(selector); + return toArray(elements); +} + +function findElement(root, selector) { + if (typeof root == "string") { + selector = root; + root = document; + } + return root.querySelector(selector); +} + +function dispatchEvent(element, type, eventInit = {}) { + const {disabled: disabled} = element; + const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit; + const event = document.createEvent("Event"); + event.initEvent(type, bubbles || true, cancelable || true); + event.detail = detail || {}; + try { + element.disabled = false; + element.dispatchEvent(event); + } finally { + element.disabled = disabled; + } + return event; +} + +function toArray(value) { + if (Array.isArray(value)) { + return value; + } else if (Array.from) { + return Array.from(value); + } else { + return [].slice.call(value); + } +} + +class BlobRecord { + constructor(file, checksum, url, customHeaders = {}) { + this.file = file; + this.attributes = { + filename: file.name, + content_type: file.type || "application/octet-stream", + byte_size: file.size, + checksum: checksum + }; + this.xhr = new XMLHttpRequest; + this.xhr.open("POST", url, true); + this.xhr.responseType = "json"; + this.xhr.setRequestHeader("Content-Type", "application/json"); + this.xhr.setRequestHeader("Accept", "application/json"); + this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + Object.keys(customHeaders).forEach((headerKey => { + this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]); + })); + const csrfToken = getMetaValue("csrf-token"); + if (csrfToken != undefined) { + this.xhr.setRequestHeader("X-CSRF-Token", csrfToken); + } + this.xhr.addEventListener("load", (event => this.requestDidLoad(event))); + this.xhr.addEventListener("error", (event => this.requestDidError(event))); + } + get status() { + return this.xhr.status; + } + get response() { + const {responseType: responseType, response: response} = this.xhr; + if (responseType == "json") { + return response; + } else { + return JSON.parse(response); + } + } + create(callback) { + this.callback = callback; + this.xhr.send(JSON.stringify({ + blob: this.attributes + })); + } + requestDidLoad(event) { + if (this.status >= 200 && this.status < 300) { + const {response: response} = this; + const {direct_upload: direct_upload} = response; + delete response.direct_upload; + this.attributes = response; + this.directUploadData = direct_upload; + this.callback(null, this.toJSON()); + } else { + this.requestDidError(event); + } + } + requestDidError(event) { + this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`); + } + toJSON() { + const result = {}; + for (const key in this.attributes) { + result[key] = this.attributes[key]; + } + return result; + } +} + +class BlobUpload { + constructor(blob) { + this.blob = blob; + this.file = blob.file; + const {url: url, headers: headers} = blob.directUploadData; + this.xhr = new XMLHttpRequest; + this.xhr.open("PUT", url, true); + this.xhr.responseType = "text"; + for (const key in headers) { + this.xhr.setRequestHeader(key, headers[key]); + } + this.xhr.addEventListener("load", (event => this.requestDidLoad(event))); + this.xhr.addEventListener("error", (event => this.requestDidError(event))); + } + create(callback) { + this.callback = callback; + this.xhr.send(this.file.slice()); + } + requestDidLoad(event) { + const {status: status, response: response} = this.xhr; + if (status >= 200 && status < 300) { + this.callback(null, response); + } else { + this.requestDidError(event); + } + } + requestDidError(event) { + this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`); + } +} + +let id = 0; + +class DirectUpload { + constructor(file, url, delegate, customHeaders = {}) { + this.id = ++id; + this.file = file; + this.url = url; + this.delegate = delegate; + this.customHeaders = customHeaders; + } + create(callback) { + FileChecksum.create(this.file, ((error, checksum) => { + if (error) { + callback(error); + return; + } + const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders); + notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr); + blob.create((error => { + if (error) { + callback(error); + } else { + const upload = new BlobUpload(blob); + notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr); + upload.create((error => { + if (error) { + callback(error); + } else { + callback(null, blob.toJSON()); + } + })); + } + })); + })); + } +} + +function notify(object, methodName, ...messages) { + if (object && typeof object[methodName] == "function") { + return object[methodName](...messages); + } +} + +class DirectUploadController { + constructor(input, file) { + this.input = input; + this.file = file; + this.directUpload = new DirectUpload(this.file, this.url, this); + this.dispatch("initialize"); + } + start(callback) { + const hiddenInput = document.createElement("input"); + hiddenInput.type = "hidden"; + hiddenInput.name = this.input.name; + this.input.insertAdjacentElement("beforebegin", hiddenInput); + this.dispatch("start"); + this.directUpload.create(((error, attributes) => { + if (error) { + hiddenInput.parentNode.removeChild(hiddenInput); + this.dispatchError(error); + } else { + hiddenInput.value = attributes.signed_id; + } + this.dispatch("end"); + callback(error); + })); + } + uploadRequestDidProgress(event) { + const progress = event.loaded / event.total * 100; + if (progress) { + this.dispatch("progress", { + progress: progress + }); + } + } + get url() { + return this.input.getAttribute("data-direct-upload-url"); + } + dispatch(name, detail = {}) { + detail.file = this.file; + detail.id = this.directUpload.id; + return dispatchEvent(this.input, `direct-upload:${name}`, { + detail: detail + }); + } + dispatchError(error) { + const event = this.dispatch("error", { + error: error + }); + if (!event.defaultPrevented) { + alert(error); + } + } + directUploadWillCreateBlobWithXHR(xhr) { + this.dispatch("before-blob-request", { + xhr: xhr + }); + } + directUploadWillStoreFileWithXHR(xhr) { + this.dispatch("before-storage-request", { + xhr: xhr + }); + xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event))); + } +} + +const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])"; + +class DirectUploadsController { + constructor(form) { + this.form = form; + this.inputs = findElements(form, inputSelector).filter((input => input.files.length)); + } + start(callback) { + const controllers = this.createDirectUploadControllers(); + const startNextController = () => { + const controller = controllers.shift(); + if (controller) { + controller.start((error => { + if (error) { + callback(error); + this.dispatch("end"); + } else { + startNextController(); + } + })); + } else { + callback(); + this.dispatch("end"); + } + }; + this.dispatch("start"); + startNextController(); + } + createDirectUploadControllers() { + const controllers = []; + this.inputs.forEach((input => { + toArray(input.files).forEach((file => { + const controller = new DirectUploadController(input, file); + controllers.push(controller); + })); + })); + return controllers; + } + dispatch(name, detail = {}) { + return dispatchEvent(this.form, `direct-uploads:${name}`, { + detail: detail + }); + } +} + +const processingAttribute = "data-direct-uploads-processing"; + +const submitButtonsByForm = new WeakMap; + +let started = false; + +function start() { + if (!started) { + started = true; + document.addEventListener("click", didClick, true); + document.addEventListener("submit", didSubmitForm, true); + document.addEventListener("ajax:before", didSubmitRemoteElement); + } +} + +function didClick(event) { + const {target: target} = event; + if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) { + submitButtonsByForm.set(target.form, target); + } +} + +function didSubmitForm(event) { + handleFormSubmissionEvent(event); +} + +function didSubmitRemoteElement(event) { + if (event.target.tagName == "FORM") { + handleFormSubmissionEvent(event); + } +} + +function handleFormSubmissionEvent(event) { + const form = event.target; + if (form.hasAttribute(processingAttribute)) { + event.preventDefault(); + return; + } + const controller = new DirectUploadsController(form); + const {inputs: inputs} = controller; + if (inputs.length) { + event.preventDefault(); + form.setAttribute(processingAttribute, ""); + inputs.forEach(disable); + controller.start((error => { + form.removeAttribute(processingAttribute); + if (error) { + inputs.forEach(enable); + } else { + submitForm(form); + } + })); + } +} + +function submitForm(form) { + let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]"); + if (button) { + const {disabled: disabled} = button; + button.disabled = false; + button.focus(); + button.click(); + button.disabled = disabled; + } else { + button = document.createElement("input"); + button.type = "submit"; + button.style.display = "none"; + form.appendChild(button); + button.click(); + form.removeChild(button); + } + submitButtonsByForm.delete(form); +} + +function disable(input) { + input.disabled = true; +} + +function enable(input) { + input.disabled = false; +} + +function autostart() { + if (window.ActiveStorage) { + start(); + } +} + +setTimeout(autostart, 1); + +class AttachmentUpload { + constructor(attachment, element) { + this.attachment = attachment; + this.element = element; + this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this); + } + start() { + this.directUpload.create(this.directUploadDidComplete.bind(this)); + } + directUploadWillStoreFileWithXHR(xhr) { + xhr.upload.addEventListener("progress", (event => { + const progress = event.loaded / event.total * 100; + this.attachment.setUploadProgress(progress); + })); + } + directUploadDidComplete(error, attributes) { + if (error) { + throw new Error(`Direct upload failed: ${error}`); + } + this.attachment.setAttributes({ + sgid: attributes.attachable_sgid, + url: this.createBlobUrl(attributes.signed_id, attributes.filename) + }); + } + createBlobUrl(signedId, filename) { + return this.blobUrlTemplate.replace(":signed_id", signedId).replace(":filename", encodeURIComponent(filename)); + } + get directUploadUrl() { + return this.element.dataset.directUploadUrl; + } + get blobUrlTemplate() { + return this.element.dataset.blobUrlTemplate; + } +} + +addEventListener("trix-attachment-add", (event => { + const {attachment: attachment, target: target} = event; + if (attachment.file) { + const upload = new AttachmentUpload(attachment, target); + upload.start(); + } +})); diff --git a/actiontext/app/assets/javascripts/actiontext.js b/actiontext/app/assets/javascripts/actiontext.js index d4b0b1f50f300..a89f5ec3eff05 100644 --- a/actiontext/app/assets/javascripts/actiontext.js +++ b/actiontext/app/assets/javascripts/actiontext.js @@ -1,899 +1,862 @@ -var sparkMd5 = { - exports: {} -}; - -(function(module, exports) { - (function(factory) { - { - module.exports = factory(); - } - })((function(undefined$1) { - var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ]; - function md5cycle(x, k) { - var a = x[0], b = x[1], c = x[2], d = x[3]; - a += (b & c | ~b & d) + k[0] - 680876936 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[1] - 389564586 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[2] + 606105819 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[3] - 1044525330 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[4] - 176418897 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[5] + 1200080426 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[6] - 1473231341 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[7] - 45705983 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[8] + 1770035416 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[9] - 1958414417 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[10] - 42063 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[11] - 1990404162 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[12] + 1804603682 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[13] - 40341101 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[14] - 1502002290 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[15] + 1236535329 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & d | c & ~d) + k[1] - 165796510 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[6] - 1069501632 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[11] + 643717713 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[0] - 373897302 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[5] - 701558691 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[10] + 38016083 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[15] - 660478335 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[4] - 405537848 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[9] + 568446438 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[14] - 1019803690 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[3] - 187363961 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[8] + 1163531501 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[13] - 1444681467 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[2] - 51403784 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[7] + 1735328473 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[12] - 1926607734 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b ^ c ^ d) + k[5] - 378558 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[8] - 2022574463 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[11] + 1839030562 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[14] - 35309556 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[1] - 1530992060 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[4] + 1272893353 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[7] - 155497632 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[10] - 1094730640 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[13] + 681279174 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[0] - 358537222 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[3] - 722521979 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[6] + 76029189 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[9] - 640364487 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[12] - 421815835 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[15] + 530742520 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[2] - 995338651 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; - b = (b << 21 | b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; - b = (b << 21 | b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; - b = (b << 21 | b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; - b = (b << 21 | b >>> 11) + c | 0; - x[0] = a + x[0] | 0; - x[1] = b + x[1] | 0; - x[2] = c + x[2] | 0; - x[3] = d + x[3] | 0; - } - function md5blk(s) { - var md5blks = [], i; - for (i = 0; i < 64; i += 4) { - md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); +(function(factory) { + typeof define === "function" && define.amd ? define(factory) : factory(); +})((function() { + "use strict"; + var sparkMd5 = { + exports: {} + }; + (function(module, exports) { + (function(factory) { + { + module.exports = factory(); } - return md5blks; - } - function md5blk_array(a) { - var md5blks = [], i; - for (i = 0; i < 64; i += 4) { - md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); - } - return md5blks; - } - function md51(s) { - var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; - for (i = 64; i <= n; i += 64) { - md5cycle(state, md5blk(s.substring(i - 64, i))); - } - s = s.substring(i - 64); - length = s.length; - tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3); + })((function(undefined$1) { + var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ]; + function md5cycle(x, k) { + var a = x[0], b = x[1], c = x[2], d = x[3]; + a += (b & c | ~b & d) + k[0] - 680876936 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[1] - 389564586 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[2] + 606105819 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[3] - 1044525330 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[4] - 176418897 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[5] + 1200080426 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[6] - 1473231341 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[7] - 45705983 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[8] + 1770035416 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[9] - 1958414417 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[10] - 42063 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[11] - 1990404162 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[12] + 1804603682 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[13] - 40341101 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[14] - 1502002290 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[15] + 1236535329 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & d | c & ~d) + k[1] - 165796510 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[6] - 1069501632 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[11] + 643717713 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[0] - 373897302 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[5] - 701558691 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[10] + 38016083 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[15] - 660478335 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[4] - 405537848 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[9] + 568446438 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[14] - 1019803690 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[3] - 187363961 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[8] + 1163531501 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[13] - 1444681467 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[2] - 51403784 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[7] + 1735328473 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[12] - 1926607734 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b ^ c ^ d) + k[5] - 378558 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[8] - 2022574463 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[11] + 1839030562 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[14] - 35309556 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[1] - 1530992060 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[4] + 1272893353 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[7] - 155497632 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[10] - 1094730640 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[13] + 681279174 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[0] - 358537222 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[3] - 722521979 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[6] + 76029189 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[9] - 640364487 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[12] - 421815835 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[15] + 530742520 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[2] - 995338651 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; + b = (b << 21 | b >>> 11) + c | 0; + x[0] = a + x[0] | 0; + x[1] = b + x[1] | 0; + x[2] = c + x[2] | 0; + x[3] = d + x[3] | 0; } - tail[i >> 2] |= 128 << (i % 4 << 3); - if (i > 55) { - md5cycle(state, tail); - for (i = 0; i < 16; i += 1) { - tail[i] = 0; + function md5blk(s) { + var md5blks = [], i; + for (i = 0; i < 64; i += 4) { + md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } + return md5blks; } - tmp = n * 8; - tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); - lo = parseInt(tmp[2], 16); - hi = parseInt(tmp[1], 16) || 0; - tail[14] = lo; - tail[15] = hi; - md5cycle(state, tail); - return state; - } - function md51_array(a) { - var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; - for (i = 64; i <= n; i += 64) { - md5cycle(state, md5blk_array(a.subarray(i - 64, i))); - } - a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0); - length = a.length; - tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= a[i] << (i % 4 << 3); + function md5blk_array(a) { + var md5blks = [], i; + for (i = 0; i < 64; i += 4) { + md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); + } + return md5blks; } - tail[i >> 2] |= 128 << (i % 4 << 3); - if (i > 55) { + function md51(s) { + var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; + for (i = 64; i <= n; i += 64) { + md5cycle(state, md5blk(s.substring(i - 64, i))); + } + s = s.substring(i - 64); + length = s.length; + tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3); + } + tail[i >> 2] |= 128 << (i % 4 << 3); + if (i > 55) { + md5cycle(state, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + tmp = n * 8; + tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); + lo = parseInt(tmp[2], 16); + hi = parseInt(tmp[1], 16) || 0; + tail[14] = lo; + tail[15] = hi; md5cycle(state, tail); - for (i = 0; i < 16; i += 1) { - tail[i] = 0; + return state; + } + function md51_array(a) { + var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; + for (i = 64; i <= n; i += 64) { + md5cycle(state, md5blk_array(a.subarray(i - 64, i))); + } + a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0); + length = a.length; + tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= a[i] << (i % 4 << 3); } + tail[i >> 2] |= 128 << (i % 4 << 3); + if (i > 55) { + md5cycle(state, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + tmp = n * 8; + tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); + lo = parseInt(tmp[2], 16); + hi = parseInt(tmp[1], 16) || 0; + tail[14] = lo; + tail[15] = hi; + md5cycle(state, tail); + return state; } - tmp = n * 8; - tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); - lo = parseInt(tmp[2], 16); - hi = parseInt(tmp[1], 16) || 0; - tail[14] = lo; - tail[15] = hi; - md5cycle(state, tail); - return state; - } - function rhex(n) { - var s = "", j; - for (j = 0; j < 4; j += 1) { - s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15]; + function rhex(n) { + var s = "", j; + for (j = 0; j < 4; j += 1) { + s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15]; + } + return s; } - return s; - } - function hex(x) { - var i; - for (i = 0; i < x.length; i += 1) { - x[i] = rhex(x[i]); + function hex(x) { + var i; + for (i = 0; i < x.length; i += 1) { + x[i] = rhex(x[i]); + } + return x.join(""); } - return x.join(""); - } - if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ; - if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) { - (function() { - function clamp(val, length) { - val = val | 0 || 0; - if (val < 0) { - return Math.max(val + length, 0); + if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ; + if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) { + (function() { + function clamp(val, length) { + val = val | 0 || 0; + if (val < 0) { + return Math.max(val + length, 0); + } + return Math.min(val, length); } - return Math.min(val, length); + ArrayBuffer.prototype.slice = function(from, to) { + var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray; + if (to !== undefined$1) { + end = clamp(to, length); + } + if (begin > end) { + return new ArrayBuffer(0); + } + num = end - begin; + target = new ArrayBuffer(num); + targetArray = new Uint8Array(target); + sourceArray = new Uint8Array(this, begin, num); + targetArray.set(sourceArray); + return target; + }; + })(); + } + function toUtf8(str) { + if (/[\u0080-\uFFFF]/.test(str)) { + str = unescape(encodeURIComponent(str)); } - ArrayBuffer.prototype.slice = function(from, to) { - var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray; - if (to !== undefined$1) { - end = clamp(to, length); - } - if (begin > end) { - return new ArrayBuffer(0); - } - num = end - begin; - target = new ArrayBuffer(num); - targetArray = new Uint8Array(target); - sourceArray = new Uint8Array(this, begin, num); - targetArray.set(sourceArray); - return target; - }; - })(); - } - function toUtf8(str) { - if (/[\u0080-\uFFFF]/.test(str)) { - str = unescape(encodeURIComponent(str)); + return str; } - return str; - } - function utf8Str2ArrayBuffer(str, returnUInt8Array) { - var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i; - for (i = 0; i < length; i += 1) { - arr[i] = str.charCodeAt(i); + function utf8Str2ArrayBuffer(str, returnUInt8Array) { + var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i; + for (i = 0; i < length; i += 1) { + arr[i] = str.charCodeAt(i); + } + return returnUInt8Array ? arr : buff; } - return returnUInt8Array ? arr : buff; - } - function arrayBuffer2Utf8Str(buff) { - return String.fromCharCode.apply(null, new Uint8Array(buff)); - } - function concatenateArrayBuffers(first, second, returnUInt8Array) { - var result = new Uint8Array(first.byteLength + second.byteLength); - result.set(new Uint8Array(first)); - result.set(new Uint8Array(second), first.byteLength); - return returnUInt8Array ? result : result.buffer; - } - function hexToBinaryString(hex) { - var bytes = [], length = hex.length, x; - for (x = 0; x < length - 1; x += 2) { - bytes.push(parseInt(hex.substr(x, 2), 16)); + function arrayBuffer2Utf8Str(buff) { + return String.fromCharCode.apply(null, new Uint8Array(buff)); } - return String.fromCharCode.apply(String, bytes); - } - function SparkMD5() { - this.reset(); - } - SparkMD5.prototype.append = function(str) { - this.appendBinary(toUtf8(str)); - return this; - }; - SparkMD5.prototype.appendBinary = function(contents) { - this._buff += contents; - this._length += contents.length; - var length = this._buff.length, i; - for (i = 64; i <= length; i += 64) { - md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i))); + function concatenateArrayBuffers(first, second, returnUInt8Array) { + var result = new Uint8Array(first.byteLength + second.byteLength); + result.set(new Uint8Array(first)); + result.set(new Uint8Array(second), first.byteLength); + return returnUInt8Array ? result : result.buffer; } - this._buff = this._buff.substring(i - 64); - return this; - }; - SparkMD5.prototype.end = function(raw) { - var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3); + function hexToBinaryString(hex) { + var bytes = [], length = hex.length, x; + for (x = 0; x < length - 1; x += 2) { + bytes.push(parseInt(hex.substr(x, 2), 16)); + } + return String.fromCharCode.apply(String, bytes); } - this._finish(tail, length); - ret = hex(this._hash); - if (raw) { - ret = hexToBinaryString(ret); + function SparkMD5() { + this.reset(); } - this.reset(); - return ret; - }; - SparkMD5.prototype.reset = function() { - this._buff = ""; - this._length = 0; - this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; - return this; - }; - SparkMD5.prototype.getState = function() { - return { - buff: this._buff, - length: this._length, - hash: this._hash.slice() + SparkMD5.prototype.append = function(str) { + this.appendBinary(toUtf8(str)); + return this; + }; + SparkMD5.prototype.appendBinary = function(contents) { + this._buff += contents; + this._length += contents.length; + var length = this._buff.length, i; + for (i = 64; i <= length; i += 64) { + md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i))); + } + this._buff = this._buff.substring(i - 64); + return this; + }; + SparkMD5.prototype.end = function(raw) { + var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3); + } + this._finish(tail, length); + ret = hex(this._hash); + if (raw) { + ret = hexToBinaryString(ret); + } + this.reset(); + return ret; + }; + SparkMD5.prototype.reset = function() { + this._buff = ""; + this._length = 0; + this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; + return this; }; - }; - SparkMD5.prototype.setState = function(state) { - this._buff = state.buff; - this._length = state.length; - this._hash = state.hash; - return this; - }; - SparkMD5.prototype.destroy = function() { - delete this._hash; - delete this._buff; - delete this._length; - }; - SparkMD5.prototype._finish = function(tail, length) { - var i = length, tmp, lo, hi; - tail[i >> 2] |= 128 << (i % 4 << 3); - if (i > 55) { + SparkMD5.prototype.getState = function() { + return { + buff: this._buff, + length: this._length, + hash: this._hash.slice() + }; + }; + SparkMD5.prototype.setState = function(state) { + this._buff = state.buff; + this._length = state.length; + this._hash = state.hash; + return this; + }; + SparkMD5.prototype.destroy = function() { + delete this._hash; + delete this._buff; + delete this._length; + }; + SparkMD5.prototype._finish = function(tail, length) { + var i = length, tmp, lo, hi; + tail[i >> 2] |= 128 << (i % 4 << 3); + if (i > 55) { + md5cycle(this._hash, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + tmp = this._length * 8; + tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); + lo = parseInt(tmp[2], 16); + hi = parseInt(tmp[1], 16) || 0; + tail[14] = lo; + tail[15] = hi; md5cycle(this._hash, tail); - for (i = 0; i < 16; i += 1) { - tail[i] = 0; + }; + SparkMD5.hash = function(str, raw) { + return SparkMD5.hashBinary(toUtf8(str), raw); + }; + SparkMD5.hashBinary = function(content, raw) { + var hash = md51(content), ret = hex(hash); + return raw ? hexToBinaryString(ret) : ret; + }; + SparkMD5.ArrayBuffer = function() { + this.reset(); + }; + SparkMD5.ArrayBuffer.prototype.append = function(arr) { + var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i; + this._length += arr.byteLength; + for (i = 64; i <= length; i += 64) { + md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i))); } + this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0); + return this; + }; + SparkMD5.ArrayBuffer.prototype.end = function(raw) { + var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret; + for (i = 0; i < length; i += 1) { + tail[i >> 2] |= buff[i] << (i % 4 << 3); + } + this._finish(tail, length); + ret = hex(this._hash); + if (raw) { + ret = hexToBinaryString(ret); + } + this.reset(); + return ret; + }; + SparkMD5.ArrayBuffer.prototype.reset = function() { + this._buff = new Uint8Array(0); + this._length = 0; + this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; + return this; + }; + SparkMD5.ArrayBuffer.prototype.getState = function() { + var state = SparkMD5.prototype.getState.call(this); + state.buff = arrayBuffer2Utf8Str(state.buff); + return state; + }; + SparkMD5.ArrayBuffer.prototype.setState = function(state) { + state.buff = utf8Str2ArrayBuffer(state.buff, true); + return SparkMD5.prototype.setState.call(this, state); + }; + SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; + SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; + SparkMD5.ArrayBuffer.hash = function(arr, raw) { + var hash = md51_array(new Uint8Array(arr)), ret = hex(hash); + return raw ? hexToBinaryString(ret) : ret; + }; + return SparkMD5; + })); + })(sparkMd5); + var SparkMD5 = sparkMd5.exports; + const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; + class FileChecksum { + static create(file, callback) { + const instance = new FileChecksum(file); + instance.create(callback); + } + constructor(file) { + this.file = file; + this.chunkSize = 2097152; + this.chunkCount = Math.ceil(this.file.size / this.chunkSize); + this.chunkIndex = 0; + } + create(callback) { + this.callback = callback; + this.md5Buffer = new SparkMD5.ArrayBuffer; + this.fileReader = new FileReader; + this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event))); + this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event))); + this.readNextChunk(); + } + fileReaderDidLoad(event) { + this.md5Buffer.append(event.target.result); + if (!this.readNextChunk()) { + const binaryDigest = this.md5Buffer.end(true); + const base64digest = btoa(binaryDigest); + this.callback(null, base64digest); } - tmp = this._length * 8; - tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); - lo = parseInt(tmp[2], 16); - hi = parseInt(tmp[1], 16) || 0; - tail[14] = lo; - tail[15] = hi; - md5cycle(this._hash, tail); - }; - SparkMD5.hash = function(str, raw) { - return SparkMD5.hashBinary(toUtf8(str), raw); - }; - SparkMD5.hashBinary = function(content, raw) { - var hash = md51(content), ret = hex(hash); - return raw ? hexToBinaryString(ret) : ret; - }; - SparkMD5.ArrayBuffer = function() { - this.reset(); - }; - SparkMD5.ArrayBuffer.prototype.append = function(arr) { - var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i; - this._length += arr.byteLength; - for (i = 64; i <= length; i += 64) { - md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i))); - } - this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0); - return this; - }; - SparkMD5.ArrayBuffer.prototype.end = function(raw) { - var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= buff[i] << (i % 4 << 3); - } - this._finish(tail, length); - ret = hex(this._hash); - if (raw) { - ret = hexToBinaryString(ret); + } + fileReaderDidError(event) { + this.callback(`Error reading ${this.file.name}`); + } + readNextChunk() { + if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) { + const start = this.chunkIndex * this.chunkSize; + const end = Math.min(start + this.chunkSize, this.file.size); + const bytes = fileSlice.call(this.file, start, end); + this.fileReader.readAsArrayBuffer(bytes); + this.chunkIndex++; + return true; + } else { + return false; } - this.reset(); - return ret; - }; - SparkMD5.ArrayBuffer.prototype.reset = function() { - this._buff = new Uint8Array(0); - this._length = 0; - this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; - return this; - }; - SparkMD5.ArrayBuffer.prototype.getState = function() { - var state = SparkMD5.prototype.getState.call(this); - state.buff = arrayBuffer2Utf8Str(state.buff); - return state; - }; - SparkMD5.ArrayBuffer.prototype.setState = function(state) { - state.buff = utf8Str2ArrayBuffer(state.buff, true); - return SparkMD5.prototype.setState.call(this, state); - }; - SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; - SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; - SparkMD5.ArrayBuffer.hash = function(arr, raw) { - var hash = md51_array(new Uint8Array(arr)), ret = hex(hash); - return raw ? hexToBinaryString(ret) : ret; - }; - return SparkMD5; - })); -})(sparkMd5); - -var SparkMD5 = sparkMd5.exports; - -const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; - -class FileChecksum { - static create(file, callback) { - const instance = new FileChecksum(file); - instance.create(callback); - } - constructor(file) { - this.file = file; - this.chunkSize = 2097152; - this.chunkCount = Math.ceil(this.file.size / this.chunkSize); - this.chunkIndex = 0; - } - create(callback) { - this.callback = callback; - this.md5Buffer = new SparkMD5.ArrayBuffer; - this.fileReader = new FileReader; - this.fileReader.addEventListener("load", (event => this.fileReaderDidLoad(event))); - this.fileReader.addEventListener("error", (event => this.fileReaderDidError(event))); - this.readNextChunk(); - } - fileReaderDidLoad(event) { - this.md5Buffer.append(event.target.result); - if (!this.readNextChunk()) { - const binaryDigest = this.md5Buffer.end(true); - const base64digest = btoa(binaryDigest); - this.callback(null, base64digest); } } - fileReaderDidError(event) { - this.callback(`Error reading ${this.file.name}`); - } - readNextChunk() { - if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) { - const start = this.chunkIndex * this.chunkSize; - const end = Math.min(start + this.chunkSize, this.file.size); - const bytes = fileSlice.call(this.file, start, end); - this.fileReader.readAsArrayBuffer(bytes); - this.chunkIndex++; - return true; - } else { - return false; + function getMetaValue(name) { + const element = findElement(document.head, `meta[name="${name}"]`); + if (element) { + return element.getAttribute("content"); } } -} - -function getMetaValue(name) { - const element = findElement(document.head, `meta[name="${name}"]`); - if (element) { - return element.getAttribute("content"); - } -} - -function findElements(root, selector) { - if (typeof root == "string") { - selector = root; - root = document; - } - const elements = root.querySelectorAll(selector); - return toArray(elements); -} - -function findElement(root, selector) { - if (typeof root == "string") { - selector = root; - root = document; - } - return root.querySelector(selector); -} - -function dispatchEvent(element, type, eventInit = {}) { - const {disabled: disabled} = element; - const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit; - const event = document.createEvent("Event"); - event.initEvent(type, bubbles || true, cancelable || true); - event.detail = detail || {}; - try { - element.disabled = false; - element.dispatchEvent(event); - } finally { - element.disabled = disabled; - } - return event; -} - -function toArray(value) { - if (Array.isArray(value)) { - return value; - } else if (Array.from) { - return Array.from(value); - } else { - return [].slice.call(value); - } -} - -class BlobRecord { - constructor(file, checksum, url, customHeaders = {}) { - this.file = file; - this.attributes = { - filename: file.name, - content_type: file.type || "application/octet-stream", - byte_size: file.size, - checksum: checksum - }; - this.xhr = new XMLHttpRequest; - this.xhr.open("POST", url, true); - this.xhr.responseType = "json"; - this.xhr.setRequestHeader("Content-Type", "application/json"); - this.xhr.setRequestHeader("Accept", "application/json"); - this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - Object.keys(customHeaders).forEach((headerKey => { - this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]); - })); - const csrfToken = getMetaValue("csrf-token"); - if (csrfToken != undefined) { - this.xhr.setRequestHeader("X-CSRF-Token", csrfToken); + function findElements(root, selector) { + if (typeof root == "string") { + selector = root; + root = document; } - this.xhr.addEventListener("load", (event => this.requestDidLoad(event))); - this.xhr.addEventListener("error", (event => this.requestDidError(event))); + const elements = root.querySelectorAll(selector); + return toArray(elements); } - get status() { - return this.xhr.status; - } - get response() { - const {responseType: responseType, response: response} = this.xhr; - if (responseType == "json") { - return response; - } else { - return JSON.parse(response); + function findElement(root, selector) { + if (typeof root == "string") { + selector = root; + root = document; } + return root.querySelector(selector); + } + function dispatchEvent(element, type, eventInit = {}) { + const {disabled: disabled} = element; + const {bubbles: bubbles, cancelable: cancelable, detail: detail} = eventInit; + const event = document.createEvent("Event"); + event.initEvent(type, bubbles || true, cancelable || true); + event.detail = detail || {}; + try { + element.disabled = false; + element.dispatchEvent(event); + } finally { + element.disabled = disabled; + } + return event; } - create(callback) { - this.callback = callback; - this.xhr.send(JSON.stringify({ - blob: this.attributes - })); - } - requestDidLoad(event) { - if (this.status >= 200 && this.status < 300) { - const {response: response} = this; - const {direct_upload: direct_upload} = response; - delete response.direct_upload; - this.attributes = response; - this.directUploadData = direct_upload; - this.callback(null, this.toJSON()); + function toArray(value) { + if (Array.isArray(value)) { + return value; + } else if (Array.from) { + return Array.from(value); } else { - this.requestDidError(event); + return [].slice.call(value); } } - requestDidError(event) { - this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`); - } - toJSON() { - const result = {}; - for (const key in this.attributes) { - result[key] = this.attributes[key]; + class BlobRecord { + constructor(file, checksum, url, customHeaders = {}) { + this.file = file; + this.attributes = { + filename: file.name, + content_type: file.type || "application/octet-stream", + byte_size: file.size, + checksum: checksum + }; + this.xhr = new XMLHttpRequest; + this.xhr.open("POST", url, true); + this.xhr.responseType = "json"; + this.xhr.setRequestHeader("Content-Type", "application/json"); + this.xhr.setRequestHeader("Accept", "application/json"); + this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + Object.keys(customHeaders).forEach((headerKey => { + this.xhr.setRequestHeader(headerKey, customHeaders[headerKey]); + })); + const csrfToken = getMetaValue("csrf-token"); + if (csrfToken != undefined) { + this.xhr.setRequestHeader("X-CSRF-Token", csrfToken); + } + this.xhr.addEventListener("load", (event => this.requestDidLoad(event))); + this.xhr.addEventListener("error", (event => this.requestDidError(event))); + } + get status() { + return this.xhr.status; + } + get response() { + const {responseType: responseType, response: response} = this.xhr; + if (responseType == "json") { + return response; + } else { + return JSON.parse(response); + } + } + create(callback) { + this.callback = callback; + this.xhr.send(JSON.stringify({ + blob: this.attributes + })); + } + requestDidLoad(event) { + if (this.status >= 200 && this.status < 300) { + const {response: response} = this; + const {direct_upload: direct_upload} = response; + delete response.direct_upload; + this.attributes = response; + this.directUploadData = direct_upload; + this.callback(null, this.toJSON()); + } else { + this.requestDidError(event); + } + } + requestDidError(event) { + this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.status}`); + } + toJSON() { + const result = {}; + for (const key in this.attributes) { + result[key] = this.attributes[key]; + } + return result; } - return result; - } -} - -class BlobUpload { - constructor(blob) { - this.blob = blob; - this.file = blob.file; - const {url: url, headers: headers} = blob.directUploadData; - this.xhr = new XMLHttpRequest; - this.xhr.open("PUT", url, true); - this.xhr.responseType = "text"; - for (const key in headers) { - this.xhr.setRequestHeader(key, headers[key]); - } - this.xhr.addEventListener("load", (event => this.requestDidLoad(event))); - this.xhr.addEventListener("error", (event => this.requestDidError(event))); - } - create(callback) { - this.callback = callback; - this.xhr.send(this.file.slice()); } - requestDidLoad(event) { - const {status: status, response: response} = this.xhr; - if (status >= 200 && status < 300) { - this.callback(null, response); - } else { - this.requestDidError(event); + class BlobUpload { + constructor(blob) { + this.blob = blob; + this.file = blob.file; + const {url: url, headers: headers} = blob.directUploadData; + this.xhr = new XMLHttpRequest; + this.xhr.open("PUT", url, true); + this.xhr.responseType = "text"; + for (const key in headers) { + this.xhr.setRequestHeader(key, headers[key]); + } + this.xhr.addEventListener("load", (event => this.requestDidLoad(event))); + this.xhr.addEventListener("error", (event => this.requestDidError(event))); + } + create(callback) { + this.callback = callback; + this.xhr.send(this.file.slice()); + } + requestDidLoad(event) { + const {status: status, response: response} = this.xhr; + if (status >= 200 && status < 300) { + this.callback(null, response); + } else { + this.requestDidError(event); + } + } + requestDidError(event) { + this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`); } } - requestDidError(event) { - this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`); + let id = 0; + class DirectUpload { + constructor(file, url, delegate, customHeaders = {}) { + this.id = ++id; + this.file = file; + this.url = url; + this.delegate = delegate; + this.customHeaders = customHeaders; + } + create(callback) { + FileChecksum.create(this.file, ((error, checksum) => { + if (error) { + callback(error); + return; + } + const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders); + notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr); + blob.create((error => { + if (error) { + callback(error); + } else { + const upload = new BlobUpload(blob); + notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr); + upload.create((error => { + if (error) { + callback(error); + } else { + callback(null, blob.toJSON()); + } + })); + } + })); + })); + } } -} - -let id = 0; - -class DirectUpload { - constructor(file, url, delegate, customHeaders = {}) { - this.id = ++id; - this.file = file; - this.url = url; - this.delegate = delegate; - this.customHeaders = customHeaders; + function notify(object, methodName, ...messages) { + if (object && typeof object[methodName] == "function") { + return object[methodName](...messages); + } } - create(callback) { - FileChecksum.create(this.file, ((error, checksum) => { - if (error) { - callback(error); - return; - } - const blob = new BlobRecord(this.file, checksum, this.url, this.customHeaders); - notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr); - blob.create((error => { + class DirectUploadController { + constructor(input, file) { + this.input = input; + this.file = file; + this.directUpload = new DirectUpload(this.file, this.url, this); + this.dispatch("initialize"); + } + start(callback) { + const hiddenInput = document.createElement("input"); + hiddenInput.type = "hidden"; + hiddenInput.name = this.input.name; + this.input.insertAdjacentElement("beforebegin", hiddenInput); + this.dispatch("start"); + this.directUpload.create(((error, attributes) => { if (error) { - callback(error); + hiddenInput.parentNode.removeChild(hiddenInput); + this.dispatchError(error); } else { - const upload = new BlobUpload(blob); - notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr); - upload.create((error => { + hiddenInput.value = attributes.signed_id; + } + this.dispatch("end"); + callback(error); + })); + } + uploadRequestDidProgress(event) { + const progress = event.loaded / event.total * 100; + if (progress) { + this.dispatch("progress", { + progress: progress + }); + } + } + get url() { + return this.input.getAttribute("data-direct-upload-url"); + } + dispatch(name, detail = {}) { + detail.file = this.file; + detail.id = this.directUpload.id; + return dispatchEvent(this.input, `direct-upload:${name}`, { + detail: detail + }); + } + dispatchError(error) { + const event = this.dispatch("error", { + error: error + }); + if (!event.defaultPrevented) { + alert(error); + } + } + directUploadWillCreateBlobWithXHR(xhr) { + this.dispatch("before-blob-request", { + xhr: xhr + }); + } + directUploadWillStoreFileWithXHR(xhr) { + this.dispatch("before-storage-request", { + xhr: xhr + }); + xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event))); + } + } + const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])"; + class DirectUploadsController { + constructor(form) { + this.form = form; + this.inputs = findElements(form, inputSelector).filter((input => input.files.length)); + } + start(callback) { + const controllers = this.createDirectUploadControllers(); + const startNextController = () => { + const controller = controllers.shift(); + if (controller) { + controller.start((error => { if (error) { callback(error); + this.dispatch("end"); } else { - callback(null, blob.toJSON()); + startNextController(); } })); + } else { + callback(); + this.dispatch("end"); } + }; + this.dispatch("start"); + startNextController(); + } + createDirectUploadControllers() { + const controllers = []; + this.inputs.forEach((input => { + toArray(input.files).forEach((file => { + const controller = new DirectUploadController(input, file); + controllers.push(controller); + })); })); - })); - } -} - -function notify(object, methodName, ...messages) { - if (object && typeof object[methodName] == "function") { - return object[methodName](...messages); - } -} - -class DirectUploadController { - constructor(input, file) { - this.input = input; - this.file = file; - this.directUpload = new DirectUpload(this.file, this.url, this); - this.dispatch("initialize"); - } - start(callback) { - const hiddenInput = document.createElement("input"); - hiddenInput.type = "hidden"; - hiddenInput.name = this.input.name; - this.input.insertAdjacentElement("beforebegin", hiddenInput); - this.dispatch("start"); - this.directUpload.create(((error, attributes) => { - if (error) { - hiddenInput.parentNode.removeChild(hiddenInput); - this.dispatchError(error); - } else { - hiddenInput.value = attributes.signed_id; - } - this.dispatch("end"); - callback(error); - })); - } - uploadRequestDidProgress(event) { - const progress = event.loaded / event.total * 100; - if (progress) { - this.dispatch("progress", { - progress: progress + return controllers; + } + dispatch(name, detail = {}) { + return dispatchEvent(this.form, `direct-uploads:${name}`, { + detail: detail }); } } - get url() { - return this.input.getAttribute("data-direct-upload-url"); - } - dispatch(name, detail = {}) { - detail.file = this.file; - detail.id = this.directUpload.id; - return dispatchEvent(this.input, `direct-upload:${name}`, { - detail: detail - }); - } - dispatchError(error) { - const event = this.dispatch("error", { - error: error - }); - if (!event.defaultPrevented) { - alert(error); + const processingAttribute = "data-direct-uploads-processing"; + const submitButtonsByForm = new WeakMap; + let started = false; + function start() { + if (!started) { + started = true; + document.addEventListener("click", didClick, true); + document.addEventListener("submit", didSubmitForm, true); + document.addEventListener("ajax:before", didSubmitRemoteElement); } } - directUploadWillCreateBlobWithXHR(xhr) { - this.dispatch("before-blob-request", { - xhr: xhr - }); - } - directUploadWillStoreFileWithXHR(xhr) { - this.dispatch("before-storage-request", { - xhr: xhr - }); - xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event))); + function didClick(event) { + const {target: target} = event; + if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) { + submitButtonsByForm.set(target.form, target); + } } -} - -const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])"; - -class DirectUploadsController { - constructor(form) { - this.form = form; - this.inputs = findElements(form, inputSelector).filter((input => input.files.length)); + function didSubmitForm(event) { + handleFormSubmissionEvent(event); } - start(callback) { - const controllers = this.createDirectUploadControllers(); - const startNextController = () => { - const controller = controllers.shift(); - if (controller) { - controller.start((error => { - if (error) { - callback(error); - this.dispatch("end"); - } else { - startNextController(); - } - })); - } else { - callback(); - this.dispatch("end"); - } - }; - this.dispatch("start"); - startNextController(); + function didSubmitRemoteElement(event) { + if (event.target.tagName == "FORM") { + handleFormSubmissionEvent(event); + } } - createDirectUploadControllers() { - const controllers = []; - this.inputs.forEach((input => { - toArray(input.files).forEach((file => { - const controller = new DirectUploadController(input, file); - controllers.push(controller); + function handleFormSubmissionEvent(event) { + const form = event.target; + if (form.hasAttribute(processingAttribute)) { + event.preventDefault(); + return; + } + const controller = new DirectUploadsController(form); + const {inputs: inputs} = controller; + if (inputs.length) { + event.preventDefault(); + form.setAttribute(processingAttribute, ""); + inputs.forEach(disable); + controller.start((error => { + form.removeAttribute(processingAttribute); + if (error) { + inputs.forEach(enable); + } else { + submitForm(form); + } })); - })); - return controllers; - } - dispatch(name, detail = {}) { - return dispatchEvent(this.form, `direct-uploads:${name}`, { - detail: detail - }); + } } -} - -const processingAttribute = "data-direct-uploads-processing"; - -const submitButtonsByForm = new WeakMap; - -let started = false; - -function start() { - if (!started) { - started = true; - document.addEventListener("click", didClick, true); - document.addEventListener("submit", didSubmitForm, true); - document.addEventListener("ajax:before", didSubmitRemoteElement); + function submitForm(form) { + let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]"); + if (button) { + const {disabled: disabled} = button; + button.disabled = false; + button.focus(); + button.click(); + button.disabled = disabled; + } else { + button = document.createElement("input"); + button.type = "submit"; + button.style.display = "none"; + form.appendChild(button); + button.click(); + form.removeChild(button); + } + submitButtonsByForm.delete(form); } -} - -function didClick(event) { - const {target: target} = event; - if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) { - submitButtonsByForm.set(target.form, target); + function disable(input) { + input.disabled = true; } -} - -function didSubmitForm(event) { - handleFormSubmissionEvent(event); -} - -function didSubmitRemoteElement(event) { - if (event.target.tagName == "FORM") { - handleFormSubmissionEvent(event); + function enable(input) { + input.disabled = false; } -} - -function handleFormSubmissionEvent(event) { - const form = event.target; - if (form.hasAttribute(processingAttribute)) { - event.preventDefault(); - return; + function autostart() { + if (window.ActiveStorage) { + start(); + } } - const controller = new DirectUploadsController(form); - const {inputs: inputs} = controller; - if (inputs.length) { - event.preventDefault(); - form.setAttribute(processingAttribute, ""); - inputs.forEach(disable); - controller.start((error => { - form.removeAttribute(processingAttribute); + setTimeout(autostart, 1); + class AttachmentUpload { + constructor(attachment, element) { + this.attachment = attachment; + this.element = element; + this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this); + } + start() { + this.directUpload.create(this.directUploadDidComplete.bind(this)); + } + directUploadWillStoreFileWithXHR(xhr) { + xhr.upload.addEventListener("progress", (event => { + const progress = event.loaded / event.total * 100; + this.attachment.setUploadProgress(progress); + })); + } + directUploadDidComplete(error, attributes) { if (error) { - inputs.forEach(enable); - } else { - submitForm(form); + throw new Error(`Direct upload failed: ${error}`); } - })); - } -} - -function submitForm(form) { - let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]"); - if (button) { - const {disabled: disabled} = button; - button.disabled = false; - button.focus(); - button.click(); - button.disabled = disabled; - } else { - button = document.createElement("input"); - button.type = "submit"; - button.style.display = "none"; - form.appendChild(button); - button.click(); - form.removeChild(button); - } - submitButtonsByForm.delete(form); -} - -function disable(input) { - input.disabled = true; -} - -function enable(input) { - input.disabled = false; -} - -function autostart() { - if (window.ActiveStorage) { - start(); - } -} - -setTimeout(autostart, 1); - -class AttachmentUpload { - constructor(attachment, element) { - this.attachment = attachment; - this.element = element; - this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this); - } - - start() { - this.directUpload.create(this.directUploadDidComplete.bind(this)); - } - - directUploadWillStoreFileWithXHR(xhr) { - xhr.upload.addEventListener("progress", event => { - const progress = event.loaded / event.total * 100; - this.attachment.setUploadProgress(progress); - }); - } - - directUploadDidComplete(error, attributes) { - if (error) { - throw new Error(`Direct upload failed: ${error}`) - } - - this.attachment.setAttributes({ - sgid: attributes.attachable_sgid, - url: this.createBlobUrl(attributes.signed_id, attributes.filename) - }); - } - - createBlobUrl(signedId, filename) { - return this.blobUrlTemplate - .replace(":signed_id", signedId) - .replace(":filename", encodeURIComponent(filename)) - } - - get directUploadUrl() { - return this.element.dataset.directUploadUrl - } - - get blobUrlTemplate() { - return this.element.dataset.blobUrlTemplate - } -} - -addEventListener("trix-attachment-add", event => { - const { attachment, target } = event; - - if (attachment.file) { - const upload = new AttachmentUpload(attachment, target); - upload.start(); + this.attachment.setAttributes({ + sgid: attributes.attachable_sgid, + url: this.createBlobUrl(attributes.signed_id, attributes.filename) + }); + } + createBlobUrl(signedId, filename) { + return this.blobUrlTemplate.replace(":signed_id", signedId).replace(":filename", encodeURIComponent(filename)); + } + get directUploadUrl() { + return this.element.dataset.directUploadUrl; + } + get blobUrlTemplate() { + return this.element.dataset.blobUrlTemplate; + } } -}); + addEventListener("trix-attachment-add", (event => { + const {attachment: attachment, target: target} = event; + if (attachment.file) { + const upload = new AttachmentUpload(attachment, target); + upload.start(); + } + })); +})); diff --git a/actiontext/lib/action_text/engine.rb b/actiontext/lib/action_text/engine.rb index 9273352b62e9b..0282dc7a9bf0b 100644 --- a/actiontext/lib/action_text/engine.rb +++ b/actiontext/lib/action_text/engine.rb @@ -32,7 +32,7 @@ class Engine < Rails::Engine initializer "action_text.asset" do if Rails.application.config.respond_to?(:assets) - Rails.application.config.assets.precompile += %w( actiontext.js trix.js trix.css ) + Rails.application.config.assets.precompile += %w( actiontext.js actiontext.esm.js trix.js trix.css ) end end diff --git a/actiontext/lib/generators/action_text/install/install_generator.rb b/actiontext/lib/generators/action_text/install/install_generator.rb index e52ce26374708..b443ebf70f111 100644 --- a/actiontext/lib/generators/action_text/install/install_generator.rb +++ b/actiontext/lib/generators/action_text/install/install_generator.rb @@ -29,7 +29,7 @@ def append_javascript_dependencies end if (importmap_path = destination.join("config/importmap.rb")).exist? - append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.js"\n) + append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.esm.js"\n) end end diff --git a/actiontext/package.json b/actiontext/package.json index 4ee836615dc62..49752ab02cd1f 100644 --- a/actiontext/package.json +++ b/actiontext/package.json @@ -2,8 +2,8 @@ "name": "@rails/actiontext", "version": "7.2.0-alpha", "description": "Edit and display rich text in Rails applications", + "module": "app/assets/javascripts/actiontext.esm.js", "main": "app/assets/javascripts/actiontext.js", - "type": "module", "files": [ "app/assets/javascripts/*.js" ], diff --git a/actiontext/rollup.config.js b/actiontext/rollup.config.js index 28a2e4c740aea..00c19ad52553a 100644 --- a/actiontext/rollup.config.js +++ b/actiontext/rollup.config.js @@ -1,14 +1,41 @@ import resolve from "@rollup/plugin-node-resolve" import commonjs from "@rollup/plugin-commonjs" +import { terser } from "rollup-plugin-terser" -export default { - input: "app/javascript/actiontext/index.js", - output: { - file: "app/assets/javascripts/actiontext.js", - format: "es" - }, - plugins: [ - resolve(), - commonjs() - ] +const terserOptions = { + mangle: false, + compress: false, + format: { + beautify: true, + indent_level: 2 + } } + +export default [ + { + input: "app/javascript/actiontext/index.js", + output: { + file: "app/assets/javascripts/actiontext.js", + format: "umd", + name: "ActionText" + }, + plugins: [ + resolve(), + commonjs(), + terser(terserOptions) + ] + }, + + { + input: "app/javascript/actiontext/index.js", + output: { + file: "app/assets/javascripts/actiontext.esm.js", + format: "es" + }, + plugins: [ + resolve(), + commonjs(), + terser(terserOptions) + ] + } +] diff --git a/actiontext/test/dummy/config/importmap.rb b/actiontext/test/dummy/config/importmap.rb index fff93ec53550f..c2071c6baf87a 100644 --- a/actiontext/test/dummy/config/importmap.rb +++ b/actiontext/test/dummy/config/importmap.rb @@ -3,4 +3,4 @@ pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "trix" -pin "@rails/actiontext", to: "actiontext.js" +pin "@rails/actiontext", to: "actiontext.esm.js" diff --git a/actiontext/test/javascript_package_test.rb b/actiontext/test/javascript_package_test.rb index 26c64eafb961e..e4531e29b45d5 100644 --- a/actiontext/test/javascript_package_test.rb +++ b/actiontext/test/javascript_package_test.rb @@ -6,6 +6,7 @@ class JavascriptPackageTest < ActiveSupport::TestCase def test_compiled_code_is_in_sync_with_source_code compiled_files = %w[ app/assets/javascripts/actiontext.js + app/assets/javascripts/actiontext.esm.js ].map do |file| Pathname(file).expand_path("#{__dir__}/..") end