1import { Dropzone } from "dropzone"; 2import { Controller } from "@hotwired/stimulus"; 3import { DirectUpload } from "@rails/activestorage"; 4import { getMetaValue, toArray, findElement, removeElement, insertAfter } from "../helpers/index"; 5 6class DirectUploadController { 7 constructor(source, file) { 8 this.directUpload = this.createDirectUpload(file, source.url); 9 this.source = source; 10 this.file = file; 11 } 12 13 start() { 14 this.file.controller = this; 15 this.hiddenInput = this.createHiddenInput(); 16 this.directUpload.create((error, attributes) => { 17 if (error) { 18 removeElement(this.hiddenInput); 19 this.emitDropzoneError(error); 20 } else { 21 this.hiddenInput.value = attributes.signed_id; 22 this.emitDropzoneSuccess(); 23 } 24 }); 25 } 26 27 createHiddenInput() { 28 const input = document.createElement("input"); 29 input.type = "hidden"; 30 input.name = this.source.inputTarget.name; 31 insertAfter(input, this.source.inputTarget); 32 return input; 33 } 34 35 directUploadWillStoreFileWithXHR(xhr) { 36 this.bindProgressEvent(xhr); 37 this.emitDropzoneUploading(); 38 } 39 40 bindProgressEvent(xhr) { 41 this.xhr = xhr; 42 this.xhr.upload.addEventListener("progress", event => 43 this.uploadRequestDidProgress(event) 44 ); 45 } 46 47 uploadRequestDidProgress(event) { 48 const element = this.source.element; 49 const progress = (event.loaded / event.total) * 100; 50 findElement( 51 this.file.previewTemplate, 52 ".dz-upload" 53 ).style.width = `${progress}%`; 54 } 55 56 emitDropzoneUploading() { 57 this.file.status = Dropzone.UPLOADING; 58 this.source.dropZone.emit("processing", this.file); 59 } 60 61 emitDropzoneError(error) { 62 this.file.status = Dropzone.ERROR; 63 this.source.dropZone.emit("error", this.file, error); 64 this.source.dropZone.emit("complete", this.file); 65 } 66 67 emitDropzoneSuccess() { 68 this.file.status = Dropzone.SUCCESS; 69 this.source.dropZone.emit("success", this.file); 70 this.source.dropZone.emit("complete", this.file); 71 } 72 73 createDirectUpload(file, url) { 74 return new DirectUpload(file, url, this); 75 } 76} 77 78export default class extends Controller { 79 static targets = ["input"]; 80 81 connect() { 82 // Todo : Find a way to execute this after the translation have been loaded without settimeout 83 setTimeout(() => { 84 this.dropZone = this.createDropZone(); 85 this.hideFileInput(); 86 this.bindEvents(); 87 Dropzone.autoDiscover = false; // necessary quirk for Dropzone error in console 88 89 this.importExistingFiles(); 90 }, 500); 91 } 92 93 // Private 94 hideFileInput() { 95 this.inputTarget.disabled = true; 96 this.inputTarget.style.display = "none"; 97 } 98 99 bindEvents() { 100 this.dropZone.on("addedfile", file => { 101 setTimeout(() => { 102 file.accepted && this.createDirectUploadController(file).start(); 103 }, 500); 104 }); 105 106 this.dropZone.on("removedfile", file => { 107 file.controller && removeElement(file.controller.hiddenInput); 108 }); 109 110 this.dropZone.on("canceled", file => { 111 file.controller && file.controller.xhr.abort(); 112 }); 113 } 114 115 get headers() { 116 return { "X-CSRF-Token": getMetaValue("csrf-token") }; 117 } 118 119 get url() { 120 return this.inputTarget.getAttribute("data-direct-upload-url"); 121 } 122 123 get maxFiles() { 124 return this.data.get("maxFiles") || 1; 125 } 126 127 get maxFileSize() { 128 return this.data.get("maxFileSize") || 256; 129 } 130 131 get acceptedFiles() { 132 return this.data.get("acceptedFiles"); 133 } 134 135 get addRemoveLinks() { 136 return this.data.get("addRemoveLinks") || true; 137 } 138 139 createDirectUploadController(file) { 140 return new DirectUploadController(this, file); 141 } 142 143 createDropZone() { 144 return new Dropzone(this.element, { 145 url: this.url, 146 headers: this.headers, 147 maxFiles: this.maxFiles, 148 maxFilesize: this.maxFileSize, 149 acceptedFiles: this.acceptedFiles, 150 addRemoveLinks: this.addRemoveLinks, 151 autoQueue: false, 152 dictFileTooBig: i18n.t("uploader.filetoobig"), 153 dictInvalidFileType: i18n.t("uploader.invalidfiletype"), 154 dictCancelUpload: i18n.t("uploader.cancelupload"), 155 dictRemoveFile: i18n.t("uploader.removefile"), 156 dictMaxFilesExceeded: i18n.t("uploader.maxfilesexceeded"), 157 dictDefaultMessage: i18n.t("uploader.defaultmessage"), 158 }); 159 } 160 161 importExistingFiles() {
162 jsonMedia.forEach((image) => { 163 let input = Object.assign(document.createElement("input"), {type: "hidden", name: "room[images][]", value: image.id}); 164 165 this.inputTarget.insertAdjacentElement("afterend", input); 166 image.controller = { hiddenInput: input }; 167 168 this.dropZone.displayExistingFile(image, image.name); 169 }); 170 } 171};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.