PageSourceSearch

https://www.lieben.no/wp-content/plugins/product-extras-for-woocommerce/assets/js/dropzone.js?ver=3.9.6

js lieben.no collected 2026-10-02 07:08:50 UTC 93,195 bytes, 2,857 lines download raw bytes

1/*
2 *
3 * More info at [www.dropzonejs.com](http://www.dropzonejs.com)
4 *
5 * Copyright (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: "file",
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 olden 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" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
679      <title>Check</title>
680      <defs></defs>
681      <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
682        <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" id="Oval-2" stroke-opacity="0.198794158" stroke="#747474" fill-opacity="0.816519475" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
683      </g>
684    </svg>
685  </div>
686  <div class="dz-error-mark">
687    <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" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
688      <title>Error</title>
689      <defs></defs>
690      <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
691        <g id="Check-+-Oval-2" sketch:type="MSLayerGroup" stroke="#747474" stroke-opacity="0.198794158" fill="#FFFFFF" fill-opacity="0.816519475">
692          <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" id="Oval-2" sketch:type="MSShapeGroup"></path>
693        </g>
694      </g>
695    </svg>
696  </div>
697</div>\
698`,
699
700      // END OPTIONS
701      // (Required by the dropzone documentation parser)
702
703
704      /*
705       Those functions register themselves to the events on init and handle all
706       the user interface specific stuff. Overwriting them won't break the upload
707       but can break the way it's displayed.
708       You can overwrite them if you don't like the default behavior. If you just
709       want to add an additional event handler, register it on the dropzone object
710       and don't overwrite those options.
711       */
712
713
714
715
716      // Those are self explanatory and simply concern the DragnDrop.
717      drop(e) {
718        return this.element.classList.remove("dz-drag-hover");
719      },
720      dragstart(e) {
721      },
722      dragend(e) {
723        return this.element.classList.remove("dz-drag-hover");
724      },
725      dragenter(e) {
726        return this.element.classList.add("dz-drag-hover");
727      },
728      dragover(e) {
729        return this.element.classList.add("dz-drag-hover");
730      },
731      dragleave(e) {
732        return this.element.classList.remove("dz-drag-hover");
733      },
734
735      paste(e) {
736      },
737
738      // Called whenever there are no files left in the dropzone anymore, and the
739      // dropzone should be displayed as if in the initial state.
740      reset() {
741        return this.element.classList.remove("dz-started");
742      },
743
744      // Called when a file is added to the queue
745      // Receives `file`
746      addedfile(file) {
747        if (this.element === this.previewsContainer) {
748          this.element.classList.add("dz-started");
749        }
750
751        if (this.previewsContainer) {
752          file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim());
753          file.previewTemplate = file.previewElement; // Backwards compatibility
754
755          this.previewsContainer.appendChild(file.previewElement);
756          for (var node of file.previewElement.querySelectorAll("[data-dz-name]")) {
757            node.textContent = file.name;
758          }
759          for (node of file.previewElement.querySelectorAll("[data-dz-size]")) {
760            node.innerHTML = this.filesize(file.size);
761          }
762
763          if (this.options.addRemoveLinks) {
764            file._removeLink = Dropzone.createElement(`<a class="dz-remove" href="javascript:undefined;" data-dz-remove>${this.options.dictRemoveFile}</a>`);
765            file.previewElement.appendChild(file._removeLink);
766          }
767
768          let removeFileEvent = e => {
769            e.preventDefault();
770            e.stopPropagation();
771            if (file.status === Dropzone.UPLOADING) {
772              return Dropzone.confirm(this.options.dictCancelUploadConfirmation, () => this.removeFile(file));
773            } else {
774              if (this.options.dictRemoveFileConfirmation) {
775                return Dropzone.confirm(this.options.dictRemoveFileConfirmation, () => this.removeFile(file));
776              } else {
777                return this.removeFile(file);
778              }
779            }
780          };
781
782          for (let removeLink of file.previewElement.querySelectorAll("[data-dz-remove]")) {
783             removeLink.addEventListener("click", removeFileEvent);
784          }
785        }
786      },
787
788
789      // Called whenever a file is removed.
790      removedfile(file) {
791        if (file.previewElement != null && file.previewElement.parentNode != null) {
792          file.previewElement.parentNode.removeChild(file.previewElement);
793        }
794        return this._updateMaxFilesReachedClass();
795      },
796
797      // Called when a thumbnail has been generated
798      // Receives `file` and `dataUrl`
799      thumbnail(file, dataUrl) {
800        if (file.previewElement) {
801          file.previewElement.classList.remove("dz-file-preview");
802          for (let thumbnailElement of file.previewElement.querySelectorAll("[data-dz-thumbnail]")) {
803            thumbnailElement.alt = file.name;
804            thumbnailElement.src = dataUrl;
805          }
806
807          return setTimeout((() => file.previewElement.classList.add("dz-image-preview")), 1);
808        }
809      },
810
811      // Called whenever an error occurs
812      // Receives `file` and `message`
813      error(file, message) {
814        if (file.previewElement) {
815          file.previewElement.classList.add("dz-error");
816          if ((typeof message !== "String") && message.error) {
817            message = message.error;
818          }
819          for (let node of file.previewElement.querySelectorAll("[data-dz-errormessage]")) {
820            node.textContent = message;
821          }
822        }
823      },
824
825      errormultiple() {
826      },
827
828      // Called when a file gets processed. Since there is a cue, not all added
829      // files are processed immediately.
830      // Receives `file`
831      processing(file) {
832        if (file.previewElement) {
833          file.previewElement.classList.add("dz-processing");
834          if (file._removeLink) {
835            return file._removeLink.innerHTML = this.options.dictCancelUpload;
836          }
837        }
838      },
839
840      processingmultiple() {
841      },
842
843      // Called whenever the upload progress gets updated.
844      // Receives `file`, `progress` (percentage 0-100) and `bytesSent`.
845      // To get the total number of bytes of the file, use `file.size`
846      uploadprogress(file, progress, bytesSent) {
847        if (file.previewElement) {
848          for (let node of file.previewElement.querySelectorAll("[data-dz-uploadprogress]")) {
849              node.nodeName === 'PROGRESS' ?
850                  (node.value = progress)
851                  :
852                  (node.style.width = `${progress}%`)
853          }
854        }
855      },
856
857      // Called whenever the total upload progress gets updated.
858      // Called with totalUploadProgress (0-100), totalBytes and totalBytesSent
859      totaluploadprogress() {
860      },
861
862      // Called just before the file is sent. Gets the `xhr` object as second
863      // parameter, so you can modify it (for example to add a CSRF token) and a
864      // `formData` object to add additional information.
865      sending() {
866      },
867
868      sendingmultiple() {},
869
870      // When the complete upload is finished and successful
871      // Receives `file`
872      success(file) {
873        if (file.previewElement) {
874          return file.previewElement.classList.add("dz-success");
875        }
876      },
877
878      successmultiple() {},
879
880      // When the upload is canceled.
881      canceled(file) {
882        return this.emit("error", file, this.options.dictUploadCanceled);
883      },
884
885      canceledmultiple() {},
886
887      // When the upload is finished, either with success or an error.
888      // Receives `file`
889      complete(file) {
890        if (file._removeLink) {
891          file._removeLink.innerHTML = this.options.dictRemoveFile;
892        }
893        if (file.previewElement) {
894          return file.previewElement.classList.add("dz-complete");
895        }
896      },
897
898      completemultiple() {},
899
900      maxfilesexceeded() {},
901
902      maxfilesreached() {},
903
904      queuecomplete() {},
905
906      addedfiles() {}
907    };
908
909
910    this.prototype._thumbnailQueue = [];
911    this.prototype._processingThumbnail = false;
912  }
913
914  // global utility
915  static extend(target, ...objects) {
916    for (let object of objects) {
917      for (let key in object) {
918        let val = object[key];
919        target[key] = val;
920      }
921    }
922    return target;
923  }
924
925  constructor(el, options) {
926    super();
927    let fallback, left;
928    this.element = el;
929    // For backwards compatibility since the version was in the prototype previously
930    this.version = Dropzone.version;
931
932    this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, "");
933
934    this.clickableElements = [];
935    this.listeners = [];
936    this.files = []; // All files
937
938    if (typeof this.element === "string") {
939      this.element = document.querySelector(this.element);
940    }
941
942    // Not checking if instance of HTMLElement or Element since IE9 is extremely weird.
943    if (!this.element || (this.element.nodeType == null)) {
944      throw new Error("Invalid dropzone element.");
945    }
946
947    if (this.element.dropzone) {
948      throw new Error("Dropzone already attached.");
949    }
950
951    // Now add this dropzone to the instances.
952    Dropzone.instances.push(this);
953
954    // Put the dropzone inside the element itself.
955    this.element.dropzone = this;
956
957    let elementOptions = (left = Dropzone.optionsForElement(this.element)) != null ? left : {};
958
959    this.options = Dropzone.extend({}, this.defaultOptions, elementOptions, options != null ? options : {});
960
961    // If the browser failed, just call the fallback and leave
962    if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {
963      return this.options.fallback.call(this);
964    }
965
966    // @options.url = @element.getAttribute "action" unless @options.url?
967    if (this.options.url == null) {
968      this.options.url = this.element.getAttribute("action");
969    }
970
971    if (!this.options.url) {
972      throw new Error("No URL provided.");
973    }
974
975    if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {
976      throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");
977    }
978
979    if (this.options.uploadMultiple && this.options.chunking) {
980      throw new Error('You cannot set both: uploadMultiple and chunking.');
981    }
982
983    // Backwards compatibility
984    if (this.options.acceptedMimeTypes) {
985      this.options.acceptedFiles = this.options.acceptedMimeTypes;
986      delete this.options.acceptedMimeTypes;
987    }
988
989    // Backwards compatibility
990    if (this.options.renameFilename != null) {
991      this.options.renameFile = file => this.options.renameFilename.call(this, file.name, file);
992    }
993
994    this.options.method = this.options.method.toUpperCase();
995
996    if ((fallback = this.getExistingFallback()) && fallback.parentNode) {
997      // Remove the fallback
998      fallback.parentNode.removeChild(fallback);
999    }
1000
1001    // Display previews in the previewsContainer element or the Dropzone element unless explicitly set to false
1002    if (this.options.previewsContainer !== false) {
1003      if (this.options.previewsContainer) {
1004        this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer");
1005      } else {
1006        this.previewsContainer = this.element;
1007      }
1008    }
1009
1010    if (this.options.clickable) {
1011      if (this.options.clickable === true) {
1012        this.clickableElements = [this.element];
1013      } else {
1014        this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable");
1015      }
1016    }
1017
1018
1019    this.init();
1020  }
1021
1022
1023  // Returns all files that have been accepted
1024  getAcceptedFiles() {
1025    return this.files.filter((file) => file.accepted).map((file) => file);
1026  }
1027
1028  // Returns all files that have been rejected
1029  // Not sure when that's going to be useful, but added for completeness.
1030  getRejectedFiles() {
1031    return this.files.filter((file) => !file.accepted).map((file) => file);
1032  }
1033
1034  getFilesWithStatus(status) {
1035    return this.files.filter((file) => file.status === status).map((file) => file);
1036  }
1037
1038  // Returns all files that are in the queue
1039  getQueuedFiles() {
1040    return this.getFilesWithStatus(Dropzone.QUEUED);
1041  }
1042
1043  getUploadingFiles() {
1044    return this.getFilesWithStatus(Dropzone.UPLOADING);
1045  }
1046
1047  getAddedFiles() {
1048    return this.getFilesWithStatus(Dropzone.ADDED);
1049  }
1050
1051  // Files that are either queued or uploading
1052  getActiveFiles() {
1053    return this.files.filter((file) => (file.status === Dropzone.UPLOADING) || (file.status === Dropzone.QUEUED)).map((file) => file);
1054  }
1055
1056  // The function that gets called when Dropzone is initialized. You
1057  // can (and should) setup event listeners inside this function.
1058  init() {
1059    // In case it isn't set already
1060    if (this.element.tagName === "form") {
1061      this.element.setAttribute("enctype", "multipart/form-data");
1062    }
1063
1064    if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) {
1065      this.element.appendChild(Dropzone.createElement(`<div class="dz-default dz-message"><span>${this.options.dictDefaultMessage}</span></div>`));
1066    }
1067
1068    if (this.clickableElements.length) {
1069      let setupHiddenFileInput = () => {
1070        if (this.hiddenFileInput) {
1071          this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
1072        }
1073        this.hiddenFileInput = document.createElement("input");
1074        this.hiddenFileInput.setAttribute("type", "file");
1075        if ((this.options.maxFiles === null) || (this.options.maxFiles > 1)) {
1076          this.hiddenFileInput.setAttribute("multiple", "multiple");
1077        }
1078        this.hiddenFileInput.className = "dz-hidden-input";
1079
1080        if (this.options.acceptedFiles !== null) {
1081          this.hiddenFileInput.setAttribute("accept", this.options.acceptedFiles);
1082        }
1083        if (this.options.capture !== null) {
1084          this.hiddenFileInput.setAttribute("capture", this.options.capture);
1085        }
1086
1087        // Not setting `display="none"` because some browsers don't accept clicks
1088        // on elements that aren't displayed.
1089        this.hiddenFileInput.style.visibility = "hidden";
1090        this.hiddenFileInput.style.position = "absolute";
1091        this.hiddenFileInput.style.top = "0";
1092        this.hiddenFileInput.style.left = "0";
1093        this.hiddenFileInput.style.height = "0";
1094        this.hiddenFileInput.style.width = "0";
1095        Dropzone.getElement(this.options.hiddenInputContainer, 'hiddenInputContainer').appendChild(this.hiddenFileInput);
1096        return this.hiddenFileInput.addEventListener("change", () => {
1097          let {files} = this.hiddenFileInput;
1098          if (files.length) {
1099            for (let file of files) {
1100              this.addFile(file);
1101            }
1102          }
1103          this.emit("addedfiles", files);
1104          return setupHiddenFileInput();
1105        });
1106      };
1107      setupHiddenFileInput();
1108    }
1109
1110    this.URL = window.URL !== null ? window.URL : window.webkitURL;
1111
1112
1113    // Setup all event listeners on the Dropzone object itself.
1114    // They're not in @setupEventListeners() because they shouldn't be removed
1115    // again when the dropzone gets disabled.
1116    for (let eventName of this.events) {
1117      this.on(eventName, this.options[eventName]);
1118    }
1119
1120    this.on("uploadprogress", () => this.updateTotalUploadProgress());
1121
1122    this.on("removedfile", () => this.updateTotalUploadProgress());
1123
1124    this.on("canceled", file => this.emit("complete", file));
1125
1126    // Emit a `queuecomplete` event if all files finished uploading.
1127    this.on("complete", file => {
1128      if ((this.getAddedFiles().length === 0) && (this.getUploadingFiles().length === 0) && (this.getQueuedFiles().length === 0)) {
1129        // This needs to be deferred so that `queuecomplete` really triggers after `complete`
1130        return setTimeout((() => this.emit("queuecomplete")), 0);
1131      }
1132    });
1133
1134
1135    let noPropagation = function (e) {
1136      e.stopPropagation();
1137      if (e.preventDefault) {
1138        return e.preventDefault();
1139      } else {
1140        return e.returnValue = false;
1141      }
1142    };
1143
1144    // Create the listeners
1145    this.listeners = [
1146      {
1147        element: this.element,
1148        events: {
1149          "dragstart": e => {
1150            return this.emit("dragstart", e);
1151          },
1152          "dragenter": e => {
1153            noPropagation(e);
1154            return this.emit("dragenter", e);
1155          },
1156          "dragover": e => {
1157            // Makes it possible to drag files from chrome's download bar
1158            // http://stackoverflow.com/questions/19526430/drag-and-drop-file-uploads-from-chrome-downloads-bar
1159            // Try is required to prevent bug in Internet Explorer 11 (SCRIPT65535 exception)
1160            let efct;
1161            try {
1162              efct = e.dataTransfer.effectAllowed;
1163            } catch (error) {
1164            }
1165            e.dataTransfer.dropEffect = ('move' === efct) || ('linkMove' === efct) ? 'move' : 'copy';
1166
1167            noPropagation(e);
1168            return this.emit("dragover", e);
1169          },
1170          "dragleave": e => {
1171            return this.emit("dragleave", e);
1172          },
1173          "drop": e => {
1174            noPropagation(e);
1175            return this.drop(e);
1176          },
1177          "dragend": e => {
1178            return this.emit("dragend", e);
1179          }
1180        }
1181
1182        // This is disabled right now, because the browsers don't implement it properly.
1183        // "paste": (e) =>
1184        //   noPropagation e
1185        //   @paste e
1186      }
1187    ];
1188
1189    this.clickableElements.forEach(clickableElement => {
1190      return this.listeners.push({
1191        element: clickableElement,
1192        events: {
1193          "click": evt => {
1194            // Only the actual dropzone or the message element should trigger file selection
1195            if ((clickableElement !== this.element) || ((evt.target === this.element) || Dropzone.elementInside(evt.target, this.element.querySelector(".dz-message")))) {
1196              this.hiddenFileInput.click(); // Forward the click
1197            }
1198            return true;
1199          }
1200        }
1201      });
1202    });
1203
1204    this.enable();
1205
1206    return this.options.init.call(this);
1207  }
1208
1209  // Not fully tested yet
1210  destroy() {
1211    this.disable();
1212    this.removeAllFiles(true);
1213    if (this.hiddenFileInput != null ? this.hiddenFileInput.parentNode : undefined) {
1214      this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
1215      this.hiddenFileInput = null;
1216    }
1217    delete this.element.dropzone;
1218    return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1);
1219  }
1220
1221
1222  updateTotalUploadProgress() {
1223    let totalUploadProgress;
1224    let totalBytesSent = 0;
1225    let totalBytes = 0;
1226
1227    let activeFiles = this.getActiveFiles();
1228
1229    if (activeFiles.length) {
1230      for (let file of this.getActiveFiles()) {
1231        totalBytesSent += file.upload.bytesSent;
1232        totalBytes += file.upload.total;
1233      }
1234      totalUploadProgress = (100 * totalBytesSent) / totalBytes;
1235    } else {
1236      totalUploadProgress = 100;
1237    }
1238
1239    return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent);
1240  }
1241
1242  // @options.paramName can be a function taking one parameter rather than a string.
1243  // A parameter name for a file is obtained simply by calling this with an index number.
1244  _getParamName(n) {
1245    if (typeof this.options.paramName === "function") {
1246      return this.options.paramName(n);
1247    } else {
1248      return `${this.options.paramName}${this.options.uploadMultiple ? `[${n}]` : ""}`;
1249    }
1250  }
1251
1252  // If @options.renameFile is a function,
1253  // the function will be used to rename the file.name before appending it to the formData
1254  _renameFile(file) {
1255    if (typeof this.options.renameFile !== "function") {
1256      return file.name;
1257    }
1258    return this.options.renameFile(file);
1259  }
1260
1261  // Returns a form that can be used as fallback if the browser does not support DragnDrop
1262  //
1263  // If the dropzone is already a form, only the input field and button are returned. Otherwise a complete form element is provided.
1264  // This code has to pass in IE7 :(
1265  getFallbackForm() {
1266    let existingFallback, form;
1267    if (existingFallback = this.getExistingFallback()) {
1268      return existingFallback;
1269    }
1270
1271    let fieldsString = "<div class=\"dz-fallback\">";
1272    if (this.options.dictFallbackText) {
1273      fieldsString += `<p>${this.options.dictFallbackText}</p>`;
1274    }
1275    fieldsString += `<input type="file" name="${this._getParamName(0)}" ${this.options.uploadMultiple ? 'multiple="multiple"' : undefined } /><input type="submit" value="Upload!"></div>`;
1276
1277    let fields = Dropzone.createElement(fieldsString);
1278    if (this.element.tagName !== "FORM") {
1279      form = Dropzone.createElement(`<form action="${this.options.url}" enctype="multipart/form-data" method="${this.options.method}"></form>`);
1280      form.appendChild(fields);
1281    } else {
1282      // Make sure that the enctype and method attributes are set properly
1283      this.element.setAttribute("enctype", "multipart/form-data");
1284      this.element.setAttribute("method", this.options.method);
1285    }
1286    return form != null ? form : fields;
1287  }
1288
1289
1290  // Returns the fallback elements if they exist already
1291  //
1292  // This code has to pass in IE7 :(
1293  getExistingFallback() {
1294    let getFallback = function (elements) {
1295      for (let el of elements) {
1296        if (/(^| )fallback($| )/.test(el.className)) {
1297          return el;
1298        }
1299      }
1300    };
1301
1302    for (let tagName of ["div", "form"]) {
1303      var fallback;
1304      if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {
1305        return fallback;
1306      }
1307    }
1308  }
1309
1310
1311  // Activates all listeners stored in @listeners
1312  setupEventListeners() {
1313    return this.listeners.map((elementListeners) =>
1314        (() => {
1315          let result = [];
1316          for (let event in elementListeners.events) {
1317            let listener = elementListeners.events[event];
1318            result.push(elementListeners.element.addEventListener(event, listener, false));
1319          }
1320          return result;
1321        })());
1322  }
1323
1324
1325  // Deactivates all listeners stored in @listeners
1326  removeEventListeners() {
1327    return this.listeners.map((elementListeners) =>
1328        (() => {
1329          let result = [];
1330          for (let event in elementListeners.events) {
1331            let listener = elementListeners.events[event];
1332            result.push(elementListeners.element.removeEventListener(event, listener, false));
1333          }
1334          return result;
1335        })());
1336  }
1337
1338  // Removes all event listeners and cancels all files in the queue or being processed.
1339  disable() {
1340    this.clickableElements.forEach(element => element.classList.remove("dz-clickable"));
1341    this.removeEventListeners();
1342    this.disabled = true;
1343
1344    return this.files.map((file) => this.cancelUpload(file));
1345  }
1346
1347  enable() {
1348    delete this.disabled;
1349    this.clickableElements.forEach(element => element.classList.add("dz-clickable"));
1350    return this.setupEventListeners();
1351  }
1352
1353  // Returns a nicely formatted filesize
1354  filesize(size) {
1355    let selectedSize = 0;
1356    let selectedUnit = "b";
1357
1358    if (size > 0) {
1359      let units = ['tb', 'gb', 'mb', 'kb', 'b'];
1360
1361      for (let i = 0; i < units.length; i++) {
1362        let unit = units[i];
1363        let cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10;
1364
1365        if (size >= cutoff) {
1366          selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i);
1367          selectedUnit = unit;
1368          break;
1369        }
1370      }
1371
1372      selectedSize = Math.round(10 * selectedSize) / 10; // Cutting of digits
1373    }
1374
1375    return `<strong>${selectedSize}</strong> ${this.options.dictFileSizeUnits[selectedUnit]}`;
1376  }
1377
1378
1379  // Adds or removes the `dz-max-files-reached` class from the form.
1380  _updateMaxFilesReachedClass() {
1381    if ((this.options.maxFiles != null) && (this.getAcceptedFiles().length >= this.options.maxFiles)) {
1382      if (this.getAcceptedFiles().length === this.options.maxFiles) {
1383        this.emit('maxfilesreached', this.files);
1384      }
1385      return this.element.classList.add("dz-max-files-reached");
1386    } else {
1387      return this.element.classList.remove("dz-max-files-reached");
1388    }
1389  }
1390
1391
1392  drop(e) {
1393    if (!e.dataTransfer) {
1394      return;
1395    }
1396    this.emit("drop", e);
1397
1398    // Convert the FileList to an Array
1399    // This is necessary for IE11
1400    let files = [];
1401    for (let i = 0; i < e.dataTransfer.files.length; i++) {
1402      files[i] = e.dataTransfer.files[i];
1403    }
1404
1405    this.emit("addedfiles", files);
1406
1407    // Even if it's a folder, files.length will contain the folders.
1408    if (files.length) {
1409      let {items} = e.dataTransfer;
1410      if (items && items.length && (items[0].webkitGetAsEntry != null)) {
1411        // The browser supports dropping of folders, so handle items instead of files
1412        this._addFilesFromItems(items);
1413      } else {
1414        this.handleFiles(files);
1415      }
1416    }
1417  }
1418
1419  paste(e) {
1420    if (__guard__(e != null ? e.clipboardData : undefined, x => x.items) == null) {
1421      return;
1422    }
1423
1424    this.emit("paste", e);
1425    let {items} = e.clipboardData;
1426
1427    if (items.length) {
1428      return this._addFilesFromItems(items);
1429    }
1430  }
1431
1432
1433  handleFiles(files) {
1434    for(let file of files) {
1435      this.addFile(file);
1436    }
1437  }
1438
1439  // When a folder is dropped (or files are pasted), items must be handled
1440  // instead of files.
1441  _addFilesFromItems(items) {
1442    return (() => {
1443      let result = [];
1444      for (let item of items) {
1445        var entry;
1446        if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {
1447          if (entry.isFile) {
1448            result.push(this.addFile(item.getAsFile()));
1449          } else if (entry.isDirectory) {
1450            // Append all files from that directory to files
1451            result.push(this._addFilesFromDirectory(entry, entry.name));
1452          } else {
1453            result.push(undefined);
1454          }
1455        } else if (item.getAsFile != null) {
1456          if ((item.kind == null) || (item.kind === "file")) {
1457            result.push(this.addFile(item.getAsFile()));
1458          } else {
1459            result.push(undefined);
1460          }
1461        } else {
1462          result.push(undefined);
1463        }
1464      }
1465      return result;
1466    })();
1467  }
1468
1469
1470  // Goes through the directory, and adds each file it finds recursively
1471  _addFilesFromDirectory(directory, path) {
1472    let dirReader = directory.createReader();
1473
1474    let errorHandler = error => __guardMethod__(console, 'log', o => o.log(error));
1475
1476    var readEntries = () => {
1477      return dirReader.readEntries(entries => {
1478            if (entries.length > 0) {
1479              for (let entry of entries) {
1480                if (entry.isFile) {
1481                  entry.file(file => {
1482                    if (this.options.ignoreHiddenFiles && (file.name.substring(0, 1) === '.')) {
1483                      return;
1484                    }
1485                    file.fullPath = `${path}/${file.name}`;
1486                    return this.addFile(file);
1487                  });
1488                } else if (entry.isDirectory) {
1489                  this._addFilesFromDirectory(entry, `${path}/${entry.name}`);
1490                }
1491              }
1492
1493              // Recursively call readEntries() again, since browser only handle
1494              // the first 100 entries.
1495              // See: https://developer.mozilla.org/en-US/docs/Web/API/DirectoryReader#readEntries
1496              readEntries();
1497            }
1498            return null;
1499          }
1500          , errorHandler);
1501    };
1502
1503    return readEntries();
1504  }
1505
1506
1507  // If `done()` is called without argument the file is accepted
1508  // If you call it with an error message, the file is rejected
1509  // (This allows for asynchronous validation)
1510  //
1511  // This function checks the filesize, and if the file.type passes the
1512  // `acceptedFiles` check.
1513  accept(file, done) {
1514    if (this.options.maxFilesize && file.size > (this.options.maxFilesize * 1024 * 1024)) {
1515      return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize));
1516    } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {
1517      return done(this.options.dictInvalidFileType);
1518    } else if ((this.options.maxFiles != null) && (this.getAcceptedFiles().length >= this.options.maxFiles)) {
1519      done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles));
1520      return this.emit("maxfilesexceeded", file);
1521    } else {
1522      return this.options.accept.call(this, file, done);
1523    }
1524  }
1525
1526  addFile(file) {
1527    file.upload = {
1528      uuid: Dropzone.uuidv4(),
1529      progress: 0,
1530      // Setting the total upload size to file.size for the beginning
1531      // It's actual different than the size to be transmitted.
1532      total: file.size,
1533      bytesSent: 0,
1534      filename: this._renameFile(file),
1535      chunked: this.options.chunking && (this.options.forceChunking || file.size > this.options.chunkSize),
1536      totalChunkCount: Math.ceil(file.size / this.options.chunkSize)
1537    };
1538    this.files.push(file);
1539
1540    file.status = Dropzone.ADDED;
1541
1542    this.emit("addedfile", file);
1543
1544    this._enqueueThumbnail(file);
1545
1546    return this.accept(file, error => {
1547      if (error) {
1548        file.accepted = false;
1549        this._errorProcessing([file], error); // Will set the file.status
1550      } else {
1551        file.accepted = true;
1552        if (this.options.autoQueue) {
1553          this.enqueueFile(file);
1554        } // Will set .accepted = true
1555      }
1556      return this._updateMaxFilesReachedClass();
1557    });
1558  }
1559
1560
1561  // Wrapper for enqueueFile
1562  enqueueFiles(files) {
1563    for (let file of files) {
1564      this.enqueueFile(file);
1565    }
1566    return null;
1567  }
1568
1569  enqueueFile(file) {
1570    if ((file.status === Dropzone.ADDED) && (file.accepted === true)) {
1571      file.status = Dropzone.QUEUED;
1572      if (this.options.autoProcessQueue) {
1573        return setTimeout((() => this.processQueue()), 0); // Deferring the call
1574      }
1575    } else {
1576      throw new Error("This file can't be queued because it has already been processed or was rejected.");
1577    }
1578  }
1579
1580  _enqueueThumbnail(file) {
1581    if (this.options.createImageThumbnails && file.type.match(/image.*/) && (file.size <= (this.options.maxThumbnailFilesize * 1024 * 1024))) {
1582      this._thumbnailQueue.push(file);
1583      return setTimeout((() => this._processThumbnailQueue()), 0); // Deferring the call
1584    }
1585  }
1586
1587  _processThumbnailQueue() {
1588    if (this._processingThumbnail || (this._thumbnailQueue.length === 0)) {
1589      return;
1590    }
1591
1592    this._processingThumbnail = true;
1593    let file = this._thumbnailQueue.shift();
1594    return this.createThumbnail(file, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, true, dataUrl => {
1595      this.emit("thumbnail", file, dataUrl);
1596      this._processingThumbnail = false;
1597      return this._processThumbnailQueue();
1598    });
1599  }
1600
1601
1602  // Can be called by the user to remove a file
1603  removeFile(file) {
1604    if (file.status === Dropzone.UPLOADING) {
1605      this.cancelUpload(file);
1606    }
1607    this.files = without(this.files, file);
1608
1609    this.emit("removedfile", file);
1610    if (this.files.length === 0) {
1611      return this.emit("reset");
1612    }
1613  }
1614
1615  // Removes all files that aren't currently processed from the list
1616  removeAllFiles(cancelIfNecessary) {
1617    // Create a copy of files since removeFile() changes the @files array.
1618    if (cancelIfNecessary == null) {
1619      cancelIfNecessary = false;
1620    }
1621    for (let file of this.files.slice()) {
1622      if ((file.status !== Dropzone.UPLOADING) || cancelIfNecessary) {
1623        this.removeFile(file);
1624      }
1625    }
1626    return null;
1627  }
1628
1629  // Resizes an image before it gets sent to the server. This function is the default behavior of
1630  // `options.transformFile` if `resizeWidth` or `resizeHeight` are set. The callback is invoked with
1631  // the resized blob.
1632  resizeImage(file, width, height, resizeMethod, callback) {
1633    return this.createThumbnail(file, width, height, resizeMethod, true, (dataUrl, canvas) => {
1634      if (canvas == null) {
1635        // The image has not been resized
1636        return callback(file);
1637      } else {
1638        let {resizeMimeType} = this.options;
1639        if (resizeMimeType == null) {
1640          resizeMimeType = file.type;
1641        }
1642        let resizedDataURL = canvas.toDataURL(resizeMimeType, this.options.resizeQuality);
1643        if ((resizeMimeType === 'image/jpeg') || (resizeMimeType === 'image/jpg')) {
1644          // Now add the original EXIF information
1645          resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL);
1646        }
1647        return callback(Dropzone.dataURItoBlob(resizedDataURL));
1648      }
1649    });
1650  }
1651
1652  createThumbnail(file, width, height, resizeMethod, fixOrientation, callback) {
1653    let fileReader = new FileReader;
1654
1655    fileReader.onload = () => {
1656
1657      file.dataURL = fileReader.result;
1658
1659      // Don't bother creating a thumbnail for SVG images since they're vector
1660      if (file.type === "image/svg+xml") {
1661        if (callback != null) {
1662          callback(fileReader.result);
1663        }
1664        return;
1665      }
1666
1667      return this.createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback);
1668    };
1669
1670    return fileReader.readAsDataURL(file);
1671  }
1672
1673  createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback, crossOrigin) {
1674    // Not using `new Image` here because of a bug in latest Chrome versions.
1675    // See https://github.com/enyo/dropzone/pull/226
1676    let img = document.createElement("img");
1677
1678    if (crossOrigin) {
1679      img.crossOrigin = crossOrigin;
1680    }
1681
1682    img.onload = () => {
1683      let loadExif = callback => callback(1);
1684      if ((typeof EXIF !== 'undefined' && EXIF !== null) && fixOrientation) {
1685        loadExif = callback =>
1686            EXIF.getData(img, function () {
1687              return callback(EXIF.getTag(this, 'Orientation'));
1688            })
1689        ;
1690      }
1691
1692      return loadExif(orientation => {
1693        file.width = img.width;
1694        file.height = img.height;
1695
1696        let resizeInfo = this.options.resize.call(this, file, width, height, resizeMethod);
1697
1698        let canvas = document.createElement("canvas");
1699        let ctx = canvas.getContext("2d");
1700
1701        canvas.width = resizeInfo.trgWidth;
1702        canvas.height = resizeInfo.trgHeight;
1703
1704        if (orientation > 4) {
1705          canvas.width = resizeInfo.trgHeight;
1706          canvas.height = resizeInfo.trgWidth;
1707        }
1708
1709        switch (orientation) {
1710          case 2:
1711            // horizontal flip
1712            ctx.translate(canvas.width, 0);
1713            ctx.scale(-1, 1);
1714            break;
1715          case 3:
1716            // 180° rotate left
1717            ctx.translate(canvas.width, canvas.height);
1718            ctx.rotate(Math.PI);
1719            break;
1720          case 4:
1721            // vertical flip
1722            ctx.translate(0, canvas.height);
1723            ctx.scale(1, -1);
1724            break;
1725          case 5:
1726            // vertical flip + 90 rotate right
1727            ctx.rotate(0.5 * Math.PI);
1728            ctx.scale(1, -1);
1729            break;
1730          case 6:
1731            // 90° rotate right
1732            ctx.rotate(0.5 * Math.PI);
1733            ctx.translate(0, -canvas.width);
1734            break;
1735          case 7:
1736            // horizontal flip + 90 rotate right
1737            ctx.rotate(0.5 * Math.PI);
1738            ctx.translate(canvas.height, -canvas.width);
1739            ctx.scale(-1, 1);
1740            break;
1741          case 8:
1742            // 90° rotate left
1743            ctx.rotate(-0.5 * Math.PI);
1744            ctx.translate(-canvas.height, 0);
1745            break;
1746        }
1747
1748        // This is a bugfix for iOS' scaling bug.
1749        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);
1750
1751        let thumbnail = canvas.toDataURL("image/png");
1752
1753        if (callback != null) {
1754          return callback(thumbnail, canvas);
1755        }
1756      });
1757    };
1758
1759    if (callback != null) {
1760      img.onerror = callback;
1761    }
1762
1763    return img.src = file.dataURL;
1764  }
1765
1766
1767  // Goes through the queue and processes files if there aren't too many already.
1768  processQueue() {
1769    let {parallelUploads} = this.options;
1770    let processingLength = this.getUploadingFiles().length;
1771    let i = processingLength;
1772
1773    // There are already at least as many files uploading than should be
1774    if (processingLength >= parallelUploads) {
1775      return;
1776    }
1777
1778    let queuedFiles = this.getQueuedFiles();
1779
1780    if (!(queuedFiles.length > 0)) {
1781      return;
1782    }
1783
1784    if (this.options.uploadMultiple) {
1785      // The files should be uploaded in one request
1786      return this.processFiles(queuedFiles.slice(0, (parallelUploads - processingLength)));
1787    } else {
1788      while (i < parallelUploads) {
1789        if (!queuedFiles.length) {
1790          return;
1791        } // Nothing left to process
1792        this.processFile(queuedFiles.shift());
1793        i++;
1794      }
1795    }
1796  }
1797
1798
1799  // Wrapper for `processFiles`
1800  processFile(file) {
1801    return this.processFiles([file]);
1802  }
1803
1804
1805  // Loads the file, then calls finishedLoading()
1806  processFiles(files) {
1807    for (let file of files) {
1808      file.processing = true; // Backwards compatibility
1809      file.status = Dropzone.UPLOADING;
1810
1811      this.emit("processing", file);
1812    }
1813
1814    if (this.options.uploadMultiple) {
1815      this.emit("processingmultiple", files);
1816    }
1817
1818    return this.uploadFiles(files);
1819  }
1820
1821
1822  _getFilesWithXhr(xhr) {
1823    let files;
1824    return files = (this.files.filter((file) => file.xhr === xhr).map((file) => file));
1825  }
1826
1827
1828  // Cancels the file upload and sets the status to CANCELED
1829  // **if** the file is actually being uploaded.
1830  // If it's still in the queue, the file is being removed from it and the status
1831  // set to CANCELED.
1832  cancelUpload(file) {
1833    if (file.status === Dropzone.UPLOADING) {
1834      let groupedFiles = this._getFilesWithXhr(file.xhr);
1835      for (let groupedFile of groupedFiles) {
1836        groupedFile.status = Dropzone.CANCELED;
1837      }
1838      if (typeof file.xhr !== 'undefined') {
1839        file.xhr.abort();
1840      }
1841      for (let groupedFile of groupedFiles) {
1842        this.emit("canceled", groupedFile);
1843      }
1844      if (this.options.uploadMultiple) {
1845        this.emit("canceledmultiple", groupedFiles);
1846      }
1847
1848    } else if (file.status === Dropzone.ADDED || file.status === Dropzone.QUEUED) {
1849      file.status = Dropzone.CANCELED;
1850      this.emit("canceled", file);
1851      if (this.options.uploadMultiple) {
1852        this.emit("canceledmultiple", [file]);
1853      }
1854    }
1855
1856    if (this.options.autoProcessQueue) {
1857      return this.processQueue();
1858    }
1859  }
1860
1861  resolveOption(option, ...args) {
1862    if (typeof option === 'function') {
1863      return option.apply(this, args);
1864    }
1865    return option;
1866  }
1867
1868  uploadFile(file) { return this.uploadFiles([file]); }
1869
1870  uploadFiles(files) {
1871    this._transformFiles(files, (transformedFiles) => {
1872      if (files[0].upload.chunked) {
1873        // This file should be sent in chunks!
1874
1875        // If the chunking option is set, we **know** that there can only be **one** file, since
1876        // uploadMultiple is not allowed with this option.
1877        let file = files[0];
1878        let transformedFile = transformedFiles[0];
1879        let startedChunkCount = 0;
1880
1881        file.upload.chunks = [];
1882
1883        let handleNextChunk = () => {
1884          let chunkIndex = 0;
1885
1886          // Find the next item in file.upload.chunks that is not defined yet.
1887          while (file.upload.chunks[chunkIndex] !== undefined) {
1888            chunkIndex++;
1889          }
1890
1891          // This means, that all chunks have already been started.
1892          if (chunkIndex >= file.upload.totalChunkCount) return;
1893
1894          startedChunkCount++;
1895
1896          let start = chunkIndex * this.options.chunkSize;
1897          let end = Math.min(start + this.options.chunkSize, file.size);
1898
1899          let dataBlock = {
1900            name: this._getParamName(0),
1901            data: transformedFile.webkitSlice ? transformedFile.webkitSlice(start, end) : transformedFile.slice(start, end),
1902            filename: file.upload.filename,
1903            chunkIndex: chunkIndex
1904          };
1905
1906          file.upload.chunks[chunkIndex] = {
1907            file: file,
1908            index: chunkIndex,
1909            dataBlock: dataBlock, // In case we want to retry.
1910            status: Dropzone.UPLOADING,
1911            progress: 0,
1912            retries: 0 // The number of times this block has been retried.
1913          };
1914
1915
1916          this._uploadData(files, [dataBlock]);
1917        };
1918
1919        file.upload.finishedChunkUpload = (chunk) => {
1920          let allFinished = true;
1921          chunk.status = Dropzone.SUCCESS;
1922
1923          // Clear the data from the chunk
1924          chunk.dataBlock = null;
1925          // Leaving this reference to xhr intact here will cause memory leaks in some browsers
1926          chunk.xhr = null;
1927
1928          for (let i = 0; i < file.upload.totalChunkCount; i ++) {
1929            if (file.upload.chunks[i] === undefined) {
1930              return handleNextChunk();
1931            }
1932            if (file.upload.chunks[i].status !== Dropzone.SUCCESS) {
1933              allFinished = false;
1934            }
1935          }
1936
1937          if (allFinished) {
1938            this.options.chunksUploaded(file, () => {
1939              this._finished(files, '', null);
1940            });
1941          }
1942        };
1943
1944        if (this.options.parallelChunkUploads) {
1945          for (let i = 0; i < file.upload.totalChunkCount; i++) {
1946            handleNextChunk();
1947          }
1948        }
1949        else {
1950          handleNextChunk();
1951        }
1952      } else {
1953        let dataBlocks = [];
1954        for (let i = 0; i < files.length; i++) {
1955          dataBlocks[i] = {
1956            name: this._getParamName(i),
1957            data: transformedFiles[i],
1958            filename: files[i].upload.filename
1959          };
1960        }
1961        this._uploadData(files, dataBlocks);
1962      }
1963    });
1964  }
1965
1966  /// Returns the right chunk for given file and xhr
1967  _getChunk(file, xhr) {
1968    for (let i = 0; i < file.upload.totalChunkCount; i++) {
1969      if (file.upload.chunks[i] !== undefined && file.upload.chunks[i].xhr === xhr) {
1970        return file.upload.chunks[i];
1971      }
1972    }
1973  }
1974
1975  // This function actually uploads the file(s) to the server.
1976  // If dataBlocks contains the actual data to upload (meaning, that this could either be transformed
1977  // files, or individual chunks for chunked upload).
1978  _uploadData(files, dataBlocks) {
1979    let xhr = new XMLHttpRequest();
1980
1981    // Put the xhr object in the file objects to be able to reference it later.
1982    for (let file of files) {
1983      file.xhr = xhr;
1984    }
1985    if (files[0].upload.chunked) {
1986      // Put the xhr object in the right chunk object, so it can be associated later, and found with _getChunk
1987      files[0].upload.chunks[dataBlocks[0].chunkIndex].xhr = xhr;
1988    }
1989
1990    let method = this.resolveOption(this.options.method, files);
1991    let url = this.resolveOption(this.options.url, files);
1992    xhr.open(method, url, true);
1993
1994    // Setting the timeout after open because of IE11 issue: https://gitlab.com/meno/dropzone/issues/8
1995    xhr.timeout = this.resolveOption(this.options.timeout, files);
1996
1997    // Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179
1998    xhr.withCredentials = !!this.options.withCredentials;
1999
2000
2001    xhr.onload = e => {
2002      this._finishedUploading(files, xhr, e);
2003    };
2004
2005    xhr.onerror = () => {
2006      this._handleUploadError(files, xhr);
2007    };
2008
2009    // Some browsers do not have the .upload property
2010    let progressObj = xhr.upload != null ? xhr.upload : xhr;
2011    progressObj.onprogress = (e) => this._updateFilesUploadProgress(files, xhr, e);
2012
2013    let headers = {
2014      "Accept": "application/json",
2015      "Cache-Control": "no-cache",
2016      "X-Requested-With": "XMLHttpRequest",
2017    };
2018
2019    if (this.options.headers) {
2020      Dropzone.extend(headers, this.options.headers);
2021    }
2022
2023    for (let headerName in headers) {
2024      let headerValue = headers[headerName];
2025      if (headerValue) {
2026        xhr.setRequestHeader(headerName, headerValue);
2027      }
2028    }
2029
2030    let formData = new FormData();
2031
2032    // Adding all @options parameters
2033    if (this.options.params) {
2034      let additionalParams = this.options.params;
2035      if (typeof additionalParams === 'function') {
2036        additionalParams = additionalParams.call(this, files, xhr, files[0].upload.chunked ? this._getChunk(files[0], xhr) : null);
2037      }
2038
2039      for (let key in additionalParams) {
2040        let value = additionalParams[key];
2041        formData.append(key, value);
2042      }
2043    }
2044
2045    // Let the user add additional data if necessary
2046    for (let file of files) {
2047      this.emit("sending", file, xhr, formData);
2048    }
2049    if (this.options.uploadMultiple) {
2050      this.emit("sendingmultiple", files, xhr, formData);
2051    }
2052
2053
2054    this._addFormElementData(formData);
2055
2056
2057    // Finally add the files
2058    // Has to be last because some servers (eg: S3) expect the file to be the last parameter
2059    for (let i = 0; i < dataBlocks.length; i++) {
2060      let dataBlock = dataBlocks[i];
2061      formData.append(dataBlock.name, dataBlock.data, dataBlock.filename);
2062    }
2063
2064    this.submitRequest(xhr, formData, files);
2065  }
2066
2067
2068  // Transforms all files with this.options.transformFile and invokes done with the transformed files when done.
2069  _transformFiles(files, done) {
2070    let transformedFiles = [];
2071    // Clumsy way of handling asynchronous calls, until I get to add a proper Future library.
2072    let doneCounter = 0;
2073    for (let i = 0; i < files.length; i++) {
2074      this.options.transformFile.call(this, files[i], (transformedFile) => {
2075        transformedFiles[i] = transformedFile;
2076        if (++doneCounter === files.length) {
2077          done(transformedFiles);
2078        }
2079      });
2080    }
2081  }
2082
2083  // Takes care of adding other input elements of the form to the AJAX request
2084  _addFormElementData(formData) {
2085    // Take care of other input elements
2086    if (this.element.tagName === "FORM") {
2087      for (let input of this.element.querySelectorAll("input, textarea, select, button")) {
2088        let inputName = input.getAttribute("name");
2089        let inputType = input.getAttribute("type");
2090        if (inputType) inputType = inputType.toLowerCase();
2091
2092        // If the input doesn't have a name, we can't use it.
2093        if (typeof inputName === 'undefined' || inputName === null) continue;
2094
2095        if ((input.tagName === "SELECT") && input.hasAttribute("multiple")) {
2096          // Possibly multiple values
2097          for (let option of input.options) {
2098            if (option.selected) {
2099              formData.append(inputName, option.value);
2100            }
2101          }
2102        } else if (!inputType || (inputType !== "checkbox" && inputType !== "radio") || input.checked) {
2103          formData.append(inputName, input.value);
2104        }
2105      }
2106    }
2107  }
2108
2109  // Invoked when there is new progress information about given files.
2110  // If e is not provided, it is assumed that the upload is finished.
2111  _updateFilesUploadProgress(files, xhr, e) {
2112    let progress;
2113    if (typeof e !== 'undefined') {
2114      progress = (100 * e.loaded) / e.total;
2115
2116      if (files[0].upload.chunked) {
2117        let file = files[0];
2118        // Since this is a chunked upload, we need to update the appropriate chunk progress.
2119        let chunk = this._getChunk(file, xhr);
2120        chunk.progress = progress;
2121        chunk.total = e.total;
2122        chunk.bytesSent = e.loaded;
2123        let fileProgress = 0, fileTotal, fileBytesSent;
2124        file.upload.progress = 0;
2125        file.upload.total = 0;
2126        file.upload.bytesSent = 0;
2127        for (let i = 0; i < file.upload.totalChunkCount; i++) {
2128          if (file.upload.chunks[i] !== undefined && file.upload.chunks[i].progress !== undefined) {
2129            file.upload.progress += file.upload.chunks[i].progress;
2130            file.upload.total += file.upload.chunks[i].total;
2131            file.upload.bytesSent += file.upload.chunks[i].bytesSent;
2132          }
2133        }
2134        file.upload.progress = file.upload.progress / file.upload.totalChunkCount;
2135      } else {
2136        for (let file of files) {
2137          file.upload.progress = progress;
2138          file.upload.total = e.total;
2139          file.upload.bytesSent = e.loaded;
2140        }
2141      }
2142      for (let file of files) {
2143        this.emit("uploadprogress", file, file.upload.progress, file.upload.bytesSent);
2144      }
2145    } else {
2146      // Called when the file finished uploading
2147
2148      let allFilesFinished = true;
2149
2150      progress = 100;
2151
2152      for (let file of files) {
2153        if ((file.upload.progress !== 100) || (file.upload.bytesSent !== file.upload.total)) {
2154          allFilesFinished = false;
2155        }
2156        file.upload.progress = progress;
2157        file.upload.bytesSent = file.upload.total;
2158      }
2159
2160      // Nothing to do, all files already at 100%
2161      if (allFilesFinished) {
2162        return;
2163      }
2164
2165      for (let file of files) {
2166        this.emit("uploadprogress", file, progress, file.upload.bytesSent);
2167      }
2168    }
2169
2170  }
2171
2172
2173  _finishedUploading(files, xhr, e) {
2174    let response;
2175
2176    if (files[0].status === Dropzone.CANCELED) {
2177      return;
2178    }
2179
2180    if (xhr.readyState !== 4) {
2181      return;
2182    }
2183
2184    if ((xhr.responseType !== 'arraybuffer') && (xhr.responseType !== 'blob')) {
2185      response = xhr.responseText;
2186
2187      if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) {
2188        try {
2189          response = JSON.parse(response);
2190        } catch (error) {
2191          e = error;
2192          response = "Invalid JSON response from server.";
2193        }
2194      }
2195    }
2196
2197    this._updateFilesUploadProgress(files);
2198
2199    if (!(200 <= xhr.status && xhr.status < 300)) {
2200      this._handleUploadError(files, xhr, response);
2201    } else {
2202      if (files[0].upload.chunked) {
2203        files[0].upload.finishedChunkUpload(this._getChunk(files[0], xhr));
2204      } else {
2205        this._finished(files, response, e);
2206      }
2207    }
2208  }
2209
2210  _handleUploadError(files, xhr, response) {
2211    if (files[0].status === Dropzone.CANCELED) {
2212      return;
2213    }
2214
2215    if (files[0].upload.chunked && this.options.retryChunks) {
2216      let chunk = this._getChunk(files[0], xhr);
2217      if (chunk.retries++ < this.options.retryChunksLimit) {
2218        this._uploadData(files, [chunk.dataBlock]);
2219        return;
2220      } else {
2221        console.warn('Retried this chunk too often. Giving up.')
2222      }
2223    }
2224
2225    for (let file of files) {
2226      this._errorProcessing(files, response || this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr);
2227    }
2228  }
2229
2230  submitRequest(xhr, formData, files) {
2231    xhr.send(formData);
2232  }
2233
2234  // Called internally when processing is finished.
2235  // Individual callbacks have to be called in the appropriate sections.
2236  _finished(files, responseText, e) {
2237    for (let file of files) {
2238      file.status = Dropzone.SUCCESS;
2239      this.emit("success", file, responseText, e);
2240      this.emit("complete", file);
2241    }
2242    if (this.options.uploadMultiple) {
2243      this.emit("successmultiple", files, responseText, e);
2244      this.emit("completemultiple", files);
2245    }
2246
2247    if (this.options.autoProcessQueue) {
2248      return this.processQueue();
2249    }
2250  }
2251
2252  // Called internally when processing is finished.
2253  // Individual callbacks have to be called in the appropriate sections.
2254  _errorProcessing(files, message, xhr) {
2255    for (let file of files) {
2256      file.status = Dropzone.ERROR;
2257      this.emit("error", file, message, xhr);
2258      this.emit("complete", file);
2259    }
2260    if (this.options.uploadMultiple) {
2261      this.emit("errormultiple", files, message, xhr);
2262      this.emit("completemultiple", files);
2263    }
2264
2265    if (this.options.autoProcessQueue) {
2266      return this.processQueue();
2267    }
2268  }
2269
2270  static uuidv4() {
2271    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
2272      let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
2273      return v.toString(16);
2274    });
2275  };
2276}
2277Dropzone.initClass();
2278
2279
2280Dropzone.version = "5.5.0";
2281
2282
2283// This is a map of options for your different dropzones. Add configurations
2284// to this object for your different dropzone elemens.
2285//
2286// Example:
2287//
2288//     Dropzone.options.myDropzoneElementId = { maxFilesize: 1 };
2289//
2290// To disable autoDiscover for a specific element, you can set `false` as an option:
2291//
2292//     Dropzone.options.myDisabledElementId = false;
2293//
2294// And in html:
2295//
2296//     <form action="/upload" id="my-dropzone-element-id" class="dropzone"></form>
2297Dropzone.options = {};
2298
2299
2300// Returns the options for an element or undefined if none available.
2301Dropzone.optionsForElement = function (element) {
2302  // Get the `Dropzone.options.elementId` for this element if it exists
2303  if (element.getAttribute("id")) {
2304    return Dropzone.options[camelize(element.getAttribute("id"))];
2305  } else {
2306    return undefined;
2307  }
2308};
2309
2310
2311// Holds a list of all dropzone instances
2312Dropzone.instances = [];
2313
2314// Returns the dropzone for given element if any
2315Dropzone.forElement = function (element) {
2316  if (typeof element === "string") {
2317    element = document.querySelector(element);
2318  }
2319  if ((element != null ? element.dropzone : undefined) == null) {
2320    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.");
2321  }
2322  return element.dropzone;
2323};
2324
2325
2326// Set to false if you don't want Dropzone to automatically find and attach to .dropzone elements.
2327Dropzone.autoDiscover = true;
2328
2329// Looks for all .dropzone elements and creates a dropzone for them
2330Dropzone.discover = function () {
2331  let dropzones;
2332  if (document.querySelectorAll) {
2333    dropzones = document.querySelectorAll(".dropzone");
2334  } else {
2335    dropzones = [];
2336    // IE :(
2337    let checkElements = elements =>
2338        (() => {
2339          let result = [];
2340          for (let el of elements) {
2341            if (/(^| )dropzone($| )/.test(el.className)) {
2342              result.push(dropzones.push(el));
2343            } else {
2344              result.push(undefined);
2345            }
2346          }
2347          return result;
2348        })()
2349    ;
2350    checkElements(document.getElementsByTagName("div"));
2351    checkElements(document.getElementsByTagName("form"));
2352  }
2353
2354  return (() => {
2355    let result = [];
2356    for (let dropzone of dropzones) {
2357      // Create a dropzone unless auto discover has been disabled for specific element
2358      if (Dropzone.optionsForElement(dropzone) !== false) {
2359        result.push(new Dropzone(dropzone));
2360      } else {
2361        result.push(undefined);
2362      }
2363    }
2364    return result;
2365  })();
2366};
2367
2368
2369// Since the whole Drag'n'Drop API is pretty new, some browsers implement it,
2370// but not correctly.
2371// So I created a blacklist of userAgents. Yes, yes. Browser sniffing, I know.
2372// But what to do when browsers *theoretically* support an API, but crash
2373// when using it.
2374//
2375// This is a list of regular expressions tested against navigator.userAgent
2376//
2377// ** It should only be used on browser that *do* support the API, but
2378// incorrectly **
2379//
2380Dropzone.blacklistedBrowsers = [
2381  // The mac os and windows phone version of opera 12 seems to have a problem with the File drag'n'drop API.
2382  /opera.*(Macintosh|Windows Phone).*version\/12/i
2383];
2384
2385
2386// Checks if the browser is supported
2387Dropzone.isBrowserSupported = function () {
2388  let capableBrowser = true;
2389
2390  if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {
2391    if (!("classList" in document.createElement("a"))) {
2392      capableBrowser = false;
2393    } else {
2394      // The browser supports the API, but may be blacklisted.
2395      for (let regex of Dropzone.blacklistedBrowsers) {
2396        if (regex.test(navigator.userAgent)) {
2397          capableBrowser = false;
2398          continue;
2399        }
2400      }
2401    }
2402  } else {
2403    capableBrowser = false;
2404  }
2405
2406  return capableBrowser;
2407};
2408
2409Dropzone.dataURItoBlob = function (dataURI) {
2410  // convert base64 to raw binary data held in a string
2411  // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
2412  let byteString = atob(dataURI.split(',')[1]);
2413
2414  // separate out the mime component
2415  let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
2416
2417  // write the bytes of the string to an ArrayBuffer
2418  let ab = new ArrayBuffer(byteString.length);
2419  let ia = new Uint8Array(ab);
2420  for (let i = 0, end = byteString.length, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) {
2421    ia[i] = byteString.charCodeAt(i);
2422  }
2423
2424  // write the ArrayBuffer to a blob
2425  return new Blob([ab], {type: mimeString});
2426};
2427
2428// Returns an array without the rejected item
2429const without = (list, rejectedItem) => list.filter((item) => item !== rejectedItem).map((item) => item);
2430
2431// abc-def_ghi -> abcDefGhi
2432const camelize = str => str.replace(/[\-_](\w)/g, match => match.charAt(1).toUpperCase());
2433
2434// Creates an element from string
2435Dropzone.createElement = function (string) {
2436  let div = document.createElement("div");
2437  div.innerHTML = string;
2438  return div.childNodes[0];
2439};
2440
2441// Tests if given element is inside (or simply is) the container
2442Dropzone.elementInside = function (element, container) {
2443  if (element === container) {
2444    return true;
2445  } // Coffeescript doesn't support do/while loops
2446  while ((element = element.parentNode)) {
2447    if (element === container) {
2448      return true;
2449    }
2450  }
2451  return false;
2452};
2453
2454
2455Dropzone.getElement = function (el, name) {
2456  let element;
2457  if (typeof el === "string") {
2458    element = document.querySelector(el);
2459  } else if (el.nodeType != null) {
2460    element = el;
2461  }
2462  if (element == null) {
2463    throw new Error(`Invalid \`${name}\` option provided. Please provide a CSS selector or a plain HTML element.`);
2464  }
2465  return element;
2466};
2467
2468
2469Dropzone.getElements = function (els, name) {
2470  let el, elements;
2471  if (els instanceof Array) {
2472    elements = [];
2473    try {
2474      for (el of els) {
2475        elements.push(this.getElement(el, name));
2476      }
2477    } catch (e) {
2478      elements = null;
2479    }
2480  } else if (typeof els === "string") {
2481    elements = [];
2482    for (el of document.querySelectorAll(els)) {
2483      elements.push(el);
2484    }
2485  } else if (els.nodeType != null) {
2486    elements = [els];
2487  }
2488
2489  if ((elements == null) || !elements.length) {
2490    throw new Error(`Invalid \`${name}\` option provided. Please provide a CSS selector, a plain HTML element or a list of those.`);
2491  }
2492
2493  return elements;
2494};
2495
2496// Asks the user the question and calls accepted or rejected accordingly
2497//
2498// The default implementation just uses `window.confirm` and then calls the
2499// appropriate callback.
2500Dropzone.confirm = function (question, accepted, rejected) {
2501  if (window.confirm(question)) {
2502    return accepted();
2503  } else if (rejected != null) {
2504    return rejected();
2505  }
2506};
2507
2508// Validates the mime type like this:
2509//
2510// https://developer.mozilla.org/en-US/docs/HTML/Element/input#attr-accept
2511Dropzone.isValidFile = function (file, acceptedFiles) {
2512  if (!acceptedFiles) {
2513    return true;
2514  } // If there are no accepted mime types, it's OK
2515  acceptedFiles = acceptedFiles.split(",");
2516
2517  let mimeType = file.type;
2518  let baseMimeType = mimeType.replace(/\/.*$/, "");
2519
2520  for (let validType of acceptedFiles) {
2521    validType = validType.trim();
2522    if (validType.charAt(0) === ".") {
2523      if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) {
2524        return true;
2525      }
2526    } else if (/\/\*$/.test(validType)) {
2527      // This is something like a image/* mime type
2528      if (baseMimeType === validType.replace(/\/.*$/, "")) {
2529        return true;
2530      }
2531    } else {
2532      if (mimeType === validType) {
2533        return true;
2534      }
2535    }
2536  }
2537
2538  return false;
2539};
2540
2541// Augment jQuery
2542if (typeof jQuery !== 'undefined' && jQuery !== null) {
2543  jQuery.fn.dropzone = function (options) {
2544    return this.each(function () {
2545      return new Dropzone(this, options);
2546    });
2547  };
2548}
2549
2550
2551if (typeof module !== 'undefined' && module !== null) {
2552  module.exports = Dropzone;
2553} else {
2554  window.Dropzone = Dropzone;
2555}
2556
2557
2558// Dropzone file status codes
2559Dropzone.ADDED = "added";
2560
2561Dropzone.QUEUED = "queued";
2562// For backwards compatibility. Now, if a file is accepted, it's either queued
2563// or uploading.
2564Dropzone.ACCEPTED = Dropzone.QUEUED;
2565
2566Dropzone.UPLOADING = "uploading";
2567Dropzone.PROCESSING = Dropzone.UPLOADING; // alias
2568
2569Dropzone.CANCELED = "canceled";
2570Dropzone.ERROR = "error";
2571Dropzone.SUCCESS = "success";
2572
2573
2574/*
2575
2576 Bugfix for iOS 6 and 7
2577 Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
2578 based on the work of https://github.com/stomita/ios-imagefile-megapixel
2579
2580 */
2581
2582// Detecting vertical squash in loaded image.
2583// Fixes a bug which squash image vertically while drawing into canvas for some images.
2584// This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel
2585let detectVerticalSquash = function (img) {
2586  let iw = img.naturalWidth;
2587  let ih = img.naturalHeight;
2588  let canvas = document.createElement("canvas");
2589  canvas.width = 1;
2590  canvas.height = ih;
2591  let ctx = canvas.getContext("2d");
2592  ctx.drawImage(img, 0, 0);
2593  let {data} = ctx.getImageData(1, 0, 1, ih);
2594
2595
2596  // search image edge pixel position in case it is squashed vertically.
2597  let sy = 0;
2598  let ey = ih;
2599  let py = ih;
2600  while (py > sy) {
2601    let alpha = data[((py - 1) * 4) + 3];
2602
2603    if (alpha === 0) {
2604      ey = py;
2605    } else {
2606      sy = py;
2607    }
2608
2609    py = (ey + sy) >> 1;
2610  }
2611  let ratio = (py / ih);
2612
2613  if (ratio === 0) {
2614    return 1;
2615  } else {
2616    return ratio;
2617  }
2618};
2619
2620// A replacement for context.drawImage
2621// (args are for source and destination).
2622var drawImageIOSFix = function (ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
2623  let vertSquashRatio = detectVerticalSquash(img);
2624  return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
2625};
2626
2627
2628// Based on MinifyJpeg
2629// Source: http://www.perry.cz/files/ExifRestorer.js
2630// http://elicon.blog57.fc2.com/blog-entry-206.html
2631class ExifRestore {
2632  static initClass() {
2633    this.KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
2634  }
2635
2636  static encode64(input) {
2637    let output = '';
2638    let chr1 = undefined;
2639    let chr2 = undefined;
2640    let chr3 = '';
2641    let enc1 = undefined;
2642    let enc2 = undefined;
2643    let enc3 = undefined;
2644    let enc4 = '';
2645    let i = 0;
2646    while (true) {
2647      chr1 = input[i++];
2648      chr2 = input[i++];
2649      chr3 = input[i++];
2650      enc1 = chr1 >> 2;
2651      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
2652      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
2653      enc4 = chr3 & 63;
2654      if (isNaN(chr2)) {
2655        enc3 = (enc4 = 64);
2656      } else if (isNaN(chr3)) {
2657        enc4 = 64;
2658      }
2659      output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4);
2660      chr1 = (chr2 = (chr3 = ''));
2661      enc1 = (enc2 = (enc3 = (enc4 = '')));
2662      if (!(i < input.length)) {
2663        break;
2664      }
2665    }
2666    return output;
2667  }
2668
2669  static restore(origFileBase64, resizedFileBase64) {
2670    if (!origFileBase64.match('data:image/jpeg;base64,')) {
2671      return resizedFileBase64;
2672    }
2673    let rawImage = this.decode64(origFileBase64.replace('data:image/jpeg;base64,', ''));
2674    let segments = this.slice2Segments(rawImage);
2675    let image = this.exifManipulation(resizedFileBase64, segments);
2676    return `data:image/jpeg;base64,${this.encode64(image)}`;
2677  }
2678
2679  static exifManipulation(resizedFileBase64, segments) {
2680    let exifArray = this.getExifArray(segments);
2681    let newImageArray = this.insertExif(resizedFileBase64, exifArray);
2682    let aBuffer = new Uint8Array(newImageArray);
2683    return aBuffer;
2684  }
2685
2686  static getExifArray(segments) {
2687    let seg = undefined;
2688    let x = 0;
2689    while (x < segments.length) {
2690      seg = segments[x];
2691      if ((seg[0] === 255) & (seg[1] === 225)) {
2692        return seg;
2693      }
2694      x++;
2695    }
2696    return [];
2697  }
2698
2699  static insertExif(resizedFileBase64, exifArray) {
2700    let imageData = resizedFileBase64.replace('data:image/jpeg;base64,', '');
2701    let buf = this.decode64(imageData);
2702    let separatePoint = buf.indexOf(255, 3);
2703    let mae = buf.slice(0, separatePoint);
2704    let ato = buf.slice(separatePoint);
2705    let array = mae;
2706    array = array.concat(exifArray);
2707    array = array.concat(ato);
2708    return array;
2709  }
2710
2711  static slice2Segments(rawImageArray) {
2712    let head = 0;
2713    let segments = [];
2714    while (true) {
2715      var length;
2716      if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 218)) {
2717        break;
2718      }
2719      if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 216)) {
2720        head += 2;
2721      } else {
2722        length = (rawImageArray[head + 2] * 256) + rawImageArray[head + 3];
2723        let endPoint = head + length + 2;
2724        let seg = rawImageArray.slice(head, endPoint);
2725        segments.push(seg);
2726        head = endPoint;
2727      }
2728      if (head > rawImageArray.length) {
2729        break;
2730      }
2731    }
2732    return segments;
2733  }
2734
2735  static decode64(input) {
2736    let output = '';
2737    let chr1 = undefined;
2738    let chr2 = undefined;
2739    let chr3 = '';
2740    let enc1 = undefined;
2741    let enc2 = undefined;
2742    let enc3 = undefined;
2743    let enc4 = '';
2744    let i = 0;
2745    let buf = [];
2746    // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
2747    let base64test = /[^A-Za-z0-9\+\/\=]/g;
2748    if (base64test.exec(input)) {
2749      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.');
2750    }
2751    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
2752    while (true) {
2753      enc1 = this.KEY_STR.indexOf(input.charAt(i++));
2754      enc2 = this.KEY_STR.indexOf(input.charAt(i++));
2755      enc3 = this.KEY_STR.indexOf(input.charAt(i++));
2756      enc4 = this.KEY_STR.indexOf(input.charAt(i++));
2757      chr1 = (enc1 << 2) | (enc2 >> 4);
2758      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
2759      chr3 = ((enc3 & 3) << 6) | enc4;
2760      buf.push(chr1);
2761      if (enc3 !== 64) {
2762        buf.push(chr2);
2763      }
2764      if (enc4 !== 64) {
2765        buf.push(chr3);
2766      }
2767      chr1 = (chr2 = (chr3 = ''));
2768      enc1 = (enc2 = (enc3 = (enc4 = '')));
2769      if (!(i < input.length)) {
2770        break;
2771      }
2772    }
2773    return buf;
2774  }
2775}
2776ExifRestore.initClass();
2777
2778
2779/*
2780 * contentloaded.js
2781 *
2782 * Author: Diego Perini (diego.perini at gmail.com)
2783 * Summary: cross-browser wrapper for DOMContentLoaded
2784 * Updated: 20101020
2785 * License: MIT
2786 * Version: 1.2
2787 *
2788 * URL:
2789 * http://javascript.nwbox.com/ContentLoaded/
2790 * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
2791 */
2792
2793// @win window reference
2794// @fn function reference
2795let contentLoaded = function (win, fn) {
2796  let done = false;
2797  let top = true;
2798  let doc = win.document;
2799  let root = doc.documentElement;
2800  let add = (doc.addEventListener ? "addEventListener" : "attachEvent");
2801  let rem = (doc.addEventListener ? "removeEventListener" : "detachEvent");
2802  let pre = (doc.addEventListener ? "" : "on");
2803  var init = function (e) {
2804    if ((e.type === "readystatechange") && (doc.readyState !== "complete")) {
2805      return;
2806    }
2807    ((e.type === "load" ? win : doc))[rem](pre + e.type, init, false);
2808    if (!done && (done = true)) {
2809      return fn.call(win, e.type || e);
2810    }
2811  };
2812
2813  var poll = function () {
2814    try {
2815      root.doScroll("left");
2816    } catch (e) {
2817      setTimeout(poll, 50);
2818      return;
2819    }
2820    return init("poll");
2821  };
2822
2823  if (doc.readyState !== "complete") {
2824    if (doc.createEventObject && root.doScroll) {
2825      try {
2826        top = !win.frameElement;
2827      } catch (error) {
2828      }
2829      if (top) {
2830        poll();
2831      }
2832    }
2833    doc[add](pre + "DOMContentLoaded", init, false);
2834    doc[add](pre + "readystatechange", init, false);
2835    return win[add](pre + "load", init, false);
2836  }
2837};
2838
2839
2840// As a single function to be able to write tests.
2841Dropzone._autoDiscoverFunction = function () {
2842  if (Dropzone.autoDiscover) {
2843    return Dropzone.discover();
2844  }
2845};
2846contentLoaded(window, Dropzone._autoDiscoverFunction);
2847
2848function __guard__(value, transform) {
2849  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
2850}
2851function __guardMethod__(obj, methodName, transform) {
2852  if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') {
2853    return transform(obj, methodName);
2854  } else {
2855    return undefined;
2856  }
2857}

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.