Move Active Storage JS to ESM and drop Babel transpiling (#42895)

* Move to ESM and drop Babel transpiling

* Make the activestorage JS directly available through the asset pipeline as a precompiled asset

* Use example with direct attachment

* Explain that direct reference is possible

* Active Storage JS is a module

* Retain umd asset for backwards compatibility, add ESM file in addition

* Explain how to use activestorage.esm with importmap

* Use untranspiled activestorage inclusion

* Don't repeat terser options
This commit is contained in:
David Heinemeier Hansson 2021-07-28 15:17:13 -04:00 committed by GitHub
parent 9d2a214e05
commit 6c96e1cd7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1427 additions and 789 deletions

@ -5,18 +5,16 @@ var activestorage = {exports: {}};
(function (module, exports) {
(function(global, factory) {
factory(exports) ;
})(commonjsGlobal, function(exports) {
function createCommonjsModule(fn, module) {
return module = {
exports: {}
}, fn(module, module.exports), module.exports;
}
var sparkMd5 = createCommonjsModule(function(module, exports) {
})(commonjsGlobal, (function(exports) {
var sparkMd5 = {
exports: {}
};
(function(module, exports) {
(function(factory) {
{
module.exports = factory();
}
})(function(undefined$1) {
})((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];
@ -331,7 +329,7 @@ var activestorage = {exports: {}};
return {
buff: this._buff,
length: this._length,
hash: this._hash
hash: this._hash.slice()
};
};
SparkMD5.prototype.setState = function(state) {
@ -416,94 +414,55 @@ var activestorage = {exports: {}};
return raw ? hexToBinaryString(ret) : ret;
};
return SparkMD5;
});
});
var classCallCheck = function(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}));
})(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);
}
};
var createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
var FileChecksum = function() {
createClass(FileChecksum, null, [ {
key: "create",
value: function create(file, callback) {
var instance = new FileChecksum(file);
instance.create(callback);
}
} ]);
function FileChecksum(file) {
classCallCheck(this, FileChecksum);
constructor(file) {
this.file = file;
this.chunkSize = 2097152;
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
this.chunkIndex = 0;
}
createClass(FileChecksum, [ {
key: "create",
value: function create(callback) {
var _this = this;
this.callback = callback;
this.md5Buffer = new sparkMd5.ArrayBuffer();
this.fileReader = new FileReader();
this.fileReader.addEventListener("load", function(event) {
return _this.fileReaderDidLoad(event);
});
this.fileReader.addEventListener("error", function(event) {
return _this.fileReaderDidError(event);
});
this.readNextChunk();
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);
}
}, {
key: "fileReaderDidLoad",
value: function fileReaderDidLoad(event) {
this.md5Buffer.append(event.target.result);
if (!this.readNextChunk()) {
var binaryDigest = this.md5Buffer.end(true);
var 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;
}
}, {
key: "fileReaderDidError",
value: function fileReaderDidError(event) {
this.callback("Error reading " + this.file.name);
}
}, {
key: "readNextChunk",
value: function readNextChunk() {
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
var start = this.chunkIndex * this.chunkSize;
var end = Math.min(start + this.chunkSize, this.file.size);
var bytes = fileSlice.call(this.file, start, end);
this.fileReader.readAsArrayBuffer(bytes);
this.chunkIndex++;
return true;
} else {
return false;
}
}
} ]);
return FileChecksum;
}();
}
}
function getMetaValue(name) {
var element = findElement(document.head, 'meta[name="' + name + '"]');
const element = findElement(document.head, `meta[name="${name}"]`);
if (element) {
return element.getAttribute("content");
}
@ -513,8 +472,8 @@ var activestorage = {exports: {}};
selector = root;
root = document;
}
var elements = root.querySelectorAll(selector);
return toArray$1(elements);
const elements = root.querySelectorAll(selector);
return toArray(elements);
}
function findElement(root, selector) {
if (typeof root == "string") {
@ -523,11 +482,10 @@ var activestorage = {exports: {}};
}
return root.querySelector(selector);
}
function dispatchEvent(element, type) {
var eventInit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var disabled = element.disabled;
var bubbles = eventInit.bubbles, cancelable = eventInit.cancelable, detail = eventInit.detail;
var event = document.createEvent("Event");
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 {
@ -538,7 +496,7 @@ var activestorage = {exports: {}};
}
return event;
}
function toArray$1(value) {
function toArray(value) {
if (Array.isArray(value)) {
return value;
} else if (Array.from) {
@ -547,10 +505,8 @@ var activestorage = {exports: {}};
return [].slice.call(value);
}
}
var BlobRecord = function() {
function BlobRecord(file, checksum, url) {
var _this = this;
classCallCheck(this, BlobRecord);
class BlobRecord {
constructor(file, checksum, url) {
this.file = file;
this.attributes = {
filename: file.name,
@ -558,312 +514,236 @@ var activestorage = {exports: {}};
byte_size: file.size,
checksum: checksum
};
this.xhr = new XMLHttpRequest();
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");
var csrfToken = getMetaValue("csrf-token");
const csrfToken = getMetaValue("csrf-token");
if (csrfToken != undefined) {
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
}
this.xhr.addEventListener("load", function(event) {
return _this.requestDidLoad(event);
});
this.xhr.addEventListener("error", function(event) {
return _this.requestDidError(event);
});
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
}
createClass(BlobRecord, [ {
key: "create",
value: function create(callback) {
this.callback = callback;
this.xhr.send(JSON.stringify({
blob: this.attributes
}));
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);
}
}, {
key: "requestDidLoad",
value: function requestDidLoad(event) {
if (this.status >= 200 && this.status < 300) {
var response = this.response;
var direct_upload = response.direct_upload;
delete response.direct_upload;
this.attributes = response;
this.directUploadData = direct_upload;
this.callback(null, this.toJSON());
} else {
this.requestDidError(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());
} else {
this.requestDidError(event);
}
}, {
key: "requestDidError",
value: function requestDidError(event) {
this.callback('Error creating Blob for "' + this.file.name + '". Status: ' + this.status);
}
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];
}
}, {
key: "toJSON",
value: function toJSON() {
var result = {};
for (var key in this.attributes) {
result[key] = this.attributes[key];
}
return result;
}
}, {
key: "status",
get: function get$$1() {
return this.xhr.status;
}
}, {
key: "response",
get: function get$$1() {
var _xhr = this.xhr, responseType = _xhr.responseType, response = _xhr.response;
if (responseType == "json") {
return response;
} else {
return JSON.parse(response);
}
}
} ]);
return BlobRecord;
}();
var BlobUpload = function() {
function BlobUpload(blob) {
var _this = this;
classCallCheck(this, BlobUpload);
return result;
}
}
class BlobUpload {
constructor(blob) {
this.blob = blob;
this.file = blob.file;
var _blob$directUploadDat = blob.directUploadData, url = _blob$directUploadDat.url, headers = _blob$directUploadDat.headers;
this.xhr = new XMLHttpRequest();
const {url: url, headers: headers} = blob.directUploadData;
this.xhr = new XMLHttpRequest;
this.xhr.open("PUT", url, true);
this.xhr.responseType = "text";
for (var key in headers) {
for (const key in headers) {
this.xhr.setRequestHeader(key, headers[key]);
}
this.xhr.addEventListener("load", function(event) {
return _this.requestDidLoad(event);
});
this.xhr.addEventListener("error", function(event) {
return _this.requestDidError(event);
});
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
}
createClass(BlobUpload, [ {
key: "create",
value: function create(callback) {
this.callback = callback;
this.xhr.send(this.file.slice());
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);
}
}, {
key: "requestDidLoad",
value: function requestDidLoad(event) {
var _xhr = this.xhr, status = _xhr.status, response = _xhr.response;
if (status >= 200 && status < 300) {
this.callback(null, response);
} else {
this.requestDidError(event);
}
}
}, {
key: "requestDidError",
value: function requestDidError(event) {
this.callback('Error storing "' + this.file.name + '". Status: ' + this.xhr.status);
}
} ]);
return BlobUpload;
}();
var id = 0;
var DirectUpload = function() {
function DirectUpload(file, url, delegate) {
classCallCheck(this, DirectUpload);
}
requestDidError(event) {
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
}
}
let id = 0;
class DirectUpload {
constructor(file, url, delegate) {
this.id = ++id;
this.file = file;
this.url = url;
this.delegate = delegate;
}
createClass(DirectUpload, [ {
key: "create",
value: function create(callback) {
var _this = this;
FileChecksum.create(this.file, function(error, checksum) {
create(callback) {
FileChecksum.create(this.file, ((error, checksum) => {
if (error) {
callback(error);
return;
}
const blob = new BlobRecord(this.file, checksum, this.url);
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
blob.create((error => {
if (error) {
callback(error);
return;
} else {
const upload = new BlobUpload(blob);
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
upload.create((error => {
if (error) {
callback(error);
} else {
callback(null, blob.toJSON());
}
}));
}
var blob = new BlobRecord(_this.file, checksum, _this.url);
notify(_this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
blob.create(function(error) {
if (error) {
callback(error);
} else {
var upload = new BlobUpload(blob);
notify(_this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
upload.create(function(error) {
if (error) {
callback(error);
} else {
callback(null, blob.toJSON());
}
});
}
});
});
}
} ]);
return DirectUpload;
}();
function notify(object, methodName) {
if (object && typeof object[methodName] == "function") {
for (var _len = arguments.length, messages = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
messages[_key - 2] = arguments[_key];
}
return object[methodName].apply(object, messages);
}));
}));
}
}
var DirectUploadController = function() {
function DirectUploadController(input, file) {
classCallCheck(this, DirectUploadController);
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");
}
createClass(DirectUploadController, [ {
key: "start",
value: function start(callback) {
var _this = this;
var hiddenInput = document.createElement("input");
hiddenInput.type = "hidden";
hiddenInput.name = this.input.name;
this.input.insertAdjacentElement("beforebegin", hiddenInput);
this.dispatch("start");
this.directUpload.create(function(error, attributes) {
if (error) {
hiddenInput.parentNode.removeChild(hiddenInput);
_this.dispatchError(error);
} else {
hiddenInput.value = attributes.signed_id;
}
_this.dispatch("end");
callback(error);
});
}
}, {
key: "uploadRequestDidProgress",
value: function uploadRequestDidProgress(event) {
var progress = event.loaded / event.total * 100;
if (progress) {
this.dispatch("progress", {
progress: progress
});
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;
}
}
}, {
key: "dispatch",
value: function dispatch(name) {
var detail = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
detail.file = this.file;
detail.id = this.directUpload.id;
return dispatchEvent(this.input, "direct-upload:" + name, {
detail: detail
this.dispatch("end");
callback(error);
}));
}
uploadRequestDidProgress(event) {
const progress = event.loaded / event.total * 100;
if (progress) {
this.dispatch("progress", {
progress: progress
});
}
}, {
key: "dispatchError",
value: function dispatchError(error) {
var event = this.dispatch("error", {
error: error
});
if (!event.defaultPrevented) {
alert(error);
}
}
}, {
key: "directUploadWillCreateBlobWithXHR",
value: function directUploadWillCreateBlobWithXHR(xhr) {
this.dispatch("before-blob-request", {
xhr: xhr
});
}
}, {
key: "directUploadWillStoreFileWithXHR",
value: function directUploadWillStoreFileWithXHR(xhr) {
var _this2 = this;
this.dispatch("before-storage-request", {
xhr: xhr
});
xhr.upload.addEventListener("progress", function(event) {
return _this2.uploadRequestDidProgress(event);
});
}
}, {
key: "url",
get: function get$$1() {
return this.input.getAttribute("data-direct-upload-url");
}
} ]);
return DirectUploadController;
}();
var inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
var DirectUploadsController = function() {
function DirectUploadsController(form) {
classCallCheck(this, DirectUploadsController);
this.form = form;
this.inputs = findElements(form, inputSelector).filter(function(input) {
return input.files.length;
}
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
});
}
createClass(DirectUploadsController, [ {
key: "start",
value: function start(callback) {
var _this = this;
var controllers = this.createDirectUploadControllers();
var startNextController = function startNextController() {
var controller = controllers.shift();
if (controller) {
controller.start(function(error) {
if (error) {
callback(error);
_this.dispatch("end");
} else {
startNextController();
}
});
} else {
callback();
_this.dispatch("end");
}
};
this.dispatch("start");
startNextController();
dispatchError(error) {
const event = this.dispatch("error", {
error: error
});
if (!event.defaultPrevented) {
alert(error);
}
}, {
key: "createDirectUploadControllers",
value: function createDirectUploadControllers() {
var controllers = [];
this.inputs.forEach(function(input) {
toArray$1(input.files).forEach(function(file) {
var controller = new DirectUploadController(input, file);
controllers.push(controller);
});
});
return controllers;
}
}, {
key: "dispatch",
value: function dispatch(name) {
var detail = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return dispatchEvent(this.form, "direct-uploads:" + name, {
detail: detail
});
}
} ]);
return DirectUploadsController;
}();
var processingAttribute = "data-direct-uploads-processing";
var submitButtonsByForm = new WeakMap();
var started = false;
}
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;
@ -873,7 +753,7 @@ var activestorage = {exports: {}};
}
}
function didClick(event) {
var target = event.target;
const {target: target} = event;
if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) {
submitButtonsByForm.set(target.form, target);
}
@ -887,31 +767,31 @@ var activestorage = {exports: {}};
}
}
function handleFormSubmissionEvent(event) {
var form = event.target;
const form = event.target;
if (form.hasAttribute(processingAttribute)) {
event.preventDefault();
return;
}
var controller = new DirectUploadsController(form);
var inputs = controller.inputs;
const controller = new DirectUploadsController(form);
const {inputs: inputs} = controller;
if (inputs.length) {
event.preventDefault();
form.setAttribute(processingAttribute, "");
inputs.forEach(disable);
controller.start(function(error) {
controller.start((error => {
form.removeAttribute(processingAttribute);
if (error) {
inputs.forEach(enable);
} else {
submitForm(form);
}
});
}));
}
}
function submitForm(form) {
var button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
if (button) {
var _button = button, disabled = _button.disabled;
const {disabled: disabled} = button;
button.disabled = false;
button.focus();
button.click();
@ -938,12 +818,12 @@ var activestorage = {exports: {}};
}
}
setTimeout(autostart, 1);
exports.start = start;
exports.DirectUpload = DirectUpload;
exports.start = start;
Object.defineProperty(exports, "__esModule", {
value: true
});
});
}));
}(activestorage, activestorage.exports));
class AttachmentUpload {

@ -88,8 +88,7 @@ class MessagesController < ApplicationController
end
def create
message = Message.create! params.require(:message).permit(:title, :content)
message.images.attach(params[:message][:images])
message = Message.create! params.require(:message).permit(:title, :content, images: [])
redirect_to message
end
@ -143,8 +142,26 @@ Active Storage, with its included JavaScript library, supports uploading directl
### Direct upload installation
1. Include `activestorage.js` in your application's JavaScript bundle.
1. Include the Active Storage JavaScript in your application's JavaScript bundle or reference it directly.
Requiring directly without bundling through the asset pipeline in the application html with autostart:
```html
<%= javascript_include_tag "activestorage" %>
```
Requiring via importmap (as used by Stimulus) without bundling through the asset pipeline in the application html without autostart as ESM:
```js
{
"imports": {
"@rails/activestorage": "<%= asset_path "activestorage.esm" %>"
}
}
```
```html
<script type="module-shim">
import * as ActiveStorage from "@rails/activestorage"
ActiveStorage.start()
</script>
```
Using the asset pipeline:
```js
//= require activestorage

@ -0,0 +1,844 @@
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) {
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");
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) {
this.id = ++id;
this.file = file;
this.url = url;
this.delegate = delegate;
}
create(callback) {
FileChecksum.create(this.file, ((error, checksum) => {
if (error) {
callback(error);
return;
}
const blob = new BlobRecord(this.file, checksum, this.url);
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);
export { DirectUpload, start };

@ -1,18 +1,17 @@
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.ActiveStorage = {});
})(this, function(exports) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
factory(global.ActiveStorage = {}));
})(this, (function(exports) {
"use strict";
function createCommonjsModule(fn, module) {
return module = {
exports: {}
}, fn(module, module.exports), module.exports;
}
var sparkMd5 = createCommonjsModule(function(module, exports) {
var sparkMd5 = {
exports: {}
};
(function(module, exports) {
(function(factory) {
{
module.exports = factory();
}
})(function(undefined) {
})((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];
@ -243,7 +242,7 @@
}
ArrayBuffer.prototype.slice = function(from, to) {
var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray;
if (to !== undefined) {
if (to !== undefined$1) {
end = clamp(to, length);
}
if (begin > end) {
@ -327,7 +326,7 @@
return {
buff: this._buff,
length: this._length,
hash: this._hash
hash: this._hash.slice()
};
};
SparkMD5.prototype.setState = function(state) {
@ -412,94 +411,55 @@
return raw ? hexToBinaryString(ret) : ret;
};
return SparkMD5;
});
});
var classCallCheck = function(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}));
})(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);
}
};
var createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
var FileChecksum = function() {
createClass(FileChecksum, null, [ {
key: "create",
value: function create(file, callback) {
var instance = new FileChecksum(file);
instance.create(callback);
}
} ]);
function FileChecksum(file) {
classCallCheck(this, FileChecksum);
constructor(file) {
this.file = file;
this.chunkSize = 2097152;
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
this.chunkIndex = 0;
}
createClass(FileChecksum, [ {
key: "create",
value: function create(callback) {
var _this = this;
this.callback = callback;
this.md5Buffer = new sparkMd5.ArrayBuffer();
this.fileReader = new FileReader();
this.fileReader.addEventListener("load", function(event) {
return _this.fileReaderDidLoad(event);
});
this.fileReader.addEventListener("error", function(event) {
return _this.fileReaderDidError(event);
});
this.readNextChunk();
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);
}
}, {
key: "fileReaderDidLoad",
value: function fileReaderDidLoad(event) {
this.md5Buffer.append(event.target.result);
if (!this.readNextChunk()) {
var binaryDigest = this.md5Buffer.end(true);
var 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;
}
}, {
key: "fileReaderDidError",
value: function fileReaderDidError(event) {
this.callback("Error reading " + this.file.name);
}
}, {
key: "readNextChunk",
value: function readNextChunk() {
if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) {
var start = this.chunkIndex * this.chunkSize;
var end = Math.min(start + this.chunkSize, this.file.size);
var bytes = fileSlice.call(this.file, start, end);
this.fileReader.readAsArrayBuffer(bytes);
this.chunkIndex++;
return true;
} else {
return false;
}
}
} ]);
return FileChecksum;
}();
}
}
function getMetaValue(name) {
var element = findElement(document.head, 'meta[name="' + name + '"]');
const element = findElement(document.head, `meta[name="${name}"]`);
if (element) {
return element.getAttribute("content");
}
@ -509,8 +469,8 @@
selector = root;
root = document;
}
var elements = root.querySelectorAll(selector);
return toArray$1(elements);
const elements = root.querySelectorAll(selector);
return toArray(elements);
}
function findElement(root, selector) {
if (typeof root == "string") {
@ -519,11 +479,10 @@
}
return root.querySelector(selector);
}
function dispatchEvent(element, type) {
var eventInit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var disabled = element.disabled;
var bubbles = eventInit.bubbles, cancelable = eventInit.cancelable, detail = eventInit.detail;
var event = document.createEvent("Event");
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 {
@ -534,7 +493,7 @@
}
return event;
}
function toArray$1(value) {
function toArray(value) {
if (Array.isArray(value)) {
return value;
} else if (Array.from) {
@ -543,10 +502,8 @@
return [].slice.call(value);
}
}
var BlobRecord = function() {
function BlobRecord(file, checksum, url) {
var _this = this;
classCallCheck(this, BlobRecord);
class BlobRecord {
constructor(file, checksum, url) {
this.file = file;
this.attributes = {
filename: file.name,
@ -554,312 +511,236 @@
byte_size: file.size,
checksum: checksum
};
this.xhr = new XMLHttpRequest();
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");
var csrfToken = getMetaValue("csrf-token");
const csrfToken = getMetaValue("csrf-token");
if (csrfToken != undefined) {
this.xhr.setRequestHeader("X-CSRF-Token", csrfToken);
}
this.xhr.addEventListener("load", function(event) {
return _this.requestDidLoad(event);
});
this.xhr.addEventListener("error", function(event) {
return _this.requestDidError(event);
});
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
}
createClass(BlobRecord, [ {
key: "create",
value: function create(callback) {
this.callback = callback;
this.xhr.send(JSON.stringify({
blob: this.attributes
}));
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);
}
}, {
key: "requestDidLoad",
value: function requestDidLoad(event) {
if (this.status >= 200 && this.status < 300) {
var response = this.response;
var direct_upload = response.direct_upload;
delete response.direct_upload;
this.attributes = response;
this.directUploadData = direct_upload;
this.callback(null, this.toJSON());
} else {
this.requestDidError(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());
} else {
this.requestDidError(event);
}
}, {
key: "requestDidError",
value: function requestDidError(event) {
this.callback('Error creating Blob for "' + this.file.name + '". Status: ' + this.status);
}
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];
}
}, {
key: "toJSON",
value: function toJSON() {
var result = {};
for (var key in this.attributes) {
result[key] = this.attributes[key];
}
return result;
}
}, {
key: "status",
get: function get$$1() {
return this.xhr.status;
}
}, {
key: "response",
get: function get$$1() {
var _xhr = this.xhr, responseType = _xhr.responseType, response = _xhr.response;
if (responseType == "json") {
return response;
} else {
return JSON.parse(response);
}
}
} ]);
return BlobRecord;
}();
var BlobUpload = function() {
function BlobUpload(blob) {
var _this = this;
classCallCheck(this, BlobUpload);
return result;
}
}
class BlobUpload {
constructor(blob) {
this.blob = blob;
this.file = blob.file;
var _blob$directUploadDat = blob.directUploadData, url = _blob$directUploadDat.url, headers = _blob$directUploadDat.headers;
this.xhr = new XMLHttpRequest();
const {url: url, headers: headers} = blob.directUploadData;
this.xhr = new XMLHttpRequest;
this.xhr.open("PUT", url, true);
this.xhr.responseType = "text";
for (var key in headers) {
for (const key in headers) {
this.xhr.setRequestHeader(key, headers[key]);
}
this.xhr.addEventListener("load", function(event) {
return _this.requestDidLoad(event);
});
this.xhr.addEventListener("error", function(event) {
return _this.requestDidError(event);
});
this.xhr.addEventListener("load", (event => this.requestDidLoad(event)));
this.xhr.addEventListener("error", (event => this.requestDidError(event)));
}
createClass(BlobUpload, [ {
key: "create",
value: function create(callback) {
this.callback = callback;
this.xhr.send(this.file.slice());
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);
}
}, {
key: "requestDidLoad",
value: function requestDidLoad(event) {
var _xhr = this.xhr, status = _xhr.status, response = _xhr.response;
if (status >= 200 && status < 300) {
this.callback(null, response);
} else {
this.requestDidError(event);
}
}
}, {
key: "requestDidError",
value: function requestDidError(event) {
this.callback('Error storing "' + this.file.name + '". Status: ' + this.xhr.status);
}
} ]);
return BlobUpload;
}();
var id = 0;
var DirectUpload = function() {
function DirectUpload(file, url, delegate) {
classCallCheck(this, DirectUpload);
}
requestDidError(event) {
this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`);
}
}
let id = 0;
class DirectUpload {
constructor(file, url, delegate) {
this.id = ++id;
this.file = file;
this.url = url;
this.delegate = delegate;
}
createClass(DirectUpload, [ {
key: "create",
value: function create(callback) {
var _this = this;
FileChecksum.create(this.file, function(error, checksum) {
create(callback) {
FileChecksum.create(this.file, ((error, checksum) => {
if (error) {
callback(error);
return;
}
const blob = new BlobRecord(this.file, checksum, this.url);
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
blob.create((error => {
if (error) {
callback(error);
return;
} else {
const upload = new BlobUpload(blob);
notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
upload.create((error => {
if (error) {
callback(error);
} else {
callback(null, blob.toJSON());
}
}));
}
var blob = new BlobRecord(_this.file, checksum, _this.url);
notify(_this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
blob.create(function(error) {
if (error) {
callback(error);
} else {
var upload = new BlobUpload(blob);
notify(_this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr);
upload.create(function(error) {
if (error) {
callback(error);
} else {
callback(null, blob.toJSON());
}
});
}
});
});
}
} ]);
return DirectUpload;
}();
function notify(object, methodName) {
if (object && typeof object[methodName] == "function") {
for (var _len = arguments.length, messages = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
messages[_key - 2] = arguments[_key];
}
return object[methodName].apply(object, messages);
}));
}));
}
}
var DirectUploadController = function() {
function DirectUploadController(input, file) {
classCallCheck(this, DirectUploadController);
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");
}
createClass(DirectUploadController, [ {
key: "start",
value: function start(callback) {
var _this = this;
var hiddenInput = document.createElement("input");
hiddenInput.type = "hidden";
hiddenInput.name = this.input.name;
this.input.insertAdjacentElement("beforebegin", hiddenInput);
this.dispatch("start");
this.directUpload.create(function(error, attributes) {
if (error) {
hiddenInput.parentNode.removeChild(hiddenInput);
_this.dispatchError(error);
} else {
hiddenInput.value = attributes.signed_id;
}
_this.dispatch("end");
callback(error);
});
}
}, {
key: "uploadRequestDidProgress",
value: function uploadRequestDidProgress(event) {
var progress = event.loaded / event.total * 100;
if (progress) {
this.dispatch("progress", {
progress: progress
});
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;
}
}
}, {
key: "dispatch",
value: function dispatch(name) {
var detail = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
detail.file = this.file;
detail.id = this.directUpload.id;
return dispatchEvent(this.input, "direct-upload:" + name, {
detail: detail
this.dispatch("end");
callback(error);
}));
}
uploadRequestDidProgress(event) {
const progress = event.loaded / event.total * 100;
if (progress) {
this.dispatch("progress", {
progress: progress
});
}
}, {
key: "dispatchError",
value: function dispatchError(error) {
var event = this.dispatch("error", {
error: error
});
if (!event.defaultPrevented) {
alert(error);
}
}
}, {
key: "directUploadWillCreateBlobWithXHR",
value: function directUploadWillCreateBlobWithXHR(xhr) {
this.dispatch("before-blob-request", {
xhr: xhr
});
}
}, {
key: "directUploadWillStoreFileWithXHR",
value: function directUploadWillStoreFileWithXHR(xhr) {
var _this2 = this;
this.dispatch("before-storage-request", {
xhr: xhr
});
xhr.upload.addEventListener("progress", function(event) {
return _this2.uploadRequestDidProgress(event);
});
}
}, {
key: "url",
get: function get$$1() {
return this.input.getAttribute("data-direct-upload-url");
}
} ]);
return DirectUploadController;
}();
var inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
var DirectUploadsController = function() {
function DirectUploadsController(form) {
classCallCheck(this, DirectUploadsController);
this.form = form;
this.inputs = findElements(form, inputSelector).filter(function(input) {
return input.files.length;
}
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
});
}
createClass(DirectUploadsController, [ {
key: "start",
value: function start(callback) {
var _this = this;
var controllers = this.createDirectUploadControllers();
var startNextController = function startNextController() {
var controller = controllers.shift();
if (controller) {
controller.start(function(error) {
if (error) {
callback(error);
_this.dispatch("end");
} else {
startNextController();
}
});
} else {
callback();
_this.dispatch("end");
}
};
this.dispatch("start");
startNextController();
dispatchError(error) {
const event = this.dispatch("error", {
error: error
});
if (!event.defaultPrevented) {
alert(error);
}
}, {
key: "createDirectUploadControllers",
value: function createDirectUploadControllers() {
var controllers = [];
this.inputs.forEach(function(input) {
toArray$1(input.files).forEach(function(file) {
var controller = new DirectUploadController(input, file);
controllers.push(controller);
});
});
return controllers;
}
}, {
key: "dispatch",
value: function dispatch(name) {
var detail = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return dispatchEvent(this.form, "direct-uploads:" + name, {
detail: detail
});
}
} ]);
return DirectUploadsController;
}();
var processingAttribute = "data-direct-uploads-processing";
var submitButtonsByForm = new WeakMap();
var started = false;
}
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;
@ -869,7 +750,7 @@
}
}
function didClick(event) {
var target = event.target;
const {target: target} = event;
if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) {
submitButtonsByForm.set(target.form, target);
}
@ -883,31 +764,31 @@
}
}
function handleFormSubmissionEvent(event) {
var form = event.target;
const form = event.target;
if (form.hasAttribute(processingAttribute)) {
event.preventDefault();
return;
}
var controller = new DirectUploadsController(form);
var inputs = controller.inputs;
const controller = new DirectUploadsController(form);
const {inputs: inputs} = controller;
if (inputs.length) {
event.preventDefault();
form.setAttribute(processingAttribute, "");
inputs.forEach(disable);
controller.start(function(error) {
controller.start((error => {
form.removeAttribute(processingAttribute);
if (error) {
inputs.forEach(enable);
} else {
submitForm(form);
}
});
}));
}
}
function submitForm(form) {
var button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
let button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]");
if (button) {
var _button = button, disabled = _button.disabled;
const {disabled: disabled} = button;
button.disabled = false;
button.focus();
button.click();
@ -934,9 +815,9 @@
}
}
setTimeout(autostart, 1);
exports.start = start;
exports.DirectUpload = DirectUpload;
exports.start = start;
Object.defineProperty(exports, "__esModule", {
value: true
});
});
}));

