/*!
 * FilePondPluginImagePreview 4.2.1
 * Licensed under MIT, https://opensource.org/licenses/MIT/
 * Please visit https://pqina.nl/filepond/ for details.
 */

/* eslint-disable */

(function(global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined'
      ? (module.exports = factory())
      : typeof define === 'function' && define.amd
      ? define(factory)
      : ((global = global || self),
        (global.FilePondPluginImagePreview = factory()));
  })(this, function() {
    'use strict';
  
    // test if file is of type image and can be viewed in canvas
    var isPreviewableImage = function isPreviewableImage(file) {
      return /^image/.test(file.type);
    };
  
    var IMAGE_SCALE_SPRING_PROPS = {
      type: 'spring',
      stiffness: 0.5,
      damping: 0.45,
      mass: 10
    };
  
    var createVector = function createVector(x, y) {
      return { x: x, y: y };
    };
  
    var vectorDot = function vectorDot(a, b) {
      return a.x * b.x + a.y * b.y;
    };
  
    var vectorSubtract = function vectorSubtract(a, b) {
      return createVector(a.x - b.x, a.y - b.y);
    };
  
    var vectorDistanceSquared = function vectorDistanceSquared(a, b) {
      return vectorDot(vectorSubtract(a, b), vectorSubtract(a, b));
    };
  
    var vectorDistance = function vectorDistance(a, b) {
      return Math.sqrt(vectorDistanceSquared(a, b));
    };
  
    var getOffsetPointOnEdge = function getOffsetPointOnEdge(length, rotation) {
      var a = length;
  
      var A = 1.5707963267948966;
      var B = rotation;
      var C = 1.5707963267948966 - rotation;
  
      var sinA = Math.sin(A);
      var sinB = Math.sin(B);
      var sinC = Math.sin(C);
      var cosC = Math.cos(C);
      var ratio = a / sinA;
      var b = ratio * sinB;
      var c = ratio * sinC;
  
      return createVector(cosC * b, cosC * c);
    };
  
    var getRotatedRectSize = function getRotatedRectSize(rect, rotation) {
      var w = rect.width;
      var h = rect.height;
  
      var hor = getOffsetPointOnEdge(w, rotation);
      var ver = getOffsetPointOnEdge(h, rotation);
  
      var tl = createVector(rect.x + Math.abs(hor.x), rect.y - Math.abs(hor.y));
  
      var tr = createVector(
        rect.x + rect.width + Math.abs(ver.y),
        rect.y + Math.abs(ver.x)
      );
  
      var bl = createVector(
        rect.x - Math.abs(ver.y),
        rect.y + rect.height - Math.abs(ver.x)
      );
  
      return {
        width: vectorDistance(tl, tr),
        height: vectorDistance(tl, bl)
      };
    };
  
    var getImageRectZoomFactor = function getImageRectZoomFactor(
      imageRect,
      cropRect,
      rotation,
      center
    ) {
      // calculate available space round image center position
      var cx = center.x > 0.5 ? 1 - center.x : center.x;
      var cy = center.y > 0.5 ? 1 - center.y : center.y;
      var imageWidth = cx * 2 * imageRect.width;
      var imageHeight = cy * 2 * imageRect.height;
  
      // calculate rotated crop rectangle size
      var rotatedCropSize = getRotatedRectSize(cropRect, rotation);
  
      // calculate scalar required to fit image
      return Math.max(
        rotatedCropSize.width / imageWidth,
        rotatedCropSize.height / imageHeight
      );
    };
  
    var getCenteredCropRect = function getCenteredCropRect(
      container,
      aspectRatio
    ) {
      var width = container.width;
      var height = width * aspectRatio;
      if (height > container.height) {
        height = container.height;
        width = height / aspectRatio;
      }
      var x = (container.width - width) * 0.5;
      var y = (container.height - height) * 0.5;
  
      return {
        x: x,
        y: y,
        width: width,
        height: height
      };
    };
  
    // does horizontal and vertical flipping
    var createBitmapView = function createBitmapView(_) {
      return _.utils.createView({
        name: 'image-bitmap',
        ignoreRect: true,
        mixins: { styles: ['scaleX', 'scaleY'] },
        create: function create(_ref) {
          var root = _ref.root,
            props = _ref.props;
          root.appendChild(props.image);
        }
      });
    };
  
    // shifts and rotates image
    var createImageCanvasWrapper = function createImageCanvasWrapper(_) {
      return _.utils.createView({
        name: 'image-canvas-wrapper',
        tag: 'div',
        ignoreRect: true,
        mixins: {
          apis: ['crop', 'width', 'height'],
  
          styles: [
            'originX',
            'originY',
            'translateX',
            'translateY',
            'scaleX',
            'scaleY',
            'rotateZ'
          ],
  
          animations: {
            originX: IMAGE_SCALE_SPRING_PROPS,
            originY: IMAGE_SCALE_SPRING_PROPS,
            scaleX: IMAGE_SCALE_SPRING_PROPS,
            scaleY: IMAGE_SCALE_SPRING_PROPS,
            translateX: IMAGE_SCALE_SPRING_PROPS,
            translateY: IMAGE_SCALE_SPRING_PROPS,
            rotateZ: IMAGE_SCALE_SPRING_PROPS
          }
        },
  
        create: function create(_ref2) {
          var root = _ref2.root,
            props = _ref2.props;
          props.width = props.image.width;
          props.height = props.image.height;
          root.ref.bitmap = root.appendChildView(
            root.createChildView(createBitmapView(_), { image: props.image })
          );
        },
        write: function write(_ref3) {
          var root = _ref3.root,
            props = _ref3.props;
          var flip = props.crop.flip;
          var bitmap = root.ref.bitmap;
          bitmap.scaleX = flip.horizontal ? -1 : 1;
          bitmap.scaleY = flip.vertical ? -1 : 1;
        }
      });
    };
  
    // clips canvas to correct aspect ratio
    var createClipView = function createClipView(_) {
      return _.utils.createView({
        name: 'image-clip',
        tag: 'div',
        ignoreRect: true,
        mixins: {
          apis: ['crop', 'width', 'height'],
          styles: ['width', 'height', 'opacity'],
          animations: {
            opacity: { type: 'tween', duration: 250 }
          }
        },
  
        create: function create(_ref4) {
          var root = _ref4.root,
            props = _ref4.props;
  
          root.ref.image = root.appendChildView(
            root.createChildView(
              createImageCanvasWrapper(_),
              Object.assign({}, props)
            )
          );
  
          // set up transparency grid
          var transparencyIndicator = root.query(
            'GET_IMAGE_PREVIEW_TRANSPARENCY_INDICATOR'
          );
          if (transparencyIndicator === null) {
            return;
          }
  
          // grid pattern
          if (transparencyIndicator === 'grid') {
            root.element.dataset.transparencyIndicator = transparencyIndicator;
          }
          // basic color
          else {
            root.element.dataset.transparencyIndicator = 'color';
          }
        },
        write: function write(_ref5) {
          var root = _ref5.root,
            props = _ref5.props,
            shouldOptimize = _ref5.shouldOptimize;
          var crop = props.crop,
            width = props.width,
            height = props.height;
  
          root.ref.image.crop = crop;
  
          var stage = {
            x: 0,
            y: 0,
            width: width,
            height: height,
            center: {
              x: width * 0.5,
              y: height * 0.5
            }
          };
  
          var image = {
            width: root.ref.image.width,
            height: root.ref.image.height
          };
  
          var origin = {
            x: crop.center.x * image.width,
            y: crop.center.y * image.height
          };
  
          var translation = {
            x: stage.center.x - image.width * crop.center.x,
            y: stage.center.y - image.height * crop.center.y
          };
  
          var rotation = Math.PI * 2 + (crop.rotation % (Math.PI * 2));
  
          var cropAspectRatio = crop.aspectRatio || image.height / image.width;
  
          var stageZoomFactor = getImageRectZoomFactor(
            image,
            getCenteredCropRect(stage, cropAspectRatio),
  
            rotation,
            crop.center
          );
  
          var scale = crop.zoom * stageZoomFactor;
  
          var imageView = root.ref.image;
  
          // don't update clip layout
          if (shouldOptimize) {
            imageView.originX = null;
            imageView.originY = null;
            imageView.translateX = null;
            imageView.translateY = null;
            imageView.rotateZ = null;
            imageView.scaleX = null;
            imageView.scaleY = null;
            return;
          }
  
          imageView.originX = origin.x;
          imageView.originY = origin.y;
          imageView.translateX = translation.x;
          imageView.translateY = translation.y;
          imageView.rotateZ = rotation;
          imageView.scaleX = scale;
          imageView.scaleY = scale;
        }
      });
    };
  
    var createImageView = function createImageView(_) {
      return _.utils.createView({
        name: 'image-preview',
        tag: 'div',
        ignoreRect: true,
        mixins: {
          apis: ['crop', 'image'],
  
          styles: ['translateY', 'scaleX', 'scaleY', 'opacity'],
  
          animations: {
            scaleX: IMAGE_SCALE_SPRING_PROPS,
            scaleY: IMAGE_SCALE_SPRING_PROPS,
            translateY: IMAGE_SCALE_SPRING_PROPS,
            opacity: { type: 'tween', duration: 400 }
          }
        },
  
        create: function create(_ref6) {
          var root = _ref6.root,
            props = _ref6.props;
          root.ref.clip = root.appendChildView(
            root.createChildView(createClipView(_), {
              image: props.image,
              crop: props.crop
            })
          );
        },
        write: function write(_ref7) {
          var root = _ref7.root,
            props = _ref7.props,
            shouldOptimize = _ref7.shouldOptimize;
          var clip = root.ref.clip;
          var crop = props.crop,
            image = props.image;
  
          clip.crop = crop;
  
          // don't update clip layout
          clip.opacity = shouldOptimize ? 0 : 1;
  
          // don't re-render if optimizing or hidden (width will be zero resulting in weird animations)
          if (shouldOptimize || root.rect.element.hidden) return;
  
          // calculate scaled preview image size
          var imageAspectRatio = image.height / image.width;
          var aspectRatio = crop.aspectRatio || imageAspectRatio;
  
          // calculate container size
          var containerWidth = root.rect.inner.width;
          var containerHeight = root.rect.inner.height;
  
          var fixedPreviewHeight = root.query('GET_IMAGE_PREVIEW_HEIGHT');
          var minPreviewHeight = root.query('GET_IMAGE_PREVIEW_MIN_HEIGHT');
          var maxPreviewHeight = root.query('GET_IMAGE_PREVIEW_MAX_HEIGHT');
  
          var panelAspectRatio = root.query('GET_PANEL_ASPECT_RATIO');
          var allowMultiple = root.query('GET_ALLOW_MULTIPLE');
  
          if (panelAspectRatio && !allowMultiple) {
            fixedPreviewHeight = containerWidth * panelAspectRatio;
            aspectRatio = panelAspectRatio;
          }
  
          // determine clip width and height
          var clipHeight =
            fixedPreviewHeight !== null
              ? fixedPreviewHeight
              : Math.max(
                  minPreviewHeight,
                  Math.min(containerWidth * aspectRatio, maxPreviewHeight)
                );
  
          var clipWidth = clipHeight / aspectRatio;
          if (clipWidth > containerWidth) {
            clipWidth = containerWidth;
            clipHeight = clipWidth * aspectRatio;
          }
  
          if (clipHeight > containerHeight) {
            clipHeight = containerHeight;
            clipWidth = containerHeight / aspectRatio;
          }
  
          clip.width = clipWidth;
          clip.height = clipHeight;
        }
      });
    };
  
    var SVG_MASK =
      '<svg width="500" height="200" viewBox="0 0 500 200" preserveAspectRatio="none">\n    <defs>\n        <radialGradient id="gradient-__UID__" cx=".5" cy="1.25" r="1.15">\n            <stop offset=\'50%\' stop-color=\'#000000\'/>\n            <stop offset=\'56%\' stop-color=\'#0a0a0a\'/>\n            <stop offset=\'63%\' stop-color=\'#262626\'/>\n            <stop offset=\'69%\' stop-color=\'#4f4f4f\'/>\n            <stop offset=\'75%\' stop-color=\'#808080\'/>\n            <stop offset=\'81%\' stop-color=\'#b1b1b1\'/>\n            <stop offset=\'88%\' stop-color=\'#dadada\'/>\n            <stop offset=\'94%\' stop-color=\'#f6f6f6\'/>\n            <stop offset=\'100%\' stop-color=\'#ffffff\'/>\n        </radialGradient>\n        <mask id="mask-__UID__">\n            <rect x="0" y="0" width="500" height="200" fill="url(#gradient-__UID__)"></rect>\n        </mask>\n    </defs>\n    <rect x="0" width="500" height="200" fill="currentColor" mask="url(#mask-__UID__)"></rect>\n</svg>';
  
    var checkedMyBases = false;
    var SVGMaskUniqueId = 0;
  
    var createImageOverlayView = function createImageOverlayView(fpAPI) {
      return fpAPI.utils.createView({
        name: 'image-preview-overlay',
        tag: 'div',
        ignoreRect: true,
        create: function create(_ref) {
          var root = _ref.root,
            props = _ref.props;
  
          if (!checkedMyBases && document.querySelector('base')) {
            SVG_MASK = SVG_MASK.replace(
              /url\(\#/g,
              'url(' +
                window.location.href.replace(window.location.hash, '') +
                '#'
            );
            checkedMyBases = true;
          }
  
          SVGMaskUniqueId++;
          root.element.classList.add(
            'filepond--image-preview-overlay-'.concat(props.status)
          );
          root.element.innerHTML = SVG_MASK.replace(/__UID__/g, SVGMaskUniqueId);
        },
        mixins: {
          styles: ['opacity'],
          animations: {
            opacity: { type: 'spring', mass: 25 }
          }
        }
      });
    };
  
    /**
     * Bitmap Worker
     */
    var BitmapWorker = function BitmapWorker() {
      self.onmessage = function(e) {
        createImageBitmap(e.data.message.file).then(function(bitmap) {
          self.postMessage({ id: e.data.id, message: bitmap }, [bitmap]);
        });
      };
    };
  
    /**
     * ColorMatrix Worker
     */
    var ColorMatrixWorker = function ColorMatrixWorker() {
      self.onmessage = function(e) {
        var imageData = e.data.message.imageData;
        var matrix = e.data.message.colorMatrix;
  
        var data = imageData.data;
        var l = data.length;
  
        var m11 = matrix[0];
        var m12 = matrix[1];
        var m13 = matrix[2];
        var m14 = matrix[3];
        var m15 = matrix[4];
  
        var m21 = matrix[5];
        var m22 = matrix[6];
        var m23 = matrix[7];
        var m24 = matrix[8];
        var m25 = matrix[9];
  
        var m31 = matrix[10];
        var m32 = matrix[11];
        var m33 = matrix[12];
        var m34 = matrix[13];
        var m35 = matrix[14];
  
        var m41 = matrix[15];
        var m42 = matrix[16];
        var m43 = matrix[17];
        var m44 = matrix[18];
        var m45 = matrix[19];
  
        var index = 0,
          r = 0.0,
          g = 0.0,
          b = 0.0,
          a = 0.0;
  
        for (; index < l; index += 4) {
          r = data[index] / 255;
          g = data[index + 1] / 255;
          b = data[index + 2] / 255;
          a = data[index + 3] / 255;
          data[index] = Math.max(
            0,
            Math.min((r * m11 + g * m12 + b * m13 + a * m14 + m15) * 255, 255)
          );
          data[index + 1] = Math.max(
            0,
            Math.min((r * m21 + g * m22 + b * m23 + a * m24 + m25) * 255, 255)
          );
          data[index + 2] = Math.max(
            0,
            Math.min((r * m31 + g * m32 + b * m33 + a * m34 + m35) * 255, 255)
          );
          data[index + 3] = Math.max(
            0,
            Math.min((r * m41 + g * m42 + b * m43 + a * m44 + m45) * 255, 255)
          );
        }
  
        self.postMessage({ id: e.data.id, message: imageData }, [
          imageData.data.buffer
        ]);
      };
    };
  
    var getImageSize = function getImageSize(url, cb) {
      var image = new Image();
      image.onload = function() {
        var width = image.naturalWidth;
        var height = image.naturalHeight;
        image = null;
        cb(width, height);
      };
      image.src = url;
    };
  
    var transforms = {
      1: function _() {
        return [1, 0, 0, 1, 0, 0];
      },
      2: function _(width) {
        return [-1, 0, 0, 1, width, 0];
      },
      3: function _(width, height) {
        return [-1, 0, 0, -1, width, height];
      },
      4: function _(width, height) {
        return [1, 0, 0, -1, 0, height];
      },
      5: function _() {
        return [0, 1, 1, 0, 0, 0];
      },
      6: function _(width, height) {
        return [0, 1, -1, 0, height, 0];
      },
      7: function _(width, height) {
        return [0, -1, -1, 0, height, width];
      },
      8: function _(width) {
        return [0, -1, 1, 0, 0, width];
      }
    };
  
    var fixImageOrientation = function fixImageOrientation(
      ctx,
      width,
      height,
      orientation
    ) {
      // no orientation supplied
      if (orientation === -1) {
        return;
      }
  
      ctx.transform.apply(ctx, transforms[orientation](width, height));
    };
  
    // draws the preview image to canvas
    var createPreviewImage = function createPreviewImage(
      data,
      width,
      height,
      orientation
    ) {
      // can't draw on half pixels
      width = Math.round(width);
      height = Math.round(height);
  
      // draw image
      var canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      var ctx = canvas.getContext('2d');
  
      // if is rotated incorrectly swap width and height
      if (orientation >= 5 && orientation <= 8) {
        var _ref = [height, width];
        width = _ref[0];
        height = _ref[1];
      }
  
      // correct image orientation
      fixImageOrientation(ctx, width, height, orientation);
  
      // draw the image
      ctx.drawImage(data, 0, 0, width, height);
  
      return canvas;
    };
  
    var isBitmap = function isBitmap(file) {
      return /^image/.test(file.type) && !/svg/.test(file.type);
    };
  
    var MAX_WIDTH = 10;
    var MAX_HEIGHT = 10;
  
    var calculateAverageColor = function calculateAverageColor(image) {
      var scalar = Math.min(MAX_WIDTH / image.width, MAX_HEIGHT / image.height);
  
      var canvas = document.createElement('canvas');
      var ctx = canvas.getContext('2d');
      var width = (canvas.width = Math.ceil(image.width * scalar));
      var height = (canvas.height = Math.ceil(image.height * scalar));
      ctx.drawImage(image, 0, 0, width, height);
      var data = null;
      try {
        data = ctx.getImageData(0, 0, width, height).data;
      } catch (e) {
        return null;
      }
      var l = data.length;
  
      var r = 0;
      var g = 0;
      var b = 0;
      var i = 0;
  
      for (; i < l; i += 4) {
        r += data[i] * data[i];
        g += data[i + 1] * data[i + 1];
        b += data[i + 2] * data[i + 2];
      }
  
      r = averageColor(r, l);
      g = averageColor(g, l);
      b = averageColor(b, l);
  
      return { r: r, g: g, b: b };
    };
  
    var averageColor = function averageColor(c, l) {
      return Math.floor(Math.sqrt(c / (l / 4)));
    };
  
    var cloneCanvas = function cloneCanvas(origin, target) {
      target = target || document.createElement('canvas');
      target.width = origin.width;
      target.height = origin.height;
      var ctx = target.getContext('2d');
      ctx.drawImage(origin, 0, 0);
      return target;
    };
  
    var cloneImageData = function cloneImageData(imageData) {
      var id;
      try {
        id = new ImageData(imageData.width, imageData.height);
      } catch (e) {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        id = ctx.createImageData(imageData.width, imageData.height);
      }
      id.data.set(new Uint8ClampedArray(imageData.data));
      return id;
    };
  
    var loadImage = function loadImage(url) {
      return new Promise(function(resolve, reject) {
        var img = new Image();
        img.crossOrigin = 'Anonymous';
        img.onload = function() {
          resolve(img);
        };
        img.onerror = function(e) {
          reject(e);
        };
        img.src = url;
      });
    };
  
    var createImageWrapperView = function createImageWrapperView(_) {
      // create overlay view
      var OverlayView = createImageOverlayView(_);
  
      var ImageView = createImageView(_);
      var createWorker = _.utils.createWorker;
  
      var applyFilter = function applyFilter(root, filter, target) {
        return new Promise(function(resolve) {
          // will store image data for future filter updates
          if (!root.ref.imageData) {
            root.ref.imageData = target
              .getContext('2d')
              .getImageData(0, 0, target.width, target.height);
          }
  
          // get image data reference
          var imageData = cloneImageData(root.ref.imageData);
  
          if (!filter || filter.length !== 20) {
            target.getContext('2d').putImageData(imageData, 0, 0);
            return resolve();
          }
  
          var worker = createWorker(ColorMatrixWorker);
          worker.post(
            {
              imageData: imageData,
              colorMatrix: filter
            },
  
            function(response) {
              // apply filtered colors
              target.getContext('2d').putImageData(response, 0, 0);
  
              // stop worker
              worker.terminate();
  
              // done!
              resolve();
            },
            [imageData.data.buffer]
          );
        });
      };
  
      var removeImageView = function removeImageView(root, imageView) {
        root.removeChildView(imageView);
        imageView.image.width = 1;
        imageView.image.height = 1;
        imageView._destroy();
      };
  
      // remove an image
      var shiftImage = function shiftImage(_ref) {
        var root = _ref.root;
        var imageView = root.ref.images.shift();
        imageView.opacity = 0;
        imageView.translateY = -15;
        root.ref.imageViewBin.push(imageView);
        return imageView;
      };
  
      // add new image
      var pushImage = function pushImage(_ref2) {
        var root = _ref2.root,
          props = _ref2.props,
          image = _ref2.image;
  
        var id = props.id;
        var item = root.query('GET_ITEM', { id: id });
        if (!item) return;
  
        var crop = item.getMetadata('crop') || {
          center: {
            x: 0.5,
            y: 0.5
          },
  
          flip: {
            horizontal: false,
            vertical: false
          },
  
          zoom: 1,
          rotation: 0,
          aspectRatio: null
        };
  
        // append image presenter
        var imageView = root.appendChildView(
          root.createChildView(ImageView, {
            image: image,
            crop: crop,
            opacity: 0,
            scaleX: 1.15,
            scaleY: 1.15,
            translateY: 15
          }),
  
          root.childViews.length
        );
  
        root.ref.images.push(imageView);
  
        // reveal the preview image
        imageView.opacity = 1;
        imageView.scaleX = 1;
        imageView.scaleY = 1;
        imageView.translateY = 0;
  
        // the preview is now ready to be drawn
        setTimeout(function() {
          root.dispatch('DID_IMAGE_PREVIEW_SHOW', { id: id });
        }, 250);
      };
  
      var updateImage = function updateImage(_ref3) {
        var root = _ref3.root,
          props = _ref3.props;
        var item = root.query('GET_ITEM', { id: props.id });
        if (!item) return;
        var imageView = root.ref.images[root.ref.images.length - 1];
        imageView.crop = item.getMetadata('crop');
      };
  
      // replace image preview
      var didUpdateItemMetadata = function didUpdateItemMetadata(_ref4) {
        var root = _ref4.root,
          props = _ref4.props,
          action = _ref4.action;
  
        // only filter and crop trigger redraw
        if (!/crop|filter/.test(action.change.key)) return;
  
        // no images to update, exit
        if (!root.ref.images.length) return;
  
        // no item found, exit
        var item = root.query('GET_ITEM', { id: props.id });
        if (!item) return;
  
        // for now, update existing image when filtering
        if (/filter/.test(action.change.key)) {
          var imageView = root.ref.images[root.ref.images.length - 1];
          applyFilter(root, action.change.value, imageView.image);
          return;
        }
  
        if (/crop/.test(action.change.key)) {
          var crop = item.getMetadata('crop');
          var image = root.ref.images[root.ref.images.length - 1];
  
          // if aspect ratio has changed, we need to create a new image
          if (Math.abs(crop.aspectRatio - image.crop.aspectRatio) > 0.00001) {
            var _imageView = shiftImage({ root: root });
            pushImage({
              root: root,
              props: props,
              image: cloneCanvas(_imageView.image)
            });
          }
          // if not, we can update the current image
          else {
            updateImage({ root: root, props: props });
          }
        }
      };
  
      var canCreateImageBitmap = function canCreateImageBitmap(file) {
        return 'createImageBitmap' in window && isBitmap(file);
      };
  
      /**
       * Write handler for when preview container has been created
       */
      var didCreatePreviewContainer = function didCreatePreviewContainer(_ref5) {
        var root = _ref5.root,
          props = _ref5.props;
        var id = props.id;
  
        // we need to get the file data to determine the eventual image size
        var item = root.query('GET_ITEM', id);
        if (!item) return;
  
        // get url to file (we'll revoke it later on when done)
        var fileURL = URL.createObjectURL(item.file);
  
        // determine image size of this item
        getImageSize(fileURL, function(width, height) {
          // we can now scale the panel to the final size
          root.dispatch('DID_IMAGE_PREVIEW_CALCULATE_SIZE', {
            id: id,
            width: width,
            height: height
          });
        });
      };
  
      var drawPreview = function drawPreview(_ref6) {
        var root = _ref6.root,
          props = _ref6.props;
        var id = props.id;
  
        // we need to get the file data to determine the eventual image size
        var item = root.query('GET_ITEM', id);
        if (!item) return;
  
        // get url to file (we'll revoke it later on when done)
        var fileURL = URL.createObjectURL(item.file);
  
        // fallback
        var loadPreviewFallback = function loadPreviewFallback() {
          // let's scale the image in the main thread :(
          loadImage(fileURL).then(previewImageLoaded);
        };
  
        // image is now ready
        var previewImageLoaded = function previewImageLoaded(imageData) {
          // the file url is no longer needed
          URL.revokeObjectURL(fileURL);
  
          // draw the scaled down version here and use that as source so bitmapdata can be closed
          // orientation info
          var exif = item.getMetadata('exif') || {};
          var orientation = exif.orientation || -1;
  
          // get width and height from action, and swap if orientation is incorrect
          var width = imageData.width,
            height = imageData.height;
          if (orientation >= 5 && orientation <= 8) {
            var _ref7 = [height, width];
            width = _ref7[0];
            height = _ref7[1];
          }
  
          // scale canvas based on pixel density
          // we multiply by .75 as that creates smaller but still clear images on screens with high res displays
          var pixelDensityFactor = Math.max(1, window.devicePixelRatio * 0.75);
  
          // we want as much pixels to work with as possible,
          // this multiplies the minimum image resolution,
          // so when zooming in it doesn't get too blurry
          var zoomFactor = root.query('GET_IMAGE_PREVIEW_ZOOM_FACTOR');
  
          // imaeg scale factor
          var scaleFactor = zoomFactor * pixelDensityFactor;
  
          // calculate scaled preview image size
          var previewImageRatio = height / width;
  
          // calculate image preview height and width
          var previewContainerWidth = root.rect.element.width;
          var previewContainerHeight = root.rect.element.height;
  
          var imageWidth = previewContainerWidth;
          var imageHeight = imageWidth * previewImageRatio;
  
          if (previewImageRatio > 1) {
            imageWidth = Math.min(width, previewContainerWidth * scaleFactor);
            imageHeight = imageWidth * previewImageRatio;
          } else {
            imageHeight = Math.min(height, previewContainerHeight * scaleFactor);
            imageWidth = imageHeight / previewImageRatio;
          }
  
          // transfer to image tag so no canvas memory wasted on iOS
          var previewImage = createPreviewImage(
            imageData,
            imageWidth,
            imageHeight,
            orientation
          );
  
          // done
          var done = function done() {
            // calculate average image color, disabled for now
            var averageColor = root.query(
              'GET_IMAGE_PREVIEW_CALCULATE_AVERAGE_IMAGE_COLOR'
            )
              ? calculateAverageColor(data)
              : null;
            item.setMetadata('color', averageColor, true);
  
            // data has been transferred to canvas ( if was ImageBitmap )
            if ('close' in imageData) {
              imageData.close();
            }
  
            // show the overlay
            root.ref.overlayShadow.opacity = 1;
  
            // create the first image
            pushImage({ root: root, props: props, image: previewImage });
          };
  
          // apply filter
          var filter = item.getMetadata('filter');
          if (filter) {
            applyFilter(root, filter, previewImage).then(done);
          } else {
            done();
          }
        };
  
        // if we support scaling using createImageBitmap we use a worker
        if (canCreateImageBitmap(item.file)) {
          // let's scale the image in a worker
          var worker = createWorker(BitmapWorker);
  
          worker.post(
            {
              file: item.file
            },
  
            function(imageBitmap) {
              // destroy worker
              worker.terminate();
  
              // no bitmap returned, must be something wrong,
              // try the oldschool way
              if (!imageBitmap) {
                loadPreviewFallback();
                return;
              }
  
              // yay we got our bitmap, let's continue showing the preview
              previewImageLoaded(imageBitmap);
            }
          );
        } else {
          // create fallback preview
          loadPreviewFallback();
        }
      };
  
      /**
       * Write handler for when the preview image is ready to be animated
       */
      var didDrawPreview = function didDrawPreview(_ref8) {
        var root = _ref8.root;
        // get last added image
        var image = root.ref.images[root.ref.images.length - 1];
        image.translateY = 0;
        image.scaleX = 1.0;
        image.scaleY = 1.0;
        image.opacity = 1;
      };
  
      /**
       * Write handler for when the preview has been loaded
       */
      var restoreOverlay = function restoreOverlay(_ref9) {
        var root = _ref9.root;
        root.ref.overlayShadow.opacity = 1;
        root.ref.overlayError.opacity = 0;
        root.ref.overlaySuccess.opacity = 0;
      };
  
      var didThrowError = function didThrowError(_ref10) {
        var root = _ref10.root;
        root.ref.overlayShadow.opacity = 0.25;
        root.ref.overlayError.opacity = 1;
      };
  
      var didCompleteProcessing = function didCompleteProcessing(_ref11) {
        var root = _ref11.root;
        root.ref.overlayShadow.opacity = 0.25;
        root.ref.overlaySuccess.opacity = 1;
      };
  
      /**
       * Constructor
       */
      var create = function create(_ref12) {
        var root = _ref12.root;
  
        // image view
        root.ref.images = [];
  
        // the preview image data (we need this to filter the image)
        root.ref.imageData = null;
  
        // image bin
        root.ref.imageViewBin = [];
  
        // image overlays
        root.ref.overlayShadow = root.appendChildView(
          root.createChildView(OverlayView, {
            opacity: 0,
            status: 'idle'
          })
        );
  
        root.ref.overlaySuccess = root.appendChildView(
          root.createChildView(OverlayView, {
            opacity: 0,
            status: 'success'
          })
        );
  
        root.ref.overlayError = root.appendChildView(
          root.createChildView(OverlayView, {
            opacity: 0,
            status: 'failure'
          })
        );
      };
  
      return _.utils.createView({
        name: 'image-preview-wrapper',
        create: create,
        styles: ['height'],
  
        apis: ['height'],
  
        destroy: function destroy(_ref13) {
          var root = _ref13.root;
          // we resize the image so memory on iOS 12 is released more quickly (it seems)
          root.ref.images.forEach(function(imageView) {
            imageView.image.width = 1;
            imageView.image.height = 1;
          });
        },
        write: _.utils.createRoute(
          {
            // image preview stated
            DID_IMAGE_PREVIEW_DRAW: didDrawPreview,
            DID_IMAGE_PREVIEW_CONTAINER_CREATE: didCreatePreviewContainer,
            DID_FINISH_CALCULATE_PREVIEWSIZE: drawPreview,
            DID_UPDATE_ITEM_METADATA: didUpdateItemMetadata,
  
            // file states
            DID_THROW_ITEM_LOAD_ERROR: didThrowError,
            DID_THROW_ITEM_PROCESSING_ERROR: didThrowError,
            DID_THROW_ITEM_INVALID: didThrowError,
            DID_COMPLETE_ITEM_PROCESSING: didCompleteProcessing,
            DID_START_ITEM_PROCESSING: restoreOverlay,
            DID_REVERT_ITEM_PROCESSING: restoreOverlay
          },
          function(_ref14) {
            var root = _ref14.root;
  
            // views on death row
            var viewsToRemove = root.ref.imageViewBin.filter(function(imageView) {
              return imageView.opacity === 0;
            });
  
            // views to retain
            root.ref.imageViewBin = root.ref.imageViewBin.filter(function(
              imageView
            ) {
              return imageView.opacity > 0;
            });
  
            // remove these views
            viewsToRemove.forEach(function(imageView) {
              return removeImageView(root, imageView);
            });
            viewsToRemove.length = 0;
          }
        )
      });
    };
  
    /**
     * Image Preview Plugin
     */
  
    var plugin = function plugin(fpAPI) {
      var addFilter = fpAPI.addFilter,
        utils = fpAPI.utils;
      var Type = utils.Type,
        createRoute = utils.createRoute,
        isFile = utils.isFile;
  
      // imagePreviewView
      var imagePreviewView = createImageWrapperView(fpAPI);
  
      // called for each view that is created right after the 'create' method
      addFilter('CREATE_VIEW', function(viewAPI) {
        // get reference to created view
        var is = viewAPI.is,
          view = viewAPI.view,
          query = viewAPI.query;
  
        // only hook up to item view and only if is enabled for this cropper
        if (!is('file') || !query('GET_ALLOW_IMAGE_PREVIEW')) {
          return;
        }
  
        // create the image preview plugin, but only do so if the item is an image
        var didLoadItem = function didLoadItem(_ref) {
          var root = _ref.root,
            props = _ref.props;
          var id = props.id;
          var item = query('GET_ITEM', id);
  
          // item could theoretically have been removed in the mean time
          if (!item || !isFile(item.file) || item.archived) return;
  
          // get the file object
          var file = item.file;
  
          // exit if this is not an image
          if (!isPreviewableImage(file)) return;
  
          // exit if image size is too high and no createImageBitmap support
          // this would simply bring the browser to its knees and that is not what we want
          var supportsCreateImageBitmap = 'createImageBitmap' in (window || {});
          var maxPreviewFileSize = query('GET_IMAGE_PREVIEW_MAX_FILE_SIZE');
          if (
            !supportsCreateImageBitmap &&
            maxPreviewFileSize &&
            file.size > maxPreviewFileSize
          )
            return;
  
          // set preview view
          root.ref.imagePreview = view.appendChildView(
            view.createChildView(imagePreviewView, { id: id })
          );
  
          // update height if is fixed
          var fixedPreviewHeight = root.query('GET_IMAGE_PREVIEW_HEIGHT');
          if (fixedPreviewHeight) {
            root.dispatch('DID_UPDATE_PANEL_HEIGHT', {
              id: item.id,
              height: fixedPreviewHeight
            });
          }
  
          // now ready
          var queue =
            !supportsCreateImageBitmap &&
            file.size > query('GET_IMAGE_PREVIEW_MAX_INSTANT_PREVIEW_FILE_SIZE');
          root.dispatch('DID_IMAGE_PREVIEW_CONTAINER_CREATE', { id: id }, queue);
        };
  
        var rescaleItem = function rescaleItem(root, props) {
          if (!root.ref.imagePreview) return;
          var id = props.id;
  
          // get item
          var item = root.query('GET_ITEM', { id: id });
          if (!item) return;
  
          // if is fixed height or panel has aspect ratio, exit here, height has already been defined
          var panelAspectRatio = root.query('GET_PANEL_ASPECT_RATIO');
          var itemPanelAspectRatio = root.query('GET_ITEM_PANEL_ASPECT_RATIO');
          var fixedHeight = root.query('GET_IMAGE_PREVIEW_HEIGHT');
          if (panelAspectRatio || itemPanelAspectRatio || fixedHeight) return;
  
          // no data!
          var _root$ref = root.ref,
            imageWidth = _root$ref.imageWidth,
            imageHeight = _root$ref.imageHeight;
          if (!imageWidth || !imageHeight) return;
  
          // get height min and max
          var minPreviewHeight = root.query('GET_IMAGE_PREVIEW_MIN_HEIGHT');
          var maxPreviewHeight = root.query('GET_IMAGE_PREVIEW_MAX_HEIGHT');
  
          // orientation info
          var exif = item.getMetadata('exif') || {};
          var orientation = exif.orientation || -1;
  
          // get width and height from action, and swap of orientation is incorrect
          if (orientation >= 5 && orientation <= 8) {
            var _ref2 = [imageHeight, imageWidth];
            imageWidth = _ref2[0];
            imageHeight = _ref2[1];
          }
  
          // scale up width and height when we're dealing with an SVG
          if (!isBitmap(item.file) || root.query('GET_IMAGE_PREVIEW_UPSCALE')) {
            var scalar = 2048 / imageWidth;
            imageWidth *= scalar;
            imageHeight *= scalar;
          }
  
          // image aspect ratio
          var imageAspectRatio = imageHeight / imageWidth;
  
          // we need the item to get to the crop size
          var previewAspectRatio =
            (item.getMetadata('crop') || {}).aspectRatio || imageAspectRatio;
  
          // preview height range
          var previewHeightMax = Math.max(
            minPreviewHeight,
            Math.min(imageHeight, maxPreviewHeight)
          );
          var itemWidth = root.rect.element.width;
          var previewHeight = Math.min(
            itemWidth * previewAspectRatio,
            previewHeightMax
          );
  
          // request update to panel height
          root.dispatch('DID_UPDATE_PANEL_HEIGHT', {
            id: item.id,
            height: previewHeight
          });
        };
  
        var didResizeView = function didResizeView(_ref3) {
          var root = _ref3.root;
          // actions in next write operation
          root.ref.shouldRescale = true;
        };
  
        var didUpdateItemMetadata = function didUpdateItemMetadata(_ref4) {
          var root = _ref4.root,
            action = _ref4.action;
  
          if (action.change.key !== 'crop') return;
  
          // actions in next write operation
          root.ref.shouldRescale = true;
        };
  
        var didCalculatePreviewSize = function didCalculatePreviewSize(_ref5) {
          var root = _ref5.root,
            action = _ref5.action;
  
          // remember dimensions
          root.ref.imageWidth = action.width;
          root.ref.imageHeight = action.height;
  
          // actions in next write operation
          root.ref.shouldRescale = true;
          root.ref.shouldDrawPreview = true;
  
          // as image load could take a while and fire when draw loop is resting we need to give it a kick
          root.dispatch('KICK');
        };
  
        // start writing
        view.registerWriter(
          createRoute(
            {
              DID_RESIZE_ROOT: didResizeView,
              DID_STOP_RESIZE: didResizeView,
              DID_LOAD_ITEM: didLoadItem,
              DID_IMAGE_PREVIEW_CALCULATE_SIZE: didCalculatePreviewSize,
              DID_UPDATE_ITEM_METADATA: didUpdateItemMetadata
            },
            function(_ref6) {
              var root = _ref6.root,
                props = _ref6.props;
  
              // don't do anything while hidden
              if (root.rect.element.hidden) return;
  
              // resize the item panel
              if (root.ref.shouldRescale) {
                rescaleItem(root, props);
                root.ref.shouldRescale = false;
              }
  
              if (root.ref.shouldDrawPreview) {
                // queue till next frame so we're sure the height has been applied this forces the draw image call inside the wrapper view to use the correct height
                requestAnimationFrame(function() {
                  root.dispatch('DID_FINISH_CALCULATE_PREVIEWSIZE', {
                    id: props.id
                  });
                });
                root.ref.shouldDrawPreview = false;
              }
            }
          )
        );
      });
  
      // expose plugin
      return {
        options: {
          // Enable or disable image preview
          allowImagePreview: [true, Type.BOOLEAN],
  
          // Fixed preview height
          imagePreviewHeight: [null, Type.INT],
  
          // Min image height
          imagePreviewMinHeight: [44, Type.INT],
  
          // Max image height
          imagePreviewMaxHeight: [256, Type.INT],
  
          // Max size of preview file for when createImageBitmap is not supported
          imagePreviewMaxFileSize: [null, Type.INT],
  
          // The amount of extra pixels added to the image preview to allow comfortable zooming
          imagePreviewZoomFactor: [2, Type.INT],
  
          // Should we upscale small images to fit the max bounding box of the preview area
          imagePreviewUpscale: [false, Type.BOOLEAN],
  
          // Max size of preview file that we allow to try to instant preview if createImageBitmap is not supported, else image is queued for loading
          imagePreviewMaxInstantPreviewFileSize: [1000000, Type.INT],
  
          // Style of the transparancy indicator used behind images
          imagePreviewTransparencyIndicator: [null, Type.STRING],
  
          // Enables or disables reading average image color
          imagePreviewCalculateAverageImageColor: [false, Type.BOOLEAN]
        }
      };
    };
  
    // fire pluginloaded event if running in browser, this allows registering the plugin when using async script tags
    var isBrowser =
      typeof window !== 'undefined' && typeof window.document !== 'undefined';
    if (isBrowser) {
      document.dispatchEvent(
        new CustomEvent('FilePond:pluginloaded', { detail: plugin })
      );
    }
  
    return plugin;
  });
  