PageSourceSearch

https://www.peelmutual.com/wp-content/plugins/product-extras-for-woocommerce/assets/js/dropzone.js?ver=4.5.2

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

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.