@ -149,6 +149,12 @@ class Engine < Rails::Engine # :nodoc:
end
end
initializer "active_storage.asset" do
if Rails.application.config.respond_to?(:assets)
Rails.application.config.assets.precompile += %w( activestorage activestorage.esm )
end
end
initializer "active_storage.fixture_set" do
ActiveSupport.on_load(:active_record_fixture_set) do
ActiveStorage::FixtureSet.file_fixture_path ||= Rails.root.join(*[

@ -3,6 +3,7 @@
"version": "7.0.0-alpha",
"description": "Attach cloud and local files in Rails applications",
"main": "app/assets/javascripts/activestorage.js",
"type": "module",
"files": [
"app/assets/javascripts/*.js",
"src/*.js"
@ -18,19 +19,15 @@
"author": "Javan Makhmali <javan@javan.us>",
"license": "MIT",
"dependencies": {
"spark-md5": "^3.0.0"
"spark-md5": "^3.0.1"
},
"devDependencies": {
"babel-core": "^6.25.0",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.6.0",
"@rollup/plugin-node-resolve": "^11.0.1",
"@rollup/plugin-commonjs": "^19.0.1",
"eslint": "^4.3.0",
"eslint-plugin-import": "^2.7.0",
"rollup": "^0.58.2",
"rollup-plugin-babel": "^3.0.4",
"rollup-plugin-commonjs": "^9.1.0",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-uglify": "^3.0.0"
"eslint-plugin-import": "^2.23.4",
"rollup": "^2.35.1",
"rollup-plugin-terser": "^7.0.2"
},
"scripts": {
"prebuild": "yarn lint",

@ -1,28 +1,41 @@
import resolve from "rollup-plugin-node-resolve"
import commonjs from "rollup-plugin-commonjs"
import babel from "rollup-plugin-babel"
import uglify from "rollup-plugin-uglify"
import resolve from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import { terser } from "rollup-plugin-terser"
const uglifyOptions = {
mangle: false,
compress: false,
output: {
beautify: true,
indent_level: 2
}
const terserOptions = {
mangle: false,
compress: false,
format: {
beautify: true,
indent_level: 2
}
}
export default {
input: "app/javascript/activestorage/index.js",
output: {
file: "app/assets/javascripts/activestorage.js",
format: "umd",
name: "ActiveStorage"
export default [
{
input: "app/javascript/activestorage/index.js",
output: {
file: "app/assets/javascripts/activestorage.js",
format: "umd",
name: "ActiveStorage"
},
plugins: [
resolve(),
commonjs(),
terser(terserOptions)
]
},
plugins: [
resolve(),
commonjs(),
babel(),
uglify(uglifyOptions)
]
}
{
input: "app/javascript/activestorage/index.js",
output: {
file: "app/assets/javascripts/activestorage.esm.js",
format: "es"
},
plugins: [
resolve(),
commonjs(),
terser(terserOptions)
]
}
]