text: import {
    ACTION_MOVE,
    ACTION_SWITCH,
    ACTION_ZOOM,
    CLASS_INVISIBLE,
    CLASS_LOADING,
    CLASS_MOVE,
    CLASS_TRANSITION,
    DATA_ACTION,
    EVENT_CLICK,
    EVENT_DBLCLICK,
    EVENT_LOAD,
    EVENT_VIEWED,
    IS_TOUCH_DEVICE,
  } from './constants';
  import {
    addClass,
    addListener,
    assign,
    dispatchEvent,
    forEach,
    getData,
    getImageNaturalSizes,
    getPointer,
    getTransforms,
    isFunction,
    isNumber,
    removeClass,
    setStyle,
    toggleClass,
  } from './utilities';
  
  export default {
    click(event) {
      const { target } = event;
      const { options, imageData } = this;
      const action = getData(target, DATA_ACTION);
  
      // Cancel the emulated click when the native click event was triggered.
      if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) {
        clearTimeout(this.clickCanvasTimeout);
      }
  
      switch (action) {
        case 'mix':
          if (this.played) {
            this.stop();
          } else if (options.inline) {
            if (this.fulled) {
              this.exit();
            } else {
              this.full();
            }
          } else {
            this.hide();
          }
  
          break;
  
        case 'hide':
          this.hide();
          break;
  
        case 'view':
          this.view(getData(target, 'index'));
          break;
  
        case 'zoom-in':
          this.zoom(0.1, true);
          break;
  
        case 'zoom-out':
          this.zoom(-0.1, true);
          break;
  
        case 'one-to-one':
          this.toggle();
          break;
  
        case 'reset':
          this.reset();
          break;
  
        case 'prev':
          this.prev(options.loop);
          break;
  
        case 'play':
          this.play(options.fullscreen);
          break;
  
        case 'next':
          this.next(options.loop);
          break;
  
        case 'rotate-left':
          this.rotate(-90);
          break;
  
        case 'rotate-right':
          this.rotate(90);
          break;
  
        case 'flip-horizontal':
          this.scaleX(-imageData.scaleX || -1);
          break;
  
        case 'flip-vertical':
          this.scaleY(-imageData.scaleY || -1);
          break;
  
        default:
          if (this.played) {
            this.stop();
          }
      }
    },
  
    dblclick(event) {
      event.preventDefault();
  
      if (this.viewed && event.target === this.image) {
        // Cancel the emulated double click when the native dblclick event was triggered.
        if (IS_TOUCH_DEVICE && event.isTrusted) {
          clearTimeout(this.doubleClickImageTimeout);
        }
  
        this.toggle();
      }
    },
  
    load() {
      if (this.timeout) {
        clearTimeout(this.timeout);
        this.timeout = false;
      }
  
      const {
        element,
        options,
        image,
        index,
        viewerData,
      } = this;
  
      removeClass(image, CLASS_INVISIBLE);
  
      if (options.loading) {
        removeClass(this.canvas, CLASS_LOADING);
      }
  
      image.style.cssText = (
        'height:0;'
        + `margin-left:${viewerData.width / 2}px;`
        + `margin-top:${viewerData.height / 2}px;`
        + 'max-width:none!important;'
        + 'position:absolute;'
        + 'width:0;'
      );
  
      this.initImage(() => {
        toggleClass(image, CLASS_MOVE, options.movable);
        toggleClass(image, CLASS_TRANSITION, options.transition);
  
        this.renderImage(() => {
          this.viewed = true;
          this.viewing = false;
  
          if (isFunction(options.viewed)) {
            addListener(element, EVENT_VIEWED, options.viewed, {
              once: true,
            });
          }
  
          dispatchEvent(element, EVENT_VIEWED, {
            originalImage: this.images[index],
            index,
            image,
          });
        });
      });
    },
  
    loadImage(event) {
      const image = event.target;
      const parent = image.parentNode;
      const parentWidth = parent.offsetWidth || 30;
      const parentHeight = parent.offsetHeight || 50;
      const filled = !!getData(image, 'filled');
  
      getImageNaturalSizes(image, (naturalWidth, naturalHeight) => {
        const aspectRatio = naturalWidth / naturalHeight;
        let width = parentWidth;
        let height = parentHeight;
  
        if (parentHeight * aspectRatio > parentWidth) {
          if (filled) {
            width = parentHeight * aspectRatio;
          } else {
            height = parentWidth / aspectRatio;
          }
        } else if (filled) {
          height = parentWidth / aspectRatio;
        } else {
          width = parentHeight * aspectRatio;
        }
  
        setStyle(image, assign({
          width,
          height,
        }, getTransforms({
          translateX: (parentWidth - width) / 2,
          translateY: (parentHeight - height) / 2,
        })));
      });
    },
  
    keydown(event) {
      const { options } = this;
  
      if (!this.fulled || !options.keyboard) {
        return;
      }
  
      switch (event.keyCode || event.which || event.charCode) {
        // Escape
        case 27:
          if (this.played) {
            this.stop();
          } else if (options.inline) {
            if (this.fulled) {
              this.exit();
            }
          } else {
            this.hide();
          }
  
          break;
  
        // Space
        case 32:
          if (this.played) {
            this.stop();
          }
  
          break;
  
        // ArrowLeft
        case 37:
          this.prev(options.loop);
          break;
  
        // ArrowUp
        case 38:
          // Prevent scroll on Firefox
          event.preventDefault();
  
          // Zoom in
          this.zoom(options.zoomRatio, true);
          break;
  
        // ArrowRight
        case 39:
          this.next(options.loop);
          break;
  
        // ArrowDown
        case 40:
          // Prevent scroll on Firefox
          event.preventDefault();
  
          // Zoom out
          this.zoom(-options.zoomRatio, true);
          break;
  
        // Ctrl + 0
        case 48:
          // Fall through
  
        // Ctrl + 1
        // eslint-disable-next-line no-fallthrough
        case 49:
          if (event.ctrlKey) {
            event.preventDefault();
            this.toggle();
          }
  
          break;
  
        default:
      }
    },
  
    dragstart(event) {
      if (event.target.tagName.toLowerCase() === 'img') {
        event.preventDefault();
      }
    },
  
    pointerdown(event) {
      const { options, pointers } = this;
      const { buttons, button } = event;
  
      if (
        !this.viewed
        || this.showing
        || this.viewing
        || this.hiding
  
        // No primary button (usually the left button)
        // Note: Touch events does not contain `buttons` and `button` properties
        || (isNumber(buttons) && buttons > 1)
        || (isNumber(button) && button > 0)
  
        // Open context menu
        || event.ctrlKey
      ) {
        return;
      }
  
      // Prevent default behaviours as page zooming in touch devices.
      event.preventDefault();
  
      if (event.changedTouches) {
        forEach(event.changedTouches, (touch) => {
          pointers[touch.identifier] = getPointer(touch);
        });
      } else {
        pointers[event.pointerId || 0] = getPointer(event);
      }
  
      let action = options.movable ? ACTION_MOVE : false;
  
      if (Object.keys(pointers).length > 1) {
        action = ACTION_ZOOM;
      } else if ((event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
        action = ACTION_SWITCH;
      }
  
      if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
        removeClass(this.image, CLASS_TRANSITION);
      }
  
      this.action = action;
    },
  
    pointermove(event) {
      const { pointers, action } = this;
  
      if (!this.viewed || !action) {
        return;
      }
  
      event.preventDefault();
  
      if (event.changedTouches) {
        forEach(event.changedTouches, (touch) => {
          assign(pointers[touch.identifier] || {}, getPointer(touch, true));
        });
      } else {
        assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
      }
  
      this.change(event);
    },
  
    pointerup(event) {
      const { options, action, pointers } = this;
      let pointer;
  
      if (event.changedTouches) {
        forEach(event.changedTouches, (touch) => {
          pointer = pointers[touch.identifier];
          delete pointers[touch.identifier];
        });
      } else {
        pointer = pointers[event.pointerId || 0];
        delete pointers[event.pointerId || 0];
      }
  
      if (!action) {
        return;
      }
  
      event.preventDefault();
  
      if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
        addClass(this.image, CLASS_TRANSITION);
      }
  
      this.action = false;
  
      // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
      if (
        IS_TOUCH_DEVICE
        && action !== ACTION_ZOOM
        && pointer
        && (Date.now() - pointer.timeStamp < 500)
      ) {
        clearTimeout(this.clickCanvasTimeout);
        clearTimeout(this.doubleClickImageTimeout);
  
        if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
          if (this.imageClicked) {
            this.imageClicked = false;
  
            // This timeout will be cleared later when a native dblclick event is triggering
            this.doubleClickImageTimeout = setTimeout(() => {
              dispatchEvent(this.image, EVENT_DBLCLICK);
            }, 50);
          } else {
            this.imageClicked = true;
  
            // The default timing of a double click in Windows is 500 ms
            this.doubleClickImageTimeout = setTimeout(() => {
              this.imageClicked = false;
            }, 500);
          }
        } else {
          this.imageClicked = false;
  
          if (options.backdrop && options.backdrop !== 'static' && event.target === this.canvas) {
            // This timeout will be cleared later when a native click event is triggering
            this.clickCanvasTimeout = setTimeout(() => {
              dispatchEvent(this.canvas, EVENT_CLICK);
            }, 50);
          }
        }
      }
    },
  
    resize() {
      if (!this.isShown || this.hiding) {
        return;
      }
  
      this.initContainer();
      this.initViewer();
      this.renderViewer();
      this.renderList();
  
      if (this.viewed) {
        this.initImage(() => {
          this.renderImage();
        });
      }
  
      if (this.played) {
        if (this.options.fullscreen && this.fulled && !(
          document.fullscreenElement
          || document.webkitFullscreenElement
          || document.mozFullScreenElement
          || document.msFullscreenElement
        )) {
          this.stop();
          return;
        }
  
        forEach(this.player.getElementsByTagName('img'), (image) => {
          addListener(image, EVENT_LOAD, this.loadImage.bind(this), {
            once: true,
          });
          dispatchEvent(image, EVENT_LOAD);
        });
      }
    },
  
    wheel(event) {
      if (!this.viewed) {
        return;
      }
  
      event.preventDefault();
  
      // Limit wheel speed to prevent zoom too fast
      if (this.wheeling) {
        return;
      }
  
      this.wheeling = true;
  
      setTimeout(() => {
        this.wheeling = false;
      }, 50);
  
      const ratio = Number(this.options.zoomRatio) || 0.1;
      let delta = 1;
  
      if (event.deltaY) {
        delta = event.deltaY > 0 ? 1 : -1;
      } else if (event.wheelDelta) {
        delta = -event.wheelDelta / 120;
      } else if (event.detail) {
        delta = event.detail > 0 ? 1 : -1;
      }
  
      this.zoom(-delta * ratio, true, event);
    },
  };
  