⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.1
Server IP:
185.238.29.86
Server:
Linux server2 6.8.12-6-pve #1 SMP PREEMPT_DYNAMIC PMX 6.8.12-6 (2024-12-19T19:05Z) x86_64
Server Software:
nginx/1.18.0
PHP Version:
8.1.31
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
var
/
www
/
invoice
/
node_modules
/
dropzone
/
src
/
View File Name :
dropzone.js
import Emitter from "./emitter.js"; import defaultOptions from "./options.js"; export default class Dropzone extends Emitter { static initClass() { // Exposing the emitter class, mainly for tests this.prototype.Emitter = Emitter; /* This is a list of all available events you can register on a dropzone object. You can register an event handler like this: dropzone.on("dragEnter", function() { }); */ this.prototype.events = [ "drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "addedfiles", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete", ]; this.prototype._thumbnailQueue = []; this.prototype._processingThumbnail = false; } // global utility static extend(target, ...objects) { for (let object of objects) { for (let key in object) { let val = object[key]; target[key] = val; } } return target; } constructor(el, options) { super(); let fallback, left; this.element = el; // For backwards compatibility since the version was in the prototype previously this.version = Dropzone.version; this.clickableElements = []; this.listeners = []; this.files = []; // All files if (typeof this.element === "string") { this.element = document.querySelector(this.element); } // Not checking if instance of HTMLElement or Element since IE9 is extremely weird. if (!this.element || this.element.nodeType == null) { throw new Error("Invalid dropzone element."); } if (this.element.dropzone) { throw new Error("Dropzone already attached."); } // Now add this dropzone to the instances. Dropzone.instances.push(this); // Put the dropzone inside the element itself. this.element.dropzone = this; let elementOptions = (left = Dropzone.optionsForElement(this.element)) != null ? left : {}; this.options = Dropzone.extend( {}, defaultOptions, elementOptions, options != null ? options : {} ); this.options.previewTemplate = this.options.previewTemplate.replace( /\n*/g, "" ); // If the browser failed, just call the fallback and leave if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { return this.options.fallback.call(this); } // @options.url = @element.getAttribute "action" unless @options.url? if (this.options.url == null) { this.options.url = this.element.getAttribute("action"); } if (!this.options.url) { throw new Error("No URL provided."); } if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { throw new Error( "You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated." ); } if (this.options.uploadMultiple && this.options.chunking) { throw new Error("You cannot set both: uploadMultiple and chunking."); } // Backwards compatibility if (this.options.acceptedMimeTypes) { this.options.acceptedFiles = this.options.acceptedMimeTypes; delete this.options.acceptedMimeTypes; } // Backwards compatibility if (this.options.renameFilename != null) { this.options.renameFile = (file) => this.options.renameFilename.call(this, file.name, file); } if (typeof this.options.method === "string") { this.options.method = this.options.method.toUpperCase(); } if ((fallback = this.getExistingFallback()) && fallback.parentNode) { // Remove the fallback fallback.parentNode.removeChild(fallback); } // Display previews in the previewsContainer element or the Dropzone element unless explicitly set to false if (this.options.previewsContainer !== false) { if (this.options.previewsContainer) { this.previewsContainer = Dropzone.getElement( this.options.previewsContainer, "previewsContainer" ); } else { this.previewsContainer = this.element; } } if (this.options.clickable) { if (this.options.clickable === true) { this.clickableElements = [this.element]; } else { this.clickableElements = Dropzone.getElements( this.options.clickable, "clickable" ); } } this.init(); } // Returns all files that have been accepted getAcceptedFiles() { return this.files.filter((file) => file.accepted).map((file) => file); } // Returns all files that have been rejected // Not sure when that's going to be useful, but added for completeness. getRejectedFiles() { return this.files.filter((file) => !file.accepted).map((file) => file); } getFilesWithStatus(status) { return this.files .filter((file) => file.status === status) .map((file) => file); } // Returns all files that are in the queue getQueuedFiles() { return this.getFilesWithStatus(Dropzone.QUEUED); } getUploadingFiles() { return this.getFilesWithStatus(Dropzone.UPLOADING); } getAddedFiles() { return this.getFilesWithStatus(Dropzone.ADDED); } // Files that are either queued or uploading getActiveFiles() { return this.files .filter( (file) => file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED ) .map((file) => file); } // The function that gets called when Dropzone is initialized. You // can (and should) setup event listeners inside this function. init() { // In case it isn't set already if (this.element.tagName === "form") { this.element.setAttribute("enctype", "multipart/form-data"); } if ( this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message") ) { this.element.appendChild( Dropzone.createElement( `<div class="dz-default dz-message"><button class="dz-button" type="button">${this.options.dictDefaultMessage}</button></div>` ) ); } if (this.clickableElements.length) { let setupHiddenFileInput = () => { if (this.hiddenFileInput) { this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); } this.hiddenFileInput = document.createElement("input"); this.hiddenFileInput.setAttribute("type", "file"); if (this.options.maxFiles === null || this.options.maxFiles > 1) { this.hiddenFileInput.setAttribute("multiple", "multiple"); } this.hiddenFileInput.className = "dz-hidden-input"; if (this.options.acceptedFiles !== null) { this.hiddenFileInput.setAttribute( "accept", this.options.acceptedFiles ); } if (this.options.capture !== null) { this.hiddenFileInput.setAttribute("capture", this.options.capture); } // Making sure that no one can "tab" into this field. this.hiddenFileInput.setAttribute("tabindex", "-1"); // Not setting `display="none"` because some browsers don't accept clicks // on elements that aren't displayed. this.hiddenFileInput.style.visibility = "hidden"; this.hiddenFileInput.style.position = "absolute"; this.hiddenFileInput.style.top = "0"; this.hiddenFileInput.style.left = "0"; this.hiddenFileInput.style.height = "0"; this.hiddenFileInput.style.width = "0"; Dropzone.getElement( this.options.hiddenInputContainer, "hiddenInputContainer" ).appendChild(this.hiddenFileInput); this.hiddenFileInput.addEventListener("change", () => { let { files } = this.hiddenFileInput; if (files.length) { for (let file of files) { this.addFile(file); } } this.emit("addedfiles", files); setupHiddenFileInput(); }); }; setupHiddenFileInput(); } this.URL = window.URL !== null ? window.URL : window.webkitURL; // Setup all event listeners on the Dropzone object itself. // They're not in @setupEventListeners() because they shouldn't be removed // again when the dropzone gets disabled. for (let eventName of this.events) { this.on(eventName, this.options[eventName]); } this.on("uploadprogress", () => this.updateTotalUploadProgress()); this.on("removedfile", () => this.updateTotalUploadProgress()); this.on("canceled", (file) => this.emit("complete", file)); // Emit a `queuecomplete` event if all files finished uploading. this.on("complete", (file) => { if ( this.getAddedFiles().length === 0 && this.getUploadingFiles().length === 0 && this.getQueuedFiles().length === 0 ) { // This needs to be deferred so that `queuecomplete` really triggers after `complete` return setTimeout(() => this.emit("queuecomplete"), 0); } }); const containsFiles = function (e) { if (e.dataTransfer.types) { // Because e.dataTransfer.types is an Object in // IE, we need to iterate like this instead of // using e.dataTransfer.types.some() for (var i = 0; i < e.dataTransfer.types.length; i++) { if (e.dataTransfer.types[i] === "Files") return true; } } return false; }; let noPropagation = function (e) { // If there are no files, we don't want to stop // propagation so we don't interfere with other // drag and drop behaviour. if (!containsFiles(e)) return; e.stopPropagation(); if (e.preventDefault) { return e.preventDefault(); } else { return (e.returnValue = false); } }; // Create the listeners this.listeners = [ { element: this.element, events: { dragstart: (e) => { return this.emit("dragstart", e); }, dragenter: (e) => { noPropagation(e); return this.emit("dragenter", e); }, dragover: (e) => { // Makes it possible to drag files from chrome's download bar // http://stackoverflow.com/questions/19526430/drag-and-drop-file-uploads-from-chrome-downloads-bar // Try is required to prevent bug in Internet Explorer 11 (SCRIPT65535 exception) let efct; try { efct = e.dataTransfer.effectAllowed; } catch (error) {} e.dataTransfer.dropEffect = "move" === efct || "linkMove" === efct ? "move" : "copy"; noPropagation(e); return this.emit("dragover", e); }, dragleave: (e) => { return this.emit("dragleave", e); }, drop: (e) => { noPropagation(e); return this.drop(e); }, dragend: (e) => { return this.emit("dragend", e); }, }, // This is disabled right now, because the browsers don't implement it properly. // "paste": (e) => // noPropagation e // @paste e }, ]; this.clickableElements.forEach((clickableElement) => { return this.listeners.push({ element: clickableElement, events: { click: (evt) => { // Only the actual dropzone or the message element should trigger file selection if ( clickableElement !== this.element || evt.target === this.element || Dropzone.elementInside( evt.target, this.element.querySelector(".dz-message") ) ) { this.hiddenFileInput.click(); // Forward the click } return true; }, }, }); }); this.enable(); return this.options.init.call(this); } // Not fully tested yet destroy() { this.disable(); this.removeAllFiles(true); if ( this.hiddenFileInput != null ? this.hiddenFileInput.parentNode : undefined ) { this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); this.hiddenFileInput = null; } delete this.element.dropzone; return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); } updateTotalUploadProgress() { let totalUploadProgress; let totalBytesSent = 0; let totalBytes = 0; let activeFiles = this.getActiveFiles(); if (activeFiles.length) { for (let file of this.getActiveFiles()) { totalBytesSent += file.upload.bytesSent; totalBytes += file.upload.total; } totalUploadProgress = (100 * totalBytesSent) / totalBytes; } else { totalUploadProgress = 100; } return this.emit( "totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent ); } // @options.paramName can be a function taking one parameter rather than a string. // A parameter name for a file is obtained simply by calling this with an index number. _getParamName(n) { if (typeof this.options.paramName === "function") { return this.options.paramName(n); } else { return `${this.options.paramName}${ this.options.uploadMultiple ? `[${n}]` : "" }`; } } // If @options.renameFile is a function, // the function will be used to rename the file.name before appending it to the formData _renameFile(file) { if (typeof this.options.renameFile !== "function") { return file.name; } return this.options.renameFile(file); } // Returns a form that can be used as fallback if the browser does not support DragnDrop // // If the dropzone is already a form, only the input field and button are returned. Otherwise a complete form element is provided. // This code has to pass in IE7 :( getFallbackForm() { let existingFallback, form; if ((existingFallback = this.getExistingFallback())) { return existingFallback; } let fieldsString = '<div class="dz-fallback">'; if (this.options.dictFallbackText) { fieldsString += `<p>${this.options.dictFallbackText}</p>`; } fieldsString += `<input type="file" name="${this._getParamName(0)}" ${ this.options.uploadMultiple ? 'multiple="multiple"' : undefined } /><input type="submit" value="Upload!"></div>`; let fields = Dropzone.createElement(fieldsString); if (this.element.tagName !== "FORM") { form = Dropzone.createElement( `<form action="${this.options.url}" enctype="multipart/form-data" method="${this.options.method}"></form>` ); form.appendChild(fields); } else { // Make sure that the enctype and method attributes are set properly this.element.setAttribute("enctype", "multipart/form-data"); this.element.setAttribute("method", this.options.method); } return form != null ? form : fields; } // Returns the fallback elements if they exist already // // This code has to pass in IE7 :( getExistingFallback() { let getFallback = function (elements) { for (let el of elements) { if (/(^| )fallback($| )/.test(el.className)) { return el; } } }; for (let tagName of ["div", "form"]) { var fallback; if ( (fallback = getFallback(this.element.getElementsByTagName(tagName))) ) { return fallback; } } } // Activates all listeners stored in @listeners setupEventListeners() { return this.listeners.map((elementListeners) => (() => { let result = []; for (let event in elementListeners.events) { let listener = elementListeners.events[event]; result.push( elementListeners.element.addEventListener(event, listener, false) ); } return result; })() ); } // Deactivates all listeners stored in @listeners removeEventListeners() { return this.listeners.map((elementListeners) => (() => { let result = []; for (let event in elementListeners.events) { let listener = elementListeners.events[event]; result.push( elementListeners.element.removeEventListener(event, listener, false) ); } return result; })() ); } // Removes all event listeners and cancels all files in the queue or being processed. disable() { this.clickableElements.forEach((element) => element.classList.remove("dz-clickable") ); this.removeEventListeners(); this.disabled = true; return this.files.map((file) => this.cancelUpload(file)); } enable() { delete this.disabled; this.clickableElements.forEach((element) => element.classList.add("dz-clickable") ); return this.setupEventListeners(); } // Returns a nicely formatted filesize filesize(size) { let selectedSize = 0; let selectedUnit = "b"; if (size > 0) { let units = ["tb", "gb", "mb", "kb", "b"]; for (let i = 0; i < units.length; i++) { let unit = units[i]; let cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; if (size >= cutoff) { selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); selectedUnit = unit; break; } } selectedSize = Math.round(10 * selectedSize) / 10; // Cutting of digits } return `<strong>${selectedSize}</strong> ${this.options.dictFileSizeUnits[selectedUnit]}`; } // Adds or removes the `dz-max-files-reached` class from the form. _updateMaxFilesReachedClass() { if ( this.options.maxFiles != null && this.getAcceptedFiles().length >= this.options.maxFiles ) { if (this.getAcceptedFiles().length === this.options.maxFiles) { this.emit("maxfilesreached", this.files); } return this.element.classList.add("dz-max-files-reached"); } else { return this.element.classList.remove("dz-max-files-reached"); } } drop(e) { if (!e.dataTransfer) { return; } this.emit("drop", e); // Convert the FileList to an Array // This is necessary for IE11 let files = []; for (let i = 0; i < e.dataTransfer.files.length; i++) { files[i] = e.dataTransfer.files[i]; } // Even if it's a folder, files.length will contain the folders. if (files.length) { let { items } = e.dataTransfer; if (items && items.length && items[0].webkitGetAsEntry != null) { // The browser supports dropping of folders, so handle items instead of files this._addFilesFromItems(items); } else { this.handleFiles(files); } } this.emit("addedfiles", files); } paste(e) { if ( __guard__(e != null ? e.clipboardData : undefined, (x) => x.items) == null ) { return; } this.emit("paste", e); let { items } = e.clipboardData; if (items.length) { return this._addFilesFromItems(items); } } handleFiles(files) { for (let file of files) { this.addFile(file); } } // When a folder is dropped (or files are pasted), items must be handled // instead of files. _addFilesFromItems(items) { return (() => { let result = []; for (let item of items) { var entry; if ( item.webkitGetAsEntry != null && (entry = item.webkitGetAsEntry()) ) { if (entry.isFile) { result.push(this.addFile(item.getAsFile())); } else if (entry.isDirectory) { // Append all files from that directory to files result.push(this._addFilesFromDirectory(entry, entry.name)); } else { result.push(undefined); } } else if (item.getAsFile != null) { if (item.kind == null || item.kind === "file") { result.push(this.addFile(item.getAsFile())); } else { result.push(undefined); } } else { result.push(undefined); } } return result; })(); } // Goes through the directory, and adds each file it finds recursively _addFilesFromDirectory(directory, path) { let dirReader = directory.createReader(); let errorHandler = (error) => __guardMethod__(console, "log", (o) => o.log(error)); var readEntries = () => { return dirReader.readEntries((entries) => { if (entries.length > 0) { for (let entry of entries) { if (entry.isFile) { entry.file((file) => { if ( this.options.ignoreHiddenFiles && file.name.substring(0, 1) === "." ) { return; } file.fullPath = `${path}/${file.name}`; return this.addFile(file); }); } else if (entry.isDirectory) { this._addFilesFromDirectory(entry, `${path}/${entry.name}`); } } // Recursively call readEntries() again, since browser only handle // the first 100 entries. // See: https://developer.mozilla.org/en-US/docs/Web/API/DirectoryReader#readEntries readEntries(); } return null; }, errorHandler); }; return readEntries(); } // If `done()` is called without argument the file is accepted // If you call it with an error message, the file is rejected // (This allows for asynchronous validation) // // This function checks the filesize, and if the file.type passes the // `acceptedFiles` check. accept(file, done) { if ( this.options.maxFilesize && file.size > this.options.maxFilesize * 1024 * 1024 ) { done( this.options.dictFileTooBig .replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100) .replace("{{maxFilesize}}", this.options.maxFilesize) ); } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { done(this.options.dictInvalidFileType); } else if ( this.options.maxFiles != null && this.getAcceptedFiles().length >= this.options.maxFiles ) { done( this.options.dictMaxFilesExceeded.replace( "{{maxFiles}}", this.options.maxFiles ) ); this.emit("maxfilesexceeded", file); } else { this.options.accept.call(this, file, done); } } addFile(file) { file.upload = { uuid: Dropzone.uuidv4(), progress: 0, // Setting the total upload size to file.size for the beginning // It's actual different than the size to be transmitted. total: file.size, bytesSent: 0, filename: this._renameFile(file), // Not setting chunking information here, because the acutal data — and // thus the chunks — might change if `options.transformFile` is set // and does something to the data. }; this.files.push(file); file.status = Dropzone.ADDED; this.emit("addedfile", file); this._enqueueThumbnail(file); this.accept(file, (error) => { if (error) { file.accepted = false; this._errorProcessing([file], error); // Will set the file.status } else { file.accepted = true; if (this.options.autoQueue) { this.enqueueFile(file); } // Will set .accepted = true } this._updateMaxFilesReachedClass(); }); } // Wrapper for enqueueFile enqueueFiles(files) { for (let file of files) { this.enqueueFile(file); } return null; } enqueueFile(file) { if (file.status === Dropzone.ADDED && file.accepted === true) { file.status = Dropzone.QUEUED; if (this.options.autoProcessQueue) { return setTimeout(() => this.processQueue(), 0); // Deferring the call } } else { throw new Error( "This file can't be queued because it has already been processed or was rejected." ); } } _enqueueThumbnail(file) { if ( this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024 ) { this._thumbnailQueue.push(file); return setTimeout(() => this._processThumbnailQueue(), 0); // Deferring the call } } _processThumbnailQueue() { if (this._processingThumbnail || this._thumbnailQueue.length === 0) { return; } this._processingThumbnail = true; let file = this._thumbnailQueue.shift(); return this.createThumbnail( file, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, true, (dataUrl) => { this.emit("thumbnail", file, dataUrl); this._processingThumbnail = false; return this._processThumbnailQueue(); } ); } // Can be called by the user to remove a file removeFile(file) { if (file.status === Dropzone.UPLOADING) { this.cancelUpload(file); } this.files = without(this.files, file); this.emit("removedfile", file); if (this.files.length === 0) { return this.emit("reset"); } } // Removes all files that aren't currently processed from the list removeAllFiles(cancelIfNecessary) { // Create a copy of files since removeFile() changes the @files array. if (cancelIfNecessary == null) { cancelIfNecessary = false; } for (let file of this.files.slice()) { if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { this.removeFile(file); } } return null; } // Resizes an image before it gets sent to the server. This function is the default behavior of // `options.transformFile` if `resizeWidth` or `resizeHeight` are set. The callback is invoked with // the resized blob. resizeImage(file, width, height, resizeMethod, callback) { return this.createThumbnail( file, width, height, resizeMethod, true, (dataUrl, canvas) => { if (canvas == null) { // The image has not been resized return callback(file); } else { let { resizeMimeType } = this.options; if (resizeMimeType == null) { resizeMimeType = file.type; } let resizedDataURL = canvas.toDataURL( resizeMimeType, this.options.resizeQuality ); if ( resizeMimeType === "image/jpeg" || resizeMimeType === "image/jpg" ) { // Now add the original EXIF information resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL); } return callback(Dropzone.dataURItoBlob(resizedDataURL)); } } ); } createThumbnail(file, width, height, resizeMethod, fixOrientation, callback) { let fileReader = new FileReader(); fileReader.onload = () => { file.dataURL = fileReader.result; // Don't bother creating a thumbnail for SVG images since they're vector if (file.type === "image/svg+xml") { if (callback != null) { callback(fileReader.result); } return; } this.createThumbnailFromUrl( file, width, height, resizeMethod, fixOrientation, callback ); }; fileReader.readAsDataURL(file); } // `mockFile` needs to have these attributes: // // { name: 'name', size: 12345, imageUrl: '' } // // `callback` will be invoked when the image has been downloaded and displayed. // `crossOrigin` will be added to the `img` tag when accessing the file. displayExistingFile( mockFile, imageUrl, callback, crossOrigin, resizeThumbnail = true ) { this.emit("addedfile", mockFile); this.emit("complete", mockFile); if (!resizeThumbnail) { this.emit("thumbnail", mockFile, imageUrl); if (callback) callback(); } else { let onDone = (thumbnail) => { this.emit("thumbnail", mockFile, thumbnail); if (callback) callback(); }; mockFile.dataURL = imageUrl; this.createThumbnailFromUrl( mockFile, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, this.options.fixOrientation, onDone, crossOrigin ); } } createThumbnailFromUrl( file, width, height, resizeMethod, fixOrientation, callback, crossOrigin ) { // Not using `new Image` here because of a bug in latest Chrome versions. // See https://github.com/enyo/dropzone/pull/226 let img = document.createElement("img"); if (crossOrigin) { img.crossOrigin = crossOrigin; } // fixOrientation is not needed anymore with browsers handling imageOrientation fixOrientation = getComputedStyle(document.body)["imageOrientation"] == "from-image" ? false : fixOrientation; img.onload = () => { let loadExif = (callback) => callback(1); if (typeof EXIF !== "undefined" && EXIF !== null && fixOrientation) { loadExif = (callback) => EXIF.getData(img, function () { return callback(EXIF.getTag(this, "Orientation")); }); } return loadExif((orientation) => { file.width = img.width; file.height = img.height; let resizeInfo = this.options.resize.call( this, file, width, height, resizeMethod ); let canvas = document.createElement("canvas"); let ctx = canvas.getContext("2d"); canvas.width = resizeInfo.trgWidth; canvas.height = resizeInfo.trgHeight; if (orientation > 4) { canvas.width = resizeInfo.trgHeight; canvas.height = resizeInfo.trgWidth; } switch (orientation) { case 2: // horizontal flip ctx.translate(canvas.width, 0); ctx.scale(-1, 1); break; case 3: // 180° rotate left ctx.translate(canvas.width, canvas.height); ctx.rotate(Math.PI); break; case 4: // vertical flip ctx.translate(0, canvas.height); ctx.scale(1, -1); break; case 5: // vertical flip + 90 rotate right ctx.rotate(0.5 * Math.PI); ctx.scale(1, -1); break; case 6: // 90° rotate right ctx.rotate(0.5 * Math.PI); ctx.translate(0, -canvas.width); break; case 7: // horizontal flip + 90 rotate right ctx.rotate(0.5 * Math.PI); ctx.translate(canvas.height, -canvas.width); ctx.scale(-1, 1); break; case 8: // 90° rotate left ctx.rotate(-0.5 * Math.PI); ctx.translate(-canvas.height, 0); break; } // This is a bugfix for iOS' scaling bug. drawImageIOSFix( ctx, img, resizeInfo.srcX != null ? resizeInfo.srcX : 0, resizeInfo.srcY != null ? resizeInfo.srcY : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, resizeInfo.trgX != null ? resizeInfo.trgX : 0, resizeInfo.trgY != null ? resizeInfo.trgY : 0, resizeInfo.trgWidth, resizeInfo.trgHeight ); let thumbnail = canvas.toDataURL("image/png"); if (callback != null) { return callback(thumbnail, canvas); } }); }; if (callback != null) { img.onerror = callback; } return (img.src = file.dataURL); } // Goes through the queue and processes files if there aren't too many already. processQueue() { let { parallelUploads } = this.options; let processingLength = this.getUploadingFiles().length; let i = processingLength; // There are already at least as many files uploading than should be if (processingLength >= parallelUploads) { return; } let queuedFiles = this.getQueuedFiles(); if (!(queuedFiles.length > 0)) { return; } if (this.options.uploadMultiple) { // The files should be uploaded in one request return this.processFiles( queuedFiles.slice(0, parallelUploads - processingLength) ); } else { while (i < parallelUploads) { if (!queuedFiles.length) { return; } // Nothing left to process this.processFile(queuedFiles.shift()); i++; } } } // Wrapper for `processFiles` processFile(file) { return this.processFiles([file]); } // Loads the file, then calls finishedLoading() processFiles(files) { for (let file of files) { file.processing = true; // Backwards compatibility file.status = Dropzone.UPLOADING; this.emit("processing", file); } if (this.options.uploadMultiple) { this.emit("processingmultiple", files); } return this.uploadFiles(files); } _getFilesWithXhr(xhr) { let files; return (files = this.files .filter((file) => file.xhr === xhr) .map((file) => file)); } // Cancels the file upload and sets the status to CANCELED // **if** the file is actually being uploaded. // If it's still in the queue, the file is being removed from it and the status // set to CANCELED. cancelUpload(file) { if (file.status === Dropzone.UPLOADING) { let groupedFiles = this._getFilesWithXhr(file.xhr); for (let groupedFile of groupedFiles) { groupedFile.status = Dropzone.CANCELED; } if (typeof file.xhr !== "undefined") { file.xhr.abort(); } for (let groupedFile of groupedFiles) { this.emit("canceled", groupedFile); } if (this.options.uploadMultiple) { this.emit("canceledmultiple", groupedFiles); } } else if ( file.status === Dropzone.ADDED || file.status === Dropzone.QUEUED ) { file.status = Dropzone.CANCELED; this.emit("canceled", file); if (this.options.uploadMultiple) { this.emit("canceledmultiple", [file]); } } if (this.options.autoProcessQueue) { return this.processQueue(); } } resolveOption(option, ...args) { if (typeof option === "function") { return option.apply(this, args); } return option; } uploadFile(file) { return this.uploadFiles([file]); } uploadFiles(files) { this._transformFiles(files, (transformedFiles) => { if (this.options.chunking) { // Chunking is not allowed to be used with `uploadMultiple` so we know // that there is only __one__file. let transformedFile = transformedFiles[0]; files[0].upload.chunked = this.options.chunking && (this.options.forceChunking || transformedFile.size > this.options.chunkSize); files[0].upload.totalChunkCount = Math.ceil( transformedFile.size / this.options.chunkSize ); } if (files[0].upload.chunked) { // This file should be sent in chunks! // If the chunking option is set, we **know** that there can only be **one** file, since // uploadMultiple is not allowed with this option. let file = files[0]; let transformedFile = transformedFiles[0]; let startedChunkCount = 0; file.upload.chunks = []; let handleNextChunk = () => { let chunkIndex = 0; // Find the next item in file.upload.chunks that is not defined yet. while (file.upload.chunks[chunkIndex] !== undefined) { chunkIndex++; } // This means, that all chunks have already been started. if (chunkIndex >= file.upload.totalChunkCount) return; startedChunkCount++; let start = chunkIndex * this.options.chunkSize; let end = Math.min( start + this.options.chunkSize, transformedFile.size ); let dataBlock = { name: this._getParamName(0), data: transformedFile.webkitSlice ? transformedFile.webkitSlice(start, end) : transformedFile.slice(start, end), filename: file.upload.filename, chunkIndex: chunkIndex, }; file.upload.chunks[chunkIndex] = { file: file, index: chunkIndex, dataBlock: dataBlock, // In case we want to retry. status: Dropzone.UPLOADING, progress: 0, retries: 0, // The number of times this block has been retried. }; this._uploadData(files, [dataBlock]); }; file.upload.finishedChunkUpload = (chunk, response) => { let allFinished = true; chunk.status = Dropzone.SUCCESS; // Clear the data from the chunk chunk.dataBlock = null; // Leaving this reference to xhr intact here will cause memory leaks in some browsers chunk.xhr = null; for (let i = 0; i < file.upload.totalChunkCount; i++) { if (file.upload.chunks[i] === undefined) { return handleNextChunk(); } if (file.upload.chunks[i].status !== Dropzone.SUCCESS) { allFinished = false; } } if (allFinished) { this.options.chunksUploaded(file, () => { this._finished(files, response, null); }); } }; if (this.options.parallelChunkUploads) { for (let i = 0; i < file.upload.totalChunkCount; i++) { handleNextChunk(); } } else { handleNextChunk(); } } else { let dataBlocks = []; for (let i = 0; i < files.length; i++) { dataBlocks[i] = { name: this._getParamName(i), data: transformedFiles[i], filename: files[i].upload.filename, }; } this._uploadData(files, dataBlocks); } }); } /// Returns the right chunk for given file and xhr _getChunk(file, xhr) { for (let i = 0; i < file.upload.totalChunkCount; i++) { if ( file.upload.chunks[i] !== undefined && file.upload.chunks[i].xhr === xhr ) { return file.upload.chunks[i]; } } } // This function actually uploads the file(s) to the server. // If dataBlocks contains the actual data to upload (meaning, that this could either be transformed // files, or individual chunks for chunked upload). _uploadData(files, dataBlocks) { let xhr = new XMLHttpRequest(); // Put the xhr object in the file objects to be able to reference it later. for (let file of files) { file.xhr = xhr; } if (files[0].upload.chunked) { // Put the xhr object in the right chunk object, so it can be associated later, and found with _getChunk files[0].upload.chunks[dataBlocks[0].chunkIndex].xhr = xhr; } let method = this.resolveOption(this.options.method, files); let url = this.resolveOption(this.options.url, files); xhr.open(method, url, true); // Setting the timeout after open because of IE11 issue: https://gitlab.com/meno/dropzone/issues/8 let timeout = this.resolveOption(this.options.timeout, files); if (timeout) xhr.timeout = this.resolveOption(this.options.timeout, files); // Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179 xhr.withCredentials = !!this.options.withCredentials; xhr.onload = (e) => { this._finishedUploading(files, xhr, e); }; xhr.ontimeout = () => { this._handleUploadError( files, xhr, `Request timedout after ${this.options.timeout / 1000} seconds` ); }; xhr.onerror = () => { this._handleUploadError(files, xhr); }; // Some browsers do not have the .upload property let progressObj = xhr.upload != null ? xhr.upload : xhr; progressObj.onprogress = (e) => this._updateFilesUploadProgress(files, xhr, e); let headers = { Accept: "application/json", "Cache-Control": "no-cache", "X-Requested-With": "XMLHttpRequest", }; if (this.options.headers) { Dropzone.extend(headers, this.options.headers); } for (let headerName in headers) { let headerValue = headers[headerName]; if (headerValue) { xhr.setRequestHeader(headerName, headerValue); } } let formData = new FormData(); // Adding all @options parameters if (this.options.params) { let additionalParams = this.options.params; if (typeof additionalParams === "function") { additionalParams = additionalParams.call( this, files, xhr, files[0].upload.chunked ? this._getChunk(files[0], xhr) : null ); } for (let key in additionalParams) { let value = additionalParams[key]; if (Array.isArray(value)) { // The additional parameter contains an array, // so lets iterate over it to attach each value // individually. for (let i = 0; i < value.length; i++) { formData.append(key, value[i]); } } else { formData.append(key, value); } } } // Let the user add additional data if necessary for (let file of files) { this.emit("sending", file, xhr, formData); } if (this.options.uploadMultiple) { this.emit("sendingmultiple", files, xhr, formData); } this._addFormElementData(formData); // Finally add the files // Has to be last because some servers (eg: S3) expect the file to be the last parameter for (let i = 0; i < dataBlocks.length; i++) { let dataBlock = dataBlocks[i]; formData.append(dataBlock.name, dataBlock.data, dataBlock.filename); } this.submitRequest(xhr, formData, files); } // Transforms all files with this.options.transformFile and invokes done with the transformed files when done. _transformFiles(files, done) { let transformedFiles = []; // Clumsy way of handling asynchronous calls, until I get to add a proper Future library. let doneCounter = 0; for (let i = 0; i < files.length; i++) { this.options.transformFile.call(this, files[i], (transformedFile) => { transformedFiles[i] = transformedFile; if (++doneCounter === files.length) { done(transformedFiles); } }); } } // Takes care of adding other input elements of the form to the AJAX request _addFormElementData(formData) { // Take care of other input elements if (this.element.tagName === "FORM") { for (let input of this.element.querySelectorAll( "input, textarea, select, button" )) { let inputName = input.getAttribute("name"); let inputType = input.getAttribute("type"); if (inputType) inputType = inputType.toLowerCase(); // If the input doesn't have a name, we can't use it. if (typeof inputName === "undefined" || inputName === null) continue; if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { // Possibly multiple values for (let option of input.options) { if (option.selected) { formData.append(inputName, option.value); } } } else if ( !inputType || (inputType !== "checkbox" && inputType !== "radio") || input.checked ) { formData.append(inputName, input.value); } } } } // Invoked when there is new progress information about given files. // If e is not provided, it is assumed that the upload is finished. _updateFilesUploadProgress(files, xhr, e) { if (!files[0].upload.chunked) { // Handle file uploads without chunking for (let file of files) { if ( file.upload.total && file.upload.bytesSent && file.upload.bytesSent == file.upload.total ) { // If both, the `total` and `bytesSent` have already been set, and // they are equal (meaning progress is at 100%), we can skip this // file, since an upload progress shouldn't go down. continue; } if (e) { file.upload.progress = (100 * e.loaded) / e.total; file.upload.total = e.total; file.upload.bytesSent = e.loaded; } else { // No event, so we're at 100% file.upload.progress = 100; file.upload.bytesSent = file.upload.total; } this.emit( "uploadprogress", file, file.upload.progress, file.upload.bytesSent ); } } else { // Handle chunked file uploads // Chunked upload is not compatible with uploading multiple files in one // request, so we know there's only one file. let file = files[0]; // Since this is a chunked upload, we need to update the appropriate chunk // progress. let chunk = this._getChunk(file, xhr); if (e) { chunk.progress = (100 * e.loaded) / e.total; chunk.total = e.total; chunk.bytesSent = e.loaded; } else { // No event, so we're at 100% chunk.progress = 100; chunk.bytesSent = chunk.total; } // Now tally the *file* upload progress from its individual chunks file.upload.progress = 0; file.upload.total = 0; file.upload.bytesSent = 0; for (let i = 0; i < file.upload.totalChunkCount; i++) { if ( file.upload.chunks[i] && typeof file.upload.chunks[i].progress !== "undefined" ) { file.upload.progress += file.upload.chunks[i].progress; file.upload.total += file.upload.chunks[i].total; file.upload.bytesSent += file.upload.chunks[i].bytesSent; } } // Since the process is a percentage, we need to divide by the amount of // chunks we've used. file.upload.progress = file.upload.progress / file.upload.totalChunkCount; this.emit( "uploadprogress", file, file.upload.progress, file.upload.bytesSent ); } } _finishedUploading(files, xhr, e) { let response; if (files[0].status === Dropzone.CANCELED) { return; } if (xhr.readyState !== 4) { return; } if (xhr.responseType !== "arraybuffer" && xhr.responseType !== "blob") { response = xhr.responseText; if ( xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json") ) { try { response = JSON.parse(response); } catch (error) { e = error; response = "Invalid JSON response from server."; } } } this._updateFilesUploadProgress(files, xhr); if (!(200 <= xhr.status && xhr.status < 300)) { this._handleUploadError(files, xhr, response); } else { if (files[0].upload.chunked) { files[0].upload.finishedChunkUpload( this._getChunk(files[0], xhr), response ); } else { this._finished(files, response, e); } } } _handleUploadError(files, xhr, response) { if (files[0].status === Dropzone.CANCELED) { return; } if (files[0].upload.chunked && this.options.retryChunks) { let chunk = this._getChunk(files[0], xhr); if (chunk.retries++ < this.options.retryChunksLimit) { this._uploadData(files, [chunk.dataBlock]); return; } else { console.warn("Retried this chunk too often. Giving up."); } } this._errorProcessing( files, response || this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr ); } submitRequest(xhr, formData, files) { if (xhr.readyState != 1) { console.warn( "Cannot send this request because the XMLHttpRequest.readyState is not OPENED." ); return; } xhr.send(formData); } // Called internally when processing is finished. // Individual callbacks have to be called in the appropriate sections. _finished(files, responseText, e) { for (let file of files) { file.status = Dropzone.SUCCESS; this.emit("success", file, responseText, e); this.emit("complete", file); } if (this.options.uploadMultiple) { this.emit("successmultiple", files, responseText, e); this.emit("completemultiple", files); } if (this.options.autoProcessQueue) { return this.processQueue(); } } // Called internally when processing is finished. // Individual callbacks have to be called in the appropriate sections. _errorProcessing(files, message, xhr) { for (let file of files) { file.status = Dropzone.ERROR; this.emit("error", file, message, xhr); this.emit("complete", file); } if (this.options.uploadMultiple) { this.emit("errormultiple", files, message, xhr); this.emit("completemultiple", files); } if (this.options.autoProcessQueue) { return this.processQueue(); } } static uuidv4() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( /[xy]/g, function (c) { let r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); } ); } } Dropzone.initClass(); Dropzone.version = "dev"; // This is a map of options for your different dropzones. Add configurations // to this object for your different dropzone elemens. // // Example: // // Dropzone.options.myDropzoneElementId = { maxFilesize: 1 }; // // To disable autoDiscover for a specific element, you can set `false` as an option: // // Dropzone.options.myDisabledElementId = false; // // And in html: // // <form action="/upload" id="my-dropzone-element-id" class="dropzone"></form> Dropzone.options = {}; // Returns the options for an element or undefined if none available. Dropzone.optionsForElement = function (element) { // Get the `Dropzone.options.elementId` for this element if it exists if (element.getAttribute("id")) { return Dropzone.options[camelize(element.getAttribute("id"))]; } else { return undefined; } }; // Holds a list of all dropzone instances Dropzone.instances = []; // Returns the dropzone for given element if any Dropzone.forElement = function (element) { if (typeof element === "string") { element = document.querySelector(element); } if ((element != null ? element.dropzone : undefined) == null) { throw new Error( "No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone." ); } return element.dropzone; }; // Set to false if you don't want Dropzone to automatically find and attach to .dropzone elements. Dropzone.autoDiscover = true; // Looks for all .dropzone elements and creates a dropzone for them Dropzone.discover = function () { let dropzones; if (document.querySelectorAll) { dropzones = document.querySelectorAll(".dropzone"); } else { dropzones = []; // IE :( let checkElements = (elements) => (() => { let result = []; for (let el of elements) { if (/(^| )dropzone($| )/.test(el.className)) { result.push(dropzones.push(el)); } else { result.push(undefined); } } return result; })(); checkElements(document.getElementsByTagName("div")); checkElements(document.getElementsByTagName("form")); } return (() => { let result = []; for (let dropzone of dropzones) { // Create a dropzone unless auto discover has been disabled for specific element if (Dropzone.optionsForElement(dropzone) !== false) { result.push(new Dropzone(dropzone)); } else { result.push(undefined); } } return result; })(); }; // Some browsers support drag and drog functionality, but not correctly. // // So I created a blocklist of userAgents. Yes, yes. Browser sniffing, I know. // But what to do when browsers *theoretically* support an API, but crash // when using it. // // This is a list of regular expressions tested against navigator.userAgent // // ** It should only be used on browser that *do* support the API, but // incorrectly ** Dropzone.blockedBrowsers = [ // The mac os and windows phone version of opera 12 seems to have a problem with the File drag'n'drop API. /opera.*(Macintosh|Windows Phone).*version\/12/i, ]; // Checks if the browser is supported Dropzone.isBrowserSupported = function () { let capableBrowser = true; if ( window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector ) { if (!("classList" in document.createElement("a"))) { capableBrowser = false; } else { if (Dropzone.blacklistedBrowsers !== undefined) { // Since this has been renamed, this makes sure we don't break older // configuration. Dropzone.blockedBrowsers = Dropzone.blacklistedBrowsers; } // The browser supports the API, but may be blocked. for (let regex of Dropzone.blockedBrowsers) { if (regex.test(navigator.userAgent)) { capableBrowser = false; continue; } } } } else { capableBrowser = false; } return capableBrowser; }; Dropzone.dataURItoBlob = function (dataURI) { // convert base64 to raw binary data held in a string // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this let byteString = atob(dataURI.split(",")[1]); // separate out the mime component let mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; // write the bytes of the string to an ArrayBuffer let ab = new ArrayBuffer(byteString.length); let ia = new Uint8Array(ab); for ( let i = 0, end = byteString.length, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i-- ) { ia[i] = byteString.charCodeAt(i); } // write the ArrayBuffer to a blob return new Blob([ab], { type: mimeString }); }; // Returns an array without the rejected item const without = (list, rejectedItem) => list.filter((item) => item !== rejectedItem).map((item) => item); // abc-def_ghi -> abcDefGhi const camelize = (str) => str.replace(/[\-_](\w)/g, (match) => match.charAt(1).toUpperCase()); // Creates an element from string Dropzone.createElement = function (string) { let div = document.createElement("div"); div.innerHTML = string; return div.childNodes[0]; }; // Tests if given element is inside (or simply is) the container Dropzone.elementInside = function (element, container) { if (element === container) { return true; } // Coffeescript doesn't support do/while loops while ((element = element.parentNode)) { if (element === container) { return true; } } return false; }; Dropzone.getElement = function (el, name) { let element; if (typeof el === "string") { element = document.querySelector(el); } else if (el.nodeType != null) { element = el; } if (element == null) { throw new Error( `Invalid \`${name}\` option provided. Please provide a CSS selector or a plain HTML element.` ); } return element; }; Dropzone.getElements = function (els, name) { let el, elements; if (els instanceof Array) { elements = []; try { for (el of els) { elements.push(this.getElement(el, name)); } } catch (e) { elements = null; } } else if (typeof els === "string") { elements = []; for (el of document.querySelectorAll(els)) { elements.push(el); } } else if (els.nodeType != null) { elements = [els]; } if (elements == null || !elements.length) { throw new Error( `Invalid \`${name}\` option provided. Please provide a CSS selector, a plain HTML element or a list of those.` ); } return elements; }; // Asks the user the question and calls accepted or rejected accordingly // // The default implementation just uses `window.confirm` and then calls the // appropriate callback. Dropzone.confirm = function (question, accepted, rejected) { if (window.confirm(question)) { return accepted(); } else if (rejected != null) { return rejected(); } }; // Validates the mime type like this: // // https://developer.mozilla.org/en-US/docs/HTML/Element/input#attr-accept Dropzone.isValidFile = function (file, acceptedFiles) { if (!acceptedFiles) { return true; } // If there are no accepted mime types, it's OK acceptedFiles = acceptedFiles.split(","); let mimeType = file.type; let baseMimeType = mimeType.replace(/\/.*$/, ""); for (let validType of acceptedFiles) { validType = validType.trim(); if (validType.charAt(0) === ".") { if ( file.name .toLowerCase() .indexOf( validType.toLowerCase(), file.name.length - validType.length ) !== -1 ) { return true; } } else if (/\/\*$/.test(validType)) { // This is something like a image/* mime type if (baseMimeType === validType.replace(/\/.*$/, "")) { return true; } } else { if (mimeType === validType) { return true; } } } return false; }; // Augment jQuery if (typeof jQuery !== "undefined" && jQuery !== null) { jQuery.fn.dropzone = function (options) { return this.each(function () { return new Dropzone(this, options); }); }; } // Dropzone file status codes Dropzone.ADDED = "added"; Dropzone.QUEUED = "queued"; // For backwards compatibility. Now, if a file is accepted, it's either queued // or uploading. Dropzone.ACCEPTED = Dropzone.QUEUED; Dropzone.UPLOADING = "uploading"; Dropzone.PROCESSING = Dropzone.UPLOADING; // alias Dropzone.CANCELED = "canceled"; Dropzone.ERROR = "error"; Dropzone.SUCCESS = "success"; /* Bugfix for iOS 6 and 7 Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios based on the work of https://github.com/stomita/ios-imagefile-megapixel */ // Detecting vertical squash in loaded image. // Fixes a bug which squash image vertically while drawing into canvas for some images. // This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel let detectVerticalSquash = function (img) { let iw = img.naturalWidth; let ih = img.naturalHeight; let canvas = document.createElement("canvas"); canvas.width = 1; canvas.height = ih; let ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); let { data } = ctx.getImageData(1, 0, 1, ih); // search image edge pixel position in case it is squashed vertically. let sy = 0; let ey = ih; let py = ih; while (py > sy) { let alpha = data[(py - 1) * 4 + 3]; if (alpha === 0) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } let ratio = py / ih; if (ratio === 0) { return 1; } else { return ratio; } }; // A replacement for context.drawImage // (args are for source and destination). var drawImageIOSFix = function (ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { let vertSquashRatio = detectVerticalSquash(img); return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); }; // Based on MinifyJpeg // Source: http://www.perry.cz/files/ExifRestorer.js // http://elicon.blog57.fc2.com/blog-entry-206.html class ExifRestore { static initClass() { this.KEY_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; } static encode64(input) { let output = ""; let chr1 = undefined; let chr2 = undefined; let chr3 = ""; let enc1 = undefined; let enc2 = undefined; let enc3 = undefined; let enc4 = ""; let i = 0; while (true) { chr1 = input[i++]; chr2 = input[i++]; chr3 = input[i++]; enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4); chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; if (!(i < input.length)) { break; } } return output; } static restore(origFileBase64, resizedFileBase64) { if (!origFileBase64.match("data:image/jpeg;base64,")) { return resizedFileBase64; } let rawImage = this.decode64( origFileBase64.replace("data:image/jpeg;base64,", "") ); let segments = this.slice2Segments(rawImage); let image = this.exifManipulation(resizedFileBase64, segments); return `data:image/jpeg;base64,${this.encode64(image)}`; } static exifManipulation(resizedFileBase64, segments) { let exifArray = this.getExifArray(segments); let newImageArray = this.insertExif(resizedFileBase64, exifArray); let aBuffer = new Uint8Array(newImageArray); return aBuffer; } static getExifArray(segments) { let seg = undefined; let x = 0; while (x < segments.length) { seg = segments[x]; if ((seg[0] === 255) & (seg[1] === 225)) { return seg; } x++; } return []; } static insertExif(resizedFileBase64, exifArray) { let imageData = resizedFileBase64.replace("data:image/jpeg;base64,", ""); let buf = this.decode64(imageData); let separatePoint = buf.indexOf(255, 3); let mae = buf.slice(0, separatePoint); let ato = buf.slice(separatePoint); let array = mae; array = array.concat(exifArray); array = array.concat(ato); return array; } static slice2Segments(rawImageArray) { let head = 0; let segments = []; while (true) { var length; if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 218)) { break; } if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 216)) { head += 2; } else { length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3]; let endPoint = head + length + 2; let seg = rawImageArray.slice(head, endPoint); segments.push(seg); head = endPoint; } if (head > rawImageArray.length) { break; } } return segments; } static decode64(input) { let output = ""; let chr1 = undefined; let chr2 = undefined; let chr3 = ""; let enc1 = undefined; let enc2 = undefined; let enc3 = undefined; let enc4 = ""; let i = 0; let buf = []; // remove all characters that are not A-Z, a-z, 0-9, +, /, or = let base64test = /[^A-Za-z0-9\+\/\=]/g; if (base64test.exec(input)) { console.warn( "There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding." ); } input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (true) { enc1 = this.KEY_STR.indexOf(input.charAt(i++)); enc2 = this.KEY_STR.indexOf(input.charAt(i++)); enc3 = this.KEY_STR.indexOf(input.charAt(i++)); enc4 = this.KEY_STR.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; buf.push(chr1); if (enc3 !== 64) { buf.push(chr2); } if (enc4 !== 64) { buf.push(chr3); } chr1 = chr2 = chr3 = ""; enc1 = enc2 = enc3 = enc4 = ""; if (!(i < input.length)) { break; } } return buf; } } ExifRestore.initClass(); /* * contentloaded.js * * Author: Diego Perini (diego.perini at gmail.com) * Summary: cross-browser wrapper for DOMContentLoaded * Updated: 20101020 * License: MIT * Version: 1.2 * * URL: * http://javascript.nwbox.com/ContentLoaded/ * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE */ // @win window reference // @fn function reference let contentLoaded = function (win, fn) { let done = false; let top = true; let doc = win.document; let root = doc.documentElement; let add = doc.addEventListener ? "addEventListener" : "attachEvent"; let rem = doc.addEventListener ? "removeEventListener" : "detachEvent"; let pre = doc.addEventListener ? "" : "on"; var init = function (e) { if (e.type === "readystatechange" && doc.readyState !== "complete") { return; } (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); if (!done && (done = true)) { return fn.call(win, e.type || e); } }; var poll = function () { try { root.doScroll("left"); } catch (e) { setTimeout(poll, 50); return; } return init("poll"); }; if (doc.readyState !== "complete") { if (doc.createEventObject && root.doScroll) { try { top = !win.frameElement; } catch (error) {} if (top) { poll(); } } doc[add](pre + "DOMContentLoaded", init, false); doc[add](pre + "readystatechange", init, false); return win[add](pre + "load", init, false); } }; // As a single function to be able to write tests. Dropzone._autoDiscoverFunction = function () { if (Dropzone.autoDiscover) { return Dropzone.discover(); } }; contentLoaded(window, Dropzone._autoDiscoverFunction); function __guard__(value, transform) { return typeof value !== "undefined" && value !== null ? transform(value) : undefined; } function __guardMethod__(obj, methodName, transform) { if ( typeof obj !== "undefined" && obj !== null && typeof obj[methodName] === "function" ) { return transform(obj, methodName); } else { return undefined; } } export { Dropzone };