PageSourceSearch

https://challenges.tekuoia.com/lib/dropzone/dropzone.js

js tekuoia.com collected 2026-09-29 09:39:24 UTC 100,784 bytes, 2,888 lines download raw bytes

1/*
2 *
3 * More info at [www.dropzonejs.com](http://www.dropzonejs.com)
4 *
5 *  (c) 2012, Matias Meno
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 */
26
27
28// The Emitter class provides the ability to call `.on()` on Dropzone to listen
29// to events.
30// It is strongly based on component's emitter class, and I removed the
31// functionality because of the dependency hell with different frameworks.
32class Emitter {
33    // Add an event listener for given event
34    on(event, fn) {
35        this._callbacks = this._callbacks || {};
36        // Create namespace for this event
37        if (!this._callbacks[event]) {
38            this._callbacks[event] = [];
39        }
40        this._callbacks[event].push(fn);
41        return this;
42    }
43
44
45    emit(event, ...args) {
46        this._callbacks = this._callbacks || {};
47        let callbacks = this._callbacks[event];
48
49        if (callbacks) {
50            for (let callback of callbacks) {
51                callback.apply(this, args);
52            }
53        }
54
55        return this;
56    }
57
58    // Remove event listener for given event. If fn is not provided, all event
59    // listeners for that event will be removed. If neither is provided, all
60    // event listeners will be removed.
61    off(event, fn) {
62        if (!this._callbacks || (arguments.length === 0)) {
63            this._callbacks = {};
64            return this;
65        }
66
67        // specific event
68        let callbacks = this._callbacks[event];
69        if (!callbacks) {
70            return this;
71        }
72
73        // remove all handlers
74        if (arguments.length === 1) {
75            delete this._callbacks[event];
76            return this;
77        }
78
79        // remove specific handler
80        for (let i = 0; i < callbacks.length; i++) {
81            let callback = callbacks[i];
82            if (callback === fn) {
83                callbacks.splice(i, 1);
84                break;
85            }
86        }
87
88        return this;
89    }
90}
91
92class Dropzone extends Emitter {
93    static initClass() {
94
95        // Exposing the emitter class, mainly for tests
96        this.prototype.Emitter = Emitter;
97
98        /*
99         This is a list of all available events you can register on a dropzone object.
100
101         You can register an event handler like this:
102
103         dropzone.on("dragEnter", function() { });
104
105         */
106        this.prototype.events = [
107            "drop",
108            "dragstart",
109            "dragend",
110            "dragenter",
111            "dragover",
112            "dragleave",
113            "addedfile",
114            "addedfiles",
115            "removedfile",
116            "thumbnail",
117            "error",
118            "errormultiple",
119            "processing",
120            "processingmultiple",
121            "uploadprogress",
122            "totaluploadprogress",
123            "sending",
124            "sendingmultiple",
125            "success",
126            "successmultiple",
127            "canceled",
128            "canceledmultiple",
129            "complete",
130            "completemultiple",
131            "reset",
132            "maxfilesexceeded",
133            "maxfilesreached",
134            "queuecomplete"
135        ];
136
137
138        this.prototype.defaultOptions = {
139            /**
140             * Has to be specified on elements other than form (or when the form
141             * doesn't have an `action` attribute). You can also
142             * provide a function that will be called with `files` and
143             * must return the url (since `v3.12.0`)
144             */
145            url: null,
146
147            /**
148             * Can be changed to `"put"` if necessary. You can also provide a function
149             * that will be called with `files` and must return the method (since `v3.12.0`).
150             */
151            method: "post",
152
153            /**
154             * Will be set on the XHRequest.
155             */
156            withCredentials: false,
157
158            /**
159             * The timeout for the XHR requests in milliseconds (since `v4.4.0`).
160             */
161            timeout: 30000,
162
163            /**
164             * How many file uploads to process in parallel (See the
165             * Enqueuing file uploads documentation section for more info)
166             */
167            parallelUploads: 2,
168
169            /**
170             * Whether to send multiple files in one request. If
171             * this it set to true, then the fallback file input element will
172             * have the `multiple` attribute as well. This option will
173             * also trigger additional events (like `processingmultiple`). See the events
174             * documentation section for more information.
175             */
176            uploadMultiple: false,
177
178            /**
179             * Whether you want files to be uploaded in chunks to your server. This can't be
180             * used in combination with `uploadMultiple`.
181             *
182             * See [chunksUploaded](#config-chunksUploaded) for the callback to finalise an upload.
183             */
184            chunking: false,
185
186            /**
187             * If `chunking` is enabled, this defines whether **every** file should be chunked,
188             * even if the file size is below chunkSize. This means, that the additional chunk
189             * form data will be submitted and the `chunksUploaded` callback will be invoked.
190             */
191            forceChunking: false,
192
193            /**
194             * If `chunking` is `true`, then this defines the chunk size in bytes.
195             */
196            chunkSize: 2000000,
197
198            /**
199             * If `true`, the individual chunks of a file are being uploaded simultaneously.
200             */
201            parallelChunkUploads: false,
202
203            /**
204             * Whether a chunk should be retried if it fails.
205             */
206            retryChunks: false,
207
208            /**
209             * If `retryChunks` is true, how many times should it be retried.
210             */
211            retryChunksLimit: 3,
212
213            /**
214             * If not `null` defines how many files this Dropzone handles. If it exceeds,
215             * the event `maxfilesexceeded` will be called. The dropzone element gets the
216             * class `dz-max-files-reached` accordingly so you can provide visual feedback.
217             */
218            maxFilesize: 256,
219
220            /**
221             * The name of the file param that gets transferred.
222             * **NOTE**: If you have the option  `uploadMultiple` set to `true`, then
223             * Dropzone will append `[]` to the name.
224             */
225            paramName: "upfile",
226
227            /**
228             * Whether thumbnails for images should be generated
229             */
230            createImageThumbnails: true,
231
232            /**
233             * In MB. When the filename exceeds this limit, the thumbnail will not be generated.
234             */
235            maxThumbnailFilesize: 10,
236
237            /**
238             * If `null`, the ratio of the image will be used to calculate it.
239             */
240            thumbnailWidth: 120,
241
242            /**
243             * The same as `thumbnailWidth`. If both are null, images will not be resized.
244             */
245            thumbnailHeight: 120,
246
247            /**
248             * How the images should be scaled down in case both, `thumbnailWidth` and `thumbnailHeight` are provided.
249             * Can be either `contain` or `crop`.
250             */
251            thumbnailMethod: 'crop',
252
253            /**
254             * If set, images will be resized to these dimensions before being **uploaded**.
255             * If only one, `resizeWidth` **or** `resizeHeight` is provided, the original aspect
256             * ratio of the file will be preserved.
257             *
258             * The `options.transformFile` function uses these options, so if the `transformFile` function
259             * is overridden, these options don't do anything.
260             */
261            resizeWidth: null,
262
263            /**
264             * See `resizeWidth`.
265             */
266            resizeHeight: null,
267
268            /**
269             * The mime type of the resized image (before it gets uploaded to the server).
270             * If `null` the original mime type will be used. To force jpeg, for example, use `image/jpeg`.
271             * See `resizeWidth` for more information.
272             */
273            resizeMimeType: null,
274
275            /**
276             * The quality of the resized images. See `resizeWidth`.
277             */
278            resizeQuality: 0.8,
279
280            /**
281             * How the images should be scaled down in case both, `resizeWidth` and `resizeHeight` are provided.
282             * Can be either `contain` or `crop`.
283             */
284            resizeMethod: 'contain',
285
286            /**
287             * The base that is used to calculate the filesize. You can change this to
288             * 1024 if you would rather display kibibytes, mebibytes, etc...
289             * 1024 is technically incorrect, because `1024 bytes` are `1 kibibyte` not `1 kilobyte`.
290             * You can change this to `1024` if you don't care about validity.
291             */
292            filesizeBase: 1000,
293
294            /**
295             * Can be used to limit the maximum number of files that will be handled by this Dropzone
296             */
297            maxFiles: null,
298
299            /**
300             * An optional object to send additional headers to the server. Eg:
301             * `{ "My-Awesome-Header": "header value" }`
302             */
303            headers: null,
304
305            /**
306             * If `true`, the dropzone element itself will be clickable, if `false`
307             * nothing will be clickable.
308             *
309             * You can also pass an HTML element, a CSS selector (for multiple elements)
310             * or an array of those. In that case, all of those elements will trigger an
311             * upload when clicked.
312             */
313            clickable: true,
314
315            /**
316             * Whether hidden files in directories should be ignored.
317             */
318            ignoreHiddenFiles: true,
319
320
321            /**
322             * The default implementation of `accept` checks the file's mime type or
323             * extension against this list. This is a comma separated list of mime
324             * types or file extensions.
325             *
326             * Eg.: `image/*,application/pdf,.psd`
327             *
328             * If the Dropzone is `clickable` this option will also be used as
329             * [`accept`](https://developer.mozilla.org/en-US/docs/HTML/Element/input#attr-accept)
330             * parameter on the hidden file input as well.
331             */
332            acceptedFiles: null,
333
334            /**
335             * **Deprecated!**
336             * Use acceptedFiles instead.
337             */
338            acceptedMimeTypes: null,
339
340            /**
341             * If false, files will be added to the queue but the queue will not be
342             * processed automatically.
343             * This can be useful if you need some additional user input before sending
344             * files (or if you want want all files sent at once).
345             * If you're ready to send the file simply call `myDropzone.processQueue()`.
346             *
347             * See the [enqueuing file uploads](#enqueuing-file-uploads) documentation
348             * section for more information.
349             */
350            autoProcessQueue: true,
351
352            /**
353             * If false, files added to the dropzone will not be queued by default.
354             * You'll have to call `enqueueFile(file)` manually.
355             */
356            autoQueue: true,
357
358            /**
359             * If `true`, this will add a link to every file preview to remove or cancel (if
360             * already uploading) the file. The `dictCancelUpload`, `dictCancelUploadConfirmation`
361             * and `dictRemoveFile` options are used for the wording.
362             */
363            addRemoveLinks: false,
364
365            /**
366             * Defines where to display the file previews – if `null` the
367             * Dropzone element itself is used. Can be a plain `HTMLElement` or a CSS
368             * selector. The element should have the `dropzone-previews` class so
369             * the previews are displayed properly.
370             */
371            previewsContainer: null,
372
373            /**
374             * This is the element the hidden input field (which is used when clicking on the
375             * dropzone to trigger file selection) will be appended to. This might
376             * be important in case you use frameworks to switch the content of your page.
377             *
378             * Can be a selector string, or an element directly.
379             */
380            hiddenInputContainer: "body",
381
382            /**
383             * If null, no capture type will be specified
384             * If camera, mobile devices will skip the file selection and choose camera
385             * If microphone, mobile devices will skip the file selection and choose the microphone
386             * If camcorder, mobile devices will skip the file selection and choose the camera in video mode
387             * On apple devices multiple must be set to false.  AcceptedFiles may need to
388             * be set to an appropriate mime type (e.g. "image/*", "audio/*", or "video/*").
389             */
390            capture: null,
391
392            /**
393             * **Deprecated**. Use `renameFile` instead.
394             */
395            renameFilename: null,
396
397            /**
398             * A function that is invoked before the file is uploaded to the server and renames the file.
399             * This function gets the `File` as argument and can use the `file.name`. The actual name of the
400             * file that gets used during the upload can be accessed through `file.upload.filename`.
401             */
402            renameFile: null,
403
404            /**
405             * If `true` the fallback will be forced. This is very useful to test your server
406             * implementations first and make sure that everything works as
407             * expected without dropzone if you experience problems, and to test
408             * how your fallbacks will look.
409             */
410            forceFallback: false,
411
412            /**
413             * The text used before any files are dropped.
414             */
415            dictDefaultMessage: "Drop files here to upload",
416
417            /**
418             * The text that replaces the default message text it the browser is not supported.
419             */
420            dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.",
421
422            /**
423             * The text that will be added before the fallback form.
424             * If you provide a  fallback element yourself, or if this option is `null` this will
425             * be ignored.
426             */
427            dictFallbackText: "Please use the fallback form below to upload your files like in the olde
427n days.",
428
429            /**
430             * If the filesize is too big.
431             * `{{filesize}}` and `{{maxFilesize}}` will be replaced with the respective configuration values.
432             */
433            dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",
434
435            /**
436             * If the file doesn't match the file type.
437             */
438            dictInvalidFileType: "You can't upload files of this type.",
439
440            /**
441             * If the server response was invalid.
442             * `{{statusCode}}` will be replaced with the servers status code.
443             */
444            dictResponseError: "Server responded with {{statusCode}} code.",
445
446            /**
447             * If `addRemoveLinks` is true, the text to be used for the cancel upload link.
448             */
449            dictCancelUpload: "Cancel upload",
450
451            /**
452             * The text that is displayed if an upload was manually canceled
453             */
454            dictUploadCanceled: "Upload canceled.",
455
456            /**
457             * If `addRemoveLinks` is true, the text to be used for confirmation when cancelling upload.
458             */
459            dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
460
461            /**
462             * If `addRemoveLinks` is true, the text to be used to remove a file.
463             */
464            dictRemoveFile: "Remove file",
465
466            /**
467             * If this is not null, then the user will be prompted before removing a file.
468             */
469            dictRemoveFileConfirmation: null,
470
471            /**
472             * Displayed if `maxFiles` is st and exceeded.
473             * The string `{{maxFiles}}` will be replaced by the configuration value.
474             */
475            dictMaxFilesExceeded: "You can not upload any more files.",
476
477            /**
478             * Allows you to translate the different units. Starting with `tb` for terabytes and going down to
479             * `b` for bytes.
480             */
481            dictFileSizeUnits: { tb: "TB", gb: "GB", mb: "MB", kb: "KB", b: "b" },
482            /**
483             * Called when dropzone initialized
484             * You can add event listeners here
485             */
486            init() {},
487
488            /**
489             * Can be an **object** of additional parameters to transfer to the server, **or** a `Function`
490             * that gets invoked with the `files`, `xhr` and, if it's a chunked upload, `chunk` arguments. In case
491             * of a function, this needs to return a map.
492             *
493             * The default implementation does nothing for normal uploads, but adds relevant information for
494             * chunked uploads.
495             *
496             * This is the same as adding hidden input fields in the form element.
497             */
498            params(files, xhr, chunk) {
499                if (chunk) {
500                    return {
501                        dzuuid: chunk.file.upload.uuid,
502                        dzchunkindex: chunk.index,
503                        dztotalfilesize: chunk.file.size,
504                        dzchunksize: this.options.chunkSize,
505                        dztotalchunkcount: chunk.file.upload.totalChunkCount,
506                        dzchunkbyteoffset: chunk.index * this.options.chunkSize
507                    };
508                }
509            },
510
511            /**
512             * A function that gets a [file](https://developer.mozilla.org/en-US/docs/DOM/File)
513             * and a `done` function as parameters.
514             *
515             * If the done function is invoked without arguments, the file is "accepted" and will
516             * be processed. If you pass an error message, the file is rejected, and the error
517             * message will be displayed.
518             * This function will not be called if the file is too big or doesn't match the mime types.
519             */
520            accept(file, done) {
521                return done();
522            },
523
524            /**
525             * The callback that will be invoked when all chunks have been uploaded for a file.
526             * It gets the file for which the chunks have been uploaded as the first parameter,
527             * and the `done` function as second. `done()` needs to be invoked when everything
528             * needed to finish the upload process is done.
529             */
530            chunksUploaded: function(file, done) { done(); },
531
532            /**
533             * Gets called when the browser is not supported.
534             * The default implementation shows the fallback input field and adds
535             * a text.
536             */
537            fallback() {
538                // This code should pass in IE7... :(
539                let messageElement;
540                this.element.className = `${this.element.className} dz-browser-not-supported`;
541
542                for (let child of this.element.getElementsByTagName("div")) {
543                    if (/(^| )dz-message($| )/.test(child.className)) {
544                        messageElement = child;
545                        child.className = "dz-message"; // Removes the 'dz-default' class
546                        break;
547                    }
548                }
549                if (!messageElement) {
550                    messageElement = Dropzone.createElement("<div class=\"dz-message\"><span></span></div>");
551                    this.element.appendChild(messageElement);
552                }
553
554                let span = messageElement.getElementsByTagName("span")[0];
555                if (span) {
556                    if (span.textContent != null) {
557                        span.textContent = this.options.dictFallbackMessage;
558                    }
558 else if (span.innerText != null) {
559                        span.innerText = this.options.dictFallbackMessage;
560                    }
561                }
562
563                return this.element.appendChild(this.getFallbackForm());
564            },
565
566
567            /**
568             * Gets called to calculate the thumbnail dimensions.
569             *
570             * It gets `file`, `width` and `height` (both may be `null`) as parameters and must return an object containing:
571             *
572             *  - `srcWidth` & `srcHeight` (required)
573             *  - `trgWidth` & `trgHeight` (required)
574             *  - `srcX` & `srcY` (optional, default `0`)
575             *  - `trgX` & `trgY` (optional, default `0`)
576             *
577             * Those values are going to be used by `ctx.drawImage()`.
578             */
579            resize(file, width, height, resizeMethod) {
580                let info = {
581                    srcX: 0,
582                    srcY: 0,
583                    srcWidth: file.width,
584                    srcHeight: file.height
585                };
586
587                let srcRatio = file.width / file.height;
588
589                // Automatically calculate dimensions if not specified
590                if ((width == null) && (height == null)) {
591                    width = info.srcWidth;
592                    height = info.srcHeight;
593                } else if ((width == null)) {
594                    width = height * srcRatio;
595                } else if ((height == null)) {
596                    height = width / srcRatio;
597                }
598
599                // Make sure images aren't upscaled
600                width = Math.min(width, info.srcWidth);
601                height = Math.min(height, info.srcHeight);
602
603                let trgRatio = width / height;
604
605                if ((info.srcWidth > width) || (info.srcHeight > height)) {
606                    // Image is bigger and needs rescaling
607                    if (resizeMethod === 'crop') {
608                        if (srcRatio > trgRatio) {
609                            info.srcHeight = file.height;
610                            info.srcWidth = info.srcHeight * trgRatio;
611                        } else {
612                            info.srcWidth = file.width;
613                            info.srcHeight = info.srcWidth / trgRatio;
614                        }
615                    } else if (resizeMethod === 'contain') {
616                        // Method 'contain'
617                        if (srcRatio > trgRatio) {
618                            height = width / srcRatio;
619                        } else {
620                            width = height * srcRatio;
621                        }
622                    } else {
623                        throw new Error(`Unknown resizeMethod '${resizeMethod}'`);
624                    }
625                }
626
627                info.srcX = (file.width - info.srcWidth) / 2;
628                info.srcY = (file.height - info.srcHeight) / 2;
629
630                info.trgWidth = width;
631                info.trgHeight = height;
632
633                return info;
634            },
635
636            /**
637             * Can be used to transform the file (for example, resize an image if necessary).
638             *
639             * The default implementation uses `resizeWidth` and `resizeHeight` (if provided) and resizes
640             * images according to those dimensions.
641             *
642             * Gets the `file` as the first parameter, and a `done()` function as the second, that needs
643             * to be invoked with the file when the transformation is done.
644             */
645            transformFile(file, done) {
646                if ((this.options.resizeWidth || this.options.resizeHeight) && file.type.match(/image.*/)) {
647                    return this.resizeImage(file, this.options.resizeWidth, this.options.resizeHeight, this.options.resizeMethod, done);
648                } else {
649                    return done(file);
650                }
651            },
652
653
654            /**
655             * A string that contains the template used for each dropped
656             * file. Change it to fulfill your needs but make sure to properly
657             * provide all elements.
658             *
659             * If you want to use an actual HTML element instead of providing a String
660             * as a config option, you could create a div with the id `tpl`,
661             * put the template inside it and provide the element like this:
662             *
663             *     document
664             *       .querySelector('#tpl')
665             *       .innerHTML
666             *
667             */
668            previewTemplate: `\
669<div class="dz-preview dz-file-preview">
670  <div class="dz-image"><img data-dz-thumbnail /></div>
671  <div class="dz-details">
672    <div class="dz-size"><span data-dz-size></span></div>
673    <div class="dz-filename"><span data-dz-name></span></div>
674  </div>
675  <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
676  <div class="dz-error-message"><span data-dz-errormessage></span></div>
677  <div class="dz-success-mark">
678    <svg width="54px" height="54px" viewBox="0 0 54 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
679      <title>Check</title>
680      <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
681        <path d="M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z" stroke-opacity="0.198794158" stroke="#747474" fill-opacity="0.816519475" fill="#FFFFFF"></path>
682      </g>
683    </svg>
684  </div>
685  <div class="dz-error-mark">
686    <svg width="54px" height="54px" viewBox="0 0 54 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
687      <title>Error</title>
688      <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
689        <g stroke="#747474" stroke-opacity="0.198794158" fill="#FFFFFF" fill-opacity="0.816519475">
690          <path d="M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z"></path>
691        </g>
692      </g>
693    </svg>
694  </div>
695</div>\
696`,
697
698            // END OPTIONS
699            // (Required by the dropzone documentation parser)
700
701
702            /*
703             Those functions register themselves to the events on init and handle all
704             the user interface specific stuff. Overwriting them won't break the upload
705             but can break the way it's displayed.
706             You can overwrite them if you don't like the default behavior. If you just
707             want to add an additional event handler, register it on the dropzone object
708             and don't overwrite those options.
709             */
710
711
712
713
714            // Those are self explanatory and simply concern the DragnDrop.
715            drop(e) {
716                return this.element.classList.remove("dz-drag-hover");
717            },
718            dragstart(e) {},
719            dragend(e) {
720                return this.element.classList.remove("dz-drag-hover");
721            },
722            dragenter(e) {
723                return this.element.classList.add("dz-drag-hover");
724            },
725            dragover(e) {
726                return this.element.classList.add("dz-drag-hover");
727            },
728            dragleave(e) {
729                return this.element.classList.remove("dz-drag-hover");
730            },
731
732            paste(e) {},
733
734            // Called whenever there are no files left in the dropzone anymore, and the
735            // dropzone should be displayed as if in the initial state.
736            reset() {
737                return this.element.classList.remove("dz-started");
738            },
739
740            // Called when a file is added to the queue
741            // Receives `file`
742            addedfile(file) {
743                if (this.element === this.previewsContainer) {
744                    this.element.classList.add("dz-started");
745                }
746
747                if (this.previewsContainer) {
748                    file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim());
749                    file.previewTemplate = file.previewElement; // Backwards compatibility
750
751                    this.previewsContainer.appendChild(file.previewElement);
752                    for (var node of file.previewElement.querySelectorAll("[data-dz-name]")) {
753                        node.textContent = file.name;
754                    }
755                    for (node of file.previewElement.querySelectorAll("[data-dz-size]")) {
756                        node.innerHTML = this.filesize(file.size);
757                    }
758
759                    if (this.options.addRemoveLinks) {
760                        file._removeLink = Dropzone.createElement(`<a class="dz-remove" href="javascript:undefined;" data-dz-remove>${this.options.dictRemoveFile}</a>`);
761                        file.previewElement.appendChild(file._removeLink);
762                    }
763
764                    let removeFileEvent = e => {
765                        e.preventDefault();
766                        e.stopPropagation();
767                        if (file.status === Dropzone.UPLOADING) {
768                            return Dropzone.confirm(this.options.dictCancelUploadConfirmation, () => this.removeFile(file));
769                        } else {
770                            if (this.options.dictRemoveFileConfirmation) {
771                                return Dropzone.confirm(this.options.dictRemoveFileConfirmation, () => this.removeFile(file));
772                            } else {
773                                return this.removeFile(file);
774                            }
775                        }
776                    };
777
778                    for (let removeLink of file.previewElement.querySelectorAll("[data-dz-remove]")) {
779                        removeLink.addEventListener("click", removeFileEvent);
780                    }
781                }
782            },
783
784
785            // Called whenever a file is removed.
786            removedfile(file) {
787                if (file.previewElement != null && file.previewElement.parentNode != null) {
788                    file.previewElement.parentNode.removeChild(file.previewElement);
789                }
790                return this._updateMaxFilesReachedClass();
791            },
792
793            // Called when a thumbnail has been generated
794            // Receives `file` and `dataUrl`
795            thumbnail(file, dataUrl) {
796                if (file.previewElement) {
797                    file.previewElement.classList.remove("dz-file-preview");
798                    for (let thumbnailElement of file.previewElement.querySelectorAll("[data-dz-thumbnail]")) {
799                        thumbnailElement.alt = file.name;
800                        thumbnailElement.src = dataUrl;
801                    }
802
803                    return setTimeout((() => file.previewElement.classList.add("dz-image-preview")), 1);
804                }
805            },
806
807            // Called whenever an error occurs
808            // Receives `file` and `message`
809            error(file, message) {
810                if (file.previewElement) {
811                    file.previewElement.classList.add("dz-error");
812                    if ((typeof message !== "String") && message.error) {
813                        message = message.error;
814                    }
815                    for (let node of file.previewElement.querySelectorAll("[data-dz-errormessage]")) {
816                        node.textContent = message;
817                    }
818                }
819            },
820
821            errormultiple() {},
822
823            // Called when a file gets processed. Since there is a cue, not all added
824            // files are processed immediately.
825            // Receives `file`
826            processing(file) {
827                if (file.previewElement) {
828                    file.previewElement.classList.add("dz-processing");
829                    if (file._removeLink) {
830                        return file._removeLink.innerHTML = this.options.dictCancelUpload;
831                    }
832                }
833            },
834
835            processingmultiple() {},
836
837            // Called whenever the upload progress gets updated.
838            // Receives `file`, `progress` (percentage 0-100) and `bytesSent`.
839            // To get the total number of bytes of the file, use `file.size`
840            uploadprogress(file, progress, bytesSent) {
841                if (file.previewElement) {
842                    for (let node of file.previewElement.querySelectorAll("[data-dz-uploadprogress]")) {
843                        node.nodeName === 'PROGRESS' ?
844                            (node.value = progress) :
845                            (node.style.width = `${progress}%`)
846                    }
847                }
848            },
849
850            // Called whenever the total upload progress gets updated.
851            // Called with totalUploadProgress (0-100), totalBytes and totalBytesSent
852            totaluploadprogress() {},
853
854            // Called just before the file is sent. Gets the `xhr` object as second
855            // parameter, so you can modify it (for example to add a CSRF token) and a
856            // `formData` object to add additional information.
857            sending() {},
858
859            sendingmultiple() {},
860
861            // When the complete upload is finished and successful
862            // Receives `file`
863            success(file) {
864                if (file.previewElement) {
865                    return file.previewElement.classList.add("dz-success");
866                }
867            },
868
869            successmultiple() {},
870
871            // When the upload is canceled.
872            canceled(file) {
873                return this.emit("error", file, this.options.dictUploadCanceled);
874            },
875
876            canceledmultiple() {},
877
878            // When the upload is finished, either with success or an error.
879            // Receives `file`
880            complete(file) {
881                if (file._removeLink) {
882                    file._removeLink.innerHTML = this.options.dictRemoveFile;
883                }
884                if (file.previewElement) {
885                    return file.previewElement.classList.add("dz-complete");
886                }
887            },
888
889            completemultiple() {},
890
891            maxfilesexceeded() {},
892
893            maxfilesreached() {},
894
895            queuecomplete() {},
896
897            addedfiles() {}
898        };
899
900
901        this.prototype._thumbnailQueue = [];
902        this.prototype._processingThumbnail = false;
903    }
904
905    // global utility
906    static extend(target, ...objects) {
907        for (let object of objects) {
908            for (let key in object) {
909                let val = object[key];
910                target[key] = val;
911            }
912        }
913        return target;
914    }
915
916    constructor(el, options) {
917        super();
918        let fallback, left;
919        this.element = el;
920        // For backwards compatibility since the version was in the prototype previously
921        this.version = Dropzone.version;
922
923        this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, "");
924
925        this.clickableElements = [];
926        this.listeners = [];
927        this.files = []; // All files
928
929        if (typeof this.element === "string") {
930            this.element = document.querySelector(this.element);
931        }
932
933        // Not checking if instance of HTMLElement or Element since IE9 is extremely weird.
934        if (!this.element || (this.element.nodeType == null)) {
935            throw new Error("Invalid dropzone element.");
936        }
937
938        if (this.element.dropzone) {
939            throw new Error("Dropzone already attached.");
940        }
941
942        // Now add this dropzone to the instances.
943        Dropzone.instances.push(this);
944
945        // Put the dropzone inside the element itself.
946        this.element.dropzone = this;
947
948        let elementOptions = (left = Dropzone.optionsForElement(this.element)) != null ? left : {};
949
950        this.options = Dropzone.extend({}, this.defaultOptions, elementOptions, options != null ? options : {});
951
952        // If the browser failed, just call the fallback and leave
953        if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {
954            return this.options.fallback.call(this);
955        }
956
957        // @options.url = @element.getAttribute "action" unless @options.url?
958        if (this.options.url == null) {
959            this.options.url = this.element.getAttribute("action");
960        }
961
962        if (!this.options.url) {
963            throw new Error("No URL provided.");
964        }
965
966        if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {
967            throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");
968        }
969
970        if (this.options.uploadMultiple && this.options.chunking) {
971            throw new Error('You cannot set both: uploadMultiple and chunking.');
972        }
973
974        // Backwards compatibility
975        if (this.options.acceptedMimeTypes) {
976            this.options.acceptedFiles = this.options.acceptedMimeTypes;
977            delete this.options.acceptedMimeTypes;
978        }
979
980        // Backwards compatibility
981        if (this.options.renameFilename != null) {
982            this.options.renameFile = file => this.options.renameFilename.call(this, file.name, file);
983        }
984
985        this.options.method = this.options.method.toUpperCase();
986
987        if ((fallback = this.getExistingFallback()) && fallback.parentNode) {
988            // Remove the fallback
989            fallback.parentNode.removeChild(fallback);
990        }
991
992        // Display previews in the previewsContainer element or the Dropzone element unless explicitly set to false
993        if (this.options.previewsContainer !== false) {
994            if (this.options.previewsContainer) {
995                this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer");
996            } else {
997                this.previewsContainer = this.element;
998            }
999        }
1000
1001        if (this.options.clickable) {
1002            if (this.options.clickable === true) {
1003                this.clickableElements = [this.element];
1004            } else {
1005                this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable");
1006            }
1007        }
1008
1009
1010        this.init();
1011    }
1012
1013
1014    // Returns all files that have been accepted
1015    getAcceptedFiles() {
1016        return this.files.filter((file) => file.accepted).map((file) => file);
1017    }
1018
1019    // Returns all files that have been rejected
1020    // Not sure when that's going to be useful, but added for completeness.
1021    getRejectedFiles() {
1022        return this.files.filter((file) => !file.accepted).map((file) => file);
1023    }
1024
1025    getFilesWithStatus(status) {
1026        return this.files.filter((file) => file.status === status).map((file) => file);
1027    }
1028
1029    // Returns all files that are in the queue
1030    getQueuedFiles() {
1031        return this.getFilesWithStatus(Dropzone.QUEUED);
1032    }
1033
1034    getUploadingFiles() {
1035        return this.getFilesWithStatus(Dropzone.UPLOADING);
1036    }
1037
1038    getAddedFiles() {
1039        return this.getFilesWithStatus(Dropzone.ADDED);
1040    }
1041
1042    // Files that are either queued or uploading
1043    getActiveFiles() {
1044        return this.files.filter((file) => (file.status === Dropzone.UPLOADING) || (file.status === Dropzone.QUEUED)).map((file) => file);
1045    }
1046
1047    // The function that gets called when Dropzone is initialized. You
1048    // can (and should) setup event listeners inside this function.
1049    init() {
1050        // In case it isn't set already
1051        if (this.element.tagName === "form") {
1052            this.element.setAttribute("enctype", "multipart/form-data");
1053        }
1054
1055        if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) {
1056            this.element.appendChild(Dropzone.createElement(`<div class="dz-default dz-message"><button class="dz-button" type="button">${this.options.dictDefaultMessage}</button></div>`));
1057        }
1058
1059        if (this.clickableElements.length) {
1060            let setupHiddenFileInput = () => {
1061                if (this.hiddenFileInput) {
1062                    this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
1063                }
1064                this.hiddenFileInput = document.createElement("input");
1065                this.hiddenFileInput.setAttribute("type", "file");
1066                if ((this.options.maxFiles === null) || (this.options.maxFiles > 1)) {
1067                    this.hiddenFileInput.setAttribute("multiple", "multiple");
1068                }
1069                this.hiddenFileInput.className = "dz-hidden-input";
1070
1071                if (this.options.acceptedFiles !== null) {
1072                    this.hiddenFileInput.setAttribute("accept", this.options.acceptedFiles);
1073                }
1074                if (this.options.capture !== null) {
1075                    this.hiddenFileInput.setAttribute("capture", this.options.capture);
1076                }
1077
1078                // Not setting `display="none"` because some browsers don't accept clicks
1079                // on elements that aren't displayed.
1080                this.hiddenFileInput.style.visibility = "hidden";
1081                this.hiddenFileInput.style.position = "absolute";
1082                this.hiddenFileInput.style.top = "0";
1083                this.hiddenFileInput.style.left = "0";
1084                this.hiddenFileInput.style.height = "0";
1085                this.hiddenFileInput.style.width = "0";
1086                Dropzone.getElement(this.options.hiddenInputContainer, 'hiddenInputContainer').appendChild(this.hiddenFileInput);
1087                return this.hiddenFileInput.addEventListener("change", () => {
1088                    let { files } = this.hiddenFileInput;
1089                    if (files.length) {
1090                        for (let file of files) {
1091                            this.addFile(file);
1092                        }
1093                    }
1094                    this.emit("addedfiles", files);
1095                    return setupHiddenFileInput();
1096                });
1097            };
1098            setupHiddenFileInput();
1099        }
1100
1101        this.URL = window.URL !== null ? window.URL : window.webkitURL;
1102
1103
1104        // Setup all event listeners on the Dropzone object itself.
1105        // They're not in @setupEventListeners() because they shouldn't be removed
1106        // again when the dropzone gets disabled.
1107        for (let eventName of this.events) {
1108            this.on(eventName, this.options[eventName]);
1109        }
1110
1111        this.on("uploadprogress", () => this.updateTotalUploadProgress());
1112
1113        this.on("removedfile", () => this.updateTotalUploadProgress());
1114
1115        this.on("canceled", file =>
1115 this.emit("complete", file));
1116
1117        // Emit a `queuecomplete` event if all files finished uploading.
1118        this.on("complete", file => {
1119            if ((this.getAddedFiles().length === 0) && (this.getUploadingFiles().length === 0) && (this.getQueuedFiles().length === 0)) {
1120                // This needs to be deferred so that `queuecomplete` really triggers after `complete`
1121                return setTimeout((() => this.emit("queuecomplete")), 0);
1122            }
1123        });
1124
1125        const containsFiles = function(e) {
1126            return (e.dataTransfer.types && e.dataTransfer.types.some(type => type == "Files"));
1127        };
1128
1129        let noPropagation = function(e) {
1130            // If there are no files, we don't want to stop
1131            // propagation so we don't interfere with other
1132            // drag and drop behaviour.
1133            if (!containsFiles(e)) return;
1134            e.stopPropagation();
1135            if (e.preventDefault) {
1136                return e.preventDefault();
1137            } else {
1138                return e.returnValue = false;
1139            }
1140        };
1141
1142        // Create the listeners
1143        this.listeners = [{
1144            element: this.element,
1145            events: {
1146                "dragstart": e => {
1147                    return this.emit("dragstart", e);
1148                },
1149                "dragenter": e => {
1150                    noPropagation(e);
1151                    return this.emit("dragenter", e);
1152                },
1153                "dragover": e => {
1154                    // Makes it possible to drag files from chrome's download bar
1155                    // http://stackoverflow.com/questions/19526430/drag-and-drop-file-uploads-from-chrome-downloads-bar
1156                    // Try is required to prevent bug in Internet Explorer 11 (SCRIPT65535 exception)
1157                    let efct;
1158                    try {
1159                        efct = e.dataTransfer.effectAllowed;
1160                    } catch (error) {}
1161                    e.dataTransfer.dropEffect = ('move' === efct) || ('linkMove' === efct) ? 'move' : 'copy';
1162
1163                    noPropagation(e);
1164                    return this.emit("dragover", e);
1165                },
1166                "dragleave": e => {
1167                    return this.emit("dragleave", e);
1168                },
1169                "drop": e => {
1170                    noPropagation(e);
1171                    return this.drop(e);
1172                },
1173                "dragend": e => {
1174                    return this.emit("dragend", e);
1175                }
1176            }
1177
1178            // This is disabled right now, because the browsers don't implement it properly.
1179            // "paste": (e) =>
1180            //   noPropagation e
1181            //   @paste e
1182        }];
1183
1184        this.clickableElements.forEach(clickableElement => {
1185            return this.listeners.push({
1186                element: clickableElement,
1187                events: {
1188                    "click": evt => {
1189                        // Only the actual dropzone or the message element should trigger file selection
1190                        if ((clickableElement !== this.element) || ((evt.target === this.element) || Dropzone.elementInside(evt.target, this.element.querySelector(".dz-message")))) {
1191                            this.hiddenFileInput.click(); // Forward the click
1192                        }
1193                        return true;
1194                    }
1195                }
1196            });
1197        });
1198
1199        this.enable();
1200
1201        return this.options.init.call(this);
1202    }
1203
1204    // Not fully tested yet
1205    destroy() {
1206        this.disable();
1207        this.removeAllFiles(true);
1208        if (this.hiddenFileInput != null ? this.hiddenFileInput.parentNode : undefined) {
1209            this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
1210            this.hiddenFileInput = null;
1211        }
1212        delete this.element.dropzone;
1213        return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1);
1214    }
1215
1216
1217    updateTotalUploadProgress() {
1218        let totalUploadProgress;
1219        let totalBytesSent = 0;
1220        let totalBytes = 0;
1221
1222        let activeFiles = this.getActiveFiles();
1223
1224        if (activeFiles.length) {
1225            for (let file of this.getActiveFiles()) {
1226                totalBytesSent += file.upload.bytesSent;
1227                totalBytes += file.upload.total;
1228            }
1229            totalUploadProgress = (100 * totalBytesSent) / totalBytes;
1230        } else {
1231            totalUploadProgress = 100;
1232        }
1233
1234        return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent);
1235    }
1236
1237    // @options.paramName can be a function taking one parameter rather than a string.
1238    // A parameter name for a file is obtained simply by calling this with an index number.
1239    _getParamName(n) {
1240            if (typeof this.options.paramName === "function") {
1241                return this.options.paramName(n);
1242            } else {
1243                return `${this.options.paramName}${this.options.uploadMultiple ? `[${n}]` : ""}`;
1244    }
1245  }
1246
1247  // If @options.renameFile is a function,
1248  // the function will be used to rename the file.name before appending it to the formData
1249  _renameFile(file) {
1250    if (typeof this.options.renameFile !== "function") {
1251      return file.name;
1252    }
1253    return this.options.renameFile(file);
1254  }
1255
1256  // Returns a form that can be used as fallback if the browser does not support DragnDrop
1257  //
1258  // If the dropzone is already a form, only the input field and button are returned. Otherwise a complete form element is provided.
1259  // This code has to pass in IE7 :(
1260  getFallbackForm() {
1261    let existingFallback, form;
1262    if (existingFallback = this.getExistingFallback()) {
1263      return existingFallback;
1264    }
1265
1266    let fieldsString = "<div class=\"dz-fallback\">";
1267    if (this.options.dictFallbackText) {
1268      fieldsString += `<p>${this.options.dictFallbackText}</p>`;
1269    }
1270    fieldsString += `<input type="file" name="${this._getParamName(0)}" ${this.options.uploadMultiple ? 'multiple="multiple"' : undefined} /><input type="submit" value="Upload!"></div>`;
1271
1272    let fields = Dropzone.createElement(fieldsString);
1273    if (this.element.tagName !== "FORM") {
1274      form = Dropzone.createElement(`<form action="${this.options.url}" enctype="multipart/form-data" method="${this.options.method}"></form>`);
1275      form.appendChild(fields);
1276    } else {
1277      // Make sure that the enctype and method attributes are set properly
1278      this.element.setAttribute("enctype", "multipart/form-data");
1279      this.element.setAttribute("method", this.options.method);
1280    }
1281    return form != null ? form : fields;
1282  }
1283
1284
1285  // Returns the fallback elements if they exist already
1286  //
1287  // This code has to pass in IE7 :(
1288  getExistingFallback() {
1289    let getFallback = function (elements) {
1290      for (let el of elements) {
1291        if (/(^| )fallback($| )/.test(el.className)) {
1292          return el;
1293        }
1294      }
1295    };
1296
1297    for (let tagName of ["div", "form"]) {
1298      var fallback;
1299      if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {
1300        return fallback;
1301      }
1302    }
1303  }
1304
1305
1306  // Activates all listeners stored in @listeners
1307  setupEventListeners() {
1308    return this.listeners.map((elementListeners) =>
1309      (() => {
1310        let result = [];
1311        for (let event in elementListeners.events) {
1312          let listener = elementListeners.events[event];
1313          result.push(elementListeners.element.addEventListener(event, listener, false));
1314        }
1315        return result;
1316      })());
1317  }
1318
1319
1320  // Deactivates all listeners stored in @listeners
1321  removeEventListeners() {
1322    return this.listeners.map((elementListeners) =>
1323      (() => {
1324        let result = [];
1325        for (let event in elementListeners.events) {
1326          let listener = elementListeners.events[event];
1327          result.push(elementListeners.element.removeEventListener(event, listener, false));
1328        }
1329        return result;
1330      })());
1331  }
1332
1333  // Removes all event listeners and cancels all files in the queue or being processed.
1334  disable() {
1335    this.clickableElements.forEach(element => element.classList.remove("dz-clickable"));
1336    this.removeEventListeners();
1337    this.disabled = true;
1338
1339    return this.files.map((file) => this.cancelUpload(file));
1340  }
1341
1342  enable() {
1343    delete this.disabled;
1344    this.clickableElements.forEach(element => element.classList.add("dz-clickable"));
1345    return this.setupEventListeners();
1346  }
1347
1348  // Returns a nicely formatted filesize
1349  filesize(size) {
1350    let selectedSize = 0;
1351    let selectedUnit = "b";
1352
1353    if (size > 0) {
1354      let units = ['tb', 'gb', 'mb', 'kb', 'b'];
1355
1356      for (let i = 0; i < units.length; i++) {
1357        let unit = units[i];
1358        let cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10;
1359
1360        if (size >= cutoff) {
1361          selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i);
1362          selectedUnit = unit;
1363          break;
1364        }
1365      }
1366
1367      selectedSize = Math.round(10 * selectedSize) / 10; // Cutting of digits
1368    }
1369
1370    return `<strong>${selectedSize}</strong> ${this.options.dictFileSizeUnits[selectedUnit]}`;
1371  }
1372
1373
1374  // Adds or removes the `dz-max-files-reached` class from the form.
1375  _updateMaxFilesReachedClass() {
1376    if ((this.options.maxFiles != null) && (this.getAcceptedFiles().length >= this.options.maxFiles)) {
1377      if (this.getAcceptedFiles().length === this.options.maxFiles) {
1378        this.emit('maxfilesreached', this.files);
1379      }
1380      return this.element.classList.add("dz-max-files-reached");
1381    } else {
1382      return this.element.classList.remove("dz-max-files-reached");
1383    }
1384  }
1385
1386
1387  drop(e) {
1388    if (!e.dataTransfer) {
1389      return;
1390    }
1391    this.emit("drop", e);
1392
1393    // Convert the FileList to an Array
1394    // This is necessary for IE11
1395    let files = [];
1396    for (let i = 0; i < e.dataTransfer.files.length; i++) {
1397      files[i] = e.dataTransfer.files[i];
1398    }
1399
1400    // Even if it's a folder, files.length will contain the folders.
1401    if (files.length) {
1402      let { items } = e.dataTransfer;
1403      if (items && items.length && (items[0].webkitGetAsEntry != null)) {
1404        // The browser supports dropping of folders, so handle items instead of files
1405        this._addFilesFromItems(items);
1406      } else {
1407        this.handleFiles(files);
1408      }
1409    }
1410
1411    this.emit("addedfiles", files);
1412  }
1413
1414  paste(e) {
1415    if (__guard__(e != null ? e.clipboardData : undefined, x => x.items) == null) {
1416      return;
1417    }
1418
1419    this.emit("paste", e);
1420    let { items } = e.clipboardData;
1421
1422    if (items.length) {
1423      return this._addFilesFromItems(items);
1424    }
1425  }
1426
1427
1428  handleFiles(files) {
1429    for (let file of files) {
1430      this.addFile(file);
1431    }
1432  }
1433
1434  // When a folder is dropped (or files are pasted), items must be handled
1435  // instead of files.
1436  _addFilesFromItems(items) {
1437    return (() => {
1438      let result = [];
1439      for (let item of items) {
1440        var entry;
1441        if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {
1442          if (entry.isFile) {
1443            result.push(this.addFile(item.getAsFile()));
1444          } else if (entry.isDirectory) {
1445            // Append all files from that directory to files
1446            result.push(this._addFilesFromDirectory(entry, entry.name));
1447          } else {
1448            result.push(undefined);
1449          }
1450        } else if (item.getAsFile != null) {
1451          if ((item.kind == null) || (item.kind === "file")) {
1452            result.push(this.addFile(item.getAsFile()));
1453          } else {
1454            result.push(undefined);
1455          }
1456        } else {
1457          result.push(undefined);
1458        }
1459      }
1460      return result;
1461    })();
1462  }
1463
1464
1465  // Goes through the directory, and adds each file it finds recursively
1466  _addFilesFromDirectory(directory, path) {
1467    let dirReader = directory.createReader();
1468
1469    let errorHandler = error => __guardMethod__(console, 'log', o => o.log(error));
1470
1471    var readEntries = () => {
1472      return dirReader.readEntries(entries => {
1473        if (entries.length > 0) {
1474          for (let entry of entries) {
1475            if (entry.isFile) {
1476              entry.file(file => {
1477                if (this.options.ignoreHiddenFiles && (file.name.substring(0, 1) === '.')) {
1478                  return;
1479                }
1480                file.fullPath = `${path}/${file.name}`;
1481                return this.addFile(file);
1482              });
1483            } else if (entry.isDirectory) {
1484              this._addFilesFromDirectory(entry, `${path}/${entry.name}`);
1485            }
1486          }
1487
1488          // Recursively call readEntries() again, since browser only handle
1489          // the first 100 entries.
1490          // See: https://developer.mozilla.org/en-US/docs/Web/API/DirectoryReader#readEntries
1491          readEntries();
1492        }
1493        return null;
1494      }
1495        , errorHandler);
1496    };
1497
1498    return readEntries();
1499  }
1500
1501
1502  // If `done()` is called without argument the file is accepted
1503  // If you call it with an error message, the file is rejected
1504  // (This allows for asynchronous validation)
1505  //
1506  // This function checks the filesize, and if the file.type passes the
1507  // `acceptedFiles` check.
1508  accept(file, done) {
1509    if (this.options.maxFilesize && file.size > (this.options.maxFilesize * 1024 * 1024)) {
1510      done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize));
1511    } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {
1512      done(this.options.dictInvalidFileType);
1513    } else if ((this.options.maxFiles != null) && (this.getAcceptedFiles().length >= this.options.maxFiles)) {
1514      done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles));
1515      this.emit("maxfilesexceeded", file);
1516    } else {
1517      this.options.accept.call(this, file, done);
1518    }
1519  }
1520
1521  addFile(file) {
1522    file.upload = {
1523      uuid: Dropzone.uuidv4(),
1524      progress: 0,
1525      // Setting the total upload size to file.size for the beginning
1526      // It's actual different than the size to be transmitted.
1527      total: file.size,
1528      bytesSent: 0,
1529      filename: this._renameFile(file)
1530      // Not setting chunking information here, because the acutal data — and
1531      // thus the chunks — might change if `options.transformFile` is set
1532      // and does something to the data.
1533    };
1534    this.files.push(file);
1535
1536    file.status = Dropzone.ADDED;
1537
1538    this.emit("addedfile", file);
1539
1540    this._enqueueThumbnail(file);
1541
1542    this.accept(file, error => {
1543      if (error) {
1544        file.accepted = false;
1545        this._errorProcessing([file], error); // Will set the file.status
1546      } else {
1547        file.accepted = true;
1548        if (this.options.autoQueue) {
1549          this.enqueueFile(file);
1550        } // Will set .accepted = true
1551      }
1552      this._updateMaxFilesReachedClass();
1553    });
1554  }
1555
1556
1557  // Wrapper for enqueueFile
1558  enqueueFiles(files) {
1559    for (let file of files) {
1560      this.enqueueFile(file);
1561    }
1562    return null;
1563  }
1564
1565  enqueueFile(file) {
1566    if ((file.status === Dropzone.ADDED) && (file.accepted === true)) {
1567      file.status = Dropzone.QUEUED;
1568      if (this.options.autoProcessQueue) {
1569        return setTimeout((() => this.processQueue()), 0); // Deferring the call
1570      }
1571    } else {
1572      throw new Error("This file can't be queued because it has already been processed or was rejected.");
1573    }
1574  }
1575
1576  _enqueueThumbnail(file) {
1577    if (this.options.createImageThumbnails && file.type.match(/image.*/) && (file.size <= (this.options.maxThumbnailFilesize * 1024 * 1024))) {
1578      this._thumbnailQueue.push(file);
1579      return setTimeout((() => this._processThumbnailQueue()), 0); // Deferring the call
1580    }
1581  }
1582
1583  _processThumbnailQueue() {
1584    if (this._processingThumbnail || (this._thumbnailQueue.length === 0)) {
1585      return;
1586    }
1587
1588    this._processingThumbnail = true;
1589    let file = this._thumbnailQueue.shift();
1590    return this.createThumbnail(file, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, true, dataUrl => {
1591      this.emit("thumbnail", file, dataUrl);
1592      this._processingThumbnail = false;
1593      return this._processThumbnailQueue();
1594    });
1595  }
1596
1597
1598  // Can be called by the user to remove a file
1599  removeFile(file) {
1600    if (file.status === Dropzone.UPLOADING) {
1601      this.cancelUpload(file);
1602    }
1603    this.files = without(this.files, file);
1604
1605    this.emit("removedfile", file);
1606    if (this.files.length === 0) {
1607      return this.emit("reset");
1608    }
1609  }
1610
1611  // Removes all files that aren't currently processed from the list
1612  removeAllFiles(cancelIfNecessary) {
1613    // Create a copy of files since removeFile() changes the @files array.
1614    if (cancelIfNecessary == null) {
1615      cancelIfNecessary = false;
1616    }
1617    for (let file of this.files.slice()) {
1618      if ((file.status !== Dropzone.UPLOADING) || cancelIfNecessary) {
1619        this.removeFile(file);
1620      }
1621    }
1622    return null;
1623  }
1624
1625  // Resizes an image before it gets sent to the server. This function is the default behavior of
1626  // `options.transformFile` if `resizeWidth` or `resizeHeight` are set. The callback is invoked with
1627  // the resized blob.
1628  resizeImage(file, width, height, resizeMethod, callback) {
1629    return this.createThumbnail(file, width, height, resizeMethod, true, (dataUrl, canvas) => {
1630      if (canvas == null) {
1631        // The image has not been resized
1632        return callback(file);
1633      } else {
1634        let { resizeMimeType } = this.options;
1635        if (resizeMimeType == null) {
1636          resizeMimeType = file.type;
1637        }
1638        let resizedDataURL = canvas.toDataURL(resizeMimeType, this.options.resizeQuality);
1639        if ((resizeMimeType === 'image/jpeg') || (resizeMimeType === 'image/jpg')) {
1640          // Now add the original EXIF information
1641          resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL);
1642        }
1643        return callback(Dropzone.dataURItoBlob(resizedDataURL));
1644      }
1645    });
1646  }
1647
1648  createThumbnail(file, width, height, resizeMethod, fixOrientation, callback) {
1649    let fileReader = new FileReader;
1650
1651    fileReader.onload = () => {
1652
1653      file.dataURL = fileReader.result;
1654
1655      // Don't bother creating a thumbnail for SVG images since they're vector
1656      if (file.type === "image/svg+xml") {
1657        if (callback != null) {
1658          callback(fileReader.result);
1659        }
1660        return;
1661      }
1662
1663      this.createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback);
1664    };
1665
1666    fileReader.readAsDataURL(file);
1667  }
1668
1669  // `mockFile` needs to have these attributes:
1670  // 
1671  //     { name: 'name', size: 12345, imageUrl: '' }
1672  //
1673  // `callback` will be invoked when the image has been downloaded and displayed.
1674  // `crossOrigin` will be added to the `img` tag when accessing the file.
1675  displayExistingFile(mockFile, imageUrl, callback, crossOrigin, resizeThumbnail = true) {
1676    this.emit("addedfile", mockFile);
1677    this.emit("complete", mockFile);
1678
1679    if (!resizeThumbnail) {
1680      this.emit("thumbnail", mockFile, imageUrl);
1681      if (callback) callback();
1682    } else {
1683      let onDone = (thumbnail) => {
1684        this.emit('thumbnail', mockFile, thumbnail);
1685        if (callback) callback();
1686      }
1687      mockFile.dataURL = imageUrl;
1688
1689      this.createThumbnailFromUrl(mockFile, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.resizeMethod, this.options.fixOrientation, onDone, crossOrigin);
1690    }
1691  }
1692
1693
1694  createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback, crossOrigin) {
1695    // Not using `new Image` here because of a bug in latest Chrome versions.
1696    // See https://github.com/enyo/dropzone/pull/226
1697    let img = document.createElement("img");
1698
1699    if (crossOrigin) {
1700      img.crossOrigin = crossOrigin;
1701    }
1702
1703    img.onload = () => {
1704      let loadExif = callback => callback(1);
1705      if ((typeof EXIF !== 'undefined' && EXIF !== null) && fixOrientation) {
1706        loadExif = callback =>
1707          EXIF.getData(img, function () {
1708            return callback(EXIF.getTag(this, 'Orientation'));
1709          })
1710          ;
1711      }
1712
1713      return loadExif(orientation => {
1714        file.width = img.width;
1715        file.height = img.height;
1716
1717        let resizeInfo = this.options.resize.call(this, file, width, height, resizeMethod);
1718
1719        let canvas = document.createElement("canvas");
1720        let ctx = canvas.getContext("2d");
1721
1722        canvas.width = resizeInfo.trgWidth;
1723        canvas.height = resizeInfo.trgHeight;
1724
1725        if (orientation > 4) {
1726          canvas.width = resizeInfo.trgHeight;
1727          canvas.height = resizeInfo.trgWidth;
1728        }
1729
1730        switch (orientation) {
1731          case 2:
1732            // horizontal flip
1733            ctx.translate(canvas.width, 0);
1734            ctx.scale(-1, 1);
1735            break;
1736          case 3:
1737            // 180° rotate left
1738            ctx.translate(canvas.width, canvas.height);
1739            ctx.rotate(Math.PI);
1740            break;
1741          case 4:
1742            // vertical flip
1743            ctx.translate(0, canvas.height);
1744            ctx.scale(1, -1);
1745            break;
1746          case 5:
1747            // vertical flip + 90 rotate right
1748            ctx.rotate(0.5 * Math.PI);
1749            ctx.scale(1, -1);
1750            break;
1751          case 6:
1752            // 90° rotate right
1753            ctx.rotate(0.5 * Math.PI);
1754            ctx.translate(0, -canvas.width);
1755            break;
1756          case 7:
1757            // horizontal flip + 90 rotate right
1758            ctx.rotate(0.5 * Math.PI);
1759            ctx.translate(canvas.height, -canvas.width);
1760            ctx.scale(-1, 1);
1761            break;
1762          case 8:
1763            // 90° rotate left
1764            ctx.rotate(-0.5 * Math.PI);
1765            ctx.translate(-canvas.height, 0);
1766            break;
1767        }
1768
1769        // This is a bugfix for iOS' scaling bug.
1770        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);
1771
1772        let thumbnail = canvas.toDataURL("image/png");
1773
1774        if (callback != null) {
1775          return callback(thumbnail, canvas);
1776        }
1777      });
1778    };
1779
1780    if (callback != null) {
1781      img.onerror = callback;
1782    }
1783
1784    return img.src = file.dataURL;
1785  }
1786
1787
1788  // Goes through the queue and processes files if there aren't too many already.
1789  processQueue() {
1790    let { parallelUploads } = this.options;
1791    let processingLength = this.getUploadingFiles().length;
1792    let i = processingLength;
1793
1794    // There are already at least as many files uploading than should be
1795    if (processingLength >= parallelUploads) {
1796      return;
1797    }
1798
1799    let queuedFiles = this.getQueuedFiles();
1800
1801    if (!(queuedFiles.length > 0)) {
1802      return;
1803    }
1804
1805    if (this.options.uploadMultiple) {
1806      // The files should be uploaded in one request
1807      return this.processFiles(queuedFiles.slice(0, (parallelUploads - processingLength)));
1808    } else {
1809      while (i < parallelUploads) {
1810        if (!queuedFiles.length) {
1811          return;
1812        } // Nothing left to process
1813        this.processFile(queuedFiles.shift());
1814        i++;
1815      }
1816    }
1817  }
1818
1819
1820  // Wrapper for `processFiles`
1821  processFile(file) {
1822    return this.processFiles([file]);
1823  }
1824
1825
1826  // Loads the file, then calls finishedLoading()
1827  processFiles(files) {
1828    for (let file of files) {
1829      file.processing = true; // Backwards compatibility
1830      file.status = Dropzone.UPLOADING;
1831
1832      this.emit("processing", file);
1833    }
1834
1835    if (this.options.uploadMultiple) {
1836      this.emit("processingmultiple", files);
1837    }
1838
1839    return this.uploadFiles(files);
1840  }
1841
1842
1843  _getFilesWithXhr(xhr) {
1844    let files;
1845    return files = (this.files.filter((file) => file.xhr === xhr).map((file) => file));
1846  }
1847
1848
1849  // Cancels the file upload and sets the status to CANCELED
1850  // **if** the file is actually being uploaded.
1851  // If it's still in the queue, the file is being removed from it and the status
1852  // set to CANCELED.
1853  cancelUpload(file) {
1854    if (file.status === Dropzone.UPLOADING) {
1855      let groupedFiles = this._getFilesWithXhr(file.xhr);
1856      for (let groupedFile of groupedFiles) {
1857        groupedFile.status = Dropzone.CANCELED;
1858      }
1859      if (typeof file.xhr !== 'undefined') {
1860        file.xhr.abort();
1861      }
1862      for (let groupedFile of groupedFiles) {
1863        this.emit("canceled", groupedFile);
1864      }
1865      if (this.options.uploadMultiple) {
1866        this.emit("canceledmultiple", groupedFiles);
1867      }
1868
1869    } else if (file.status === Dropzone.ADDED || file.status === Dropzone.QUEUED) {
1870      file.status = Dropzone.CANCELED;
1871      this.emit("canceled", file);
1872      if (this.options.uploadMultiple) {
1873        this.emit("canceledmultiple", [file]);
1874      }
1875    }
1876
1877    if (this.options.autoProcessQueue) {
1878      return this.processQueue();
1879    }
1880  }
1881
1882  resolveOption(option, ...args) {
1883    if (typeof option === 'function') {
1884      return option.apply(this, args);
1885    }
1886    return option;
1887  }
1888
1889  uploadFile(file) { return this.uploadFiles([file]); }
1890
1891  uploadFiles(files) {
1892    this._transformFiles(files, (transformedFiles) => {
1893      if (this.options.chunking) {
1894        // Chunking is not allowed to be used with `uploadMultiple` so we know
1895        // that there is only __one__file.
1896        let transformedFile = transformedFiles[0];
1897        files[0].upload.chunked = this.options.chunking && (this.options.forceChunking || transformedFile.size > this.options.chunkSize);
1898        files[0].upload.totalChunkCount = Math.ceil(transformedFile.size / this.options.chunkSize);
1899      }
1900
1901      if (files[0].upload.chunked) {
1902        // This file should be sent in chunks!
1903
1904        // If the chunking option is set, we **know** that there can only be **one** file, since
1905        // uploadMultiple is not allowed with this option.
1906        let file = files[0];
1907        let transformedFile = transformedFiles[0];
1908        let startedChunkCount = 0;
1909
1910        file.upload.chunks = [];
1911
1912        let handleNextChunk = () => {
1913          let chunkIndex = 0;
1914
1915          // Find the next item in file.upload.chunks that is not defined yet.
1916          while (file.upload.chunks[chunkIndex] !== undefined) {
1917            chunkIndex++;
1918          }
1919
1920          // This means, that all chunks have already been started.
1921          if (chunkIndex >= file.upload.totalChunkCount) return;
1922
1923          startedChunkCount++;
1924
1925          let start = chunkIndex * this.options.chunkSize;
1926          let end = Math.min(start + this.options.chunkSize, file.size);
1927
1928          let dataBlock = {
1929            name: this._getParamName(0),
1930            data: transformedFile.webkitSlice ? transformedFile.webkitSlice(start, end) : transformedFile.slice(start, end),
1931            filename: file.upload.filename,
1932            chunkIndex: chunkIndex
1933          };
1934
1935          file.upload.chunks[chunkIndex] = {
1936            file: file,
1937            index: chunkIndex,
1938            dataBlock: dataBlock, // In case we want to retry.
1939            status: Dropzone.UPLOADING,
1940            progress: 0,
1941            retries: 0 // The number of times this block has been retried.
1942          };
1943
1944
1945          this._uploadData(files, [dataBlock]);
1946        };
1947
1948        file.upload.finishedChunkUpload = (chunk) => {
1949          let allFinished = true;
1950          chunk.status = Dropzone.SUCCESS;
1951
1952          // Clear the data from the chunk
1953          chunk.dataBlock = null;
1954          // Leaving this reference to xhr intact here will cause memory leaks in some browsers
1955          chunk.xhr = null;
1956
1957          for (let i = 0; i < file.upload.totalChunkCount; i++) {
1958            if (file.upload.chunks[i] === undefined) {
1959              return handleNextChunk();
1960            }
1961            if (file.upload.chunks[i].status !== Dropzone.SUCCESS) {
1962              allFinished = false;
1963            }
1964          }
1965
1966          if (allFinished) {
1967            this.options.chunksUploaded(file, () => {
1968              this._finished(files, '', null);
1969            });
1970          }
1971        };
1972
1973        if (this.options.parallelChunkUploads) {
1974          for (let i = 0; i < file.upload.totalChunkCount; i++) {
1975            handleNextChunk();
1976          }
1977        }
1978        else {
1979          handleNextChunk();
1980        }
1981      } else {
1982        let dataBlocks = [];
1983        for (let i = 0; i < files.length; i++) {
1984          dataBlocks[i] = {
1985            name: this._getParamName(i),
1986            data: transformedFiles[i],
1987            filename: files[i].upload.filename
1988          };
1989        }
1990        this._uploadData(files, dataBlocks);
1991      }
1992    });
1993  }
1994
1995  /// Returns the right chunk for given file and xhr
1996  _getChunk(file, xhr) {
1997    for (let i = 0; i < file.upload.totalChunkCount; i++) {
1998      if (file.upload.chunks[i] !== undefined && file.upload.chunks[i].xhr === xhr) {
1999        return file.upload.chunks[i];
2000      }
2001    }
2002  }
2003
2004  // This function actually uploads the file(s) to the server.
2005  // If dataBlocks contains the actual data to upload (meaning, that this could either be transformed
2006  // files, or individual chunks for chunked upload).
2007  _uploadData(files, dataBlocks) {
2008    let xhr = new XMLHttpRequest();
2009
2010    // Put the xhr object in the file objects to be able to reference it later.
2011    for (let file of files) {
2012      file.xhr = xhr;
2013    }
2014    if (files[0].upload.chunked) {
2015      // Put the xhr object in the right chunk object, so it can be associated later, and found with _getChunk
2016      files[0].upload.chunks[dataBlocks[0].chunkIndex].xhr = xhr;
2017    }
2018
2019    let method = this.resolveOption(this.options.method, files);
2020    let url = this.resolveOption(this.options.url, files);
2021    xhr.open(method, url, true);
2022
2023    // Setting the timeout after open because of IE11 issue: https://gitlab.com/meno/dropzone/issues/8
2024    xhr.timeout = this.resolveOption(this.options.timeout, files);
2025
2026    // Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179
2027    xhr.withCredentials = !!this.options.withCredentials;
2028
2029
2030    xhr.onload = e => {
2031      this._finishedUploading(files, xhr, e);
2032    };
2033
2034    xhr.ontimeout = () => {
2035      this._handleUploadError(files, xhr, `Request timedout after ${this.options.timeout} seconds`);
2036    };
2037
2038    xhr.onerror = () => {
2039      this._handleUploadError(files, xhr);
2040    };
2041
2042    // Some browsers do not have the .upload property
2043    let progressObj = xhr.upload != null ? xhr.upload : xhr;
2044    progressObj.onprogress = (e) => this._updateFilesUploadProgress(files, xhr, e);
2045
2046    let headers = {
2047      "Accept": "application/json",
2048      "Cache-Control": "no-cache",
2049      "X-Requested-With": "XMLHttpRequest",
2050    };
2051
2052    if (this.options.headers) {
2053      Dropzone.extend(headers, this.options.headers);
2054    }
2055
2056    for (let headerName in headers) {
2057      let headerValue = headers[headerName];
2058      if (headerValue) {
2059        xhr.setRequestHeader(headerName, headerValue);
2060      }
2061    }
2062
2063    let formData = new FormData();
2064
2065    // Adding all @options parameters
2066    if (this.options.params) {
2067      let additionalParams = this.options.params;
2068      if (typeof additionalParams === 'function') {
2069        additionalParams = additionalParams.call(this, files, xhr, files[0].upload.chunked ? this._getChunk(files[0], xhr) : null);
2070      }
2071
2072      for (let key in additionalParams) {
2073        let value = additionalParams[key];
2074        formData.append(key, value);
2075      }
2076    }
2077
2078    // Let the user add additional data if necessary
2079    for (let file of files) {
2080      this.emit("sending", file, xhr, formData);
2081    }
2082    if (this.options.uploadMultiple) {
2083      this.emit("sendingmultiple", files, xhr, formData);
2084    }
2085
2086
2087    this._addFormElementData(formData);
2088
2089
2090    // Finally add the files
2091    // Has to be last because some servers (eg: S3) expect the file to be the last parameter
2092    for (let i = 0; i < dataBlocks.length; i++) {
2093      let dataBlock = dataBlocks[i];
2094      formData.append(dataBlock.name, dataBlock.data, dataBlock.filename);
2095    }
2096
2097    this.submitRequest(xhr, formData, files);
2098  }
2099
2100
2101  // Transforms all files with this.options.transformFile and invokes done with the transformed files when done.
2102  _transformFiles(files, done) {
2103    let transformedFiles = [];
2104    // Clumsy way of handling asynchronous calls, until I get to add a proper Future library.
2105    let doneCounter = 0;
2106    for (let i = 0; i < files.length; i++) {
2107      this.options.transformFile.call(this, files[i], (transformedFile) => {
2108        transformedFiles[i] = transformedFile;
2109        if (++doneCounter === files.length) {
2110          done(transformedFiles);
2111        }
2112      });
2113    }
2114  }
2115
2116  // Takes care of adding other input elements of the form to the AJAX request
2117  _addFormElementData(formData) {
2118    // Take care of other input elements
2119    if (this.element.tagName === "FORM") {
2120      for (let input of this.element.querySelectorAll("input, textarea, select, button")) {
2121        let inputName = input.getAttribute("name");
2122        let inputType = input.getAttribute("type");
2123        if (inputType) inputType = inputType.toLowerCase();
2124
2125        // If the input doesn't have a name, we can't use it.
2126        if (typeof inputName === 'undefined' || inputName === null) continue;
2127
2128        if ((input.tagName === "SELECT") && input.hasAttribute("multiple")) {
2129          // Possibly multiple values
2130          for (let option of input.options) {
2131            if (option.selected) {
2132              formData.append(inputName, option.value);
2133            }
2134          }
2135        } else if (!inputType || (inputType !== "checkbox" && inputType !== "radio") || input.checked) {
2136          formData.append(inputName, input.value);
2137        }
2138      }
2139    }
2140  }
2141
2142  // Invoked when there is new progress information about given files.
2143  // If e is not provided, it is assumed that the upload is finished.
2144  _updateFilesUploadProgress(files, xhr, e) {
2145    let progress;
2146    if (typeof e !== 'undefined') {
2147      progress = (100 * e.loaded) / e.total;
2148
2149      if (files[0].upload.chunked) {
2150        let file = files[0];
2151        // Since this is a chunked upload, we need to update the appropriate chunk progress.
2152        let chunk = this._getChunk(file, xhr);
2153        chunk.progress = progress;
2154        chunk.total = e.total;
2155        chunk.bytesSent = e.loaded;
2156        let fileProgress = 0, fileTotal, fileBytesSent;
2157        file.upload.progress = 0;
2158        file.upload.total = 0;
2159        file.upload.bytesSent = 0;
2160        for (let i = 0; i < file.upload.totalChunkCount; i++) {
2161          if (file.upload.chunks[i] !== undefined && file.upload.chunks[i].progress !== undefined) {
2162            file.upload.progress += file.upload.chunks[i].progress;
2163            file.upload.total += file.upload.chunks[i].total;
2164            file.upload.bytesSent += file.upload.chunks[i].bytesSent;
2165          }
2166        }
2167        file.upload.progress = file.upload.progress / file.upload.totalChunkCount;
2168      } else {
2169        for (let file of files) {
2170          file.upload.progress = progress;
2171          file.upload.total = e.total;
2172          file.upload.bytesSent = e.loaded;
2173        }
2174      }
2175      for (let file of files) {
2176        this.emit("uploadprogress", file, file.upload.progress, file.upload.bytesSent);
2177      }
2178    } else {
2179      // Called when the file finished uploading
2180
2181      let allFilesFinished = true;
2182
2183      progress = 100;
2184
2185      for (let file of files) {
2186        if ((file.upload.progress !== 100) || (file.upload.bytesSent !== file.upload.total)) {
2187          allFilesFinished = false;
2188        }
2189        file.upload.progress = progress;
2190        file.upload.bytesSent = file.upload.total;
2191      }
2192
2193      // Nothing to do, all files already at 100%
2194      if (allFilesFinished) {
2195        return;
2196      }
2197
2198      for (let file of files) {
2199        this.emit("uploadprogress", file, progress, file.upload.bytesSent);
2200      }
2201    }
2202
2203  }
2204
2205
2206  _finishedUploading(files, xhr, e) {
2207    let response;
2208
2209    if (files[0].status === Dropzone.CANCELED) {
2210      return;
2211    }
2212
2213    if (xhr.readyState !== 4) {
2214      return;
2215    }
2216
2217    if ((xhr.responseType !== 'arraybuffer') && (xhr.responseType !== 'blob')) {
2218      response = xhr.responseText;
2219
2220      if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) {
2221        try {
2222          response = JSON.parse(response);
2223        } catch (error) {
2224          e = error;
2225          response = "Invalid JSON response from server.";
2226        }
2227      }
2228    }
2229
2230    this._updateFilesUploadProgress(files);
2231
2232    if (!(200 <= xhr.status && xhr.status < 300)) {
2233      this._handleUploadError(files, xhr, response);
2234    } else {
2235      if (files[0].upload.chunked) {
2236        files[0].upload.finishedChunkUpload(this._getChunk(files[0], xhr));
2237      } else {
2238        this._finished(files, response, e);
2239      }
2240    }
2241  }
2242
2243  _handleUploadError(files, xhr, response) {
2244    if (files[0].status === Dropzone.CANCELED) {
2245      return;
2246    }
2247
2248    if (files[0].upload.chunked && this.options.retryChunks) {
2249      let chunk = this._getChunk(files[0], xhr);
2250      if (chunk.retries++ < this.options.retryChunksLimit) {
2251        this._uploadData(files, [chunk.dataBlock]);
2252        return;
2253      } else {
2254        console.warn('Retried this chunk too often. Giving up.')
2255      }
2256    }
2257
2258    this._errorProcessing(files, response || this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr);
2259  }
2260
2261  submitRequest(xhr, formData, files) {
2262    xhr.send(formData);
2263  }
2264
2265  // Called internally when processing is finished.
2266  // Individual callbacks have to be called in the appropriate sections.
2267  _finished(files, responseText, e) {
2268    for (let file of files) {
2269      file.status = Dropzone.SUCCESS;
2270      this.emit("success", file, responseText, e);
2271      this.emit("complete", file);
2272    }
2273    if (this.options.uploadMultiple) {
2274      this.emit("successmultiple", files, responseText, e);
2275      this.emit("completemultiple", files);
2276    }
2277
2278    if (this.options.autoProcessQueue) {
2279      return this.processQueue();
2280    }
2281  }
2282
2283  // Called internally when processing is finished.
2284  // Individual callbacks have to be called in the appropriate sections.
2285  _errorProcessing(files, message, xhr) {
2286    for (let file of files) {
2287      file.status = Dropzone.ERROR;
2288      this.emit("error", file, message, xhr);
2289      this.emit("complete", file);
2290    }
2291    if (this.options.uploadMultiple) {
2292      this.emit("errormultiple", files, message, xhr);
2293      this.emit("completemultiple", files);
2294    }
2295
2296    if (this.options.autoProcessQueue) {
2297      return this.processQueue();
2298    }
2299  }
2300
2301  static uuidv4() {
2302    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
2303      let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
2304      return v.toString(16);
2305    });
2306  };
2307}
2308Dropzone.initClass();
2309
2310
2311Dropzone.version = "5.7.0";
2312
2313
2314// This is a map of options for your different dropzones. Add configurations
2315// to this object for your different dropzone elemens.
2316//
2317// Example:
2318//
2319//     Dropzone.options.myDropzoneElementId = { maxFilesize: 1 };
2320//
2321// To disable autoDiscover for a specific element, you can set `false` as an option:
2322//
2323//     Dropzone.options.myDisabledElementId = false;
2324//
2325// And in html:
2326//
2327//     <form action="/upload" id="my-dropzone-element-id" class="dropzone"></form>
2328Dropzone.options = {};
2329
2330
2331// Returns the options for an element or undefined if none available.
2332Dropzone.optionsForElement = function (element) {
2333  // Get the `Dropzone.options.elementId` for this element if it exists
2334  if (element.getAttribute("id")) {
2335    return Dropzone.options[camelize(element.getAttribute("id"))];
2336  } else {
2337    return undefined;
2338  }
2339};
2340
2341
2342// Holds a list of all dropzone instances
2343Dropzone.instances = [];
2344
2345// Returns the dropzone for given element if any
2346Dropzone.forElement = function (element) {
2347  if (typeof element === "string") {
2348    element = document.querySelector(element);
2349  }
2350  if ((element != null ? element.dropzone : undefined) == null) {
2351    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.");
2352  }
2353  return element.dropzone;
2354};
2355
2356
2357// Set to false if you don't want Dropzone to automatically find and attach to .dropzone elements.
2358Dropzone.autoDiscover = false;
2359
2360// Looks for all .dropzone elements and creates a dropzone for them
2361Dropzone.discover = function () {
2362  let dropzones;
2363  if (document.querySelectorAll) {
2364    dropzones = document.querySelectorAll(".dropzone");
2365  } else {
2366    dropzones = [];
2367    // IE :(
2368    let checkElements = elements =>
2369      (() => {
2370        let result = [];
2371        for (let el of elements) {
2372          if (/(^| )dropzone($| )/.test(el.className)) {
2373            result.push(dropzones.push(el));
2374          } else {
2375            result.push(undefined);
2376          }
2377        }
2378        return result;
2379      })()
2380      ;
2381    checkElements(document.getElementsByTagName("div"));
2382    checkElements(document.getElementsByTagName("form"));
2383  }
2384
2385  return (() => {
2386    let result = [];
2387    for (let dropzone of dropzones) {
2388      // Create a dropzone unless auto discover has been disabled for specific element
2389      if (Dropzone.optionsForElement(dropzone) !== false) {
2390        result.push(new Dropzone(dropzone));
2391      } else {
2392        result.push(undefined);
2393      }
2394    }
2395    return result;
2396  })();
2397};
2398
2399
2400// Since the whole Drag'n'Drop API is pretty new, some browsers implement it,
2401// but not correctly.
2402// So I created a blacklist of userAgents. Yes, yes. Browser sniffing, I know.
2403// But what to do when browsers *theoretically* support an API, but crash
2404// when using it.
2405//
2406// This is a list of regular expressions tested against navigator.userAgent
2407//
2408// ** It should only be used on browser that *do* support the API, but
2409// incorrectly **
2410//
2411Dropzone.blacklistedBrowsers = [
2412  // The mac os and windows phone version of opera 12 seems to have a problem with the File drag'n'drop API.
2413  /opera.*(Macintosh|Windows Phone).*version\/12/i
2414];
2415
2416
2417// Checks if the browser is supported
2418Dropzone.isBrowserSupported = function () {
2419  let capableBrowser = true;
2420
2421  if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {
2422    if (!("classList" in document.createElement("a"))) {
2423      capableBrowser = false;
2424    } else {
2425      // The browser supports the API, but may be blacklisted.
2426      for (let regex of Dropzone.blacklistedBrowsers) {
2427        if (regex.test(navigator.userAgent)) {
2428          capableBrowser = false;
2429          continue;
2430        }
2431      }
2432    }
2433  } else {
2434    capableBrowser = false;
2435  }
2436
2437  return capableBrowser;
2438};
2439
2440Dropzone.dataURItoBlob = function (dataURI) {
2441  // convert base64 to raw binary data held in a string
2442  // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
2443  let byteString = atob(dataURI.split(',')[1]);
2444
2445  // separate out the mime component
2446  let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
2447
2448  // write the bytes of the string to an ArrayBuffer
2449  let ab = new ArrayBuffer(byteString.length);
2450  let ia = new Uint8Array(ab);
2451  for (let i = 0, end = byteString.length, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) {
2452    ia[i] = byteString.charCodeAt(i);
2453  }
2454
2455  // write the ArrayBuffer to a blob
2456  return new Blob([ab], { type: mimeString });
2457};
2458
2459// Returns an array without the rejected item
2460const without = (list, rejectedItem) => list.filter((item) => item !== rejectedItem).map((item) => item);
2461
2462// abc-def_ghi -> abcDefGhi
2463const camelize = str => str.replace(/[\-_](\w)/g, match => match.charAt(1).toUpperCase());
2464
2465// Creates an element from string
2466Dropzone.createElement = function (string) {
2467  let div = document.createElement("div");
2468  div.innerHTML = string;
2469  return div.childNodes[0];
2470};
2471
2472// Tests if given element is inside (or simply is) the container
2473Dropzone.elementInside = function (element, container) {
2474  if (element === container) {
2475    return true;
2476  } // Coffeescript doesn't support do/while loops
2477  while ((element = element.parentNode)) {
2478    if (element === container) {
2479      return true;
2480    }
2481  }
2482  return false;
2483};
2484
2485
2486Dropzone.getElement = function (el, name) {
2487  let element;
2488  if (typeof el === "string") {
2489    element = document.querySelector(el);
2490  } else if (el.nodeType != null) {
2491    element = el;
2492  }
2493  if (element == null) {
2494    throw new Error(`Invalid \`${name}\` option provided. Please provide a CSS selector or a plain HTML element.`);
2495  }
2496  return element;
2497};
2498
2499
2500Dropzone.getElements = function (els, name) {
2501  let el, elements;
2502  if (els instanceof Array) {
2503    elements = [];
2504    try {
2505      for (el of els) {
2506        elements.push(this.getElement(el, name));
2507      }
2508    } catch (e) {
2509      elements = null;
2510    }
2511  } else if (typeof els === "string") {
2512    elements = [];
2513    for (el of document.querySelectorAll(els)) {
2514      elements.push(el);
2515    }
2516  } else if (els.nodeType != null) {
2517    elements = [els];
2518  }
2519
2520  if ((elements == null) || !elements.length) {
2521    throw new Error(`Invalid \`${name}\` option provided. Please provide a CSS selector, a plain HTML element or a list of those.`);
2522  }
2523
2524  return elements;
2525};
2526
2527// Asks the user the question and calls accepted or rejected accordingly
2528//
2529// The default implementation just uses `window.confirm` and then calls the
2530// appropriate callback.
2531Dropzone.confirm = function (question, accepted, rejected) {
2532  if (window.confirm(question)) {
2533    return accepted();
2534  } else if (rejected != null) {
2535    return rejected();
2536  }
2537};
2538
2539// Validates the mime type like this:
2540//
2541// https://developer.mozilla.org/en-US/docs/HTML/Element/input#attr-accept
2542Dropzone.isValidFile = function (file, acceptedFiles) {
2543  if (!acceptedFiles) {
2544    return true;
2545  } // If there are no accepted mime types, it's OK
2546  acceptedFiles = acceptedFiles.split(",");
2547
2548  let mimeType = file.type;
2549  let baseMimeType = mimeType.replace(/\/.*$/, "");
2550
2551  for (let validType of acceptedFiles) {
2552    validType = validType.trim();
2553    if (validType.charAt(0) === ".") {
2554      if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) {
2555        return true;
2556      }
2557    } else if (/\/\*$/.test(validType)) {
2558      // This is something like a image/* mime type
2559      if (baseMimeType === validType.replace(/\/.*$/, "")) {
2560        return true;
2561      }
2562    } else {
2563      if (mimeType === validType) {
2564        return true;
2565      }
2566    }
2567  }
2568
2569  return false;
2570};
2571
2572// Augment jQuery
2573if (typeof jQuery !== 'undefined' && jQuery !== null) {
2574  jQuery.fn.dropzone = function (options) {
2575    return this.each(function () {
2576      return new Dropzone(this, options);
2577    });
2578  };
2579}
2580
2581
2582if (typeof module !== 'undefined' && module !== null) {
2583  module.exports = Dropzone;
2584} else {
2585  window.Dropzone = Dropzone;
2586}
2587
2588
2589// Dropzone file status codes
2590Dropzone.ADDED = "added";
2591
2592Dropzone.QUEUED = "queued";
2593// For backwards compatibility. Now, if a file is accepted, it's either queued
2594// or uploading.
2595Dropzone.ACCEPTED = Dropzone.QUEUED;
2596
2597Dropzone.UPLOADING = "uploading";
2598Dropzone.PROCESSING = Dropzone.UPLOADING; // alias
2599
2600Dropzone.CANCELED = "canceled";
2601Dropzone.ERROR = "error";
2602Dropzone.SUCCESS = "success";
2603
2604
2605/*
2606
2607 Bugfix for iOS 6 and 7
2608 Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
2609 based on the work of https://github.com/stomita/ios-imagefile-megapixel
2610
2611 */
2612
2613// Detecting vertical squash in loaded image.
2614// Fixes a bug which squash image vertically while drawing into canvas for some images.
2615// This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel
2616let detectVerticalSquash = function (img) {
2617  let iw = img.naturalWidth;
2618  let ih = img.naturalHeight;
2619  let canvas = document.createElement("canvas");
2620  canvas.width = 1;
2621  canvas.height = ih;
2622  let ctx = canvas.getContext("2d");
2623  ctx.drawImage(img, 0, 0);
2624  let { data } = ctx.getImageData(1, 0, 1, ih);
2625
2626
2627  // search image edge pixel position in case it is squashed vertically.
2628  let sy = 0;
2629  let ey = ih;
2630  let py = ih;
2631  while (py > sy) {
2632    let alpha = data[((py - 1) * 4) + 3];
2633
2634    if (alpha === 0) {
2635      ey = py;
2636    } else {
2637      sy = py;
2638    }
2639
2640    py = (ey + sy) >> 1;
2641  }
2642  let ratio = (py / ih);
2643
2644  if (ratio === 0) {
2645    return 1;
2646  } else {
2647    return ratio;
2648  }
2649};
2650
2651// A replacement for context.drawImage
2652// (args are for source and destination).
2653var drawImageIOSFix = function (ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
2654  let vertSquashRatio = detectVerticalSquash(img);
2655  return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
2656};
2657
2658
2659// Based on MinifyJpeg
2660// Source: http://www.perry.cz/files/ExifRestorer.js
2661// http://elicon.blog57.fc2.com/blog-entry-206.html
2662class ExifRestore {
2663  static initClass() {
2664    this.KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
2664+/=';
2665  }
2666
2667  static encode64(input) {
2668    let output = '';
2669    let chr1 = undefined;
2670    let chr2 = undefined;
2671    let chr3 = '';
2672    let enc1 = undefined;
2673    let enc2 = undefined;
2674    let enc3 = undefined;
2675    let enc4 = '';
2676    let i = 0;
2677    while (true) {
2678      chr1 = input[i++];
2679      chr2 = input[i++];
2680      chr3 = input[i++];
2681      enc1 = chr1 >> 2;
2682      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
2683      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
2684      enc4 = chr3 & 63;
2685      if (isNaN(chr2)) {
2686        enc3 = (enc4 = 64);
2687      } else if (isNaN(chr3)) {
2688        enc4 = 64;
2689      }
2690      output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4);
2691      chr1 = (chr2 = (chr3 = ''));
2692      enc1 = (enc2 = (enc3 = (enc4 = '')));
2693      if (!(i < input.length)) {
2694        break;
2695      }
2696    }
2697    return output;
2698  }
2699
2700  static restore(origFileBase64, resizedFileBase64) {
2701    if (!origFileBase64.match('data:image/jpeg;base64,')) {
2702      return resizedFileBase64;
2703    }
2704    let rawImage = this.decode64(origFileBase64.replace('data:image/jpeg;base64,', ''));
2705    let segments = this.slice2Segments(rawImage);
2706    let image = this.exifManipulation(resizedFileBase64, segments);
2707    return `data:image/jpeg;base64,${this.encode64(image)}`;
2708  }
2709
2710  static exifManipulation(resizedFileBase64, segments) {
2711    let exifArray = this.getExifArray(segments);
2712    let newImageArray = this.insertExif(resizedFileBase64, exifArray);
2713    let aBuffer = new Uint8Array(newImageArray);
2714    return aBuffer;
2715  }
2716
2717  static getExifArray(segments) {
2718    let seg = undefined;
2719    let x = 0;
2720    while (x < segments.length) {
2721      seg = segments[x];
2722      if ((seg[0] === 255) & (seg[1] === 225)) {
2723        return seg;
2724      }
2725      x++;
2726    }
2727    return [];
2728  }
2729
2730  static insertExif(resizedFileBase64, exifArray) {
2731    let imageData = resizedFileBase64.replace('data:image/jpeg;base64,', '');
2732    let buf = this.decode64(imageData);
2733    let separatePoint = buf.indexOf(255, 3);
2734    let mae = buf.slice(0, separatePoint);
2735    let ato = buf.slice(separatePoint);
2736    let array = mae;
2737    array = array.concat(exifArray);
2738    array = array.concat(ato);
2739    return array;
2740  }
2741
2742  static slice2Segments(rawImageArray) {
2743    let head = 0;
2744    let segments = [];
2745    while (true) {
2746      var length;
2747      if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 218)) {
2748        break;
2749      }
2750      if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 216)) {
2751        head += 2;
2752      } else {
2753        length = (rawImageArray[head + 2] * 256) + rawImageArray[head + 3];
2754        let endPoint = head + length + 2;
2755        let seg = rawImageArray.slice(head, endPoint);
2756        segments.push(seg);
2757        head = endPoint;
2758      }
2759      if (head > rawImageArray.length) {
2760        break;
2761      }
2762    }
2763    return segments;
2764  }
2765
2766  static decode64(input) {
2767    let output = '';
2768    let chr1 = undefined;
2769    let chr2 = undefined;
2770    let chr3 = '';
2771    let enc1 = undefined;
2772    let enc2 = undefined;
2773    let enc3 = undefined;
2774    let enc4 = '';
2775    let i = 0;
2776    let buf = [];
2777    // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
2778    let base64test = /[^A-Za-z0-9\+\/\=]/g;
2779    if (base64test.exec(input)) {
2780      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.');
2781    }
2782    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
2783    while (true) {
2784      enc1 = this.KEY_STR.indexOf(input.charAt(i++));
2785      enc2 = this.KEY_STR.indexOf(input.charAt(i++));
2786      enc3 = this.KEY_STR.indexOf(input.charAt(i++));
2787      enc4 = this.KEY_STR.indexOf(input.charAt(i++));
2788      chr1 = (enc1 << 2) | (enc2 >> 4);
2789      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
2790      chr3 = ((enc3 & 3) << 6) | enc4;
2791      buf.push(chr1);
2792      if (enc3 !== 64) {
2793        buf.push(chr2);
2794      }
2795      if (enc4 !== 64) {
2796        buf.push(chr3);
2797      }
2798      chr1 = (chr2 = (chr3 = ''));
2799      enc1 = (enc2 = (enc3 = (enc4 = '')));
2800      if (!(i < input.length)) {
2801        break;
2802      }
2803    }
2804    return buf;
2805  }
2806}
2807ExifRestore.initClass();
2808
2809
2810/*
2811 * contentloaded.js
2812 *
2813 * Author: Diego Perini (diego.perini at gmail.com)
2814 * Summary: cross-browser wrapper for DOMContentLoaded
2815 * Updated: 20101020
2816 * License: MIT
2817 * Version: 1.2
2818 *
2819 * URL:
2820 * http://javascript.nwbox.com/ContentLoaded/
2821 * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
2822 */
2823
2824// @win window reference
2825// @fn function reference
2826let contentLoaded = function (win, fn) {
2827  let done = false;
2828  let top = true;
2829  let doc = win.document;
2830  let root = doc.documentElement;
2831  let add = (doc.addEventListener ? "addEventListener" : "attachEvent");
2832  let rem = (doc.addEventListener ? "removeEventListener" : "detachEvent");
2833  let pre = (doc.addEventListener ? "" : "on");
2834  var init = function (e) {
2835    if ((e.type === "readystatechange") && (doc.readyState !== "complete")) {
2836      return;
2837    }
2838    ((e.type === "load" ? win : doc))[rem](pre + e.type, init, false);
2839    if (!done && (done = true)) {
2840      return fn.call(win, e.type || e);
2841    }
2842  };
2843
2844  var poll = function () {
2845    try {
2846      root.doScroll("left");
2847    } catch (e) {
2848      setTimeout(poll, 50);
2849      return;
2850    }
2851    return init("poll");
2852  };
2853
2854  if (doc.readyState !== "complete") {
2855    if (doc.createEventObject && root.doScroll) {
2856      try {
2857        top = !win.frameElement;
2858      } catch (error) {
2859      }
2860      if (top) {
2861        poll();
2862      }
2863    }
2864    doc[add](pre + "DOMContentLoaded", init, false);
2865    doc[add](pre + "readystatechange", init, false);
2866    return win[add](pre + "load", init, false);
2867  }
2868};
2869
2870
2871// As a single function to be able to write tests.
2872Dropzone._autoDiscoverFunction = function () {
2873  if (Dropzone.autoDiscover) {
2874    return Dropzone.discover();
2875  }
2876};
2877contentLoaded(window, Dropzone._autoDiscoverFunction);
2878
2879function __guard__(value, transform) {
2880  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
2881}
2882function __guardMethod__(obj, methodName, transform) {
2883  if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') {
2884    return transform(obj, methodName);
2885  } else {
2886    return undefined;
2887  }
2888}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.