/* Resources: - https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications - https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ - https://eloquentjavascript.net/18_http.html */ application.register( 'upload-images', class extends Stimulus.Controller { static get targets() { return ['input', 'output', 'dropzone'] } openFileSelector() { this.inputTarget.click() } addActiveClass(event) { event.preventDefault() this.dropzoneTarget.classList.add('active') } removeActiveClass() { this.dropzoneTarget.classList.remove('active') } handleImages(event) { event.preventDefault() let images = [] if (event.type == 'change') { images = event.target.files } else if (event.type == 'drop') { images = event.dataTransfer.files } Array.from(images).forEach(image => { const imageName = image.name const imageSize = image.size const isOversized = Math.round(imageSize / 1024) > Number(this.data.get('maxSizeKb')) if (isOversized) { this.outputTarget.classList.add('oversized') this.outputTarget.innerText = 'Oversized!' this.inputTarget.value = '' return } else { this.outputTarget.classList.remove('oversized') this.outputTarget.innerText = '' this.dropzoneTarget.classList.add('hidden') } const figure = document.createElement('figure') const img = document.createElement('img') img.file = image // Required for future upload, fragile? img.src = window.URL.createObjectURL(image) img.onload = event => window.URL.revokeObjectURL(event.target.src) img.alt = 'Picture preview' figure.appendChild(img) const figcaption = document.createElement('figcaption') figcaption.innerText = imageName figure.appendChild(figcaption) this.outputTarget.appendChild(figure) }) } submit(event) { event.preventDefault() const images = this.outputTarget.querySelectorAll('img') Array.from(images).forEach(image => { const formdata = new FormData() formdata.append('images', image.file) const options = { method: this.element.method, body: formdata } console.log(`Uploading "${image.file.name}"`) this._request( this.element.action, options, this._displayPercentage ) .then(response => console.log('Uploaded!')) .catch(console.log.bind(console)) }) } _request(url, options = {}, onProgress) { // See https://github.com/github/fetch/issues/89#issuecomment-256610849 return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open(options.method || 'get', url) for (let header in options.headers || {}) xhr.setRequestHeader(header, options.headers[header]) xhr.onload = event => resolve(event.target.responseText) xhr.onerror = reject if (xhr.upload && onProgress) xhr.upload.onprogress = onProgress xhr.send(options.body) }) } _displayPercentage(event) { if (event.lengthComputable) { const percentage = Math.round( (event.loaded * 100) / event.total ) console.log(percentage) } } } )