Software: Apache/2.4.41 (Ubuntu). PHP/8.0.30 uname -a: Linux apirnd 5.4.0-204-generic #224-Ubuntu SMP Thu Dec 5 13:38:28 UTC 2024 x86_64 uid=33(www-data) gid=33(www-data) groups=33(www-data) Safe-mode: OFF (not secure) /var/www/html/queuepro/node_modules/dropzone/src/ drwxrwxr-x | |
| Viewing file: Select action/file-type: import extend from "just-extend";
import Emitter from "./emitter";
import defaultOptions from "./options";
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;
}
constructor(el, options) {
super();
let fallback, left;
this.element = el;
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 = extend(
true,
{},
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.");
}
if (this.options.binaryBody && this.options.uploadMultiple) {
throw new Error("You cannot set both: binaryBody and uploadMultiple.");
}
// 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;
chunk.response = chunk.xhr.responseText;
chunk.responseHeaders = chunk.xhr.getAllResponseHeaders();
// Leaving this reference to xhr will cause memory leaks.
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) then
// they will be used for the actual data to 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, dataBlocks);
let url = this.resolveOption(this.options.url, files, dataBlocks);
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 = this.options.defaultHeaders
? {
Accept: "application/json",
"Cache-Control": "no-cache",
"X-Requested-With": "XMLHttpRequest",
}
: {};
if (this.options.binaryBody) {
headers["Content-Type"] = files[0].type;
}
if (this.options.headers) {
extend(headers, this.options.headers);
}
for (let headerName in headers) {
let headerValue = headers[headerName];
if (headerValue) {
xhr.setRequestHeader(headerName, headerValue);
}
}
if (this.options.binaryBody) {
// Since the file is going to be sent as binary body, it doesn't make
// any sense to generate `FormData` for it.
for (let file of files) {
this.emit("sending", file, xhr);
}
if (this.options.uploadMultiple) {
this.emit("sendingmultiple", files, xhr);
}
this.submitRequest(xhr, null, files);
} else {
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;
}
if (this.options.binaryBody) {
if (files[0].upload.chunked) {
const chunk = this._getChunk(files[0], xhr);
xhr.send(chunk.dataBlock.data);
} else {
xhr.send(files[0]);
}
} else {
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();
// 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 };
//
// 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;
};
// 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);
}
};
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 };
|
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0061 ]-- |