UNPKG

@egjs/flicking

Version:

Everyday 30 million people experience. It's reliable, flexible and extendable carousel.

7,134 lines 231 kB
"use strict";
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __reflectGet = Reflect.get;
var __pow = Math.pow;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
  for (var prop in b || (b = {}))
    if (__hasOwnProp.call(b, prop))
      __defNormalProp(a, prop, b[prop]);
  if (__getOwnPropSymbols)
    for (var prop of __getOwnPropSymbols(b)) {
      if (__propIsEnum.call(b, prop))
        __defNormalProp(a, prop, b[prop]);
    }
  return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
var __async = (__this, __arguments, generator) => {
  return new Promise((resolve, reject) => {
    var fulfilled = (value) => {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    };
    var rejected = (value) => {
      try {
        step(generator.throw(value));
      } catch (e) {
        reject(e);
      }
    };
    var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
    step((generator = generator.apply(__this, __arguments)).next());
  });
};
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const Component = require("@egjs/component");
const Axes = require("@egjs/axes");
const ImReady = require("@egjs/imready");
const ALIGN = {
  /** left/top align */
  PREV: "prev",
  /** center align */
  CENTER: "center",
  /** right/bottom align */
  NEXT: "next"
};
const DIRECTION = {
  /** "left" when {@link Flicking.horizontal | horizontal} is true, and "top" when {@link Flicking.horizontal | horizontal} is false */
  PREV: "PREV",
  /** "right" when {@link Flicking.horizontal | horizontal} is true, and "bottom" when {@link Flicking.horizontal | horizontal} is false */
  NEXT: "NEXT",
  /** This value usually means it's the same position */
  NONE: null
};
const MOVE_TYPE = {
  /** Flicking's {@link Flicking.moveType | moveType} that enables {@link SnapControl} as a Flicking's {@link Flicking.control | control} */
  SNAP: "snap",
  /** Flicking's {@link Flicking.moveType | moveType} that enables {@link FreeControl} as a Flicking's {@link Flicking.control | control} */
  FREE_SCROLL: "freeScroll",
  /** Flicking's {@link Flicking.moveType | moveType} that enables {@link StrictControl} as a Flicking's {@link Flicking.control | control} */
  STRICT: "strict"
};
const CLASS = {
  VIEWPORT: "flicking-viewport",
  CAMERA: "flicking-camera",
  VERTICAL: "vertical",
  HIDDEN: "flicking-hidden",
  DEFAULT_VIRTUAL: "flicking-panel"
};
const CIRCULAR_FALLBACK = {
  /** "linear" */
  LINEAR: "linear",
  /** "bound" */
  BOUND: "bound"
};
const ORDER = {
  /** "ltr" */
  LTR: "ltr",
  /** "rtl" */
  RTL: "rtl"
};
const MOVE_DIRECTION = {
  /** horizontal */
  HORIZONTAL: "horizontal",
  /** vertical */
  VERTICAL: "vertical"
};
class AnchorPoint {
  /**
   * Index of AnchorPoint
   * @readonly
   */
  get index() {
    return this._index;
  }
  /**
   * Position of AnchorPoint
   * @readonly
   */
  get position() {
    return this._pos;
  }
  /**
   * A {@link Panel} instance AnchorPoint is referencing to
   * @readonly
   */
  get panel() {
    return this._panel;
  }
  /**
   * @param options - {@link AnchorPointOptions}
   */
  constructor(options) {
    const { index, position, panel } = options;
    this._index = index;
    this._pos = position;
    this._panel = panel;
  }
}
const errors = {
  WRONG_TYPE: {
    code: 0,
    message: (wrongVal, correctTypes) => `${wrongVal}(${typeof wrongVal}) is not a ${correctTypes.map((type) => `"${type}"`).join(" or ")}.`
  },
  ELEMENT_NOT_FOUND: {
    code: 1,
    message: (selector) => `Element with selector "${selector}" not found.`
  },
  VAL_MUST_NOT_NULL: {
    code: 2,
    message: (val, name) => `${name} should be provided. Given: ${val}`
  },
  NOT_ATTACHED_TO_FLICKING: {
    code: 3,
    message: 'This module is not attached to the Flicking instance. "init()" should be called first.'
  },
  WRONG_OPTION: {
    code: 4,
    message: (optionName, val) => `Option "${optionName}" is not in correct format, given: ${val}`
  },
  INDEX_OUT_OF_RANGE: {
    code: 5,
    message: (val, min, max) => `Index "${val}" is out of range: should be between ${min} and ${max}.`
  },
  POSITION_NOT_REACHABLE: {
    code: 6,
    message: (position) => `Position "${position}" is not reachable.`
  },
  TRANSFORM_NOT_SUPPORTED: {
    code: 7,
    message: "Browser does not support CSS transform."
  },
  STOP_CALLED_BY_USER: {
    code: 8,
    message: "Event stop() is called by user."
  },
  ANIMATION_INTERRUPTED: {
    code: 9,
    message: "Animation is interrupted by user input."
  },
  ANIMATION_ALREADY_PLAYING: {
    code: 10,
    message: "Animation is already playing."
  },
  NOT_ALLOWED_IN_FRAMEWORK: {
    code: 11,
    message: "This behavior is not allowed in the frameworks like React, Vue, or Angular."
  },
  NOT_INITIALIZED: {
    code: 12,
    message: "Flicking is not initialized yet, call init() first."
  },
  NO_ACTIVE: {
    code: 13,
    message: "There's no active panel that Flicking has selected. This may be due to the absence of any panels."
  },
  NOT_ALLOWED_IN_VIRTUAL: {
    code: 14,
    message: "This behavior is not allowed when the virtual option is enabled"
  }
};
const CODE = Object.keys(errors).reduce(
  (acc, key) => {
    acc[key] = errors[key].code;
    return acc;
  },
  {}
);
const MESSAGE = Object.keys(errors).reduce(
  (acc, key) => {
    acc[key] = errors[key].message;
    return acc;
  },
  {}
);
const merge = (target, ...sources) => {
  sources.forEach((source) => {
    Object.keys(source).forEach((key) => {
      target[key] = source[key];
    });
  });
  return target;
};
const getElement = (el, parent) => {
  let targetEl = null;
  if (isString(el)) {
    const parentEl = parent ? parent : document;
    const queryResult = parentEl.querySelector(el);
    if (!queryResult) {
      throw new FlickingError(MESSAGE.ELEMENT_NOT_FOUND(el), CODE.ELEMENT_NOT_FOUND);
    }
    targetEl = queryResult;
  } else if (el && el.nodeType === Node.ELEMENT_NODE) {
    targetEl = el;
  }
  if (!targetEl) {
    throw new FlickingError(MESSAGE.WRONG_TYPE(el, ["HTMLElement", "string"]), CODE.WRONG_TYPE);
  }
  return targetEl;
};
const checkExistence = (value, nameOnErrMsg) => {
  if (value == null) {
    throw new FlickingError(MESSAGE.VAL_MUST_NOT_NULL(value, nameOnErrMsg), CODE.VAL_MUST_NOT_NULL);
  }
};
const clamp = (x, min, max) => Math.max(Math.min(x, max), min);
const getFlickingAttached = (val) => {
  if (!val) {
    throw new FlickingError(MESSAGE.NOT_ATTACHED_TO_FLICKING, CODE.NOT_ATTACHED_TO_FLICKING);
  }
  return val;
};
const toArray = (iterable) => [].slice.call(iterable);
const parseAlign$1 = (align, size) => {
  let alignPoint;
  if (isString(align)) {
    switch (align) {
      case ALIGN.PREV:
        alignPoint = 0;
        break;
      case ALIGN.CENTER:
        alignPoint = 0.5 * size;
        break;
      case ALIGN.NEXT:
        alignPoint = size;
        break;
      default:
        alignPoint = parseArithmeticSize(align, size);
        if (alignPoint == null) {
          throw new FlickingError(MESSAGE.WRONG_OPTION("align", align), CODE.WRONG_OPTION);
        }
    }
  } else {
    alignPoint = align;
  }
  return alignPoint;
};
const parseBounce = (bounce, size) => {
  let parsedBounce;
  if (Array.isArray(bounce)) {
    parsedBounce = bounce.map((val) => parseArithmeticSize(val, size));
  } else {
    const parsedVal = parseArithmeticSize(bounce, size);
    parsedBounce = [parsedVal, parsedVal];
  }
  return parsedBounce.map((val) => {
    if (val == null) {
      throw new FlickingError(MESSAGE.WRONG_OPTION("bounce", bounce), CODE.WRONG_OPTION);
    }
    return val;
  });
};
const parseArithmeticSize = (cssValue, base) => {
  const parsed = parseArithmeticExpression(cssValue);
  if (parsed == null) return null;
  return parsed.percentage * base + parsed.absolute;
};
const parseArithmeticExpression = (cssValue) => {
  const cssRegex = /(?:(\+|-)\s*)?(\d+(?:\.\d+)?(%|px)?)/g;
  if (typeof cssValue === "number") {
    return { percentage: 0, absolute: cssValue };
  }
  const parsed = {
    percentage: 0,
    absolute: 0
  };
  let idx = 0;
  let matchResult = cssRegex.exec(cssValue);
  while (matchResult != null) {
    let sign = matchResult[1];
    const value = matchResult[2];
    const unit = matchResult[3];
    const parsedValue = parseFloat(value);
    if (idx <= 0) {
      sign = sign || "+";
    }
    if (!sign) {
      return null;
    }
    const signMultiplier = sign === "+" ? 1 : -1;
    if (unit === "%") {
      parsed.percentage += signMultiplier * (parsedValue / 100);
    } else {
      parsed.absolute += signMultiplier * parsedValue;
    }
    ++idx;
    matchResult = cssRegex.exec(cssValue);
  }
  if (idx === 0) {
    return null;
  }
  return parsed;
};
const parseCSSSizeValue = (val) => isString(val) ? val : `${val}px`;
const parsePanelAlign = (align) => typeof align === "object" ? align.panel : align;
const getDirection = (start, end) => {
  if (start === end) return DIRECTION.NONE;
  return start < end ? DIRECTION.NEXT : DIRECTION.PREV;
};
const parseElement = (element) => {
  if (!Array.isArray(element)) {
    element = [element];
  }
  const elements = [];
  element.forEach((el) => {
    if (isString(el)) {
      const tempDiv = document.createElement("div");
      tempDiv.innerHTML = el;
      elements.push(...toArray(tempDiv.children));
      while (tempDiv.firstChild) {
        tempDiv.removeChild(tempDiv.firstChild);
      }
    } else if (el && el.nodeType === Node.ELEMENT_NODE) {
      elements.push(el);
    } else {
      throw new FlickingError(MESSAGE.WRONG_TYPE(el, ["HTMLElement", "string"]), CODE.WRONG_TYPE);
    }
  });
  return elements;
};
const getMinusCompensatedIndex = (idx, max) => idx < 0 ? clamp(idx + max, 0, max) : clamp(idx, 0, max);
const includes = (array, target) => {
  for (const val of array) {
    if (val === target) return true;
  }
  return false;
};
const isString = (val) => typeof val === "string";
const circulatePosition = (pos, min, max) => {
  const size = max - min;
  if (pos < min) {
    const offset = (min - pos) % size;
    pos = max - offset;
  } else if (pos > max) {
    const offset = (pos - max) % size;
    pos = min + offset;
  }
  return pos;
};
const find = (array, checker) => {
  for (const val of array) {
    if (checker(val)) {
      return val;
    }
  }
  return null;
};
const findRight = (array, checker) => {
  for (let idx = array.length - 1; idx >= 0; idx--) {
    const val = array[idx];
    if (checker(val)) {
      return val;
    }
  }
  return null;
};
const findIndex = (array, checker) => {
  for (let idx = 0; idx < array.length; idx++) {
    if (checker(array[idx])) {
      return idx;
    }
  }
  return -1;
};
const getProgress$1 = (pos, prev, next) => (pos - prev) / (next - prev);
const getStyle = (el) => {
  if (!el) {
    return {};
  }
  return window.getComputedStyle(el) || el.currentStyle;
};
const setSize = (el, {
  width,
  height
}) => {
  if (!el) {
    return;
  }
  if (width != null) {
    if (isString(width)) {
      el.style.width = width;
    } else {
      el.style.width = `${width}px`;
    }
  }
  if (height != null) {
    if (isString(height)) {
      el.style.height = height;
    } else {
      el.style.height = `${height}px`;
    }
  }
};
const isBetween = (val, min, max) => val >= min && val <= max;
const circulateIndex = (index, max) => {
  if (index >= max) {
    return index % max;
  } else if (index < 0) {
    return getMinusCompensatedIndex((index + 1) % max - 1, max);
  } else {
    return index;
  }
};
const range = (end) => {
  const arr = new Array(end);
  for (let i = 0; i < end; i++) {
    arr[i] = i;
  }
  return arr;
};
const getElementSize = ({
  el,
  horizontal,
  useFractionalSize,
  useOffset,
  style
}) => {
  let size = 0;
  if (useFractionalSize) {
    const baseSize = parseFloat(horizontal ? style.width : style.height) || 0;
    const isBorderBoxSizing = style.boxSizing === "border-box";
    const border = horizontal ? parseFloat(style.borderLeftWidth || "0") + parseFloat(style.borderRightWidth || "0") : parseFloat(style.borderTopWidth || "0") + parseFloat(style.borderBottomWidth || "0");
    if (isBorderBoxSizing) {
      size = useOffset ? baseSize : baseSize - border;
    } else {
      const padding = horizontal ? parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0") : parseFloat(style.paddingTop || "0") + parseFloat(style.paddingBottom || "0");
      size = useOffset ? baseSize + padding + border : baseSize + padding;
    }
  } else {
    const sizeStr = horizontal ? "Width" : "Height";
    size = useOffset ? el[`offset${sizeStr}`] : el[`client${sizeStr}`];
  }
  return Math.max(size, 0);
};
const setPrototypeOf = Object.setPrototypeOf || ((obj, proto) => {
  obj.__proto__ = proto;
  return obj;
});
const camelize = (str) => {
  return str.replace(/[\s-_]([a-z])/g, (all, letter) => letter.toUpperCase());
};
const getDataAttributes = (element, attributePrefix) => {
  const dataAttributes = {};
  const attributes = element.attributes;
  const length = attributes.length;
  for (let i = 0; i < length; ++i) {
    const attribute = attributes[i];
    const { name, value } = attribute;
    if (name.indexOf(attributePrefix) === -1) {
      continue;
    }
    dataAttributes[camelize(name.replace(attributePrefix, ""))] = value;
  }
  return dataAttributes;
};
class FlickingError extends Error {
  /**
   * @param message - Error message
   * @param code - Error code
   */
  constructor(message, code) {
    super(message);
    setPrototypeOf(this, FlickingError.prototype);
    this.name = "FlickingError";
    this.code = code;
  }
}
const EVENTS = {
  /** ready event */
  READY: "ready",
  /** beforeResize event */
  BEFORE_RESIZE: "beforeResize",
  /** afterResize event */
  AFTER_RESIZE: "afterResize",
  /** holdStart event */
  HOLD_START: "holdStart",
  /** holdEnd event */
  HOLD_END: "holdEnd",
  /** moveStart event */
  MOVE_START: "moveStart",
  /** move event */
  MOVE: "move",
  /** moveEnd event */
  MOVE_END: "moveEnd",
  /** willChange event */
  WILL_CHANGE: "willChange",
  /** changed event */
  CHANGED: "changed",
  /** willRestore event */
  WILL_RESTORE: "willRestore",
  /** restored event */
  RESTORED: "restored",
  /** select event */
  SELECT: "select",
  /** needPanel event */
  NEED_PANEL: "needPanel",
  /** visibleChange event */
  VISIBLE_CHANGE: "visibleChange",
  /** reachEdge event */
  REACH_EDGE: "reachEdge",
  /**
   * panelChange event
   * @since 4.1.0
   */
  PANEL_CHANGE: "panelChange"
};
class CameraMode {
  constructor(flicking) {
    this._flicking = flicking;
  }
  getAnchors() {
    const panels = this._flicking.renderer.panels;
    return panels.map(
      (panel, index) => new AnchorPoint({
        index,
        position: panel.position,
        panel
      })
    );
  }
  findAnchorIncludePosition(position) {
    const anchors = this._flicking.camera.anchorPoints;
    const anchorsIncludingPosition = anchors.filter((anchor) => anchor.panel.includePosition(position, true));
    return anchorsIncludingPosition.reduce((nearest, anchor) => {
      if (!nearest) return anchor;
      return Math.abs(nearest.position - position) < Math.abs(anchor.position - position) ? nearest : anchor;
    }, null);
  }
  findNearestAnchor(position) {
    const anchors = this._flicking.camera.anchorPoints;
    if (anchors.length <= 0) return null;
    let prevDist = Infinity;
    for (let anchorIdx = 0; anchorIdx < anchors.length; anchorIdx++) {
      const anchor = anchors[anchorIdx];
      const dist = Math.abs(anchor.position - position);
      if (dist > prevDist) {
        return anchors[anchorIdx - 1];
      }
      prevDist = dist;
    }
    return anchors[anchors.length - 1];
  }
  clampToReachablePosition(position) {
    const camera = this._flicking.camera;
    const range2 = camera.range;
    return clamp(position, range2.min, range2.max);
  }
  getCircularOffset() {
    return 0;
  }
  canReach(panel) {
    const camera = this._flicking.camera;
    const range2 = camera.range;
    if (panel.removed) return false;
    const panelPos = panel.position;
    return panelPos >= range2.min && panelPos <= range2.max;
  }
  canSee(panel) {
    const camera = this._flicking.camera;
    const visibleRange = camera.visibleRange;
    return panel.isVisibleOnRange(visibleRange.min, visibleRange.max);
  }
}
class BoundCameraMode extends CameraMode {
  checkAvailability() {
    const flicking = this._flicking;
    const renderer = flicking.renderer;
    const firstPanel = renderer.getPanel(0);
    const lastPanel = renderer.getPanel(renderer.panelCount - 1);
    if (!firstPanel || !lastPanel) {
      return false;
    }
    const viewportSize = flicking.camera.size;
    const firstPanelPrev = firstPanel.range.min;
    const lastPanelNext = lastPanel.range.max;
    const panelAreaSize = lastPanelNext - firstPanelPrev;
    const isBiggerThanViewport = viewportSize < panelAreaSize;
    return isBiggerThanViewport;
  }
  getRange() {
    const flicking = this._flicking;
    const renderer = flicking.renderer;
    const alignPos = flicking.camera.alignPosition;
    const firstPanel = renderer.getPanel(0);
    const lastPanel = renderer.getPanel(renderer.panelCount - 1);
    if (!firstPanel || !lastPanel) {
      return { min: 0, max: 0 };
    }
    const viewportSize = flicking.camera.size;
    const firstPanelPrev = firstPanel.range.min;
    const lastPanelNext = lastPanel.range.max;
    const panelAreaSize = lastPanelNext - firstPanelPrev;
    const isBiggerThanViewport = viewportSize < panelAreaSize;
    const firstPos = firstPanelPrev + alignPos;
    const lastPos = lastPanelNext - viewportSize + alignPos;
    if (isBiggerThanViewport) {
      return { min: firstPos, max: lastPos };
    } else {
      const align = flicking.camera.align;
      const alignVal = typeof align === "object" ? align.camera : align;
      const pos = firstPos + parseAlign$1(alignVal, lastPos - firstPos);
      return { min: pos, max: pos };
    }
  }
  getAnchors() {
    const flicking = this._flicking;
    const camera = flicking.camera;
    const panels = flicking.renderer.panels;
    if (panels.length <= 0) {
      return [];
    }
    const range2 = flicking.camera.range;
    const reachablePanels = panels.filter((panel) => camera.canReach(panel));
    if (reachablePanels.length > 0) {
      const shouldPrependBoundAnchor = reachablePanels[0].position !== range2.min;
      const shouldAppendBoundAnchor = reachablePanels[reachablePanels.length - 1].position !== range2.max;
      const indexOffset = shouldPrependBoundAnchor ? 1 : 0;
      const newAnchors = reachablePanels.map(
        (panel, idx) => new AnchorPoint({
          index: idx + indexOffset,
          position: panel.position,
          panel
        })
      );
      if (shouldPrependBoundAnchor) {
        newAnchors.splice(
          0,
          0,
          new AnchorPoint({
            index: 0,
            position: range2.min,
            panel: panels[reachablePanels[0].index - 1]
          })
        );
      }
      if (shouldAppendBoundAnchor) {
        newAnchors.push(
          new AnchorPoint({
            index: newAnchors.length,
            position: range2.max,
            panel: panels[reachablePanels[reachablePanels.length - 1].index + 1]
          })
        );
      }
      return newAnchors;
    } else if (range2.min !== range2.max) {
      const nearestPanelAtMin = this._findNearestPanel(range2.min, panels);
      const panelAtMin = nearestPanelAtMin.index === panels.length - 1 ? nearestPanelAtMin.prev() : nearestPanelAtMin;
      const panelAtMax = panelAtMin.next();
      return [
        new AnchorPoint({
          index: 0,
          position: range2.min,
          panel: panelAtMin
        }),
        new AnchorPoint({
          index: 1,
          position: range2.max,
          panel: panelAtMax
        })
      ];
    } else {
      return [
        new AnchorPoint({
          index: 0,
          position: range2.min,
          panel: this._findNearestPanel(range2.min, panels)
        })
      ];
    }
  }
  findAnchorIncludePosition(position) {
    const camera = this._flicking.camera;
    const range2 = camera.range;
    const anchors = camera.anchorPoints;
    if (anchors.length <= 0) return null;
    if (position <= range2.min) {
      return anchors[0];
    } else if (position >= range2.max) {
      return anchors[anchors.length - 1];
    } else {
      return super.findAnchorIncludePosition(position);
    }
  }
  /**
   * @internal
   */
  _findNearestPanel(pos, panels) {
    let prevDist = Infinity;
    for (let panelIdx = 0; panelIdx < panels.length; panelIdx++) {
      const panel = panels[panelIdx];
      const dist = Math.abs(panel.position - pos);
      if (dist > prevDist) {
        return panels[panelIdx - 1];
      }
      prevDist = dist;
    }
    return panels[panels.length - 1];
  }
}
class CircularCameraMode extends CameraMode {
  checkAvailability() {
    const flicking = this._flicking;
    const renderer = flicking.renderer;
    const panels = renderer.panels;
    if (panels.length <= 0) {
      return false;
    }
    const firstPanel = panels[0];
    const lastPanel = panels[panels.length - 1];
    const firstPanelPrev = firstPanel.range.min - firstPanel.margin.prev;
    const lastPanelNext = lastPanel.range.max + lastPanel.margin.next;
    const visibleSize = flicking.camera.size;
    const panelSizeSum = lastPanelNext - firstPanelPrev;
    const canSetCircularMode = panels.every((panel) => panelSizeSum - panel.size >= visibleSize);
    return canSetCircularMode;
  }
  getRange() {
    const flicking = this._flicking;
    const panels = flicking.renderer.panels;
    if (panels.length <= 0) {
      return { min: 0, max: 0 };
    }
    const firstPanel = panels[0];
    const lastPanel = panels[panels.length - 1];
    const firstPanelPrev = firstPanel.range.min - firstPanel.margin.prev;
    const lastPanelNext = lastPanel.range.max + lastPanel.margin.next;
    return { min: firstPanelPrev, max: lastPanelNext };
  }
  getAnchors() {
    const flicking = this._flicking;
    const panels = flicking.renderer.panels;
    return panels.map(
      (panel, index) => new AnchorPoint({
        index,
        position: panel.position,
        panel
      })
    );
  }
  findNearestAnchor(position) {
    const camera = this._flicking.camera;
    const anchors = camera.anchorPoints;
    if (anchors.length <= 0) return null;
    const camRange = camera.range;
    let minDist = Infinity;
    let minDistIndex = -1;
    for (let anchorIdx = 0; anchorIdx < anchors.length; anchorIdx++) {
      const anchor = anchors[anchorIdx];
      const dist = Math.min(
        Math.abs(anchor.position - position),
        Math.abs(anchor.position - camRange.min + camRange.max - position),
        Math.abs(position - camRange.min + camRange.max - anchor.position)
      );
      if (dist < minDist) {
        minDist = dist;
        minDistIndex = anchorIdx;
      }
    }
    return anchors[minDistIndex];
  }
  findAnchorIncludePosition(position) {
    const camera = this._flicking.camera;
    const range2 = camera.range;
    const anchors = camera.anchorPoints;
    const rangeDiff = camera.rangeDiff;
    const anchorCount = anchors.length;
    const positionInRange = circulatePosition(position, range2.min, range2.max);
    let anchorInRange = super.findAnchorIncludePosition(positionInRange);
    if (anchorCount > 0 && (position === range2.min || position === range2.max)) {
      const possibleAnchors = [
        anchorInRange,
        new AnchorPoint({
          index: 0,
          position: anchors[0].position + rangeDiff,
          panel: anchors[0].panel
        }),
        new AnchorPoint({
          index: anchorCount - 1,
          position: anchors[anchorCount - 1].position - rangeDiff,
          panel: anchors[anchorCount - 1].panel
        })
      ].filter((anchor) => !!anchor);
      anchorInRange = possibleAnchors.reduce((nearest, anchor) => {
        if (!nearest) return anchor;
        return Math.abs(nearest.position - position) < Math.abs(anchor.position - position) ? nearest : anchor;
      }, null);
    }
    if (!anchorInRange) return null;
    if (position < range2.min) {
      const loopCount = -Math.floor((range2.min - position) / rangeDiff) - 1;
      return new AnchorPoint({
        index: anchorInRange.index,
        position: anchorInRange.position + rangeDiff * loopCount,
        panel: anchorInRange.panel
      });
    } else if (position > range2.max) {
      const loopCount = Math.floor((position - range2.max) / rangeDiff) + 1;
      return new AnchorPoint({
        index: anchorInRange.index,
        position: anchorInRange.position + rangeDiff * loopCount,
        panel: anchorInRange.panel
      });
    }
    return anchorInRange;
  }
  getCircularOffset() {
    const flicking = this._flicking;
    const camera = flicking.camera;
    if (!camera.circularEnabled) return 0;
    const toggled = flicking.panels.filter((panel) => panel.toggled);
    const toggledPrev = toggled.filter((panel) => panel.toggleDirection === DIRECTION.PREV);
    const toggledNext = toggled.filter((panel) => panel.toggleDirection === DIRECTION.NEXT);
    return this._calcPanelAreaSum(toggledPrev) - this._calcPanelAreaSum(toggledNext);
  }
  clampToReachablePosition(position) {
    return position;
  }
  canReach(panel) {
    if (panel.removed) return false;
    return true;
  }
  canSee(panel) {
    const camera = this._flicking.camera;
    const range2 = camera.range;
    const rangeDiff = camera.rangeDiff;
    const visibleRange = camera.visibleRange;
    const visibleInCurrentRange = super.canSee(panel);
    if (visibleRange.min < range2.min) {
      return visibleInCurrentRange || panel.isVisibleOnRange(visibleRange.min + rangeDiff, visibleRange.max + rangeDiff);
    } else if (visibleRange.max > range2.max) {
      return visibleInCurrentRange || panel.isVisibleOnRange(visibleRange.min - rangeDiff, visibleRange.max - rangeDiff);
    }
    return visibleInCurrentRange;
  }
  /**
   * @internal
   */
  _calcPanelAreaSum(panels) {
    return panels.reduce((sum, panel) => sum + panel.sizeIncludingMargin, 0);
  }
}
class LinearCameraMode extends CameraMode {
  checkAvailability() {
    return true;
  }
  getRange() {
    var _a, _b;
    const renderer = this._flicking.renderer;
    const firstPanel = renderer.getPanel(0);
    const lastPanel = renderer.getPanel(renderer.panelCount - 1);
    return { min: (_a = firstPanel == null ? void 0 : firstPanel.position) != null ? _a : 0, max: (_b = lastPanel == null ? void 0 : lastPanel.position) != null ? _b : 0 };
  }
}
class Camera {
  /**
   * Creates a new Camera instance
   * @param flicking - An instance of {@link Flicking}
   * @param options - Options for the Camera
   */
  constructor(flicking, { align = ALIGN.CENTER } = {}) {
    this._lookedOffset = 0;
    this._checkTranslateSupport = () => {
      const transforms = ["webkitTransform", "msTransform", "MozTransform", "OTransform", "transform"];
      const supportedStyle = document.documentElement.style;
      let transformName = "";
      for (const prefixedTransform of transforms) {
        if (prefixedTransform in supportedStyle) {
          transformName = prefixedTransform;
        }
      }
      if (!transformName) {
        throw new FlickingError(MESSAGE.TRANSFORM_NOT_SUPPORTED, CODE.TRANSFORM_NOT_SUPPORTED);
      }
      this._transform = transformName;
    };
    this._flicking = flicking;
    this._resetInternalValues();
    this._align = align;
  }
  // Internal states getter
  /**
   * The camera element(`.flicking-camera`)
   * @readonly
   */
  get element() {
    return this._el;
  }
  /**
   * An array of the child elements of the camera element(`.flicking-camera`)
   * @readonly
   */
  get children() {
    return toArray(this._el.children);
  }
  /**
   * Current position of the camera
   * @readonly
   */
  get position() {
    return this._position;
  }
  /**
   * Align position inside the viewport where {@link Panel}'s {@link Panel.alignPosition | alignPosition} should be located at
   * @readonly
   */
  get alignPosition() {
    return this._alignPos;
  }
  /**
   * Position offset, used for the {@link Flicking.renderOnlyVisible | renderOnlyVisible} option
   * @defaultValue 0
   * @readonly
   */
  get offset() {
    return this._offset - this._circularOffset;
  }
  /**
   * Whether the `circular` option is enabled.
   * @remarks
   * The {@link Flicking.circular | circular} option can't be enabled when sum of the panel sizes are too small.
   * @defaultValue false
   * @readonly
   */
  get circularEnabled() {
    return this._circularEnabled;
  }
  /**
   * A current camera mode
   * @readonly
   */
  get mode() {
    return this._mode;
  }
  /**
   * A range that Camera's {@link Camera.position | position} can reach
   * @readonly
   */
  get range() {
    return this._range;
  }
  /**
   * A difference between Camera's minimum and maximum position that can reach
   * @readonly
   */
  get rangeDiff() {
    return this._range.max - this._range.min;
  }
  /**
   * An array of visible panels from the current position
   * @readonly
   */
  get visiblePanels() {
    return this._visiblePanels;
  }
  /**
   * A range of the visible area from the current position
   * @readonly
   */
  get visibleRange() {
    return { min: this._position - this._alignPos, max: this._position - this._alignPos + this.size };
  }
  /**
   * An array of {@link AnchorPoint}s that Camera can be stopped at
   * @readonly
   */
  get anchorPoints() {
    return this._anchors;
  }
  /**
   * A current parameters of the Camera for updating {@link AxesController}
   * @readonly
   */
  get controlParams() {
    return { range: this._range, position: this._position, circular: this._circularEnabled };
  }
  /**
   * A Boolean value indicating whether Camera's over the minimum or maximum position reachable
   * @readonly
   */
  get atEdge() {
    return this._position <= this._range.min || this._position >= this._range.max;
  }
  /**
   * Return the size of the viewport
   * @readonly
   */
  get size() {
    const flicking = this._flicking;
    return flicking ? flicking.horizontal ? flicking.viewport.width : flicking.viewport.height : 0;
  }
  /**
   * Return the camera's position progress from the first panel to last panel
   * @remarks
   * Range is from 0 to last panel's index
   * @readonly
   */
  get progress() {
    const flicking = this._flicking;
    const position = this._position + this._offset;
    const nearestAnchor = this.findNearestAnchor(this._position);
    if (!flicking || !nearestAnchor) {
      return NaN;
    }
    const nearestPanel = nearestAnchor.panel;
    const panelPos = nearestPanel.position + nearestPanel.offset;
    const bounceSize = flicking.control.controller.bounce;
    const { min: prevRange, max: nextRange } = this.range;
    const rangeDiff = this.rangeDiff;
    if (position === panelPos) {
      return nearestPanel.index;
    }
    if (position < panelPos) {
      const prevPanel = nearestPanel.prev();
      let prevPosition = prevPanel ? prevPanel.position + prevPanel.offset : prevRange - bounceSize[0];
      if (prevPosition > panelPos) {
        prevPosition -= rangeDiff;
      }
      return nearestPanel.index - 1 + getProgress$1(position, prevPosition, panelPos);
    } else {
      const nextPanel = nearestPanel.next();
      let nextPosition = nextPanel ? nextPanel.position + nextPanel.offset : nextRange + bounceSize[1];
      if (nextPosition < panelPos) {
        nextPosition += rangeDiff;
      }
      return nearestPanel.index + getProgress$1(position, panelPos, nextPosition);
    }
  }
  /**
   * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/direction | direction} CSS property applied to the camera element(`.flicking-camera`)
   * @readonly
   */
  get panelOrder() {
    return this._panelOrder;
  }
  // Options Getter
  /**
   * A value indicating where the {@link Camera.alignPosition | alignPosition} should be located at inside the viewport element
   */
  get align() {
    return this._align;
  }
  // Options Setter
  set align(val) {
    this._align = val;
  }
  /**
   * Initialize Camera
   * @remarks
   * This method is called automatically during {@link Flicking.init}. It finds the camera element inside the viewport.
   * @throws {@link InitializationErrors}
   * @returns The current instance for method chaining
   */
  init() {
    const viewportEl = this._flicking.viewport.element;
    checkExistence(viewportEl.firstElementChild, "First element child of the viewport element");
    this._el = viewportEl.firstElementChild;
    this._checkTranslateSupport();
    this._updateMode();
    this.updatePanelOrder();
    return this;
  }
  /**
   * Destroy Camera and return to initial state
   * @remarks
   * This method resets all internal values to their initial state.
   * @returns The current instance for method chaining
   */
  destroy() {
    this._resetInternalValues();
    return this;
  }
  /**
   * Move to the given position and apply CSS transform
   * @remarks
   * This method updates the camera position, toggles panels for circular mode, and refreshes visible panels.
   * @param pos - A new position
   * @throws {@link InitializationErrors}
   */
  lookAt(pos) {
    const prevOffset = this._offset;
    const isChangedOffset = this._lookedOffset !== prevOffset;
    const flicking = getFlickingAttached(this._flicking);
    const prevPos = this._position;
    this._position = pos;
    const toggled = this._togglePanels(prevPos, pos);
    this._refreshVisiblePanels();
    this._checkNeedPanel();
    this._checkReachEnd(prevPos, pos);
    if (toggled) {
      void flicking.renderer.render().then(() => {
        this.updateOffset();
        this._lookedOffset = this._offset;
      });
    } else if (isChangedOffset) {
      this.updateOffset();
      this._lookedOffset = this._offset;
    } else {
      this.applyTransform();
    }
  }
  /**
   * Return a previous {@link AnchorPoint} of given {@link AnchorPoint}
   * @remarks
   * If it does not exist, return `null` instead
   * @param anchor - A reference {@link AnchorPoint}
   * @returns The previous {@link AnchorPoint}
   */
  getPrevAnchor(anchor) {
    if (!this._circularEnabled || anchor.index !== 0) {
      return this._anchors[anchor.index - 1] || null;
    } else {
      const anchors = this._anchors;
      const rangeDiff = this.rangeDiff;
      const lastAnchor = anchors[anchors.length - 1];
      return new AnchorPoint({
        index: lastAnchor.index,
        position: lastAnchor.position - rangeDiff,
        panel: lastAnchor.panel
      });
    }
  }
  /**
   * Return a next {@link AnchorPoint} of given {@link AnchorPoint}
   * @remarks
   * If it does not exist, return `null` instead
   * @param anchor - A reference {@link AnchorPoint}
   * @returns The next {@link AnchorPoint}
   */
  getNextAnchor(anchor) {
    const anchors = this._anchors;
    if (!this._circularEnabled || anchor.index !== anchors.length - 1) {
      return anchors[anchor.index + 1] || null;
    } else {
      const rangeDiff = this.rangeDiff;
      const firstAnchor = anchors[0];
      return new AnchorPoint({
        index: firstAnchor.index,
        position: firstAnchor.position + rangeDiff,
        panel: firstAnchor.panel
      });
    }
  }
  /**
   * Return the camera's position progress in the panel below
   * @remarks
   * Value is from 0 to 1 when the camera's inside panel.
   * Value can be lower than 0 or bigger than 1 when it's in the margin area
   * @param panel - A panel to check
   * @returns Progress value from 0 to 1 (or outside this range when in margin area)
   */
  getProgressInPanel(panel) {
    const panelRange = panel.range;
    return (this._position - panelRange.min) / (panelRange.max - panelRange.min);
  }
  /**
   * Return {@link AnchorPoint} that includes given position
   * @remarks
   * If there's no {@link AnchorPoint} that includes the given position, return `null` instead
   * @param position - A position to check
   * @returns The {@link AnchorPoint} that includes the given position
   */
  findAnchorIncludePosition(position) {
    return this._mode.findAnchorIncludePosition(position);
  }
  /**
   * Return {@link AnchorPoint} nearest to given position
   * @remarks
   * If there're no {@link AnchorPoint}s, return `null` instead
   * @param position - A position to check
   * @returns The {@link AnchorPoint} nearest to the given position
   */
  findNearestAnchor(position) {
    return this._mode.findNearestAnchor(position);
  }
  /**
   * Return {@link AnchorPoint} that matches {@link Flicking.currentPanel}
   * @returns The {@link AnchorPoint} that matches current panel
   */
  findActiveAnchor() {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    const activePanel = flicking.control.activePanel;
    if (!activePanel) return null;
    return (_a = find(this._anchors, (anchor) => anchor.panel.index === activePanel.index)) != null ? _a : this.findNearestAnchor(activePanel.position);
  }
  /**
   * Clamp the given position between camera's range
   * @param position - A position to clamp
   * @returns A clamped position
   */
  clampToReachablePosition(position) {
    return this._mode.clampToReachablePosition(position);
  }
  /**
   * Check whether the given panel is inside of the Camera's range
   * @param panel - An instance of {@link Panel} to check
   * @returns Whether the panel's inside Camera's range
   */
  canReach(panel) {
    return this._mode.canReach(panel);
  }
  /**
   * Check whether the given panel element is visible at the current position
   * @param panel - An instance of {@link Panel} to check
   * @returns Whether the panel element is visible at the current position
   */
  canSee(panel) {
    return this._mode.canSee(panel);
  }
  /**
   * Update {@link Camera.range | range} of Camera
   * @remarks
   * This method recalculates the camera range based on the current panel positions and circular mode settings.
   * @throws {@link InitializationErrors}
   * @returns The current instance for method chaining
   */
  updateRange() {
    const flicking = getFlickingAttached(this._flicking);
    const renderer = flicking.renderer;
    const panels = renderer.panels;
    this._updateMode();
    this._range = this._mode.getRange();
    panels.forEach((panel) => panel.updateCircularToggleDirection());
    return this;
  }
  /**
   * Update Camera's {@link Camera.alignPosition | alignPosition}
   * @returns The current instance for method chaining
   */
  updateAlignPos() {
    const align = this._align;
    const alignVal = typeof align === "object" ? align.camera : align;
    this._alignPos = parseAlign$1(alignVal, this.size);
    return this;
  }
  /**
   * Update Camera's {@link Camera.anchorPoints | anchorPoints}
   * @remarks
   * Anchor points are positions where the camera can stop. This method recalculates them based on the current mode.
   * @throws {@link InitializationErrors}
   * @returns The current instance for method chaining
   */
  updateAnchors() {
    this._anchors = this._mode.getAnchors();
    return this;
  }
  /**
   * Update Viewport's height to visible panel's max height
   * @remarks
   * This method only takes effect when {@link FlickingOptions.horizontal | horizontal} is `true` and {@link FlickingOptions.adaptive | adaptive} is enabled.
   * @throws {@link InitializationErrors}
   */
  updateAdaptiveHeight() {
    const flicking = getFlickingAttached(this._flicking);
    const activePanel = flicking.control.activePanel;
    const visiblePanels = flicking.visiblePanels;
    const selectedPanels = [...visiblePanels];
    if (activePanel) {
      selectedPanels.push(activePanel);
    }
    if (!flicking.horizontal || !flicking.adaptive || !selectedPanels.length) return;
    const maxHeight = Math.max(...selectedPanels.map((panel) => panel.height));
    flicking.viewport.setSize({
      height: maxHeight
    });
  }
  /**
   * Update current offset of the camera
   * @returns The current instance for method chaining
   */
  updateOffset() {
    const flicking = getFlickingAttached(this._flicking);
    const position = this._position;
    const unRenderedPanels = flicking.panels.filter((panel) => !panel.rendered);
    this._offset = unRenderedPanels.filter((panel) => panel.position + panel.offset < position).reduce((offset, panel) => offset + panel.sizeIncludingMargin, 0);
    this._circularOffset = this._mode.getCircularOffset();
    this.applyTransform();
    return this;
  }
  /**
   * Update direction to match the {@link https://developer.mozilla.org/en-US/docs/Web/CSS/direction | direction} CSS property applied to the camera element
   * @returns The current instance for method chaining
   */
  updatePanelOrder() {
    const flicking = getFlickingAttached(this._flicking);
    if (!flicking.horizontal) return this;
    const el = this._el;
    const direction = getStyle(el).direction;
    if (direction !== this._panelOrder) {
      this._panelOrder = direction === ORDER.RTL ? ORDER.RTL : ORDER.LTR;
      if (flicking.initialized) {
        flicking.control.controller.updateDirection();
      }
    }
    return this;
  }
  /**
   * Reset the history of {@link Flicking.event:needPanel | needPanel} events so it can be triggered again
   * @returns The current instance for method chaining
   */
  resetNeedPanelHistory() {
    this._needPanelTriggered = { prev: false, next: false };
    return this;
  }
  /**
   * Apply "transform" style with the current position to camera element
   * @returns The current instance for method chaining
   */
  applyTransform() {
    const el = this._el;
    const flicking = getFlickingAttached(this._flicking);
    const renderer = flicking.renderer;
    if (renderer.rendering || !flicking.initialized) return this;
    const actualPosition = this._position - this._alignPos - this._offset + this._circularOffset;
    const sign = !flicking.horizontal || this._panelOrder !== ORDER.RTL ? -1 : 1;
    const posText = flicking.usePercentagePos ? `${sign * actualPosition / this.size * 100}%` : `${sign * actualPosition}px`;
    el.style[this._transform] = flicking.horizontal ? `translate(${posText})` : `translate(0, ${posText})`;
    return this;
  }
  /**
   * @internal
   * @privateRemarks
   * Resets all internal state values to their defaults. Called during construction and destruction.
   */
  _resetInternalValues() {
    this._position = 0;
    this._lookedOffset = 0;
    this._alignPos = 0;
    this._offset = 0;
    this._circularOffset = 0;
    this._circularEnabled = false;
    this._range = { min: 0, max: 0 };
    this._visiblePanels = [];
    this._anchors = [];
    this._needPanelTriggered = { prev: false, next: false };
  }
  /**
   * @internal
   * @privateRemarks
   * Updates the list of visible panels and triggers {@link VisibleChangeEvent} if panels were added or removed.
   */
  _refreshVisiblePanels() {
    const flicking = getFlickingAttached(this._flicking);
    const panels = flicking.renderer.panels;
    const newVisiblePanels = panels.filter((panel) => this.canSee(panel));
    const prevVisiblePanels = this._visiblePanels;
    this._visiblePanels = newVisiblePanels;
    const added = newVisiblePanels.filter((panel) => !includes(prevVisiblePanels, panel));
    const removed = prevVisiblePanels.filter((panel) => !includes(newVisiblePanels, panel));
    if (added.length > 0 || removed.length > 0) {
      void flicking.renderer.render().then(() => {
        flicking.trigger(
          new Component.ComponentEvent(EVENTS.VISIBLE_CHANGE, {
            added,
            removed,
            visiblePanels: newVisiblePanels
          })
        );
      });
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Checks if the camera is near the edges and triggers {@link NeedPanelEvent} for infinite scrolling implementations.
   */
  _checkNeedPanel() {
    const needPanelTriggered = this._needPanelTriggered;
    if (needPanelTriggered.prev && needPanelTriggered.next) return;
    const flicking = getFlickingAttached(this._flicking);
    const panels = flicking.renderer.panels;
    if (panels.length <= 0) {
      if (!needPanelTriggered.prev) {
        flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.PREV }));
        needPanelTriggered.prev = true;
      }
      if (!needPanelTriggered.next) {
        flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.NEXT }));
        needPanelTriggered.next = true;
      }
      return;
    }
    const cameraPosition = this._position;
    const cameraSize = this.size;
    const cameraRange = this._range;
    const needPanelThreshold = flicking.needPanelThreshold;
    const cameraPrev = cameraPosition - this._alignPos;
    const cameraNext = cameraPrev + cameraSize;
    const firstPanel = panels[0];
    const lastPanel = panels[panels.length - 1];
    if (!needPanelTriggered.prev) {
      const firstPanelPrev = firstPanel.range.min;
      if (cameraPrev <= firstPanelPrev + needPanelThreshold || cameraPosition <= cameraRange.min + needPanelThreshold) {
        flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.PREV }));
        needPanelTriggered.prev = true;
      }
    }
    if (!needPanelTriggered.next) {
      const lastPanelNext = lastPanel.range.max;
      if (cameraNext >= lastPanelNext - needPanelThreshold || cameraPosition >= cameraRange.max - needPanelThreshold) {
        flicking.trigger(new Component.ComponentEvent(EVENTS.NEED_PANEL, { direction: DIRECTION.NEXT }));
        needPanelTriggered.next = true;
      }
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Checks if the camera has reached the edge of the range and triggers {@link ReachEdgeEvent}.
   */
  _checkReachEnd(prevPos, newPos) {
    const flicking = getFlickingAttached(this._flicking);
    const range2 = this._range;
    const wasBetweenRange = prevPos > range2.min && prevPos < range2.max;
    const isBetweenRange = newPos > range2.min && newPos < range2.max;
    if (!wasBetweenRange || isBetweenRange) return;
    const direction = newPos <= range2.min ? DIRECTION.PREV : DIRECTION.NEXT;
    flicking.trigger(
      new Component.ComponentEvent(EVENTS.REACH_EDGE, {
        direction
      })
    );
  }
  /**
   * @internal
   * @privateRemarks
   * Updates the camera mode based on {@link FlickingOptions.circular | circular} and {@link FlickingOptions.bound | bound} options.
   */
  _updateMode() {
    const flicking = getFlickingAttached(this._flicking);
    if (flicking.circular) {
      const circularMode = new CircularCameraMode(flicking);
      const canSetCircularMode = circularMode.checkAvailability();
      if (canSetCircularMode) {
        this._mode = circularMode;
      } else {
        const fallbackMode = flicking.circularFallback;
        this._mode = fallbackMode === CIRCULAR_FALLBACK.BOUND ? new BoundCameraMode(flicking) : new LinearCameraMode(flicking);
      }
      this._circularEnabled = canSetCircularMode;
    } else {
      this._mode = flicking.bound ? new BoundCameraMode(flicking) : new LinearCameraMode(flicking);
      this._circularEnabled = false;
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Toggles panel positions for circular mode. Returns true if any panel was toggled.
   */
  _togglePanels(prevPos, pos) {
    if (pos === prevPos) return false;
    const flicking = getFlickingAttached(this._flicking);
    const panels = flicking.renderer.panels;
    const toggled = panels.map((panel) => panel.toggle(prevPos, pos));
    return toggled.some((isToggled) => isToggled);
  }
}
const EVENT = {
  HOLD: "hold",
  CHANGE: "change",
  RELEASE: "release",
  ANIMATION_END: "animationEnd",
  FINISH: "finish"
};
const POSITION_KEY = "flick";
var STATE_TYPE = /* @__PURE__ */ ((STATE_TYPE2) => {
  STATE_TYPE2[STATE_TYPE2["IDLE"] = 0] = "IDLE";
  STATE_TYPE2[STATE_TYPE2["HOLDING"] = 1] = "HOLDING";
  STATE_TYPE2[STATE_TYPE2["DRAGGING"] = 2] = "DRAGGING";
  STATE_TYPE2[STATE_TYPE2["ANIMATING"] = 3] = "ANIMATING";
  STATE_TYPE2[STATE_TYPE2["DISABLED"] = 4] = "DISABLED";
  return STATE_TYPE2;
})(STATE_TYPE || {});
class State {
  constructor() {
    this._delta = 0;
    this._targetPanel = null;
  }
  /**
   * A sum of delta values of change events from the last hold event of Axes
   * @readonly
   */
  get delta() {
    return this._delta;
  }
  /**
   * A panel to set as {@link Control.activePanel} after the animation is finished
   * @readonly
   */
  get targetPanel() {
    return this._targetPanel;
  }
  set targetPanel(val) {
    this._targetPanel = val;
  }
  /**
   * An callback which is called when state has changed to this state
   * @param prevState - An previous state
   */
  onEnter(prevState) {
    this._delta = prevState._delta;
    this._targetPanel = prevState._targetPanel;
  }
  /**
   * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-hold | hold} event
   * @param ctx - {@link StateContext}
   */
  onHold(ctx) {
  }
  /**
   * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-change | change} event
   * @param ctx - {@link StateContext}
   */
  onChange(ctx) {
  }
  /**
   * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event
   * @param ctx - {@link StateContext}
   */
  onRelease(ctx) {
  }
  /**
   * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-animationEnd | animationEnd} event
   * @param ctx - {@link StateContext}
   */
  onAnimationEnd(ctx) {
  }
  /**
   * An event handler for Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-finish | finish} event
   * @param ctx - {@link StateContext}
   */
  onFinish(ctx) {
  }
  /**
   * @internal
   */
  _moveToChangedPosition(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    const delta = axesEvent.delta[POSITION_KEY];
    if (!delta) {
      return;
    }
    this._delta += delta;
    const camera = flicking.camera;
    const prevPosition = camera.position;
    const position = axesEvent.pos[POSITION_KEY];
    const newPosition = flicking.circularEnabled ? circulatePosition(position, camera.range.min, camera.range.max) : position;
    camera.lookAt(newPosition);
    const moveEvent = new Component.ComponentEvent(EVENTS.MOVE, {
      isTrusted: axesEvent.isTrusted,
      holding: this.holding,
      direction: getDirection(0, axesEvent.delta[POSITION_KEY]),
      axesEvent
    });
    flicking.trigger(moveEvent);
    if (moveEvent.isCanceled()) {
      camera.lookAt(prevPosition);
      transitTo(
        4
        /* DISABLED */
      );
    }
  }
}
class AnimatingState extends State {
  constructor() {
    super(...arguments);
    this.holding = false;
    this.animating = true;
  }
  onHold(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    const targetPanel = this._targetPanel;
    const control = flicking.control;
    this._delta = 0;
    flicking.control.updateInput();
    if (flicking.changeOnHold && targetPanel) {
      control.setActive(targetPanel, control.activePanel, axesEvent.isTrusted);
    }
    const holdStartEvent = new Component.ComponentEvent(EVENTS.HOLD_START, { axesEvent });
    flicking.trigger(holdStartEvent);
    if (holdStartEvent.isCanceled()) {
      transitTo(STATE_TYPE.DISABLED);
    } else {
      transitTo(STATE_TYPE.DRAGGING);
    }
  }
  onChange(ctx) {
    this._moveToChangedPosition(ctx);
  }
  onFinish(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    const control = flicking.control;
    const controller = control.controller;
    const animatingContext = controller.animatingContext;
    transitTo(STATE_TYPE.IDLE);
    flicking.trigger(
      new Component.ComponentEvent(EVENTS.MOVE_END, {
        isTrusted: axesEvent.isTrusted,
        direction: getDirection(animatingContext.start, animatingContext.end),
        axesEvent
      })
    );
    const targetPanel = this._targetPanel;
    if (targetPanel) {
      control.setActive(targetPanel, control.activePanel, axesEvent.isTrusted);
    }
  }
}
class DisabledState extends State {
  constructor() {
    super(...arguments);
    this.holding = false;
    this.animating = true;
  }
  onAnimationEnd(ctx) {
    const { transitTo } = ctx;
    transitTo(STATE_TYPE.IDLE);
  }
  onChange(ctx) {
    const { axesEvent, transitTo } = ctx;
    axesEvent.stop();
    transitTo(STATE_TYPE.IDLE);
  }
  onRelease(ctx) {
    const { axesEvent, transitTo } = ctx;
    if (axesEvent.delta.flick === 0) {
      transitTo(STATE_TYPE.IDLE);
    }
  }
}
class DraggingState extends State {
  constructor() {
    super(...arguments);
    this.holding = true;
    this.animating = true;
  }
  onChange(ctx) {
    this._moveToChangedPosition(ctx);
  }
  onRelease(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    flicking.trigger(
      new Component.ComponentEvent(EVENTS.HOLD_END, {
        axesEvent
      })
    );
    if (flicking.renderer.panelCount <= 0) {
      transitTo(STATE_TYPE.IDLE);
      return;
    }
    transitTo(STATE_TYPE.ANIMATING);
    const control = flicking.control;
    const position = axesEvent.destPos[POSITION_KEY];
    const duration = Math.max(axesEvent.duration, flicking.duration);
    try {
      void control.moveToPosition(position, duration, axesEvent);
    } catch (_err) {
      transitTo(STATE_TYPE.IDLE);
      axesEvent.setTo({ [POSITION_KEY]: flicking.camera.position }, 0);
    }
  }
}
class HoldingState extends State {
  constructor() {
    super(...arguments);
    this.holding = true;
    this.animating = false;
    this._releaseEvent = null;
  }
  onChange(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    const inputEvent = axesEvent.inputEvent;
    if (!inputEvent) {
      return;
    }
    const offset = flicking.horizontal ? inputEvent.offsetX : inputEvent.offsetY;
    const moveStartEvent = new Component.ComponentEvent(EVENTS.MOVE_START, {
      isTrusted: axesEvent.isTrusted,
      holding: this.holding,
      direction: getDirection(0, -offset),
      axesEvent
    });
    flicking.trigger(moveStartEvent);
    if (moveStartEvent.isCanceled()) {
      transitTo(STATE_TYPE.DISABLED);
    } else {
      transitTo(STATE_TYPE.DRAGGING).onChange(ctx);
    }
  }
  onRelease(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    flicking.trigger(new Component.ComponentEvent(EVENTS.HOLD_END, { axesEvent }));
    if (axesEvent.delta.flick !== 0) {
      axesEvent.setTo({ flick: flicking.camera.position }, 0);
      transitTo(STATE_TYPE.IDLE);
      return;
    }
    this._releaseEvent = axesEvent;
  }
  onFinish(ctx) {
    const { flicking, transitTo } = ctx;
    transitTo(STATE_TYPE.IDLE);
    if (!this._releaseEvent) {
      return;
    }
    const releaseEvent = this._releaseEvent;
    const srcEvent = releaseEvent.inputEvent.srcEvent;
    let clickedElement;
    if (srcEvent.type === "touchend") {
      const touchEvent = srcEvent;
      const touch = touchEvent.changedTouches[0];
      clickedElement = document.elementFromPoint(touch.clientX, touch.clientY);
    } else {
      clickedElement = srcEvent.target;
    }
    const panels = flicking.renderer.panels;
    let clickedPanel = null;
    for (const panel of panels) {
      if (panel.contains(clickedElement)) {
        clickedPanel = panel;
        break;
      }
    }
    if (clickedPanel) {
      const cameraPosition = flicking.camera.position;
      const clickedPanelPosition = clickedPanel.position;
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.SELECT, {
          index: clickedPanel.index,
          panel: clickedPanel,
          // Direction to the clicked panel
          direction: getDirection(cameraPosition, clickedPanelPosition)
        })
      );
    }
  }
}
class IdleState extends State {
  constructor() {
    super(...arguments);
    this.holding = false;
    this.animating = false;
  }
  onEnter() {
    this._delta = 0;
    this._targetPanel = null;
  }
  onHold(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    if (flicking.renderer.panelCount <= 0) {
      transitTo(STATE_TYPE.DISABLED);
      return;
    }
    const holdStartEvent = new Component.ComponentEvent(EVENTS.HOLD_START, {
      axesEvent
    });
    flicking.trigger(holdStartEvent);
    if (holdStartEvent.isCanceled()) {
      transitTo(STATE_TYPE.DISABLED);
    } else {
      transitTo(STATE_TYPE.HOLDING);
    }
  }
  // By methods call
  onChange(ctx) {
    const { flicking, axesEvent, transitTo } = ctx;
    const controller = flicking.control.controller;
    const animatingContext = controller.animatingContext;
    const moveStartEvent = new Component.ComponentEvent(EVENTS.MOVE_START, {
      isTrusted: axesEvent.isTrusted,
      holding: this.holding,
      direction: getDirection(animatingContext.start, animatingContext.end),
      axesEvent
    });
    flicking.trigger(moveStartEvent);
    if (moveStartEvent.isCanceled()) {
      transitTo(STATE_TYPE.DISABLED);
    } else {
      transitTo(STATE_TYPE.ANIMATING).onChange(ctx);
    }
  }
}
class StateMachine {
  constructor() {
    this.transitTo = (nextStateType) => {
      let nextState;
      switch (nextStateType) {
        case STATE_TYPE.IDLE:
          nextState = new IdleState();
          break;
        case STATE_TYPE.HOLDING:
          nextState = new HoldingState();
          break;
        case STATE_TYPE.DRAGGING:
          nextState = new DraggingState();
          break;
        case STATE_TYPE.ANIMATING:
          nextState = new AnimatingState();
          break;
        case STATE_TYPE.DISABLED:
          nextState = new DisabledState();
          break;
      }
      nextState.onEnter(this._state);
      this._state = nextState;
      return this._state;
    };
    this._state = new IdleState();
  }
  get state() {
    return this._state;
  }
  fire(eventType, externalCtx) {
    const currentState = this._state;
    const ctx = __spreadProps(__spreadValues({}, externalCtx), { transitTo: this.transitTo });
    switch (eventType) {
      case EVENT.HOLD:
        currentState.onHold(ctx);
        break;
      case EVENT.CHANGE:
        currentState.onChange(ctx);
        break;
      case EVENT.RELEASE:
        currentState.onRelease(ctx);
        break;
      case EVENT.ANIMATION_END:
        currentState.onAnimationEnd(ctx);
        break;
      case EVENT.FINISH:
        currentState.onFinish(ctx);
        break;
    }
  }
}
class AxesController {
  constructor() {
    this._onAxesHold = () => {
      this._dragged = false;
    };
    this._onAxesChange = () => {
      var _a;
      this._dragged = !!((_a = this._panInput) == null ? void 0 : _a.isEnabled());
    };
    this._preventClickWhenDragged = (e) => {
      if (this._dragged) {
        e.preventDefault();
        e.stopPropagation();
      }
      this._dragged = false;
    };
    this._resetInternalValues();
    this._stateMachine = new StateMachine();
  }
  /**
   * An {@link https://naver.github.io/egjs-axes/docs/api/Axes | Axes} instance
   * @see https://naver.github.io/egjs-axes/docs/api/Axes
   * @readonly
   */
  get axes() {
    return this._axes;
  }
  /**
   * An {@link https://naver.github.io/egjs-axes/docs/api/PanInput | PanInput} instance
   * @see https://naver.github.io/egjs-axes/docs/api/PanInput
   * @readonly
   */
  get panInput() {
    return this._panInput;
  }
  /**
   * @internal
   */
  get stateMachine() {
    return this._stateMachine;
  }
  /**
   * A activated {@link State} that shows the current status of the user input or the animation
   */
  get state() {
    return this._stateMachine.state;
  }
  /**
   * A context of the current animation playing
   * @readonly
   */
  get animatingContext() {
    return this._animatingContext;
  }
  /**
   * A current control parameters of the Axes instance
   */
  get controlParams() {
    const axes = this._axes;
    if (!axes) {
      return {
        range: { min: 0, max: 0 },
        position: 0,
        circular: false
      };
    }
    const axis = axes.axis[POSITION_KEY];
    return {
      range: { min: axis.range[0], max: axis.range[1] },
      circular: axis.circular[0],
      position: this.position
    };
  }
  /**
   * A Boolean indicating whether the user input is enabled
   * @readonly
   */
  get enabled() {
    var _a, _b;
    return (_b = (_a = this._panInput) == null ? void 0 : _a.isEnabled()) != null ? _b : false;
  }
  /**
   * Current position value in {@link https://naver.github.io/egjs-axes/docs/api/Axes | Axes} instance
   * @readonly
   */
  get position() {
    var _a, _b;
    return (_b = (_a = this._axes) == null ? void 0 : _a.get([POSITION_KEY])[POSITION_KEY]) != null ? _b : 0;
  }
  /**
   * Current range value in {@link https://naver.github.io/egjs-axes/docs/api/Axes | Axes} instance
   * @readonly
   */
  get range() {
    var _a, _b;
    return (_b = (_a = this._axes) == null ? void 0 : _a.axis[POSITION_KEY].range) != null ? _b : [0, 0];
  }
  /**
   * Actual bounce size(px)
   * @readonly
   */
  get bounce() {
    var _a;
    return (_a = this._axes) == null ? void 0 : _a.axis[POSITION_KEY].bounce;
  }
  /**
   * Initialize AxesController
   * @remarks
   * This method creates and configures the Axes and PanInput instances.
   * @param flicking - An instance of {@link Flicking}
   * @returns The current instance for method chaining
   */
  init(flicking) {
    this._flicking = flicking;
    this._axes = new Axes(
      {
        [POSITION_KEY]: {
          range: [0, 0],
          circular: false,
          bounce: [0, 0]
        }
      },
      {
        deceleration: flicking.deceleration,
        interruptable: flicking.interruptable,
        nested: flicking.nested,
        easing: flicking.easing
      }
    );
    this._panInput = new Axes.PanInput(flicking.viewport.element, {
      inputType: flicking.inputType,
      threshold: flicking.dragThreshold,
      iOSEdgeSwipeThreshold: flicking.iOSEdgeSwipeThreshold,
      preventDefaultOnDrag: flicking.preventDefaultOnDrag,
      scale: flicking.horizontal ? [flicking.camera.panelOrder === ORDER.RTL ? 1 : -1, 0] : [0, -1],
      releaseOnScroll: true
    });
    const axes = this._axes;
    axes.connect(flicking.horizontal ? [POSITION_KEY, ""] : ["", POSITION_KEY], this._panInput);
    for (const key in EVENT) {
      const eventType = EVENT[key];
      axes.on(eventType, (e) => {
        this._stateMachine.fire(eventType, {
          flicking,
          axesEvent: e
        });
      });
    }
    return this;
  }
  /**
   * Destroy AxesController and return to initial state
   * @remarks
   * This method destroys the Axes and PanInput instances and removes all event handlers.
   */
  destroy() {
    var _a;
    if (this._axes) {
      this.removePreventClickHandler();
      this._axes.destroy();
    }
    (_a = this._panInput) == null ? void 0 : _a.destroy();
    this._resetInternalValues();
  }
  /**
   * Enable input from the user (mouse/touch)
   * @remarks
   * Enables the PanInput to receive user interactions.
   * @returns The current instance for method chaining
   */
  enable() {
    var _a;
    (_a = this._panInput) == null ? void 0 : _a.enable();
    return this;
  }
  /**
   * Disable input from the user (mouse/touch)
   * @remarks
   * Disables the PanInput to ignore user interactions.
   * @returns The current instance for method chaining
   */
  disable() {
    var _a;
    (_a = this._panInput) == null ? void 0 : _a.disable();
    return this;
  }
  /**
   * Releases ongoing user input (mouse/touch)
   * @remarks
   * Immediately releases the PanInput, simulating the user lifting their finger.
   * @returns The current instance for method chaining
   */
  release() {
    var _a;
    (_a = this._panInput) == null ? void 0 : _a.release();
    return this;
  }
  /**
   * Change the destination and duration of the animation currently playing
   * @remarks
   * This method updates the Axes animation target position and optionally the duration.
   * @param position - A position to move
   * @param duration - Duration of the animation (unit: ms)
   * @returns The current instance for method chaining
   */
  updateAnimation(position, duration) {
    var _a;
    this._animatingContext = __spreadProps(__spreadValues({}, this._animatingContext), {
      end: position
    });
    (_a = this._axes) == null ? void 0 : _a.updateAnimation({
      destPos: { [POSITION_KEY]: position },
      duration
    });
    return this;
  }
  /**
   * Stops the animation currently playing
   * @remarks
   * This method immediately stops the Axes animation at the current position.
   * @returns The current instance for method chaining
   */
  stopAnimation() {
    var _a;
    (_a = this._axes) == null ? void 0 : _a.stopAnimation();
    return this;
  }
  /**
   * Update {@link https://naver.github.io/egjs-axes/ | @egjs/axes}'s state
   * @remarks
   * This method synchronizes the Axes state with the given control parameters.
   * @param controlParams - Control parameters
   * @throws {@link InitializationErrors}
   * @returns The current instance for method chaining
   */
  update(controlParams) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const axes = this._axes;
    const axis = axes.axis[POSITION_KEY];
    axis.circular = [controlParams.circular, controlParams.circular];
    axis.range = [controlParams.range.min, controlParams.range.max];
    axis.bounce = parseBounce(flicking.bounce, camera.size);
    axes.axisManager.set({ [POSITION_KEY]: controlParams.position });
    return this;
  }
  /**
   * Attach a handler to the camera element to prevent click events during animation
   * @remarks
   * This is used when {@link FlickingOptions.preventClickOnDrag | preventClickOnDrag} is enabled.
   * @returns The current instance for method chaining
   */
  addPreventClickHandler() {
    const flicking = getFlickingAttached(this._flicking);
    const axes = this._axes;
    const cameraEl = flicking.camera.element;
    axes.on(EVENT.HOLD, this._onAxesHold);
    axes.on(EVENT.CHANGE, this._onAxesChange);
    cameraEl.addEventListener("click", this._preventClickWhenDragged, true);
    return this;
  }
  /**
   * Detach a handler to the camera element to prevent click events during animation
   * @remarks
   * This is used when {@link FlickingOptions.preventClickOnDrag | preventClickOnDrag} is disabled.
   * @returns The current instance for method chaining
   */
  removePreventClickHandler() {
    const flicking = getFlickingAttached(this._flicking);
    const axes = this._axes;
    const cameraEl = flicking.camera.element;
    axes.off(EVENT.HOLD, this._onAxesHold);
    axes.off(EVENT.CHANGE, this._onAxesChange);
    cameraEl.removeEventListener("click", this._preventClickWhenDragged, true);
    return this;
  }
  /**
   * Run Axes's {@link https://naver.github.io/egjs-axes/docs/api/Axes#setTo | setTo} using the given position
   * @remarks
   * If the target position equals the current position, the promise resolves immediately without animation.
   * @param position - A position to move
   * @param duration - Duration of the animation (unit: ms)
   * @param axesEvent - If provided, it'll use its {@link https://naver.github.io/egjs-axes/docs/api/Axes#setTo | setTo} method instead
   * @throws {@link MovementErrors}
   * @returns A Promise which will be resolved after reaching the target position
   */
  animateTo(position, duration, axesEvent) {
    var _a;
    const axes = this._axes;
    const state = this._stateMachine.state;
    if (!axes) {
      return Promise.reject(
        new FlickingError(MESSAGE.NOT_ATTACHED_TO_FLICKING, CODE.NOT_ATTACHED_TO_FLICKING)
      );
    }
    const startPos = this.getCurrentPosition();
    if (startPos === position) {
      const flicking = getFlickingAttached(this._flicking);
      flicking.camera.lookAt(position);
      if (state.targetPanel) {
        flicking.control.setActive(state.targetPanel, flicking.control.activePanel, (_a = axesEvent == null ? void 0 : axesEvent.isTrusted) != null ? _a : false);
      }
      return Promise.resolve();
    }
    this._animatingContext = {
      start: startPos,
      end: position,
      offset: 0
    };
    const animate = () => {
      const resetContext = () => {
        this._animatingContext = { start: 0, end: 0, offset: 0 };
      };
      axes.once(EVENT.FINISH, resetContext);
      if (axesEvent) {
        axesEvent.setTo({ [POSITION_KEY]: position }, duration);
      } else {
        axes.setTo({ [POSITION_KEY]: position }, duration);
      }
    };
    return new Promise((resolve, reject) => {
      const animationFinishHandler = () => {
        axes.off(EVENT.HOLD, interruptionHandler);
        resolve();
      };
      const interruptionHandler = () => {
        axes.off(EVENT.FINISH, animationFinishHandler);
        reject(new FlickingError(MESSAGE.ANIMATION_INTERRUPTED, CODE.ANIMATION_INTERRUPTED));
      };
      axes.once(EVENT.FINISH, animationFinishHandler);
      axes.once(EVENT.HOLD, interruptionHandler);
      animate();
    });
  }
  /**
   * Returns the current axes position
   */
  getCurrentPosition() {
    var _a, _b;
    return (_b = (_a = this._axes) == null ? void 0 : _a.get([POSITION_KEY])[POSITION_KEY]) != null ? _b : 0;
  }
  updateDirection() {
    const flicking = getFlickingAttached(this._flicking);
    const axes = this._axes;
    const panInput = this._panInput;
    axes.disconnect(panInput);
    axes.connect(flicking.horizontal ? [POSITION_KEY, ""] : ["", POSITION_KEY], panInput);
    panInput.options.scale = flicking.horizontal ? [flicking.camera.panelOrder === ORDER.RTL ? 1 : -1, 0] : [0, -1];
  }
  /**
   * @internal
   * @privateRemarks
   * Resets all internal values to their defaults. Called during construction and destruction.
   */
  _resetInternalValues() {
    this._flicking = null;
    this._axes = null;
    this._panInput = null;
    this._animatingContext = { start: 0, end: 0, offset: 0 };
    this._dragged = false;
  }
}
class Control {
  /**
   * A controller that handles the {@link https://naver.github.io/egjs-axes/ | @egjs/axes} events
   * @readonly
   */
  get controller() {
    return this._controller;
  }
  /**
   * Index number of the {@link Flicking.currentPanel | currentPanel}
   * @defaultValue 0
   * @readonly
   */
  get activeIndex() {
    var _a, _b;
    return (_b = (_a = this._activePanel) == null ? void 0 : _a.index) != null ? _b : -1;
  }
  /**
   * An active panel
   * @readonly
   */
  get activePanel() {
    return this._activePanel;
  }
  /**
   * Whether Flicking's animating
   * @readonly
   */
  get animating() {
    return this._controller.state.animating;
  }
  /**
   * Whether user is clicking or touching
   * @readonly
   */
  get holding() {
    return this._controller.state.holding;
  }
  constructor() {
    this._flicking = null;
    this._controller = new AxesController();
    this._activePanel = null;
  }
  /**
   * Initialize Control
   * @remarks
   * This method is called automatically during {@link Flicking.init}. It initializes the internal controller.
   * @param flicking - An instance of {@link Flicking}
   * @returns The current instance for method chaining
   */
  init(flicking) {
    this._flicking = flicking;
    this._controller.init(flicking);
    return this;
  }
  /**
   * Destroy Control and return to initial state
   * @remarks
   * This method destroys the internal controller and resets all internal values.
   */
  destroy() {
    this._controller.destroy();
    this._flicking = null;
    this._activePanel = null;
  }
  /**
   * Enable input from the user (mouse/touch)
   * @remarks
   * This is a shorthand for `Flicking.enableInput`.
   * @returns The current instance for method chaining
   */
  enable() {
    this._controller.enable();
    return this;
  }
  /**
   * Disable input from the user (mouse/touch)
   * @remarks
   * This is a shorthand for `Flicking.disableInput`.
   * @returns The current instance for method chaining
   */
  disable() {
    this._controller.disable();
    return this;
  }
  /**
   * Releases ongoing user input (mouse/touch)
   * @remarks
   * This method immediately releases the user's input, similar to the user lifting their finger.
   * @returns The current instance for method chaining
   */
  release() {
    this._controller.release();
    return this;
  }
  /**
   * Change the destination and duration of the animation currently playing
   * @remarks
   * This method does nothing if no animation is currently playing.
   * @param panel - The target panel to move
   * @param duration - Duration of the animation (unit: ms)
   * @param direction - Direction to move, only available in the {@link Flicking.circular | circular} mode
   * @throws {@link AnimationUpdateErrors}
   * @returns The current instance for method chaining
   */
  updateAnimation(panel, duration, direction) {
    const state = this._controller.state;
    const position = this._getPosition(panel, direction != null ? direction : DIRECTION.NONE);
    state.targetPanel = panel;
    this._controller.updateAnimation(position, duration);
    return this;
  }
  /**
   * Stops the animation currently playing
   * @remarks
   * This method does nothing if no animation is currently playing.
   * @returns The current instance for method chaining
   */
  stopAnimation() {
    const state = this._controller.state;
    state.targetPanel = null;
    this._controller.stopAnimation();
    return this;
  }
  /**
   * Update position after resizing
   * @remarks
   * This method moves the camera to the active panel's position after a resize operation.
   * @param progressInPanel - Previous camera's progress in active panel before resize
   * @throws {@link InitializationErrors}
   */
  updatePosition(progressInPanel) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const activePanel = this._activePanel;
    if (activePanel) {
      camera.lookAt(camera.clampToReachablePosition(activePanel.position));
    }
  }
  /**
   * Update {@link Control.controller | controller}'s state
   * @remarks
   * This method synchronizes the controller state with the current camera parameters.
   * @returns The current instance for method chaining
   */
  updateInput() {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    this._controller.update(camera.controlParams);
    return this;
  }
  /**
   * Reset {@link Control.activePanel | activePanel} to `null`
   * @remarks
   * This method is called when the active panel is removed from the renderer.
   * @returns The current instance for method chaining
   */
  resetActive() {
    this._activePanel = null;
    return this;
  }
  /**
   * Move {@link Camera} to the given panel
   * @param panel - The target panel to move
   * @param options - {@link MoveToPanelParams}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns A Promise which will be resolved after reaching the target panel
   */
  moveToPanel(_0, _1) {
    return __async(this, arguments, function* (panel, { duration, direction = DIRECTION.NONE, axesEvent }) {
      const position = this._getPosition(panel, direction);
      this._triggerIndexChangeEvent(panel, panel.position, axesEvent, direction);
      return this._animateToPosition({ position, duration, newActivePanel: panel, axesEvent });
    });
  }
  /**
   * @internal
   * @privateRemarks
   * Sets the active panel and triggers {@link ChangedEvent} or {@link RestoredEvent} based on whether the panel changed.
   */
  setActive(newActivePanel, prevActivePanel, isTrusted) {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    this._activePanel = newActivePanel;
    this._nextPanel = null;
    flicking.camera.updateAdaptiveHeight();
    if (newActivePanel !== prevActivePanel) {
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.CHANGED, {
          index: newActivePanel.index,
          panel: newActivePanel,
          prevIndex: (_a = prevActivePanel == null ? void 0 : prevActivePanel.index) != null ? _a : -1,
          prevPanel: prevActivePanel,
          isTrusted,
          direction: prevActivePanel ? getDirection(prevActivePanel.position, newActivePanel.position) : DIRECTION.NONE
        })
      );
    } else {
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.RESTORED, {
          isTrusted
        })
      );
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Copies internal state from another Control instance. Used when changing moveType option.
   */
  copy(control) {
    this._flicking = control._flicking;
    this._activePanel = control._activePanel;
    this._controller = control._controller;
  }
  /**
   * @internal
   * @privateRemarks
   * Triggers {@link WillChangeEvent} or {@link WillRestoreEvent} based on whether the target panel differs from the active panel.
   */
  _triggerIndexChangeEvent(panel, position, axesEvent, direction) {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    const triggeringEvent = panel !== this._activePanel ? EVENTS.WILL_CHANGE : EVENTS.WILL_RESTORE;
    const camera = flicking.camera;
    const activePanel = this._activePanel;
    const event = new Component.ComponentEvent(triggeringEvent, {
      index: panel.index,
      panel,
      isTrusted: (axesEvent == null ? void 0 : axesEvent.isTrusted) || false,
      direction: direction != null ? direction : getDirection((_a = activePanel == null ? void 0 : activePanel.position) != null ? _a : camera.position, position)
    });
    this._nextPanel = panel;
    flicking.trigger(event);
    if (event.isCanceled()) {
      throw new FlickingError(MESSAGE.STOP_CALLED_BY_USER, CODE.STOP_CALLED_BY_USER);
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Animates the camera to the target position and handles animation completion or interruption.
   */
  _animateToPosition(_0) {
    return __async(this, arguments, function* ({
      position,
      duration,
      newActivePanel,
      axesEvent
    }) {
      const flicking = getFlickingAttached(this._flicking);
      let nextDuration = duration;
      if (Math.abs(nextDuration - position) < flicking.animationThreshold) {
        nextDuration = 0;
      }
      const animate = () => this._controller.animateTo(position, nextDuration, axesEvent);
      const state = this._controller.state;
      state.targetPanel = newActivePanel;
      if (nextDuration <= 0) {
        return animate();
      } else {
        return animate().then(() => __async(this, null, function* () {
          if (flicking.initialized) {
            yield flicking.renderer.render();
          }
        })).catch((err) => {
          if (axesEvent && err instanceof FlickingError && err.code === CODE.ANIMATION_INTERRUPTED) return;
          throw err;
        });
      }
    });
  }
  /**
   * @internal
   * @privateRemarks
   * Calculates the target position for a panel, considering circular mode and direction constraints.
   */
  _getPosition(panel, direction = DIRECTION.NONE) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    let position = panel.position;
    const nearestAnchor = camera.findNearestAnchor(position);
    if (panel.removed || !nearestAnchor) {
      throw new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(panel.position), CODE.POSITION_NOT_REACHABLE);
    }
    if (!camera.canReach(panel)) {
      position = nearestAnchor.position;
      panel = nearestAnchor.panel;
    } else if (flicking.circularEnabled) {
      const camPos = this._controller.position;
      const camRangeDiff = camera.rangeDiff;
      const possiblePositions = [position, position + camRangeDiff, position - camRangeDiff].filter((pos) => {
        if (direction === DIRECTION.NONE) return true;
        return direction === DIRECTION.PREV ? pos <= camPos : pos >= camPos;
      });
      position = possiblePositions.reduce((nearestPosition, pos) => {
        if (Math.abs(camPos - pos) < Math.abs(camPos - nearestPosition)) {
          return pos;
        } else {
          return nearestPosition;
        }
      }, Infinity);
    }
    return position;
  }
}
class FreeControl extends Control {
  /**
   * Make scroll animation to stop at the start/end of the scroll area, not going out the bounce area
   * @defaultValue true
   */
  get stopAtEdge() {
    return this._stopAtEdge;
  }
  set stopAtEdge(val) {
    this._stopAtEdge = val;
  }
  constructor(options = {}) {
    super();
    const { stopAtEdge = true } = options;
    this._stopAtEdge = stopAtEdge;
  }
  /**
   * Update position after resizing
   * @remarks
   * Unlike the base Control, FreeControl preserves the progress within the panel instead of snapping to the panel position.
   * @param progressInPanel - Previous camera's progress in active panel before resize
   * @throws {@link InitializationErrors}
   */
  updatePosition(progressInPanel) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const activePanel = this._activePanel;
    if (activePanel) {
      const panelRange = activePanel.range;
      const newPosition = panelRange.min + (panelRange.max - panelRange.min) * progressInPanel;
      camera.lookAt(camera.clampToReachablePosition(newPosition));
    }
  }
  /**
   * Move {@link Camera} to the given position
   * @remarks
   * Unlike SnapControl, FreeControl moves to the exact position without snapping to panel boundaries.
   * @param position - The target position to move
   * @param duration - Duration of the panel movement animation (unit: ms)
   * @param axesEvent - {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event of {@link https://naver.github.io/egjs-axes/ | Axes}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns A Promise which will be resolved after reaching the target position
   */
  moveToPosition(position, duration, axesEvent) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const targetPos = camera.clampToReachablePosition(position);
    const anchorAtPosition = camera.findAnchorIncludePosition(targetPos);
    if (!anchorAtPosition) {
      return Promise.reject(
        new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
      );
    }
    const targetPanel = anchorAtPosition.panel;
    if (targetPanel !== this._activePanel) {
      this._triggerIndexChangeEvent(targetPanel, position, axesEvent);
    }
    return this._animateToPosition({
      position: this._stopAtEdge ? targetPos : position,
      duration,
      newActivePanel: targetPanel,
      axesEvent
    });
  }
}
class SnapControl extends Control {
  /**
   * Maximum number of panels can go after release
   * @defaultValue Infinity
   */
  get count() {
    return this._count;
  }
  set count(val) {
    this._count = val;
  }
  constructor(options = {}) {
    super();
    const { count = Infinity } = options;
    this._count = count;
  }
  /**
   * Move {@link Camera} to the given position
   * @remarks
   * This method calculates the snap target based on the release momentum and threshold settings.
   * @param position - The target position to move
   * @param duration - Duration of the panel movement animation (unit: ms)
   * @param axesEvent - {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event of {@link https://naver.github.io/egjs-axes/ | Axes}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns A Promise which will be resolved after reaching the target position
   */
  moveToPosition(position, duration, axesEvent) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const activeAnchor = camera.findActiveAnchor();
    const anchorAtCamera = camera.findNearestAnchor(camera.position);
    const state = this._controller.state;
    if (!activeAnchor || !anchorAtCamera) {
      return Promise.reject(
        new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
      );
    }
    const snapThreshold = this._calcSnapThreshold(flicking.threshold, position, activeAnchor);
    const posDelta = flicking.animating ? state.delta : position - camera.position;
    const absPosDelta = Math.abs(posDelta);
    const snapDelta = axesEvent && axesEvent.delta[POSITION_KEY] !== 0 ? Math.abs(axesEvent.delta[POSITION_KEY]) : absPosDelta;
    let targetAnchor;
    if (snapDelta >= snapThreshold && snapDelta > 0) {
      targetAnchor = this._findSnappedAnchor(position, anchorAtCamera);
    } else if (absPosDelta >= flicking.threshold && absPosDelta > 0) {
      targetAnchor = this._findAdjacentAnchor(position, posDelta, anchorAtCamera);
    } else {
      return this.moveToPanel(anchorAtCamera.panel, {
        duration,
        axesEvent
      });
    }
    this._triggerIndexChangeEvent(targetAnchor.panel, position, axesEvent);
    return this._animateToPosition({
      position: camera.clampToReachablePosition(targetAnchor.position),
      duration,
      newActivePanel: targetAnchor.panel,
      axesEvent
    });
  }
  /**
   * @internal
   * @privateRemarks
   * Finds the anchor point to snap to based on the target position and count option.
   */
  _findSnappedAnchor(position, anchorAtCamera) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const count = this._count;
    const currentPos = camera.position;
    const clampedPosition = camera.clampToReachablePosition(position);
    const anchorAtPosition = camera.findAnchorIncludePosition(clampedPosition);
    if (!anchorAtCamera || !anchorAtPosition) {
      throw new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE);
    }
    if (!Number.isFinite(count)) {
      return anchorAtPosition;
    }
    const panelCount = flicking.panelCount;
    const anchors = camera.anchorPoints;
    let loopCount = Math.sign(position - currentPos) * Math.floor(Math.abs(position - currentPos) / camera.rangeDiff);
    if (position > currentPos && anchorAtPosition.index < anchorAtCamera.index || anchorAtPosition.position > anchorAtCamera.position && anchorAtPosition.index === anchorAtCamera.index) {
      loopCount += 1;
    } else if (position < currentPos && anchorAtPosition.index > anchorAtCamera.index || anchorAtPosition.position < anchorAtCamera.position && anchorAtPosition.index === anchorAtCamera.index) {
      loopCount -= 1;
    }
    const circularIndexOffset = loopCount * panelCount;
    const anchorAtPositionIndex = anchorAtPosition.index + circularIndexOffset;
    if (Math.abs(anchorAtPositionIndex - anchorAtCamera.index) <= count) {
      const anchor = anchors[anchorAtPosition.index];
      return new AnchorPoint({
        index: anchor.index,
        position: anchor.position + loopCount * camera.rangeDiff,
        panel: anchor.panel
      });
    }
    if (flicking.circularEnabled) {
      const targetAnchor = anchors[circulateIndex(anchorAtCamera.index + Math.sign(position - currentPos) * count, panelCount)];
      let loop = Math.floor(count / panelCount);
      if (position > currentPos && targetAnchor.index < anchorAtCamera.index) {
        loop += 1;
      } else if (position < currentPos && targetAnchor.index > anchorAtCamera.index) {
        loop -= 1;
      }
      return new AnchorPoint({
        index: targetAnchor.index,
        position: targetAnchor.position + loop * camera.rangeDiff,
        panel: targetAnchor.panel
      });
    } else {
      return anchors[clamp(anchorAtCamera.index + Math.sign(position - currentPos) * count, 0, anchors.length - 1)];
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Finds the adjacent anchor point based on the movement direction.
   */
  _findAdjacentAnchor(position, posDelta, anchorAtCamera) {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    if (camera.circularEnabled) {
      const anchorIncludePosition = camera.findAnchorIncludePosition(position);
      if (anchorIncludePosition && anchorIncludePosition.position !== anchorAtCamera.position) {
        return anchorIncludePosition;
      }
    }
    const adjacentAnchor = (_a = posDelta > 0 ? camera.getNextAnchor(anchorAtCamera) : camera.getPrevAnchor(anchorAtCamera)) != null ? _a : anchorAtCamera;
    return adjacentAnchor;
  }
  /**
   * @internal
   * @privateRemarks
   * Calculates the snap threshold based on the panel size and alignment.
   */
  _calcSnapThreshold(threshold, position, activeAnchor) {
    const isNextDirection = position > activeAnchor.position;
    const panel = activeAnchor.panel;
    const panelSize = panel.size;
    const alignPos = panel.alignPosition;
    return Math.max(
      threshold,
      isNextDirection ? panelSize - alignPos + panel.margin.next : alignPos + panel.margin.prev
    );
  }
}
class StrictControl extends Control {
  constructor(options = {}) {
    super();
    this.setActive = (newActivePanel, prevActivePanel, isTrusted) => {
      super.setActive(newActivePanel, prevActivePanel, isTrusted);
      this.updateInput();
    };
    const { count = 1 } = options;
    this._count = count;
    this._resetIndexRange();
  }
  /**
   * Maximum number of panels that can be moved at a time
   * @defaultValue 1
   */
  get count() {
    return this._count;
  }
  set count(val) {
    this._count = val;
  }
  /**
   * Destroy Control and return to initial state
   * @remarks
   * This method also resets the index range used for movement constraints.
   */
  destroy() {
    super.destroy();
    this._resetIndexRange();
  }
  /**
   * Update {@link Control.controller | controller}'s state
   * @remarks
   * StrictControl limits the movement range based on the {@link StrictControlOptions.count | count} option.
   * @returns The current instance for method chaining
   */
  updateInput() {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const renderer = flicking.renderer;
    const controller = this._controller;
    const controlParams = camera.controlParams;
    const count = this._count;
    const activePanel = controller.state.animating ? (_a = camera.findNearestAnchor(camera.position)) == null ? void 0 : _a.panel : this._activePanel;
    if (!activePanel) {
      controller.update(controlParams);
      this._resetIndexRange();
      return this;
    }
    const cameraRange = controlParams.range;
    const currentPos = activePanel.position;
    const currentIndex = activePanel.index;
    const panelCount = renderer.panelCount;
    let prevPanelIndex = currentIndex - count;
    let nextPanelIndex = currentIndex + count;
    if (prevPanelIndex < 0) {
      prevPanelIndex = flicking.circularEnabled ? getMinusCompensatedIndex((prevPanelIndex + 1) % panelCount - 1, panelCount) : clamp(prevPanelIndex, 0, panelCount - 1);
    }
    if (nextPanelIndex >= panelCount) {
      nextPanelIndex = flicking.circularEnabled ? nextPanelIndex % panelCount : clamp(nextPanelIndex, 0, panelCount - 1);
    }
    const prevPanel = renderer.panels[prevPanelIndex];
    const nextPanel = renderer.panels[nextPanelIndex];
    let prevPos = Math.max(prevPanel.position, cameraRange.min);
    let nextPos = Math.min(nextPanel.position, cameraRange.max);
    if (prevPos > currentPos) {
      prevPos -= camera.rangeDiff;
    }
    if (nextPos < currentPos) {
      nextPos += camera.rangeDiff;
    }
    controlParams.range = {
      min: prevPos,
      max: nextPos
    };
    if (controlParams.circular) {
      if (controlParams.position < prevPos) {
        controlParams.position += camera.rangeDiff;
      }
      if (controlParams.position > nextPos) {
        controlParams.position -= camera.rangeDiff;
      }
    }
    controlParams.circular = false;
    controller.update(controlParams);
    this._indexRange = {
      min: prevPanel.index,
      max: nextPanel.index
    };
    return this;
  }
  moveToPanel(panel, options) {
    return __async(this, null, function* () {
      const flicking = getFlickingAttached(this._flicking);
      const camera = flicking.camera;
      const controller = this._controller;
      controller.update(camera.controlParams);
      return __superGet(StrictControl.prototype, this, "moveToPanel").call(this, panel, options);
    });
  }
  /**
   * Move {@link Camera} to the given position
   * @remarks
   * StrictControl restricts movement to panels within the allowed index range based on the count option.
   * @param position - The target position to move
   * @param duration - Duration of the panel movement animation (unit: ms)
   * @param axesEvent - {@link https://naver.github.io/egjs-axes/docs/api/Axes#event-release | release} event of {@link https://naver.github.io/egjs-axes/ | Axes}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns A Promise which will be resolved after reaching the target position
   */
  moveToPosition(position, duration, axesEvent) {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const currentPanel = (_a = this._nextPanel) != null ? _a : this._activePanel;
    const axesRange = this._controller.range;
    const indexRange = this._indexRange;
    const cameraRange = camera.range;
    const state = this._controller.state;
    const clampedPosition = clamp(camera.clampToReachablePosition(position), axesRange[0], axesRange[1]);
    const anchorAtPosition = camera.findAnchorIncludePosition(clampedPosition);
    if (!anchorAtPosition || !currentPanel) {
      return Promise.reject(
        new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
      );
    }
    const prevPos = currentPanel.position;
    const posDelta = flicking.animating ? state.delta : position - camera.position;
    const isOverThreshold = Math.abs(posDelta) >= flicking.threshold;
    const adjacentAnchor = position > prevPos ? camera.getNextAnchor(anchorAtPosition) : camera.getPrevAnchor(anchorAtPosition);
    let targetPos;
    let targetPanel;
    const anchors = camera.anchorPoints;
    const firstAnchor = anchors[0];
    const lastAnchor = anchors[anchors.length - 1];
    const shouldBounceToFirst = position < cameraRange.min && isBetween(firstAnchor.panel.index, indexRange.min, indexRange.max);
    const shouldBounceToLast = position > cameraRange.max && isBetween(lastAnchor.panel.index, indexRange.min, indexRange.max);
    const isAdjacent = adjacentAnchor && (indexRange.min <= indexRange.max ? isBetween(adjacentAnchor.index, indexRange.min, indexRange.max) : adjacentAnchor.index >= indexRange.min || adjacentAnchor.index <= indexRange.max);
    if (shouldBounceToFirst || shouldBounceToLast) {
      const targetAnchor = position < cameraRange.min ? firstAnchor : lastAnchor;
      targetPanel = targetAnchor.panel;
      targetPos = targetAnchor.position;
    } else if (isOverThreshold && anchorAtPosition.position !== currentPanel.position) {
      targetPanel = anchorAtPosition.panel;
      targetPos = anchorAtPosition.position;
    } else if (isOverThreshold && isAdjacent) {
      targetPanel = adjacentAnchor.panel;
      targetPos = adjacentAnchor.position;
    } else {
      const anchorAtCamera = camera.findNearestAnchor(camera.position);
      if (!anchorAtCamera) {
        return Promise.reject(
          new FlickingError(MESSAGE.POSITION_NOT_REACHABLE(position), CODE.POSITION_NOT_REACHABLE)
        );
      }
      return this.moveToPanel(anchorAtCamera.panel, {
        duration,
        axesEvent
      });
    }
    this._triggerIndexChangeEvent(targetPanel, position, axesEvent);
    return this._animateToPosition({
      position: targetPos,
      duration,
      newActivePanel: targetPanel,
      axesEvent
    });
  }
  /**
   * @internal
   * @privateRemarks
   * Resets the index range to default values.
   */
  _resetIndexRange() {
    this._indexRange = { min: 0, max: 0 };
  }
}
class AutoResizer {
  /**
   * @param flicking
   */
  constructor(flicking) {
    this._onResizeWrapper = () => {
      this._onResize([]);
    };
    this._onResize = (entries) => {
      const flicking2 = this._flicking;
      const resizeDebounce = flicking2.resizeDebounce;
      const maxResizeDebounce = flicking2.maxResizeDebounce;
      const resizedViewportElement = flicking2.element;
      const isResizedViewportOnly = entries.find((e) => e.target === flicking2.element) && entries.length === 1;
      if (isResizedViewportOnly) {
        const beforeSize = {
          width: flicking2.viewport.width,
          height: flicking2.viewport.height
        };
        const afterSize = {
          width: getElementSize({
            el: resizedViewportElement,
            horizontal: true,
            useFractionalSize: this._flicking.useFractionalSize,
            useOffset: false,
            style: getStyle(resizedViewportElement)
          }),
          height: getElementSize({
            el: resizedViewportElement,
            horizontal: false,
            useFractionalSize: this._flicking.useFractionalSize,
            useOffset: false,
            style: getStyle(resizedViewportElement)
          })
        };
        if (beforeSize.height === afterSize.height && beforeSize.width === afterSize.width) {
          return;
        }
      }
      if (resizeDebounce <= 0) {
        void flicking2.resize();
      } else {
        if (this._maxResizeDebounceTimer <= 0) {
          if (maxResizeDebounce > 0 && maxResizeDebounce >= resizeDebounce) {
            this._maxResizeDebounceTimer = window.setTimeout(this._doScheduledResize, maxResizeDebounce);
          }
        }
        if (this._resizeTimer > 0) {
          clearTimeout(this._resizeTimer);
          this._resizeTimer = 0;
        }
        this._resizeTimer = window.setTimeout(this._doScheduledResize, resizeDebounce);
      }
    };
    this._doScheduledResize = () => {
      clearTimeout(this._resizeTimer);
      clearTimeout(this._maxResizeDebounceTimer);
      this._maxResizeDebounceTimer = -1;
      this._resizeTimer = -1;
      void this._flicking.resize();
    };
    this._skipFirstResize = /* @__PURE__ */ (() => {
      let isFirstResize = true;
      return (entries) => {
        if (isFirstResize) {
          isFirstResize = false;
          return;
        }
        this._onResize(entries);
      };
    })();
    this._flicking = flicking;
    this._enabled = false;
    this._resizeObserver = null;
    this._resizeTimer = -1;
    this._maxResizeDebounceTimer = -1;
  }
  get enabled() {
    return this._enabled;
  }
  enable() {
    const flicking = this._flicking;
    const viewport = flicking.viewport;
    if (this._enabled) {
      this.disable();
    }
    if (flicking.useResizeObserver && !!window.ResizeObserver) {
      const viewportSizeNot0 = viewport.width !== 0 || viewport.height !== 0;
      const resizeObserver = viewportSizeNot0 ? new ResizeObserver(this._skipFirstResize) : new ResizeObserver(this._onResize);
      this._resizeObserver = resizeObserver;
      this.observe(flicking.viewport.element);
      if (flicking.observePanelResize) {
        this.observePanels();
      }
    } else {
      window.addEventListener("resize", this._onResizeWrapper);
    }
    this._enabled = true;
    return this;
  }
  observePanels() {
    this._flicking.panels.forEach((panel) => {
      this.observe(panel.element);
    });
    return this;
  }
  unobservePanels() {
    this._flicking.panels.forEach((panel) => {
      this.unobserve(panel.element);
    });
    return this;
  }
  observe(element) {
    const resizeObserver = this._resizeObserver;
    if (!resizeObserver) return this;
    resizeObserver.observe(element);
    return this;
  }
  unobserve(element) {
    const resizeObserver = this._resizeObserver;
    if (!resizeObserver) return this;
    resizeObserver.unobserve(element);
    if (this._flicking.observePanelResize) {
      this.unobservePanels();
    }
    return this;
  }
  disable() {
    if (!this._enabled) return this;
    const resizeObserver = this._resizeObserver;
    if (resizeObserver) {
      resizeObserver.disconnect();
      this._resizeObserver = null;
    } else {
      window.removeEventListener("resize", this._onResizeWrapper);
    }
    this._enabled = false;
    return this;
  }
}
class VanillaElementProvider {
  get element() {
    return this._element;
  }
  get rendered() {
    return this._rendered;
  }
  constructor(element) {
    this._element = element;
    this._rendered = true;
  }
  show(flicking) {
    const el = this.element;
    const cameraEl = flicking.camera.element;
    if (el.parentElement !== cameraEl) {
      cameraEl.appendChild(el);
      this._rendered = true;
    }
  }
  hide(flicking) {
    const el = this.element;
    const cameraEl = flicking.camera.element;
    if (el.parentElement === cameraEl) {
      cameraEl.removeChild(el);
      this._rendered = false;
    }
  }
}
class VirtualElementProvider {
  get element() {
    return this._virtualElement.nativeElement;
  }
  get rendered() {
    return this._virtualElement.visible;
  }
  get _virtualElement() {
    const flicking = this._flicking;
    const elIndex = this._panel.elementIndex;
    const virtualElements = flicking.virtual.elements;
    return virtualElements[elIndex];
  }
  constructor(flicking) {
    this._flicking = flicking;
  }
  init(panel) {
    this._panel = panel;
  }
  show() {
  }
  hide() {
  }
}
class Viewport {
  /**
   * A viewport(root) element
   * @readonly
   */
  get element() {
    return this._el;
  }
  /**
   * Viewport width, without paddings
   * @readonly
   */
  get width() {
    return this._width - this._padding.left - this._padding.right;
  }
  /**
   * Viewport height, without paddings
   * @readonly
   */
  get height() {
    return this._height - this._padding.top - this._padding.bottom;
  }
  /**
   * Viewport paddings
   * @readonly
   */
  get padding() {
    return this._padding;
  }
  /**
   * @param flicking - Flicking instance
   * @param el - A viewport element
   */
  constructor(flicking, el) {
    this._flicking = flicking;
    this._el = el;
    this._width = 0;
    this._height = 0;
    this._padding = {
      left: 0,
      right: 0,
      top: 0,
      bottom: 0
    };
    this._isBorderBoxSizing = false;
  }
  /**
   * Change viewport's size.
   * @remarks
   * This will change the actual size of `.flicking-viewport` element by changing its CSS width/height property
   * @param size - {@link SetSizeParams}
   */
  setSize(size) {
    const { width, height } = size;
    const el = this._el;
    const padding = this._padding;
    const isBorderBoxSizing = this._isBorderBoxSizing;
    if (width != null) {
      if (isString(width)) {
        el.style.width = width;
      } else {
        const newWidth = isBorderBoxSizing ? width + padding.left + padding.right : width;
        el.style.width = `${newWidth}px`;
      }
    }
    if (height != null) {
      if (isString(height)) {
        el.style.height = height;
      } else {
        const newHeight = isBorderBoxSizing ? height + padding.top + padding.bottom : height;
        el.style.height = `${newHeight}px`;
      }
    }
    this.resize();
  }
  /**
   * Measure the current size of the viewport element without updating {@link Viewport.width | width}/{@link Viewport.height | height}
   * @internal
   * @privateRemarks
   * Used inside {@link Flicking.resize} to determine whether the panels should be re-rendered (optimizeSizeUpdate)
   * while keeping the previous viewport size until the re-rendering finishes.
   * The new size is committed afterwards by calling {@link Viewport.resize | resize()}.
   * @returns Current width/height of the viewport element, without paddings
   */
  measureSize() {
    const { width, height, padding } = this._measure();
    return {
      width: width - padding.left - padding.right,
      height: height - padding.top - padding.bottom
    };
  }
  /**
   * Update width/height to the current viewport element's size
   */
  resize() {
    const { width, height, padding, isBorderBoxSizing } = this._measure();
    this._width = width;
    this._height = height;
    this._padding = padding;
    this._isBorderBoxSizing = isBorderBoxSizing;
  }
  _measure() {
    const el = this._el;
    const elStyle = getStyle(el);
    const { useFractionalSize } = this._flicking;
    return {
      width: getElementSize({
        el,
        horizontal: true,
        useFractionalSize,
        useOffset: false,
        style: elStyle
      }),
      height: getElementSize({
        el,
        horizontal: false,
        useFractionalSize,
        useOffset: false,
        style: elStyle
      }),
      padding: {
        left: elStyle.paddingLeft ? parseFloat(elStyle.paddingLeft) : 0,
        right: elStyle.paddingRight ? parseFloat(elStyle.paddingRight) : 0,
        top: elStyle.paddingTop ? parseFloat(elStyle.paddingTop) : 0,
        bottom: elStyle.paddingBottom ? parseFloat(elStyle.paddingBottom) : 0
      },
      isBorderBoxSizing: elStyle.boxSizing === "border-box"
    };
  }
}
class VirtualManager {
  get elements() {
    return this._elements;
  }
  // Options
  /**
   * A rendering function for the panel element's innerHTML
   */
  get renderPanel() {
    return this._renderPanel;
  }
  /**
   * Initial panel count to render
   * @readonly
   * @defaultValue -1
   */
  get initialPanelCount() {
    return this._initialPanelCount;
  }
  /**
   * Whether to cache rendered panel's innerHTML
   * @defaultValue false
   */
  get cache() {
    return this._cache;
  }
  /**
   * The class name that will be applied to rendered panel elements
   * @defaultValue "flicking-panel"
   */
  get panelClass() {
    return this._panelClass;
  }
  set renderPanel(val) {
    this._renderPanel = val;
    this._flicking.renderer.panels.forEach((panel) => panel.uncacheRenderResult());
  }
  set cache(val) {
    this._cache = val;
  }
  set panelClass(val) {
    this._panelClass = val;
  }
  constructor(flicking, options) {
    var _a, _b, _c, _d;
    this._flicking = flicking;
    this._renderPanel = (_a = options == null ? void 0 : options.renderPanel) != null ? _a : (() => "");
    this._initialPanelCount = (_b = options == null ? void 0 : options.initialPanelCount) != null ? _b : -1;
    this._cache = (_c = options == null ? void 0 : options.cache) != null ? _c : false;
    this._panelClass = (_d = options == null ? void 0 : options.panelClass) != null ? _d : CLASS.DEFAULT_VIRTUAL;
    this._elements = [];
  }
  init() {
    const flicking = this._flicking;
    if (!flicking.virtualEnabled) return;
    if (!flicking.externalRenderer && !flicking.renderExternal) {
      this._initVirtualElements();
    }
    const virtualElements = flicking.camera.children;
    this._elements = virtualElements.map((el) => ({ nativeElement: el, visible: true }));
  }
  show(index) {
    const el = this._elements[index];
    const nativeEl = el.nativeElement;
    el.visible = true;
    if (nativeEl.style.display) {
      nativeEl.style.display = "";
    }
  }
  hide(index) {
    const el = this._elements[index];
    const nativeEl = el.nativeElement;
    el.visible = false;
    nativeEl.style.display = "none";
  }
  /**
   * Add new virtual panels at the end of the list
   * @param count - The number of panels to add
   * @returns The new panels added
   */
  append(count = 1) {
    const flicking = this._flicking;
    return this.insert(flicking.panels.length, count);
  }
  /**
   * Add new virtual panels at the start of the list
   * @param count - The number of panels to add
   * @returns The new panels added
   */
  prepend(count = 1) {
    return this.insert(0, count);
  }
  /**
   * Add new virtual panels at the given index
   * @param count - The number of panels to add
   * @returns The new panels added
   */
  insert(index, count = 1) {
    if (count <= 0) return [];
    const flicking = this._flicking;
    return flicking.renderer.batchInsert({ index, elements: range(count), hasDOMInElements: false });
  }
  /**
   * Remove panels at the given index
   * @param count - The number of panels to remove
   * @returns The panels removed
   */
  remove(index, count) {
    if (count <= 0) return [];
    const flicking = this._flicking;
    return flicking.renderer.batchRemove({ index, deleteCount: count, hasDOMInElements: false });
  }
  /**
   * @internal
   */
  _initVirtualElements() {
    const flicking = this._flicking;
    const cameraElement = flicking.camera.element;
    const panelsPerView = flicking.panelsPerView;
    const fragment = document.createDocumentFragment();
    const newElements = range(panelsPerView + 1).map((idx) => {
      const panelEl = document.createElement("div");
      panelEl.className = this._panelClass;
      panelEl.dataset.elementIndex = idx.toString();
      return panelEl;
    });
    newElements.forEach((el) => {
      fragment.appendChild(el);
    });
    cameraElement.appendChild(fragment);
  }
}
class Renderer {
  // Internal states Getter
  /**
   * Array of panels
   * @readonly
   * @see Panel
   */
  get panels() {
    return this._panels;
  }
  /**
   * A boolean value indicating whether rendering is in progress
   * @readonly
   * @internal
   */
  get rendering() {
    return this._rendering;
  }
  /**
   * Count of panels
   * @readonly
   */
  get panelCount() {
    return this._panels.length;
  }
  /**
   * @internal
   */
  get strategy() {
    return this._strategy;
  }
  // Options Getter
  /**
   * A {@link Panel}'s {@link Panel.align | align} value that applied to all panels
   */
  get align() {
    return this._align;
  }
  // Options Setter
  set align(val) {
    this._align = val;
    const panelAlign = parsePanelAlign(val);
    this._panels.forEach((panel) => {
      panel.align = panelAlign;
    });
  }
  /**
   * @param options - {@link RendererOptions}
   */
  constructor(options) {
    const { align = ALIGN.CENTER, strategy } = options;
    this._flicking = null;
    this._panels = [];
    this._rendering = false;
    this._align = align;
    this._strategy = strategy;
  }
  /**
   * Initialize Renderer
   * @remarks
   * This method is called automatically during {@link Flicking.init}. It collects existing panel elements.
   * @param flicking - An instance of {@link Flicking}
   * @returns The current instance for method chaining
   */
  init(flicking) {
    this._flicking = flicking;
    this._collectPanels();
    return this;
  }
  /**
   * Destroy Renderer and return to initial state
   * @remarks
   * This method clears all panel references and resets the internal state.
   */
  destroy() {
    this._flicking = null;
    this._panels = [];
  }
  /**
   * Return the {@link Panel} at the given index. `null` if it doesn't exist.
   * @remarks
   * This is equivalent to accessing `Flicking.panels[index]`.
   * @param index - Index of the panel to get
   * @returns Panel at the given index
   * @see Panel
   */
  getPanel(index) {
    return this._panels[index] || null;
  }
  forceRenderAllPanels() {
    this._panels.forEach((panel) => panel.markForShow());
    return Promise.resolve();
  }
  /**
   * Return Rendered Panels
   * @returns Rendered Panels
   */
  getRenderedPanels() {
    const flicking = getFlickingAttached(this._flicking);
    return flicking.renderer.panels.filter((panel) => panel.rendered);
  }
  /**
   * Update all panel sizes
   * @returns The current instance for method chaining
   */
  updatePanelSize() {
    const flicking = getFlickingAttached(this._flicking);
    const panels = this._panels;
    if (panels.length <= 0) return this;
    if (flicking.panelsPerView > 0) {
      const firstPanel = panels[0];
      firstPanel.resize();
      this._updatePanelSizeByGrid(firstPanel, panels);
    } else {
      flicking.panels.forEach((panel) => panel.resize());
    }
    return this;
  }
  /**
   * Insert new panels at given index
   * @remarks
   * This will increase index of panels after by the number of panels added.
   * @param items - An array of {@link BatchInsertParams}
   * @throws {@link DOMManipulationErrors}
   * @returns An array of inserted panels
   */
  batchInsert(...items) {
    const allPanelsInserted = this.batchInsertDefer(...items);
    if (allPanelsInserted.length <= 0) return [];
    this.updateAfterPanelChange(allPanelsInserted, []);
    return allPanelsInserted;
  }
  /**
   * @internal
   * @privateRemarks
   * Defers update. Camera position & others will be updated after calling updateAfterPanelChange.
   */
  batchInsertDefer(...items) {
    const panels = this._panels;
    const flicking = getFlickingAttached(this._flicking);
    const prevFirstPanel = panels[0];
    const align = parsePanelAlign(this._align);
    const allPanelsInserted = items.reduce((addedPanels, item) => {
      var _a;
      const insertingIdx = getMinusCompensatedIndex(item.index, panels.length);
      const panelsPushed = panels.slice(insertingIdx);
      const panelsInserted = item.elements.map(
        (el, idx) => this._createPanel(el, { index: insertingIdx + idx, align, flicking })
      );
      panels.splice(insertingIdx, 0, ...panelsInserted);
      if (item.hasDOMInElements) {
        this._insertPanelElements(panelsInserted, (_a = panelsPushed[0]) != null ? _a : null);
      }
      if (flicking.panelsPerView > 0) {
        const firstPanel = prevFirstPanel || panelsInserted[0].resize();
        this._updatePanelSizeByGrid(firstPanel, panelsInserted);
      } else {
        panelsInserted.forEach((panel) => panel.resize());
      }
      panelsPushed.forEach((panel) => {
        panel.increaseIndex(panelsInserted.length);
        panel.updatePosition();
      });
      return [...addedPanels, ...panelsInserted];
    }, []);
    return allPanelsInserted;
  }
  /**
   * Remove the panel at the given index
   * @remarks
   * This will decrease index of panels after by the number of panels removed.
   * @param items - An array of {@link BatchRemoveParams}
   * @throws {@link DOMManipulationErrors}
   * @returns An array of removed panels
   */
  batchRemove(...items) {
    const allPanelsRemoved = this.batchRemoveDefer(...items);
    if (allPanelsRemoved.length <= 0) return [];
    this.updateAfterPanelChange([], allPanelsRemoved);
    return allPanelsRemoved;
  }
  /**
   * @internal
   * @privateRemarks
   * Defers update. Camera position & others will be updated after calling updateAfterPanelChange.
   */
  batchRemoveDefer(...items) {
    const panels = this._panels;
    const flicking = getFlickingAttached(this._flicking);
    const { control } = flicking;
    const activePanel = control.activePanel;
    const allPanelsRemoved = items.reduce((removed, item) => {
      const { index, deleteCount } = item;
      const removingIdx = getMinusCompensatedIndex(index, panels.length);
      const panelsPulled = panels.slice(removingIdx + deleteCount);
      const panelsRemoved = panels.splice(removingIdx, deleteCount);
      if (panelsRemoved.length <= 0) return [];
      panelsPulled.forEach((panel) => {
        panel.decreaseIndex(panelsRemoved.length);
        panel.updatePosition();
      });
      if (item.hasDOMInElements) {
        this._removePanelElements(panelsRemoved);
      }
      panelsRemoved.forEach((panel) => panel.destroy());
      if (includes(panelsRemoved, activePanel)) {
        control.resetActive();
      }
      return [...removed, ...panelsRemoved];
    }, []);
    return allPanelsRemoved;
  }
  /**
   * @internal
   * @privateRemarks
   * Updates camera, control, and triggers {@link PanelChangeEvent} after panels are added or removed.
   */
  updateAfterPanelChange(panelsAdded, panelsRemoved) {
    var _a;
    const flicking = getFlickingAttached(this._flicking);
    const { camera, control } = flicking;
    const panels = this._panels;
    const activePanel = control.activePanel;
    this._updateCameraAndControl();
    if (flicking.autoResize && flicking.useResizeObserver) {
      panelsAdded.forEach((panel) => {
        if (panel.element) {
          flicking.autoResizer.observe(panel.element);
        }
      });
      panelsRemoved.forEach((panel) => {
        if (panel.element) {
          flicking.autoResizer.unobserve(panel.element);
        }
      });
    }
    void this.render();
    if (!flicking.animating) {
      if (!activePanel || activePanel.removed) {
        if (panels.length <= 0) {
          camera.lookAt(0);
        } else {
          let targetIndex = (_a = activePanel == null ? void 0 : activePanel.index) != null ? _a : 0;
          if (targetIndex > panels.length - 1) {
            targetIndex = panels.length - 1;
          }
          void control.moveToPanel(panels[targetIndex], {
            duration: 0
          }).catch(() => void 0);
        }
      } else {
        void control.moveToPanel(activePanel, {
          duration: 0
        }).catch(() => void 0);
      }
    }
    flicking.camera.updateOffset();
    if (panelsAdded.length > 0 || panelsRemoved.length > 0) {
      flicking.trigger(
        new Component.ComponentEvent(EVENTS.PANEL_CHANGE, {
          added: panelsAdded,
          removed: panelsRemoved
        })
      );
      this.checkPanelContentsReady([...panelsAdded, ...panelsRemoved]);
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Checks if panel contents (images/videos) are ready and triggers resize when loaded.
   */
  checkPanelContentsReady(checkingPanels) {
    const flicking = getFlickingAttached(this._flicking);
    const resizeOnContentsReady = flicking.resizeOnContentsReady;
    const panels = this._panels;
    if (!resizeOnContentsReady || flicking.virtualEnabled) return;
    const hasContents = (panel) => panel.element && !!panel.element.querySelector("img, video");
    checkingPanels = checkingPanels.filter((panel) => hasContents(panel));
    if (checkingPanels.length <= 0) return;
    const contentsReadyChecker = new ImReady();
    checkingPanels.forEach((panel) => {
      panel.loading = true;
    });
    contentsReadyChecker.on("readyElement", (e) => {
      if (!this._flicking) {
        contentsReadyChecker.destroy();
        return;
      }
      const panel = checkingPanels[e.index];
      const camera = flicking.camera;
      const control = flicking.control;
      const prevProgressInPanel = control.activePanel ? camera.getProgressInPanel(control.activePanel) : 0;
      panel.loading = false;
      panel.resize();
      panels.slice(panel.index + 1).forEach((panelBehind) => panelBehind.updatePosition());
      if (!flicking.initialized) return;
      camera.updateRange();
      camera.updateOffset();
      camera.updateAnchors();
      if (control.animating) ;
      else {
        control.updatePosition(prevProgressInPanel);
        control.updateInput();
      }
    });
    contentsReadyChecker.on("preReady", (e) => {
      if (this._flicking) {
        void this.render();
      }
      if (e.readyCount === e.totalCount) {
        contentsReadyChecker.destroy();
      }
    });
    contentsReadyChecker.on("ready", () => {
      if (this._flicking) {
        void this.render();
      }
      contentsReadyChecker.destroy();
    });
    contentsReadyChecker.check(checkingPanels.map((panel) => panel.element));
  }
  /**
   * @internal
   * @privateRemarks
   * Updates camera range, anchors, and control input after panel changes.
   */
  _updateCameraAndControl() {
    const flicking = getFlickingAttached(this._flicking);
    const { camera, control } = flicking;
    camera.updateRange();
    camera.updateOffset();
    camera.updateAnchors();
    camera.resetNeedPanelHistory();
    control.updateInput();
  }
  /**
   * @internal
   * @privateRemarks
   * Marks only visible panels for rendering when {@link FlickingOptions.renderOnlyVisible | renderOnlyVisible} is enabled.
   */
  _showOnlyVisiblePanels(flicking) {
    const panels = flicking.renderer.panels;
    const camera = flicking.camera;
    const visibleIndexes = camera.visiblePanels.reduce((visibles, panel) => {
      visibles[panel.index] = true;
      return visibles;
    }, {});
    panels.forEach((panel) => {
      if (panel.index in visibleIndexes || panel.loading) {
        panel.markForShow();
      } else if (!flicking.holding) {
        panel.markForHide();
      }
    });
  }
  /**
   * @internal
   * @privateRemarks
   * Calculates and applies panel sizes when {@link FlickingOptions.panelsPerView | panelsPerView} is enabled.
   */
  _updatePanelSizeByGrid(referencePanel, panels) {
    const flicking = getFlickingAttached(this._flicking);
    const panelsPerView = flicking.panelsPerView;
    if (panelsPerView <= 0) {
      throw new FlickingError(MESSAGE.WRONG_OPTION("panelsPerView", panelsPerView), CODE.WRONG_OPTION);
    }
    if (panels.length <= 0) return;
    const viewportSize = flicking.camera.size;
    const gap = referencePanel.margin.prev + referencePanel.margin.next;
    const panelSize = (viewportSize - gap * (panelsPerView - 1)) / panelsPerView;
    const panelSizeObj = flicking.horizontal ? { width: panelSize } : { height: panelSize };
    const firstPanelSizeObj = __spreadValues({
      size: panelSize,
      margin: referencePanel.margin
    }, !flicking.horizontal && { height: referencePanel.height });
    if (!flicking.noPanelStyleOverride) {
      this._strategy.updatePanelSizes(flicking, panelSizeObj);
    }
    flicking.panels.forEach((panel) => panel.resize(firstPanelSizeObj));
  }
  /**
   * @internal
   * @privateRemarks
   * Removes all child elements from the camera element.
   */
  _removeAllChildsFromCamera() {
    const flicking = getFlickingAttached(this._flicking);
    const cameraElement = flicking.camera.element;
    while (cameraElement.firstChild) {
      cameraElement.removeChild(cameraElement.firstChild);
    }
  }
  /**
   * @internal
   * @privateRemarks
   * Inserts panel elements into the camera element at the specified position.
   */
  _insertPanelElements(panels, nextSibling = null) {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    const cameraElement = camera.element;
    const nextSiblingElement = (nextSibling == null ? void 0 : nextSibling.element) || null;
    const fragment = document.createDocumentFragment();
    panels.forEach((panel) => fragment.appendChild(panel.element));
    const referenceElement = nextSiblingElement && nextSiblingElement.parentNode === cameraElement ? nextSiblingElement : null;
    cameraElement.insertBefore(fragment, referenceElement);
  }
  /**
   * @internal
   * @privateRemarks
   * Removes panel elements from the camera element.
   */
  _removePanelElements(panels) {
    const flicking = getFlickingAttached(this._flicking);
    const cameraElement = flicking.camera.element;
    panels.forEach((panel) => {
      if (panel.element.parentNode === cameraElement) {
        cameraElement.removeChild(panel.element);
      }
    });
  }
  /**
   * @internal
   * @privateRemarks
   * Called after rendering to apply the camera transform.
   */
  _afterRender() {
    const flicking = getFlickingAttached(this._flicking);
    flicking.camera.applyTransform();
    if (flicking.useCSSOrder) {
      const panels = flicking.panels;
      this._strategy.getRenderingIndexesByOrder(flicking).forEach((domIndex, index) => {
        var _a;
        if ((_a = panels[domIndex]) == null ? void 0 : _a.element) {
          panels[domIndex].element.style.order = `${index}`;
        }
      });
    }
  }
}
class ExternalRenderer extends Renderer {
  /* eslint-disable @typescript-eslint/no-unused-vars */
  _removePanelElements(panels) {
  }
  _removeAllChildsFromCamera() {
  }
  /* eslint-enable @typescript-eslint/no-unused-vars */
}
class VanillaRenderer extends Renderer {
  // eslint-disable-next-line @typescript-eslint/require-await
  render() {
    return __async(this, null, function* () {
      const flicking = getFlickingAttached(this._flicking);
      const strategy = this._strategy;
      strategy.updateRenderingPanels(flicking);
      strategy.renderPanels(flicking);
      this._resetPanelElementOrder();
      this._afterRender();
    });
  }
  _collectPanels() {
    const flicking = getFlickingAttached(this._flicking);
    const camera = flicking.camera;
    this._removeAllTextNodes();
    this._panels = this._strategy.collectPanels(flicking, camera.children);
  }
  _createPanel(el, options) {
    return this._strategy.createPanel(el, options);
  }
  /**
   * @internal
   */
  _resetPanelElementOrder() {
    const flicking = getFlickingAttached(this._flicking);
    const cameraEl = flicking.camera.element;
    let reversedElements = [];
    if (flicking.useCSSOrder) {
      reversedElements = this.getRenderedPanels().map((panel) => panel.element).reverse();
    } else {
      reversedElements = this._strategy.getRenderingElementsByOrder(flicking).reverse();
    }
    reversedElements.forEach((el, idx) => {
      const nextEl = reversedElements[idx - 1] ? reversedElements[idx - 1] : null;
      if (el.nextElementSibling !== nextEl) {
        cameraEl.insertBefore(el, nextEl);
      }
    });
  }
  /**
   * @internal
   */
  _removeAllTextNodes() {
    const flicking = getFlickingAttached(this._flicking);
    const cameraElement = flicking.camera.element;
    toArray(cameraElement.childNodes).forEach((node) => {
      if (node.nodeType === Node.TEXT_NODE) {
        cameraElement.removeChild(node);
      }
    });
  }
}
class Panel {
  // Internal States Getter
  /**
   * `HTMLElement` that panel's referencing
   * @readonly
   */
  get element() {
    return this._elProvider.element;
  }
  /**
   * @internal
   * @readonly
   */
  get elementProvider() {
    return this._elProvider;
  }
  /**
   * Index of the panel
   * @readonly
   */
  get index() {
    return this._index;
  }
  /**
   * Position of the panel, including {@link Panel.alignPosition | alignPosition}
   * @readonly
   */
  get position() {
    return this._pos + this._alignPos;
  }
  /**
   * Cached size of the panel element
   * @remarks
   * This is equal to {@link Panel.element | element}'s `offsetWidth` if {@link Flicking.horizontal | horizontal} is `true`, and `offsetHeight` else
   * @readonly
   */
  get size() {
    return this._size;
  }
  /**
   * Panel's size including CSS `margin`
   * @remarks
   * This value includes {@link Panel.element | element}'s margin left/right if {@link Flicking.horizontal | horizontal} is `true`, and margin top/bottom else
   * @readonly
   */
  get sizeIncludingMargin() {
    return this._size + this._margin.prev + this._margin.next;
  }
  /**
   * Height of the panel element
   * @readonly
   */
  get height() {
    return this._height;
  }
  /**
   * Cached CSS `margin` value of the panel element
   * @readonly
   */
  get margin() {
    return this._margin;
  }
  /**
   * Align position inside the panel where {@link Camera}'s {@link Camera.alignPosition | alignPosition} inside viewport should be located at
   * @readonly
   */
  get alignPosition() {
    return this._alignPos;
  }
  /**
   * A value indicating whether the panel's {@link Flicking.remove | remove}d
   * @readonly
   */
  get removed() {
    return this._removed;
  }
  /**
   * A value indicating whether the panel's element is being rendered on the screen
   * @readonly
   */
  get rendered() {
    return this._rendered;
  }
  /**
   * A value indicating whether the panel's image/video is not loaded and waiting for resize
   * @readonly
   */
  get loading() {
    return this._loading;
  }
  /**
   * Panel element's range of the bounding box
   * @readonly
   */
  get range() {
    return { min: this._pos, max: this._pos + this._size };
  }
  /**
   * A value indicating whether the panel's position is toggled by circular behavior
   * @readonly
   */
  get toggled() {
    return this._toggled;
  }
  /**
   * A direction where the panel's position is toggled
   * @readonly
   */
  get toggleDirection() {
    return this._toggleDirection;
  }
  /**
   * Actual position offset determined by {@link Panel.order}
   * @readonly
   */
  get offset() {
    const toggleDirection = this._toggleDirection;
    const cameraRangeDiff = this._flicking.camera.rangeDiff;
    return toggleDirection === DIRECTION.NONE || !this._toggled ? 0 : toggleDirection === DIRECTION.PREV ? -cameraRangeDiff : cameraRangeDiff;
  }
  /**
   * Progress of movement between previous or next panel relative to current panel
   * @readonly
   */
  get progress() {
    const flicking = this._flicking;
    return this.index - flicking.camera.progress;
  }
  /**
   * Progress of movement between points that panel is completely invisible outside of viewport(prev direction: -1, selected point: 0, next direction: 1)
   * @readonly
   */
  get outsetProgress() {
    const position = this.position + this.offset;
    const alignPosition = this._alignPos;
    const camera = this._flicking.camera;
    const camPos = camera.position;
    if (camPos === position) {
      return 0;
    }
    if (camPos < position) {
      const disappearPosNext = position + (camera.size - camera.alignPosition) + alignPosition;
      return -getProgress$1(camPos, position, disappearPosNext);
    } else {
      const disappearPosPrev = position - (camera.alignPosition + this._size - alignPosition);
      return 1 - getProgress$1(camPos, disappearPosPrev, position);
    }
  }
  /**
   * Percentage of area where panel is visible in the viewport
   * @readonly
   */
  get visibleRatio() {
    const range2 = this.range;
    const size = this._size;
    const offset = this.offset;
    const visibleRange = this._flicking.camera.visibleRange;
    const checkingRange = {
      min: range2.min + offset,
      max: range2.max + offset
    };
    if (checkingRange.max <= visibleRange.min || checkingRange.min >= visibleRange.max) {
      return 0;
    }
    let visibleSize = size;
    if (visibleRange.min > checkingRange.min) {
      visibleSize -= visibleRange.min - checkingRange.min;
    }
    if (visibleRange.max < checkingRange.max) {
      visibleSize -= checkingRange.max - visibleRange.max;
    }
    return visibleSize / size;
  }
  set loading(val) {
    this._loading = val;
  }
  // Options Getter
  /**
   * A value indicating where the {@link Panel.alignPosition | alignPosition} should be located at inside the panel element
   */
  get align() {
    return this._align;
  }
  // Options Setter
  set align(val) {
    this._align = val;
    this._updateAlignPos();
  }
  /**
   * Creates a new Panel instance
   * @param panelOptions - Options for creating the panel
   */
  constructor(panelOptions) {
    const { index, align, flicking, elementProvider } = panelOptions;
    this._index = index;
    this._flicking = flicking;
    this._elProvider = elementProvider;
    this._align = align;
    this._removed = false;
    this._rendered = true;
    this._loading = false;
    this._resetInternalStates();
  }
  /**
   * @internal
   * @privateRemarks
   * Marks this panel to be rendered on the camera element.
   */
  markForShow() {
    this._rendered = true;
    this._elProvider.show(this._flicking);
  }
  /**
   * @internal
   * @privateRemarks
   * Marks this panel to be hidden from the camera element.
   */
  markForHide() {
    this._rendered = false;
    this._elProvider.hide(this._flicking);
  }
  /**
   * Update size of the panel
   * @remarks
   * This method recalculates the panel's size, margin, and position based on the current DOM state.
   * @param cached - Predefined cached size of the panel
   * @returns The current instance for method chaining
   */
  resize(cached) {
    var _a;
    const el = this.element;
    const flicking = this._flicking;
    const { horizontal, useFractionalSize } = flicking;
    if (!el) {
      return this;
    }
    if (cached) {
      this._size = cached.size;
      this._margin = __spreadValues({}, cached.margin);
      this._height = (_a = cached.height) != null ? _a : getElementSize({
        el,
        horizontal: false,
        useFractionalSize,
        useOffset: true,
        style: getStyle(el)
      });
    } else {
      const elStyle = getStyle(el);
      this._size = getElementSize({
        el,
        horizontal,
        useFractionalSize,
        useOffset: true,
        style: elStyle
      });
      this._margin = horizontal ? {
        prev: parseFloat(elStyle.marginLeft || "0"),
        next: parseFloat(elStyle.marginRight || "0")
      } : {
        prev: parseFloat(elStyle.marginTop || "0"),
        next: parseFloat(elStyle.marginBottom || "0")
      };
      this._height = horizontal ? getElementSize({
        el,
        horizontal: false,
        useFractionalSize,
        useOffset: true,
        style: elStyle
      }) : this._size;
    }
    this.updatePosition();
    this._updateAlignPos();
    return this;
  }
  /**
   * Change panel's size
   * @remarks
   * This will change the actual size of the panel element by changing its CSS width/height property.
   * @param size - {@link SetSizeParams}
   * @returns The current instance for method chaining
   */
  setSize(size) {
    setSize(this.element, size);
    return this;
  }
  /**
   * Check whether the given element is inside of this panel's {@link Panel.element | element}
   * @remarks
   * This is useful for determining which panel contains a clicked element.
   * @param element - The HTMLElement to check
   * @returns A Boolean value indicating the element is inside of this panel {@link Panel.element | element}
   */
  contains(element) {
    var _a;
    return !!((_a = this.element) == null ? void 0 : _a.contains(element));
  }
  /**
   * Reset internal state and set {@link Panel.removed | removed} to `true`
   * @remarks
   * After calling this method, the panel should no longer be used.
   */
  destroy() {
    this._resetInternalStates();
    this._removed = true;
  }
  /**
   * Check whether the given position is inside of this panel's {@link Panel.range | range}
   * @param pos - A position to check
   * @param includeMargin - Include {@link Panel.margin | margin} to the range
   * @returns A Boolean value indicating whether the given position is included in the panel range
   */
  includePosition(pos, includeMargin = false) {
    return this.includeRange(pos, pos, includeMargin);
  }
  /**
   * Check whether the given range is fully included in this panel's area (inclusive)
   * @param min - Minimum value of the range to check
   * @param max - Maximum value of the range to check
   * @param includeMargin - Include {@link Panel.margin | margin} to the range
   * @returns A Boolean value indicating whether the given range is fully included in the panel range
   */
  includeRange(min, max, includeMargin = false) {
    const margin = this._margin;
    const panelRange = this.range;
    if (includeMargin) {
      panelRange.min -= margin.prev;
      panelRange.max += margin.next;
    }
    return max >= panelRange.min && min <= panelRange.max;
  }
  /**
   * Check whether the panel is visble in the given range (exclusive)
   * @param min - Minimum value of the range to check
   * @param max - Maximum value of the range to check
   * @returns A Boolean value indicating whether the panel is visible
   */
  isVisibleOnRange(min, max) {
    const panelRange = this.range;
    return max > panelRange.min && min < panelRange.max;
  }
  /**
   * Move {@link Camera} to this panel
   * @remarks
   * This is equivalent to calling `Flicking.moveTo(panel.index, duration)`.
   * @param duration - Duration of the animation (unit: ms). Defaults to {@link FlickingOptions.duration}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns A Promise which will be resolved after reaching the panel
   */
  focus(duration) {
    return this._flicking.moveTo(this._index, duration);
  }
  /**
   * Get previous(`index - 1`) panel.
   * @remarks
   * When the previous panel does not exist, this will return `null` instead
   * If the {@link Flicking.circularEnabled | circular} is enabled, this will return the last panel if called from the first panel
   * @returns The previous panel
   */
  prev() {
    const index = this._index;
    const flicking = this._flicking;
    const renderer = flicking.renderer;
    const panelCount = renderer.panelCount;
    if (panelCount === 1) return null;
    return flicking.circularEnabled ? renderer.getPanel(index === 0 ? panelCount - 1 : index - 1) : renderer.getPanel(index - 1);
  }
  /**
   * Get next(`index + 1`) panel.
   * @remarks
   * When the next panel does not exist, this will return `null` instead
   * If the {@link Flicking.circularEnabled | circular} is enabled, this will return the first panel if called from the last panel
   * @returns The next panel
   */
  next() {
    const index = this._index;
    const flicking = this._flicking;
    const renderer = flicking.renderer;
    const panelCount = renderer.panelCount;
    if (panelCount === 1) return null;
    return flicking.circularEnabled ? renderer.getPanel(index === panelCount - 1 ? 0 : index + 1) : renderer.getPanel(index + 1);
  }
  /**
   * @internal
   * @privateRemarks
   * Increases the panel's index by the given value. Called when panels are inserted before this panel.
   */
  increaseIndex(val) {
    this._index += Math.max(val, 0);
    return this;
  }
  /**
   * @internal
   * @privateRemarks
   * Decreases the panel's index by the given value. Called when panels are removed before this panel.
   */
  decreaseIndex(val) {
    this._index -= Math.max(val, 0);
    return this;
  }
  /**
   * @internal
   * @privateRemarks
   * Recalculates the panel's position based on the previous panel's position and margins.
   */
  updatePosition() {
    const prevPanel = this._flicking.renderer.panels[this._index - 1];
    this._pos = prevPanel ? prevPanel.range.max + prevPanel.margin.next + this._margin.prev : this._margin.prev;
    return this;
  }
  /**
   * @internal
   * @privateRemarks
   * Toggles the panel's position for circular mode. Returns true if the panel was toggled.
   */
  toggle(prevPos, newPos) {
    const toggleDirection = this._toggleDirection;
    const togglePosition = this._togglePosition;
    if (toggleDirection === DIRECTION.NONE || newPos === prevPos) return false;
    const prevToggled = this._toggled;
    if (newPos > prevPos) {
      if (togglePosition >= prevPos && togglePosition <= newPos) {
        this._toggled = toggleDirection === DIRECTION.NEXT;
      }
    } else {
      if (togglePosition <= prevPos && togglePosition >= newPos) {
        this._toggled = toggleDirection !== DIRECTION.NEXT;
      }
    }
    return prevToggled !== this._toggled;
  }
  /**
   * @internal
   * @privateRemarks
   * Updates the toggle direction for circular mode based on the panel's visibility at the camera range edges.
   */
  updateCircularToggleDirection() {
    const flicking = this._flicking;
    if (!flicking.circularEnabled) {
      this._toggleDirection = DIRECTION.NONE;
      this._togglePosition = 0;
      this._toggled = false;
      return this;
    }
    const camera = flicking.camera;
    const camRange = camera.range;
    const camAlignPosition = camera.alignPosition;
    const camVisibleRange = camera.visibleRange;
    const camVisibleSize = camVisibleRange.max - camVisibleRange.min;
    const minimumVisible = camRange.min - camAlignPosition;
    const maximumVisible = camRange.max - camAlignPosition + camVisibleSize;
    const shouldBeVisibleAtMin = this.includeRange(maximumVisible - camVisibleSize, maximumVisible, false);
    const shouldBeVisibleAtMax = this.includeRange(minimumVisible, minimumVisible + camVisibleSize, false);
    this._toggled = false;
    if (shouldBeVisibleAtMin) {
      this._toggleDirection = DIRECTION.PREV;
      this._togglePosition = this.range.max + camRange.min - camRange.max + camAlignPosition;
      this.toggle(Infinity, camera.position);
    } else if (shouldBeVisibleAtMax) {
      this._toggleDirection = DIRECTION.NEXT;
      this._togglePosition = this.range.min + camRange.max - camVisibleSize + camAlignPosition;
      this.toggle(-Infinity, camera.position);
    } else {
      this._toggleDirection = DIRECTION.NONE;
      this._togglePosition = 0;
    }
    return this;
  }
  /**
   * @internal
   * @privateRemarks
   * Recalculates the align position based on the align option and panel size.
   */
  _updateAlignPos() {
    this._alignPos = parseAlign$1(this._align, this._size);
  }
  /**
   * @internal
   * @privateRemarks
   * Resets all internal state values to their defaults.
   */
  _resetInternalStates() {
    this._size = 0;
    this._pos = 0;
    this._margin = { prev: 0, next: 0 };
    this._height = 0;
    this._alignPos = 0;
    this._toggled = false;
    this._togglePosition = 0;
    this._toggleDirection = DIRECTION.NONE;
  }
}
class NormalRenderingStrategy {
  constructor(options) {
    const { providerCtor } = options;
    this._providerCtor = providerCtor;
  }
  renderPanels() {
  }
  getRenderingIndexesByOrder(flicking) {
    const renderedPanels = flicking.renderer.panels.filter((panel) => panel.rendered);
    const toggledPrev = renderedPanels.filter((panel) => panel.toggled && panel.toggleDirection === DIRECTION.PREV);
    const toggledNext = renderedPanels.filter((panel) => panel.toggled && panel.toggleDirection === DIRECTION.NEXT);
    const notToggled = renderedPanels.filter((panel) => !panel.toggled);
    return [...toggledPrev, ...notToggled, ...toggledNext].map((panel) => panel.index);
  }
  getRenderingElementsByOrder(flicking) {
    const panels = flicking.panels;
    return this.getRenderingIndexesByOrder(flicking).map((index) => panels[index].element);
  }
  updateRenderingPanels(flicking) {
    if (flicking.renderOnlyVisible) {
      this._showOnlyVisiblePanels(flicking);
    } else {
      flicking.panels.forEach((panel) => panel.markForShow());
    }
  }
  collectPanels(flicking, elements) {
    const align = parsePanelAlign(flicking.renderer.align);
    return elements.map(
      (el, index) => new Panel({
        index,
        elementProvider: new this._providerCtor(el),
        align,
        flicking
      })
    );
  }
  createPanel(element, options) {
    return new Panel(__spreadProps(__spreadValues({}, options), {
      elementProvider: new this._providerCtor(element)
    }));
  }
  updatePanelSizes(flicking, size) {
    flicking.panels.forEach((panel) => panel.setSize(size));
  }
  /**
   * @internal
   */
  _showOnlyVisiblePanels(flicking) {
    const panels = flicking.renderer.panels;
    const camera = flicking.camera;
    const visibleIndexes = camera.visiblePanels.reduce((visibles, panel) => {
      visibles[panel.index] = true;
      return visibles;
    }, {});
    panels.forEach((panel) => {
      if (panel.index in visibleIndexes || panel.loading) {
        panel.markForShow();
      } else if (!flicking.holding) {
        panel.markForHide();
      }
    });
    camera.updateOffset();
  }
}
class VirtualPanel extends Panel {
  /**
   * `HTMLElement` that panel's referencing
   * @readonly
   */
  get element() {
    return this._elProvider.element;
  }
  /**
   * Cached innerHTML by the previous render function
   * @readonly
   */
  get cachedInnerHTML() {
    return this._cachedInnerHTML;
  }
  /**
   * A number for indexing which element it will be rendered on
   * @readonly
   */
  get elementIndex() {
    const flicking = this._flicking;
    const virtualElCount = flicking.panelsPerView + 1;
    const panelCount = flicking.panelCount;
    let index = this._index;
    if (this._toggled) {
      index = this._toggleDirection === DIRECTION.NEXT ? index + panelCount : index - panelCount;
    }
    return circulateIndex(index, virtualElCount);
  }
  /**
   * @param options - {@link VirtualPanelOptions}
   */
  constructor(options) {
    super(options);
    options.elementProvider.init(this);
    this._elProvider = options.elementProvider;
    this._cachedInnerHTML = null;
  }
  cacheRenderResult(result) {
    this._cachedInnerHTML = result;
  }
  uncacheRenderResult() {
    this._cachedInnerHTML = null;
  }
  render() {
    const flicking = this._flicking;
    const { renderPanel, cache } = flicking.virtual;
    const element = this._elProvider.element;
    const newInnerHTML = this._cachedInnerHTML || renderPanel(this, this._index);
    if (newInnerHTML === element.innerHTML) return;
    element.innerHTML = newInnerHTML;
    if (cache) {
      this.cacheRenderResult(newInnerHTML);
    }
  }
  increaseIndex(val) {
    this.uncacheRenderResult();
    return super.increaseIndex(val);
  }
  decreaseIndex(val) {
    this.uncacheRenderResult();
    return super.decreaseIndex(val);
  }
}
class VirtualRenderingStrategy {
  renderPanels(flicking) {
    const virtualManager = flicking.virtual;
    const visiblePanels = flicking.visiblePanels;
    const invisibleIndexes = range(flicking.panelsPerView + 1);
    visiblePanels.forEach((panel) => {
      const elementIndex = panel.elementIndex;
      panel.render();
      virtualManager.show(elementIndex);
      invisibleIndexes[elementIndex] = -1;
    });
    invisibleIndexes.filter((val) => val >= 0).forEach((idx) => {
      virtualManager.hide(idx);
    });
  }
  getRenderingIndexesByOrder(flicking) {
    const virtualManager = flicking.virtual;
    const visiblePanels = [...flicking.visiblePanels].filter((panel) => panel.rendered).sort((panel1, panel2) => {
      return panel1.position + panel1.offset - (panel2.position + panel2.offset);
    });
    if (visiblePanels.length <= 0) return virtualManager.elements.map((_, idx) => idx);
    const visibleIndexes = visiblePanels.map((panel) => panel.elementIndex);
    const invisibleIndexes = virtualManager.elements.map((el, idx) => __spreadProps(__spreadValues({}, el), { idx })).filter((el) => !el.visible).map((el) => el.idx);
    return [...visibleIndexes, ...invisibleIndexes];
  }
  getRenderingElementsByOrder(flicking) {
    const virtualManager = flicking.virtual;
    const elements = virtualManager.elements;
    return this.getRenderingIndexesByOrder(flicking).map((index) => elements[index].nativeElement);
  }
  updateRenderingPanels(flicking) {
    const panels = flicking.renderer.panels;
    const camera = flicking.camera;
    const visibleIndexes = camera.visiblePanels.reduce((visibles, panel) => {
      visibles[panel.index] = true;
      return visibles;
    }, {});
    panels.forEach((panel) => {
      if (panel.index in visibleIndexes || panel.loading) {
        panel.markForShow();
      } else {
        panel.markForHide();
      }
    });
    camera.updateOffset();
  }
  collectPanels(flicking) {
    const align = parsePanelAlign(flicking.renderer.align);
    return range(flicking.virtual.initialPanelCount).map(
      (index) => new VirtualPanel({
        index,
        elementProvider: new VirtualElementProvider(flicking),
        align,
        flicking
      })
    );
  }
  createPanel(_el, options) {
    return new VirtualPanel(__spreadProps(__spreadValues({}, options), {
      elementProvider: new VirtualElementProvider(options.flicking)
    }));
  }
  updatePanelSizes(flicking, size) {
    flicking.virtual.elements.forEach((el) => {
      setSize(el.nativeElement, size);
    });
    flicking.panels.forEach((panel) => panel.setSize(size));
  }
}
const _Flicking = class _Flicking extends Component {
  /** Creates a new Flicking instance
   * @param root - A root HTMLElement to initialize Flicking on it. When it's a typeof `string`, it should be a css selector string
   * @param options - A {@link FlickingOptions} object
   * @throws {@link InitializationErrors}
   * @example
   * ```ts
   * import Flicking from "@egjs/flicking";
   *
   * // Creating new instance of Flicking with HTMLElement
   * const flicking = new Flicking(document.querySelector(".flicking-viewport"), { circular: true });
   *
   * // Creating new instance of Flicking with CSS selector
   * const flicking2 = new Flicking(".flicking-viewport", { circular: true });
   * ```
   */
  constructor(root, options = {}) {
    super();
    this._scheduleResize = false;
    const {
      align = ALIGN.CENTER,
      defaultIndex = 0,
      horizontal = true,
      circular = false,
      circularFallback = CIRCULAR_FALLBACK.LINEAR,
      bound = false,
      adaptive = false,
      panelsPerView = -1,
      noPanelStyleOverride = false,
      resizeOnContentsReady = false,
      nested = false,
      needPanelThreshold = 0,
      preventEventsBeforeInit = true,
      deceleration = 75e-4,
      duration = 500,
      easing = (x) => 1 - __pow(1 - x, 3),
      inputType = ["mouse", "touch"],
      moveType = "snap",
      threshold = 40,
      dragThreshold = 1,
      interruptable = true,
      bounce = "20%",
      iOSEdgeSwipeThreshold = 30,
      preventClickOnDrag = true,
      preventDefaultOnDrag = false,
      disableOnInit = false,
      changeOnHold = false,
      renderOnlyVisible = false,
      virtual = null,
      autoInit = true,
      autoResize = true,
      useResizeObserver = true,
      resizeDebounce = 0,
      observePanelResize = false,
      maxResizeDebounce = 100,
      useFractionalSize = false,
      usePercentagePos = false,
      externalRenderer = null,
      renderExternal = null,
      optimizeSizeUpdate = false,
      animationThreshold = 0.5,
      useCSSOrder = false
    } = options;
    this._initialized = false;
    this._plugins = [];
    this._isResizing = false;
    this._align = align;
    this._defaultIndex = defaultIndex;
    this._horizontal = horizontal;
    this._circular = circular;
    this._circularFallback = circularFallback;
    this._bound = bound;
    this._adaptive = adaptive;
    this._panelsPerView = panelsPerView;
    this._noPanelStyleOverride = noPanelStyleOverride;
    this._resizeOnContentsReady = resizeOnContentsReady;
    this._nested = nested;
    this._virtual = virtual;
    this._needPanelThreshold = needPanelThreshold;
    this._preventEventsBeforeInit = preventEventsBeforeInit;
    this._deceleration = deceleration;
    this._duration = duration;
    this._easing = easing;
    this._inputType = inputType;
    this._moveType = moveType;
    this._threshold = threshold;
    this._dragThreshold = dragThreshold;
    this._interruptable = interruptable;
    this._bounce = bounce;
    this._iOSEdgeSwipeThreshold = iOSEdgeSwipeThreshold;
    this._preventClickOnDrag = preventClickOnDrag;
    this._preventDefaultOnDrag = preventDefaultOnDrag;
    this._disableOnInit = disableOnInit;
    this._changeOnHold = changeOnHold;
    this._renderOnlyVisible = renderOnlyVisible;
    this._autoInit = autoInit;
    this._autoResize = autoResize;
    this._useResizeObserver = useResizeObserver;
    this._resizeDebounce = resizeDebounce;
    this._maxResizeDebounce = maxResizeDebounce;
    this._observePanelResize = observePanelResize;
    this._useFractionalSize = useFractionalSize;
    this._usePercentagePos = usePercentagePos;
    this._externalRenderer = externalRenderer;
    this._renderExternal = renderExternal;
    this._optimizeSizeUpdate = optimizeSizeUpdate;
    this._animationThreshold = animationThreshold;
    this._useCSSOrder = useCSSOrder;
    this._viewport = new Viewport(this, getElement(root));
    this._autoResizer = new AutoResizer(this);
    this._renderer = this._createRenderer();
    this._camera = this._createCamera();
    this._control = this._createControl();
    this._virtualManager = new VirtualManager(this, virtual);
    if (this._autoInit) {
      void this.init();
    }
  }
  // Components
  /**
   * {@link Control} instance that manages user input and panel movement animations
   * @remarks
   * The concrete Control implementation is selected based on {@link FlickingOptions.moveType | moveType} option.
   * @privateRemarks
   * The control instance is created during construction by {@link Flicking._createControl}.
   * @readonly
   */
  get control() {
    return this._control;
  }
  /**
   * {@link Camera} instance that manages actual movement and positioning inside the viewport
   * @remarks
   * The concrete Camera implementation is selected based on {@link FlickingOptions.circular} and {@link FlickingOptions.bound} options.
   * @privateRemarks
   * The camera instance is created during construction by {@link Flicking._createCamera}.
   * @readonly
   */
  get camera() {
    return this._camera;
  }
  /**
   * {@link Renderer} instance that manages panels and their elements
   * @remarks
   * The concrete Renderer implementation is selected based on {@link Flicking.externalRenderer} and {@link FlickingOptions.virtual} options.
   * @privateRemarks
   * The renderer instance is created during construction by {@link Flicking._createRenderer}.
   * @readonly
   */
  get renderer() {
    return this._renderer;
  }
  /**
   * {@link Viewport} instance that manages viewport size and element
   * @privateRemarks
   * The viewport instance is created during construction by {@link Flicking} constructor.
   * @readonly
   */
  get viewport() {
    return this._viewport;
  }
  /**
   * {@link AutoResizer} instance that detects size changes and triggers resize when {@link FlickingOptions.autoResize | autoResize} option is enabled
   * @privateRemarks
   * The autoResizer instance is created during construction by {@link Flicking} constructor.
   * @readonly
   */
  get autoResizer() {
    return this._autoResizer;
  }
  // Internal States
  /**
   * Whether Flicking's {@link Flicking.init} is called.
   * @remarks
   * This is `true` when {@link Flicking.init} is called, and is `false` after calling {@link Flicking.destroy}.
   * Use this to check if Flicking is ready before calling certain methods that require initialization.
   * @defaultValue false
   * @readonly
   * @example
   * ```ts
   * if (flicking.initialized) {
   *   flicking.setStatus(status);
   * } else {
   *   await flicking.init();
   *   flicking.setStatus(status);
   * }
   * ```
   */
  get initialized() {
    return this._initialized;
  }
  /**
   * Whether the circular mode is actually enabled.
   * @remarks
   * The {@link FlickingOptions.circular} option may not be enabled when the sum of panel sizes is too small.
   * This property reflects the actual enabled state, which may differ from the {@link FlickingOptions.circular} option value.
   * @defaultValue false
   * @readonly
   */
  get circularEnabled() {
    return this._camera.circularEnabled;
  }
  /**
   * Whether the virtual mode is actually enabled.
   * @remarks
   * The {@link FlickingOptions.virtual} option may not be enabled when {@link FlickingOptions.panelsPerView} is less than or equal to zero.
   * This property reflects the actual enabled state, which may differ from the {@link FlickingOptions.virtual} option value.
   * @defaultValue false
   * @readonly
   */
  get virtualEnabled() {
    return this._panelsPerView > 0 && this._virtual != null;
  }
  /**
   * Index of the currently active panel.
   * @remarks
   * Returns -1 when there is no active panel. This is a shorthand for `Flicking.currentPanel.index`.
   * @readonly
   */
  get index() {
    return this._control.activeIndex;
  }
  /**
   * The root viewport element (`.flicking-viewport`).
   * @remarks
   * This is the element passed to the Flicking constructor. It is a shorthand for `Flicking.viewport.element`.
   * @readonly
   */
  get element() {
    return this._viewport.element;
  }
  /**
   * The currently active panel.
   * @remarks
   * Returns `null` when there is no active panel. This is a shorthand for `Flicking.control.activePanel`.
   * @readonly
   */
  get currentPanel() {
    return this._control.activePanel;
  }
  /**
   * Array of all panels.
   * @remarks
   * This is a shorthand for `Flicking.renderer.panels`.
   * @readonly
   */
  get panels() {
    return this._renderer.panels;
  }
  /**
   * Total number of panels.
   * @remarks
   * This is a shorthand for `Flicking.renderer.panelCount`.
   * @readonly
   */
  get panelCount() {
    return this._renderer.panelCount;
  }
  /**
   * Array of panels that are currently visible in the viewport.
   * @remarks
   * This is a shorthand for `Flicking.camera.visiblePanels`.
   * @readonly
   */
  get visiblePanels() {
    return this._camera.visiblePanels;
  }
  /**
   * Whether Flicking is currently animating.
   * @remarks
   * This is a shorthand for `Flicking.control.animating`.
   * @readonly
   */
  get animating() {
    return this._control.animating;
  }
  /**
   * Whether the user is currently clicking or touching the viewport.
   * @remarks
   * This is a shorthand for `Flicking.control.holding`.
   * @readonly
   */
  get holding() {
    return this._control.holding;
  }
  /**
   * Array of currently activated plugins.
   * @remarks
   * Plugins are added via {@link Flicking.addPlugins} and removed via {@link Flicking.removePlugins}.
   * @readonly
   */
  get activePlugins() {
    return this._plugins;
  }
  // Options Getter
  // UI / LAYOUT
  /** Current value of the {@link FlickingOptions.align | align} option. */
  get align() {
    return this._align;
  }
  /** Current value of the {@link FlickingOptions.defaultIndex | defaultIndex} option. */
  get defaultIndex() {
    return this._defaultIndex;
  }
  /** Current value of the {@link FlickingOptions.horizontal | horizontal} option. */
  get horizontal() {
    return this._horizontal;
  }
  /** Current value of the {@link FlickingOptions.circular | circular} option. */
  get circular() {
    return this._circular;
  }
  /**
   * Current value of the {@link FlickingOptions.circularFallback | circularFallback} option.
   * @since 4.5.0
   */
  get circularFallback() {
    return this._circularFallback;
  }
  /** Current value of the {@link FlickingOptions.bound | bound} option. */
  get bound() {
    return this._bound;
  }
  /** Current value of the {@link FlickingOptions.adaptive | adaptive} option. */
  get adaptive() {
    return this._adaptive;
  }
  /**
   * Current value of the {@link FlickingOptions.panelsPerView | panelsPerView} option.
   * @since 4.2.0
   */
  get panelsPerView() {
    return this._panelsPerView;
  }
  /** Current value of the {@link FlickingOptions.noPanelStyleOverride | noPanelStyleOverride} option. */
  get noPanelStyleOverride() {
    return this._noPanelStyleOverride;
  }
  /**
   * Current value of the {@link FlickingOptions.resizeOnContentsReady | resizeOnContentsReady} option.
   * @since 4.3.0
   */
  get resizeOnContentsReady() {
    return this._resizeOnContentsReady;
  }
  /**
   * Current value of the {@link FlickingOptions.nested | nested} option.
   * @since 4.7.0
   */
  get nested() {
    return this._nested;
  }
  // EVENTS
  /** Current value of the {@link FlickingOptions.needPanelThreshold | needPanelThreshold} option. */
  get needPanelThreshold() {
    return this._needPanelThreshold;
  }
  /**
   * Current value of the {@link FlickingOptions.preventEventsBeforeInit | preventEventsBeforeInit} option.
   * @since 4.2.0
   */
  get preventEventsBeforeInit() {
    return this._preventEventsBeforeInit;
  }
  // ANIMATION
  /** Current value of the {@link FlickingOptions.deceleration | deceleration} option. */
  get deceleration() {
    return this._deceleration;
  }
  /** Current value of the {@link FlickingOptions.easing | easing} option. */
  get easing() {
    return this._easing;
  }
  /** Current value of the {@link FlickingOptions.duration | duration} option. */
  get duration() {
    return this._duration;
  }
  // INPUT
  /** Current value of the {@link FlickingOptions.inputType | inputType} option. */
  get inputType() {
    return this._inputType;
  }
  /** Current value of the {@link FlickingOptions.moveType | moveType} option. */
  get moveType() {
    return this._moveType;
  }
  /** Current value of the {@link FlickingOptions.threshold | threshold} option. */
  get threshold() {
    return this._threshold;
  }
  /** Current value of the {@link FlickingOptions.dragThreshold | dragThreshold} option. */
  get dragThreshold() {
    return this._dragThreshold;
  }
  /**
   * Current value of the {@link FlickingOptions.animationThreshold | animationThreshold} option.
   * @since 4.15.0
   */
  get animationThreshold() {
    return this._animationThreshold;
  }
  /**
   * Current value of the {@link FlickingOptions.useCSSOrder | useCSSOrder} option.
   * @since 4.15.0
   */
  get useCSSOrder() {
    return this._useCSSOrder;
  }
  /** Current value of the {@link FlickingOptions.interruptable | interruptable} option. */
  get interruptable() {
    return this._interruptable;
  }
  /** Current value of the {@link FlickingOptions.bounce | bounce} option. */
  get bounce() {
    return this._bounce;
  }
  /** Current value of the {@link FlickingOptions.iOSEdgeSwipeThreshold | iOSEdgeSwipeThreshold} option. */
  get iOSEdgeSwipeThreshold() {
    return this._iOSEdgeSwipeThreshold;
  }
  /** Current value of the {@link FlickingOptions.preventClickOnDrag | preventClickOnDrag} option. */
  get preventClickOnDrag() {
    return this._preventClickOnDrag;
  }
  /**
   * Current value of the {@link FlickingOptions.preventDefaultOnDrag | preventDefaultOnDrag} option.
   * @since 4.11.0
   */
  get preventDefaultOnDrag() {
    return this._preventDefaultOnDrag;
  }
  /** Current value of the {@link FlickingOptions.disableOnInit | disableOnInit} option. */
  get disableOnInit() {
    return this._disableOnInit;
  }
  /**
   * Current value of the {@link FlickingOptions.changeOnHold | changeOnHold} option.
   * @since 4.8.0
   */
  get changeOnHold() {
    return this._changeOnHold;
  }
  // PERFORMANCE
  /** Current value of the {@link FlickingOptions.renderOnlyVisible | renderOnlyVisible} option. */
  get renderOnlyVisible() {
    return this._renderOnlyVisible;
  }
  /**
   * {@link VirtualManager} instance that manages virtual panels
   * @privateRemarks
   * The virtualManager instance is created during construction by {@link Flicking} constructor.
   * @readonly
   */
  get virtual() {
    return this._virtualManager;
  }
  // OTHERS
  /** Current value of the {@link FlickingOptions.autoInit | autoInit} option. */
  get autoInit() {
    return this._autoInit;
  }
  /** Current value of the {@link FlickingOptions.autoResize | autoResize} option. */
  get autoResize() {
    return this._autoResize;
  }
  /**
   * Current value of the {@link FlickingOptions.useResizeObserver | useResizeObserver} option.
   * @since 4.4.0
   */
  get useResizeObserver() {
    return this._useResizeObserver;
  }
  /**
   * Current value of the {@link FlickingOptions.observePanelResize | observePanelResize} option.
   * @since 4.13.1
   */
  get observePanelResize() {
    return this._observePanelResize;
  }
  /**
   * Current value of the {@link FlickingOptions.resizeDebounce | resizeDebounce} option.
   * @since 4.6.0
   */
  get resizeDebounce() {
    return this._resizeDebounce;
  }
  /**
   * Current value of the {@link FlickingOptions.maxResizeDebounce | maxResizeDebounce} option.
   * @since 4.6.0
   */
  get maxResizeDebounce() {
    return this._maxResizeDebounce;
  }
  /**
   * Current value of the {@link FlickingOptions.useFractionalSize | useFractionalSize} option.
   * @since 4.9.0
   */
  get useFractionalSize() {
    return this._useFractionalSize;
  }
  /**
   * Current value of the {@link FlickingOptions.usePercentagePos | usePercentagePos} option.
   * @since 4.17.0
   */
  get usePercentagePos() {
    return this._usePercentagePos;
  }
  /** Current value of the {@link FlickingOptions.externalRenderer | externalRenderer} option. */
  get externalRenderer() {
    return this._externalRenderer;
  }
  /**
   * @deprecated Use {@link Flicking.externalRenderer | externalRenderer} instead.
   * Current value of the {@link FlickingOptions.renderExternal | renderExternal} option.
   */
  get renderExternal() {
    return this._renderExternal;
  }
  /** @internal */
  get optimizeSizeUpdate() {
    return this._optimizeSizeUpdate;
  }
  // Options Setter
  // UI / LAYOUT
  /**
   * Sets {@link FlickingOptions.align}.
   * @privateRemarks
   * Setting this value updates the renderer and camera alignment, and triggers a resize operation.
   */
  set align(val) {
    this._align = val;
    this._renderer.align = val;
    this._camera.align = val;
    void this.resize();
  }
  set defaultIndex(val) {
    this._defaultIndex = val;
  }
  /**
   * Sets {@link FlickingOptions.horizontal}.
   * @privateRemarks
   * Setting this value updates the control direction and triggers a resize operation.
   */
  set horizontal(val) {
    this._horizontal = val;
    this._control.controller.updateDirection();
    void this.resize();
  }
  /**
   * Sets {@link FlickingOptions.circular}.
   * @privateRemarks
   * Setting this value triggers a resize operation to recalculate panel positions.
   */
  set circular(val) {
    this._circular = val;
    void this.resize();
  }
  /**
   * Sets {@link FlickingOptions.bound}.
   * @privateRemarks
   * Setting this value triggers a resize operation to recalculate panel positions.
   */
  set bound(val) {
    this._bound = val;
    void this.resize();
  }
  /**
   * Sets {@link FlickingOptions.adaptive}.
   * @privateRemarks
   * Setting this value triggers a resize operation to recalculate panel sizes.
   */
  set adaptive(val) {
    this._adaptive = val;
    void this.resize();
  }
  /**
   * Sets {@link FlickingOptions.panelsPerView}.
   * @privateRemarks
   * Setting this value triggers a resize operation to recalculate panel sizes.
   */
  set panelsPerView(val) {
    this._panelsPerView = val;
    void this.resize();
  }
  /**
   * Sets {@link FlickingOptions.noPanelStyleOverride}.
   * @privateRemarks
   * Setting this value triggers a resize operation to update panel styles.
   */
  set noPanelStyleOverride(val) {
    this._noPanelStyleOverride = val;
    void this.resize();
  }
  /**
   * Sets {@link FlickingOptions.resizeOnContentsReady}.
   * @privateRemarks
   * When set to `true`, immediately checks all panels for content readiness.
   */
  set resizeOnContentsReady(val) {
    this._resizeOnContentsReady = val;
    if (val) {
      this._renderer.checkPanelContentsReady(this._renderer.panels);
    }
  }
  /**
   * Sets {@link FlickingOptions.nested}.
   * @privateRemarks
   * Setting this value updates the control's axes options.
   */
  set nested(val) {
    this._nested = val;
    const axes = this._control.controller.axes;
    if (axes) {
      axes.options.nested = val;
    }
  }
  // EVENTS
  set needPanelThreshold(val) {
    this._needPanelThreshold = val;
  }
  set preventEventsBeforeInit(val) {
    this._preventEventsBeforeInit = val;
  }
  // ANIMATION
  /**
   * Sets {@link FlickingOptions.deceleration}.
   * @privateRemarks
   * Setting this value updates the control's axes deceleration option.
   */
  set deceleration(val) {
    this._deceleration = val;
    const axes = this._control.controller.axes;
    if (axes) {
      axes.options.deceleration = val;
    }
  }
  /**
   * Sets {@link FlickingOptions.easing}.
   * @privateRemarks
   * Setting this value updates the control's axes easing option.
   */
  set easing(val) {
    this._easing = val;
    const axes = this._control.controller.axes;
    if (axes) {
      axes.options.easing = val;
    }
  }
  set duration(val) {
    this._duration = val;
  }
  // INPUT
  /**
   * Sets {@link FlickingOptions.inputType}.
   * @privateRemarks
   * Setting this value updates the control's pan input options.
   */
  set inputType(val) {
    this._inputType = val;
    const panInput = this._control.controller.panInput;
    if (panInput) {
      panInput.options.inputType = val;
    }
  }
  /**
   * Sets {@link FlickingOptions.moveType}.
   * @privateRemarks
   * Setting this value creates a new Control instance based on the moveType and preserves the current panel position and progress.
   */
  set moveType(val) {
    this._moveType = val;
    const prevControl = this._control;
    const newControl = this._createControl();
    const activePanel = prevControl.activePanel;
    newControl.copy(prevControl);
    const prevProgressInPanel = activePanel ? this._camera.getProgressInPanel(activePanel) : 0;
    this._control = newControl;
    this._control.updatePosition(prevProgressInPanel);
    this._control.updateInput();
  }
  set threshold(val) {
    this._threshold = val;
  }
  /**
   * Sets {@link FlickingOptions.dragThreshold}.
   * @privateRemarks
   * Setting this value updates the control's pan input threshold option.
   */
  set dragThreshold(val) {
    this._dragThreshold = val;
    const panInput = this._control.controller.panInput;
    if (panInput) {
      panInput.options.threshold = val;
    }
  }
  /**
   * Sets {@link FlickingOptions.animationThreshold}.
   */
  set animationThreshold(val) {
    this._animationThreshold = val;
  }
  /**
   * Sets {@link FlickingOptions.useCSSOrder}.
   */
  set useCSSOrder(val) {
    this._useCSSOrder = val;
  }
  /**
   * Sets {@link FlickingOptions.interruptable}.
   * @privateRemarks
   * Setting this value updates the control's axes interruptable option.
   */
  set interruptable(val) {
    this._interruptable = val;
    const axes = this._control.controller.axes;
    if (axes) {
      axes.options.interruptable = val;
    }
  }
  /**
   * Sets {@link FlickingOptions.bounce}.
   * @privateRemarks
   * Setting this value updates the control input configuration.
   */
  set bounce(val) {
    this._bounce = val;
    this._control.updateInput();
  }
  /**
   * Sets {@link FlickingOptions.iOSEdgeSwipeThreshold}.
   * @privateRemarks
   * Setting this value updates the control's pan input iOS edge swipe threshold option.
   */
  set iOSEdgeSwipeThreshold(val) {
    this._iOSEdgeSwipeThreshold = val;
    const panInput = this._control.controller.panInput;
    if (panInput) {
      panInput.options.iOSEdgeSwipeThreshold = val;
    }
  }
  /**
   * Sets {@link FlickingOptions.preventClickOnDrag}.
   * @privateRemarks
   * Setting this value adds or removes the prevent click handler from the control.
   */
  set preventClickOnDrag(val) {
    const prevVal = this._preventClickOnDrag;
    if (val === prevVal) return;
    const controller = this._control.controller;
    if (val) {
      controller.addPreventClickHandler();
    } else {
      controller.removePreventClickHandler();
    }
    this._preventClickOnDrag = val;
  }
  /**
   * Sets {@link FlickingOptions.preventDefaultOnDrag}.
   * @privateRemarks
   * Setting this value updates the control's pan input preventDefaultOnDrag option.
   */
  set preventDefaultOnDrag(val) {
    this._preventDefaultOnDrag = val;
    const panInput = this._control.controller.panInput;
    if (panInput) {
      panInput.options.preventDefaultOnDrag = val;
    }
  }
  set disableOnInit(val) {
    this._disableOnInit = val;
  }
  set changeOnHold(val) {
    this._changeOnHold = val;
  }
  // PERFORMANCE
  /**
   * Sets {@link FlickingOptions.renderOnlyVisible}.
   * @privateRemarks
   * Setting this value triggers an immediate render operation.
   */
  set renderOnlyVisible(val) {
    this._renderOnlyVisible = val;
    void this._renderer.render();
  }
  // OTHERS
  /**
   * Sets {@link FlickingOptions.autoResize}.
   * @privateRemarks
   * Setting this value enables or disables the auto resizer if Flicking is already initialized.
   */
  set autoResize(val) {
    this._autoResize = val;
    if (!this._initialized) {
      return;
    }
    if (val) {
      this._autoResizer.enable();
    } else {
      this._autoResizer.disable();
    }
  }
  /**
   * Sets {@link FlickingOptions.useResizeObserver}.
   * @privateRemarks
   * Setting this value re-enables the auto resizer if Flicking is initialized and autoResize is enabled.
   */
  set useResizeObserver(val) {
    this._useResizeObserver = val;
    if (this._initialized && this._autoResize) {
      this._autoResizer.enable();
    }
  }
  /**
   * Sets {@link FlickingOptions.observePanelResize}.
   * @privateRemarks
   * Setting this value starts or stops observing panel sizes if Flicking is initialized and autoResize is enabled.
   */
  set observePanelResize(val) {
    this._observePanelResize = val;
    if (this._initialized && this._autoResize) {
      if (val) {
        this._autoResizer.observePanels();
      } else {
        this._autoResizer.unobservePanels();
      }
    }
  }
  set optimizeSizeUpdate(val) {
    this._optimizeSizeUpdate = val;
  }
  /**
   * Sets {@link FlickingOptions.usePercentagePos}.
   * @privateRemarks
   * Setting this value immediately re-applies the camera element's transform with the new unit.
   * The transform is not re-applied when Flicking is not initialized or the renderer is rendering.
   */
  set usePercentagePos(val) {
    this._usePercentagePos = val;
    this._camera.applyTransform();
  }
  /**
   * Initialize Flicking and move to the default index.
   * @remarks
   * This method is automatically called in the constructor when {@link FlickingOptions.autoInit | autoInit} is `true` (default).
   * If Flicking is already initialized, this method returns immediately without doing anything.
   * @fires {@link ReadyEvent}
   * @returns Promise that resolves when initialization is complete
   */
  init() {
    if (this._initialized) return Promise.resolve();
    const camera = this._camera;
    const renderer = this._renderer;
    const control = this._control;
    const virtualManager = this._virtualManager;
    const originalTrigger = this.trigger;
    const preventEventsBeforeInit = this._preventEventsBeforeInit;
    camera.init();
    virtualManager.init();
    renderer.init(this);
    control.init(this);
    if (preventEventsBeforeInit) {
      this.trigger = () => this;
    }
    this._initialResize();
    this._moveToInitialPanel();
    if (this._autoResize) {
      this._autoResizer.enable();
    }
    if (this._preventClickOnDrag) {
      control.controller.addPreventClickHandler();
    }
    if (this._disableOnInit) {
      this.disableInput();
    }
    renderer.checkPanelContentsReady(renderer.panels);
    this._initialized = true;
    return renderer.render().then(() => {
      this._plugins.forEach((plugin) => plugin.init(this));
      if (preventEventsBeforeInit) {
        this.trigger = originalTrigger;
      }
      this.trigger(new Component.ComponentEvent(EVENTS.READY));
    });
  }
  /**
   * Destroy Flicking and remove all event handlers.
   * @remarks
   * This method cleans up all resources including event handlers, components, and plugins.
   * After calling this method, {@link Flicking.initialized} will be `false` and the instance should not be used.
   */
  destroy() {
    this.off();
    this._autoResizer.disable();
    this._control.destroy();
    this._camera.destroy();
    this._renderer.destroy();
    this._plugins.forEach((plugin) => plugin.destroy());
    this._scheduleResize = false;
    this._initialized = false;
    this._isResizing = false;
  }
  /**
   * Move to the previous panel (current index - 1).
   * @param duration - Duration of the panel movement animation (unit: ms). Defaults to {@link FlickingOptions.duration}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns Promise that resolves after reaching the previous panel
   */
  prev(duration = this._duration) {
    var _a, _b, _c;
    return this.moveTo((_c = (_b = (_a = this._control.activePanel) == null ? void 0 : _a.prev()) == null ? void 0 : _b.index) != null ? _c : -1, duration, DIRECTION.PREV);
  }
  /**
   * Move to the next panel (current index + 1).
   * @param duration - Duration of the panel movement animation (unit: ms). Defaults to {@link FlickingOptions.duration}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns Promise that resolves after reaching the next panel
   */
  next(duration = this._duration) {
    var _a, _b, _c;
    return this.moveTo((_c = (_b = (_a = this._control.activePanel) == null ? void 0 : _a.next()) == null ? void 0 : _b.index) != null ? _c : this._renderer.panelCount, duration, DIRECTION.NEXT);
  }
  /**
   * Move to the panel with the given index.
   * @param index - The index of the panel to move to
   * @param duration - Duration of the animation (unit: ms). Defaults to {@link FlickingOptions.duration}
   * @param direction - Direction to move (circular mode only). Defaults to {@link DIRECTION.NONE}
   * @fires {@link MovementEvents}
   * @throws {@link MovementErrors}
   * @returns Promise that resolves after reaching the target panel
   */
  moveTo(index, duration = this._duration, direction = DIRECTION.NONE) {
    const renderer = this._renderer;
    const panelCount = renderer.panelCount;
    const panel = renderer.getPanel(index);
    if (!panel) {
      return Promise.reject(
        new FlickingError(MESSAGE.INDEX_OUT_OF_RANGE(index, 0, panelCount - 1), CODE.INDEX_OUT_OF_RANGE)
      );
    }
    if (this._control.animating) {
      return Promise.reject(
        new FlickingError(MESSAGE.ANIMATION_ALREADY_PLAYING, CODE.ANIMATION_ALREADY_PLAYING)
      );
    }
    if (this._control.holding) {
      this._control.controller.release();
    }
    return this._control.moveToPanel(panel, {
      duration,
      direction
    });
  }
  /**
   * Change the destination and duration of the animation currently playing.
   * @remarks
   * This method does nothing if no animation is currently playing.
   * @param index - The index of the panel to move to
   * @param duration - Duration of the animation (unit: ms)
   * @param direction - Direction to move. Only available when {@link FlickingOptions.circular} is enabled
   * @since 4.10.0
   * @throws {@link AnimationUpdateErrors}
   */
  updateAnimation(index, duration, direction) {
    if (!this._control.animating) {
      return;
    }
    const renderer = this._renderer;
    const panelCount = renderer.panelCount;
    const panel = renderer.getPanel(index);
    if (!panel) {
      throw new FlickingError(
        MESSAGE.INDEX_OUT_OF_RANGE(index, 0, panelCount - 1),
        CODE.INDEX_OUT_OF_RANGE
      );
    }
    this._control.updateAnimation(panel, duration, direction);
  }
  /**
   * Stop the animation currently playing.
   * @remarks
   * This method does nothing if no animation is currently playing.
   * @since 4.10.0
   * @fires {@link MoveEndEvent}
   */
  stopAnimation() {
    if (!this._control.animating) {
      return;
    }
    this._control.stopAnimation();
  }
  /**
   * Get the panel at the given index.
   * @param index - The index of the panel to get
   * @returns The panel at the given index, or `null` if it doesn't exist. This is a shorthand for `Flicking.renderer.getPanel(index)`.
   * @example
   * ```ts
   * const panel = flicking.getPanel(0);
   * // Which is a shorthand to...
   * const samePanel = flicking.panels[0];
   * ```
   */
  getPanel(index) {
    return this._renderer.getPanel(index);
  }
  /**
   * Enable user input (mouse/touch).
   * @remarks
   * This is a shorthand for `Flicking.control.enable`.
   * @returns The current instance for method chaining
   */
  enableInput() {
    this._control.enable();
    return this;
  }
  /**
   * Disable user input (mouse/touch).
   * @remarks
   * This is a shorthand for `Flicking.control.disable`.
   * @returns The current instance for method chaining
   */
  disableInput() {
    this._control.disable();
    return this;
  }
  /**
   * Get the current Flicking status.
   * @param options - {@link GetStatusParams}
   * @returns Status object that can be used with {@link Flicking.setStatus} to restore the state
   */
  getStatus(options = {}) {
    var _a, _b;
    const { index = true, position = true, includePanelHTML = false, visiblePanelsOnly = false } = options;
    const camera = this._camera;
    const panels = visiblePanelsOnly ? this.visiblePanels : this.panels;
    const status = {
      panels: panels.map((panel) => {
        const panelInfo = { index: panel.index };
        if (includePanelHTML) {
          panelInfo.html = panel.element.outerHTML;
        }
        return panelInfo;
      })
    };
    if (index) {
      status.index = this.index;
    }
    if (position) {
      const nearestAnchor = camera.findNearestAnchor(camera.position);
      if (nearestAnchor) {
        status.position = {
          panel: nearestAnchor.panel.index,
          progressInPanel: camera.getProgressInPanel(nearestAnchor.panel)
        };
      }
    }
    if (visiblePanelsOnly) {
      const visiblePanels = this.visiblePanels;
      status.visibleOffset = (_b = (_a = visiblePanels[0]) == null ? void 0 : _a.index) != null ? _b : 0;
    }
    return status;
  }
  /**
   * Restore Flicking to the state of the given {@link Status}.
   * @param status - {@link Status}
   * @throws {@link StatusRestoreErrors}
   */
  setStatus(status) {
    var _a;
    if (!this._initialized) {
      throw new FlickingError(MESSAGE.NOT_INITIALIZED, CODE.NOT_INITIALIZED);
    }
    const { index, position, visibleOffset, panels } = status;
    const renderer = this._renderer;
    const control = this._control;
    if (((_a = panels[0]) == null ? void 0 : _a.html) && !this._renderExternal) {
      renderer.batchRemove({
        index: 0,
        deleteCount: this.panels.length,
        hasDOMInElements: true
      });
      renderer.batchInsert({
        index: 0,
        elements: parseElement(panels.map((panel) => panel.html)),
        hasDOMInElements: true
      });
    }
    if (index != null) {
      const panelIndex = visibleOffset ? index - visibleOffset : index;
      void this.moveTo(panelIndex, 0).catch(() => void 0);
    }
    if (position && this._moveType === MOVE_TYPE.FREE_SCROLL) {
      const { panel, progressInPanel } = position;
      const panelIndex = visibleOffset ? panel - visibleOffset : panel;
      const panelRange = renderer.panels[panelIndex].range;
      const newCameraPos = panelRange.min + (panelRange.max - panelRange.min) * progressInPanel;
      void control.moveToPosition(newCameraPos, 0).catch(() => void 0);
    }
  }
  /**
   * Add plugins to Flicking.
   * @remarks
   * Plugins are automatically initialized if Flicking is already initialized.
   * @param plugins - {@link Plugin}
   * @returns The current instance for method chaining
   * @see https://github.com/naver/egjs-flicking-plugins
   */
  addPlugins(...plugins) {
    if (this._initialized) {
      plugins.forEach((item) => item.init(this));
    }
    this._plugins.push(...plugins);
    return this;
  }
  /**
   * Remove plugins from Flicking.
   * @param plugins - {@link Plugin}
   * @returns The current instance for method chaining
   * @see https://github.com/naver/egjs-flicking-plugins
   */
  removePlugins(...plugins) {
    plugins.forEach((item) => {
      const foundIndex = findIndex(this._plugins, (val) => val === item);
      if (foundIndex >= 0) {
        item.destroy();
        this._plugins.splice(foundIndex, 1);
      }
    });
    return this;
  }
  /**
   * Update viewport and panel sizes.
   * @remarks
   * This method does nothing if a resize is already in progress.
   * @fires {@link ResizeEvents}
   * @returns Promise that resolves when resize is complete
   */
  resize() {
    return __async(this, null, function* () {
      if (!this._initialized) {
        return;
      }
      if (this._isResizing) {
        this._scheduleResize = true;
        return;
      }
      this._scheduleResize = false;
      this._isResizing = true;
      const viewport = this._viewport;
      const renderer = this._renderer;
      const camera = this._camera;
      const control = this._control;
      const activePanel = control.activePanel;
      const prevWidth = viewport.width;
      const prevHeight = viewport.height;
      const prevProgressInPanel = activePanel ? camera.getProgressInPanel(activePanel) : 0;
      this.trigger(
        new Component.ComponentEvent(EVENTS.BEFORE_RESIZE, {
          width: prevWidth,
          height: prevHeight,
          element: viewport.element
        })
      );
      if (this._optimizeSizeUpdate) {
        const measuredSize = viewport.measureSize();
        if (this.horizontal && measuredSize.width !== prevWidth || !this.horizontal && measuredSize.height !== prevHeight) {
          yield renderer.forceRenderAllPanels();
        }
      } else {
        yield renderer.forceRenderAllPanels();
      }
      if (!this._initialized) {
        return;
      }
      viewport.resize();
      renderer.updatePanelSize();
      camera.updateAlignPos();
      camera.updateRange();
      camera.updateAnchors();
      camera.updateAdaptiveHeight();
      camera.updatePanelOrder();
      if (!control.animating) {
        control.updatePosition(prevProgressInPanel);
      }
      camera.updateOffset();
      yield renderer.render();
      if (!this._initialized) {
        return;
      }
      if (control.animating) ;
      else {
        control.updatePosition(prevProgressInPanel);
        control.updateInput();
      }
      const newWidth = viewport.width;
      const newHeight = viewport.height;
      const sizeChanged = newWidth !== prevWidth || newHeight !== prevHeight;
      this.trigger(
        new Component.ComponentEvent(EVENTS.AFTER_RESIZE, {
          width: viewport.width,
          height: viewport.height,
          prev: {
            width: prevWidth,
            height: prevHeight
          },
          sizeChanged,
          element: viewport.element
        })
      );
      this._isResizing = false;
      if (this._scheduleResize) {
        void this.resize();
      }
      return;
    });
  }
  /**
   * Add new panels after the last panel.
   * @param element - A new HTMLElement, outerHTML string, or an array of both
   * @throws {@link DOMManipulationErrors}
   * @returns Array of appended panels
   * @example
   * ```ts
   * const flicking = new Flicking("#flick");
   * flicking.append(document.createElement("div"));
   * flicking.append("<div>Panel</div>");
   * flicking.append(["<div>Panel</div>", document.createElement("div")]);
   * ```
   */
  append(element) {
    return this.insert(this._renderer.panelCount, element);
  }
  /**
   * Add new panels before the first panel.
   * @remarks
   * This will increase the index of existing panels by the number of panels added.
   * @param element - A new HTMLElement, outerHTML string, or an array of both
   * @throws {@link DOMManipulationErrors}
   * @returns Array of prepended panels
   * @example
   * ```ts
   * const flicking = new Flicking("#flick");
   * flicking.prepend(document.createElement("div"));
   * flicking.prepend("<div>Panel</div>");
   * flicking.prepend(["<div>Panel</div>", document.createElement("div")]);
   * ```
   */
  prepend(element) {
    return this.insert(0, element);
  }
  /**
   * Insert new panels at the given index.
   * @remarks
   * This will increase the index of panels at or after the given index by the number of panels added.
   * @param index - Index to insert new panels at
   * @param element - A new HTMLElement, outerHTML string, or an array of both
   * @throws {@link DOMManipulationErrors}
   * @returns Array of inserted panels
   * @example
   * ```ts
   * const flicking = new Flicking("#flick");
   * flicking.insert(0, document.createElement("div"));
   * flicking.insert(2, "<div>Panel</div>");
   * flicking.insert(1, ["<div>Panel</div>", document.createElement("div")]);
   * ```
   */
  insert(index, element) {
    if (this._renderExternal) {
      throw new FlickingError(MESSAGE.NOT_ALLOWED_IN_FRAMEWORK, CODE.NOT_ALLOWED_IN_FRAMEWORK);
    }
    return this._renderer.batchInsert({
      index,
      elements: parseElement(element),
      hasDOMInElements: true
    });
  }
  /**
   * Remove panels starting from the given index.
   * @remarks
   * This will decrease the index of panels after the removed ones by the number of panels removed.
   * @param index - Index of the first panel to remove
   * @param deleteCount - Number of panels to remove. Defaults to `1`
   * @throws {@link DOMManipulationErrors}
   * @returns Array of removed panels
   */
  remove(index, deleteCount = 1) {
    if (this._renderExternal) {
      throw new FlickingError(MESSAGE.NOT_ALLOWED_IN_FRAMEWORK, CODE.NOT_ALLOWED_IN_FRAMEWORK);
    }
    return this._renderer.batchRemove({
      index,
      deleteCount,
      hasDOMInElements: true
    });
  }
  /**
   * Factory method to create the appropriate Control implementation based on moveType option.
   * @internal
   * @privateRemarks
   * Called during constructor and when moveType option is changed. The moveType option must be set before calling this method.
   * Throws error if moveType is invalid.
   */
  _createControl() {
    var _a;
    const moveType = this._moveType;
    const moveTypes = Object.keys(MOVE_TYPE).map((key) => MOVE_TYPE[key]);
    const moveTypeStr = Array.isArray(moveType) ? moveType[0] : moveType;
    const moveTypeOptions = Array.isArray(moveType) ? (_a = moveType[1]) != null ? _a : {} : {};
    if (!includes(moveTypes, moveTypeStr)) {
      throw new FlickingError(
        MESSAGE.WRONG_OPTION("moveType", JSON.stringify(moveType)),
        CODE.WRONG_OPTION
      );
    }
    switch (moveTypeStr) {
      case MOVE_TYPE.SNAP:
        return new SnapControl(moveTypeOptions);
      case MOVE_TYPE.FREE_SCROLL:
        return new FreeControl(moveTypeOptions);
      case MOVE_TYPE.STRICT:
        return new StrictControl(moveTypeOptions);
    }
  }
  /**
   * Factory method to create Camera instance for managing viewport movement and positioning.
   * @internal
   * @privateRemarks
   * Called during constructor. The align option must be set before calling this method.
   * Warns if both circular and bound options are enabled (bound is ignored).
   */
  _createCamera() {
    if (this._circular && this._bound) {
      console.warn('"circular" and "bound" option cannot be used together, ignoring bound.');
    }
    return new Camera(this, {
      align: this._align
    });
  }
  /**
   * Factory method to create the appropriate Renderer implementation based on externalRenderer and virtual options.
   * @internal
   * @privateRemarks
   * Called during constructor. Selects ExternalRenderer if externalRenderer is provided, otherwise creates VanillaRenderer or ExternalRenderer based on renderExternal option.
   * Warns if virtual is enabled without panelsPerView.
   */
  _createRenderer() {
    const externalRenderer = this._externalRenderer;
    if (this._virtual && this._panelsPerView <= 0) {
      console.warn('"virtual" and "panelsPerView" option should be used together, ignoring virtual.');
    }
    return externalRenderer ? externalRenderer : this._renderExternal ? this._createExternalRenderer() : this._createVanillaRenderer();
  }
  /**
   * Factory method to create ExternalRenderer from renderExternal option (deprecated).
   * @internal
   * @privateRemarks
   * Called by _createRenderer when renderExternal option is set. The renderExternal option must not be null and must contain renderer class and options.
   */
  _createExternalRenderer() {
    const { renderer, rendererOptions } = this._renderExternal;
    return new renderer(__spreadValues({ align: this._align }, rendererOptions));
  }
  /**
   * Factory method to create VanillaRenderer for vanilla JavaScript rendering.
   * @internal
   * @privateRemarks
   * Called by _createRenderer when neither externalRenderer nor renderExternal is set.
   * Uses VirtualRenderingStrategy if virtual is enabled, otherwise NormalRenderingStrategy.
   */
  _createVanillaRenderer() {
    const virtual = this.virtualEnabled;
    return new VanillaRenderer({
      align: this._align,
      strategy: virtual ? new VirtualRenderingStrategy() : new NormalRenderingStrategy({
        providerCtor: VanillaElementProvider
      })
    });
  }
  /**
   * Move camera to the initial panel position based on defaultIndex option.
   * @internal
   * @privateRemarks
   * Called during init() method, after _initialResize(). Requires camera, renderer, and control to be already initialized.
   * Throws error if the initial panel position is not reachable.
   */
  _moveToInitialPanel() {
    const renderer = this._renderer;
    const control = this._control;
    const camera = this._camera;
    const defaultPanel = renderer.getPanel(this._defaultIndex) || renderer.getPanel(0);
    if (!defaultPanel) return;
    const nearestAnchor = camera.findNearestAnchor(defaultPanel.position);
    const initialPanel = nearestAnchor && defaultPanel.position !== nearestAnchor.panel.position && defaultPanel.index !== nearestAnchor.panel.index ? nearestAnchor.panel : defaultPanel;
    control.setActive(initialPanel, null, false);
    if (!nearestAnchor) {
      throw new FlickingError(
        MESSAGE.POSITION_NOT_REACHABLE(initialPanel.position),
        CODE.POSITION_NOT_REACHABLE
      );
    }
    let position = initialPanel.position;
    if (!camera.canReach(initialPanel)) {
      position = nearestAnchor.position;
    }
    camera.lookAt(position);
    control.updateInput();
    camera.updateOffset();
  }
  /**
   * Calculate initial viewport and panel sizes during initialization.
   * @internal
   * @privateRemarks
   * Called during init() method, before _moveToInitialPanel(). This is separate from the regular resize() to avoid triggering events before initialization is complete.
   * Requires viewport, renderer, camera, and control to be already initialized. Triggers BEFORE_RESIZE and AFTER_RESIZE events.
   */
  _initialResize() {
    const viewport = this._viewport;
    const renderer = this._renderer;
    const camera = this._camera;
    const control = this._control;
    this.trigger(
      new Component.ComponentEvent(EVENTS.BEFORE_RESIZE, {
        width: 0,
        height: 0,
        element: viewport.element
      })
    );
    viewport.resize();
    renderer.updatePanelSize();
    camera.updateAlignPos();
    camera.updateRange();
    camera.updateAnchors();
    camera.updateOffset();
    control.updateInput();
    const newWidth = viewport.width;
    const newHeight = viewport.height;
    const sizeChanged = newWidth !== 0 || newHeight !== 0;
    this.trigger(
      new Component.ComponentEvent(EVENTS.AFTER_RESIZE, {
        width: viewport.width,
        height: viewport.height,
        prev: {
          width: 0,
          height: 0
        },
        sizeChanged,
        element: viewport.element
      })
    );
  }
};
_Flicking.VERSION = "4.17.0";
let Flicking = _Flicking;
const SIDE_EVENTS = {
  HOLD_START: "sideHoldStart",
  HOLD_END: "sideHoldEnd",
  MOVE_START: "sideMoveStart",
  MOVE: "sideMove",
  MOVE_END: "sideMoveEnd",
  WILL_CHANGE: "sideWillChange",
  CHANGED: "sideChanged",
  WILL_RESTORE: "sideWillRestore",
  RESTORED: "sideRestored"
};
class CrossFlicking extends Flicking {
  constructor(root, options) {
    super(root, options);
    this._syncToCategory = (index, outerIndex) => {
      if (this._disableIndexSync) {
        return;
      }
      this.stopAnimation();
      this._sideFlicking.forEach((child, i) => {
        const { start, end } = this._sideState[i];
        if (start <= index && end >= index && outerIndex !== i) {
          child.stopAnimation();
          void child.moveTo(index, 0);
          void this.moveTo(i, 0);
        }
      });
    };
    this._setDraggable = (direction, draggable) => {
      if (!this._disableSlideOnHold) {
        return;
      }
      const dragThreshold = this._originalDragThreshold;
      const threshold = draggable ? dragThreshold && dragThreshold >= 10 ? dragThreshold : 10 : Infinity;
      if (direction === MOVE_DIRECTION.HORIZONTAL === this.horizontal) {
        this.dragThreshold = threshold;
      } else if (direction === MOVE_DIRECTION.VERTICAL === this.horizontal) {
        this._sideFlicking.forEach((child) => {
          child.dragThreshold = threshold;
        });
      }
    };
    this._setPreviousSideIndex = () => {
      this._sideFlicking.forEach((child, i) => {
        const { start, end } = this._sideState[i];
        if (this._preserveIndex) {
          if (this._nextIndex !== i) {
            if (child.index < start) {
              child.stopAnimation();
              void child.moveTo(start, 0);
            } else if (child.index > end) {
              child.stopAnimation();
              void child.moveTo(end, 0);
            }
          }
        } else {
          if (this._nextIndex !== i) {
            void child.moveTo(start, 0);
          }
        }
      });
    };
    this._addSideIndex = (e) => {
      e.sideIndex = this._sideFlicking[e.index].index;
    };
    this._onHorizontalHoldStart = () => {
      this._setDraggable(MOVE_DIRECTION.HORIZONTAL, true);
      this._moveDirection = null;
    };
    this._onHorizontalMove = (e) => {
      if (e.isTrusted && !this._moveDirection) {
        this._setDraggable(MOVE_DIRECTION.VERTICAL, false);
        this._moveDirection = MOVE_DIRECTION.HORIZONTAL;
      }
    };
    this._onHorizontalMoveEnd = (e) => {
      const visiblePanels = this.visiblePanels;
      if (visiblePanels.length > 1) {
        this._nextIndex = e.direction === "NEXT" ? visiblePanels[1].index : visiblePanels[0].index;
      } else {
        this._nextIndex = visiblePanels[0].index;
      }
      this._setDraggable(MOVE_DIRECTION.VERTICAL, true);
      this._moveDirection = null;
      requestAnimationFrame(() => this._setPreviousSideIndex());
      if (e.isTrusted) {
        this._syncToCategory(this._sideFlicking[this._nextIndex].index, this._nextIndex);
      }
    };
    this._onSideHoldStart = () => {
      this._setDraggable(MOVE_DIRECTION.VERTICAL, true);
      this._moveDirection = null;
    };
    this._onSideMove = (e) => {
      if (e.isTrusted && !this._moveDirection) {
        this._setDraggable(MOVE_DIRECTION.HORIZONTAL, false);
        this._moveDirection = MOVE_DIRECTION.VERTICAL;
      }
    };
    this._onSideMoveEnd = () => {
      this._setDraggable(MOVE_DIRECTION.HORIZONTAL, true);
      this._moveDirection = null;
    };
    this._onSideChanged = (e) => {
      if (this.visiblePanels.length < 2 && this._sideFlicking[this.index] === e.currentTarget) {
        this._syncToCategory(e.index, this.index);
      }
    };
    const { sideOptions = {}, preserveIndex = true, disableSlideOnHold = true, disableIndexSync = false } = options;
    this._moveDirection = null;
    this._nextIndex = 0;
    this._originalDragThreshold = this.dragThreshold;
    this._sideOptions = sideOptions;
    this._preserveIndex = preserveIndex;
    this._disableSlideOnHold = disableSlideOnHold;
    this._disableIndexSync = disableIndexSync;
  }
  // Components
  get sideFlicking() {
    return this._sideFlicking;
  }
  get sideIndex() {
    return this._sideFlicking.map((i) => i.index);
  }
  get sideState() {
    return this._sideState;
  }
  // Options Getter
  get sideOptions() {
    return this._sideOptions;
  }
  get preserveIndex() {
    return this._preserveIndex;
  }
  get disableSlideOnHold() {
    return this._disableSlideOnHold;
  }
  get disableIndexSync() {
    return this._disableIndexSync;
  }
  // Options Setter
  set sideOptions(val) {
    this._sideOptions = val;
  }
  set preserveIndex(val) {
    this._preserveIndex = val;
  }
  set disableSlideOnHold(val) {
    this._disableSlideOnHold = val;
  }
  set disableIndexSync(val) {
    this._disableIndexSync = val;
  }
  init() {
    return super.init().then(() => {
      this._sideState = this._createSideState();
      this._sideFlicking = this._createSideFlicking();
      this._addComponentEvents();
    });
  }
  destroy() {
    this._sideFlicking.forEach((flicking) => {
      flicking.destroy();
    });
    super.destroy();
  }
  _addComponentEvents() {
    this.on(EVENTS.HOLD_START, this._onHorizontalHoldStart);
    this.on(EVENTS.MOVE, this._onHorizontalMove);
    this.on(EVENTS.MOVE_END, this._onHorizontalMoveEnd);
    [EVENTS.CHANGED, EVENTS.WILL_CHANGE].forEach((event) => {
      this.on(event, this._addSideIndex);
    });
    this._sideFlicking.forEach((flicking, mainIndex) => {
      flicking.on(EVENTS.HOLD_START, this._onSideHoldStart);
      flicking.on(EVENTS.MOVE, this._onSideMove);
      flicking.on(EVENTS.MOVE_END, this._onSideMoveEnd);
      flicking.on(EVENTS.CHANGED, this._onSideChanged);
      Object.keys(SIDE_EVENTS).forEach((name) => {
        flicking.on(EVENTS[name], (event) => {
          this.trigger(
            new Component.ComponentEvent(SIDE_EVENTS[name], __spreadValues({
              mainIndex
            }, event))
          );
        });
      });
    });
  }
  _createSideState() {
    const viewportEl = this.element;
    const cameraEl = this.camera.element;
    const panels = toArray(cameraEl.children);
    const isCrossStructure = getDataAttributes(viewportEl, "data-cross-").structure;
    let sideState = [];
    if (!isCrossStructure) {
      const groupPanels = this._getGroupFromAttribute(panels);
      const groupKeys = Object.keys(groupPanels);
      if (groupKeys.length) {
        sideState = this._getSideStateFromGroup(groupPanels);
        this.remove(0, this.panelCount - groupKeys.length);
      } else {
        sideState = this._getSideStateFromPanels(panels);
      }
      this._createCrossStructure(sideState);
    } else {
      sideState = this._getSideStateFromCrossStructure(panels);
    }
    void this.resize();
    return sideState;
  }
  _createCrossStructure(sideState) {
    const sideCamera = document.createElement("div");
    let sidePanels = "";
    sideCamera.classList.add(CLASS.CAMERA);
    sideState.forEach((state, i) => {
      const panel = this.camera.children[i];
      sidePanels += state.element.innerHTML;
      Array.from(panel.attributes).forEach((attribute) => panel.removeAttribute(attribute.name));
    });
    sideCamera.innerHTML = sidePanels;
    sideState.forEach((_, i) => {
      const panel = this.camera.children[i];
      [CLASS.VIEWPORT, CLASS.VERTICAL].forEach((className) => {
        if (!panel.classList.contains(className)) {
          panel.classList.add(className);
        }
      });
      panel.innerHTML = sideCamera.outerHTML;
    });
    this.element.setAttribute("data-cross-structure", "true");
  }
  _getGroupFromAttribute(panels) {
    const groupKeys = [];
    const groupPanels = {};
    panels.forEach((panel) => {
      const groupKey = getDataAttributes(panel, "data-cross-").groupkey;
      if (groupKey && !includes(groupKeys, groupKey)) {
        groupKeys.push(groupKey);
        groupPanels[groupKey] = [panel];
      } else if (groupKey) {
        groupPanels[groupKey].push(panel);
      }
    });
    return groupPanels;
  }
  _getSideStateFromGroup(groupPanels) {
    return Object.keys(groupPanels).reduce((state, key) => {
      const start = state.length ? +state[state.length - 1].end + 1 : 0;
      const element = groupPanels[key].reduce((el, panel) => {
        el.innerHTML += panel.outerHTML;
        return el;
      }, document.createElement("div"));
      return [
        ...state,
        {
          key,
          start,
          end: start + groupPanels[key].length - 1,
          element
        }
      ];
    }, []);
  }
  _getSideStateFromPanels(panels) {
    return panels.reduce((state, panel, i) => {
      const start = state.length ? +state[state.length - 1].end + 1 : 0;
      return [
        ...state,
        {
          key: i.toString(),
          start,
          end: start + panel.children.length - 1,
          element: panel
        }
      ];
    }, []);
  }
  _getSideStateFromCrossStructure(panels) {
    const groupPanels = this._getGroupFromAttribute(panels);
    return this._getSideStateFromGroup(groupPanels);
  }
  _createSideFlicking() {
    return this.sideState.map((state, i) => {
      return new Flicking(this.camera.children[i], __spreadProps(__spreadValues({}, this.sideOptions), {
        horizontal: false,
        panelsPerView: 1,
        defaultIndex: state.start
      }));
    });
  }
}
const getDefaultCameraTransform = (align = ALIGN.CENTER, horizontal = true, firstPanelSize) => {
  const cameraAlign = getCameraAlign(align);
  const panelAlign = getPanelAlign(align);
  if (panelAlign == null) return "";
  const camPosition = `calc(${cameraAlign} - (${firstPanelSize || "0px"} * ${panelAlign.percentage}) - ${panelAlign.absolute}px)`;
  return horizontal ? `translate(${camPosition})` : `translate(0, ${camPosition})`;
};
const getCameraAlign = (align) => {
  const alignVal = typeof align === "object" ? align.camera : align;
  return parseAlign(alignVal);
};
const getPanelAlign = (align) => {
  const alignVal = typeof align === "object" ? align.panel : align;
  return parseArithmeticExpression(parseAlign(alignVal));
};
const parseAlign = (alignVal) => {
  if (typeof alignVal === "number") {
    return `${alignVal}px`;
  }
  switch (alignVal) {
    case ALIGN.CENTER:
      return "50%";
    case ALIGN.NEXT:
      return "100%";
    case ALIGN.PREV:
      return "0%";
    default:
      return alignVal;
  }
};
const getRenderingPanels = (flicking, diffResult) => {
  const removedPanels = diffResult.removed.reduce((map, idx) => {
    map[idx] = true;
    return map;
  }, {});
  const maintainedMap = diffResult.maintained.reduce((map, [prev, current]) => {
    map[prev] = current;
    return map;
  }, {});
  const renderingPanels = flicking.panels.filter((panel) => !removedPanels[panel.index]);
  if (!flicking.useCSSOrder) {
    renderingPanels.sort((panel1, panel2) => panel1.position + panel1.offset - (panel2.position + panel2.offset));
  }
  return [
    ...renderingPanels.map((panel) => diffResult.list[maintainedMap[panel.index]]),
    ...diffResult.added.map((idx) => diffResult.list[idx])
  ];
};
const sync = (flicking, diffResult, rendered) => {
  const renderer = flicking.renderer;
  const panels = renderer.panels;
  const prevList = [...diffResult.prevList];
  const added = [];
  const removed = [];
  if (diffResult.removed.length > 0) {
    let endIdx = -1;
    let prevIdx = -1;
    diffResult.removed.forEach((removedIdx) => {
      if (endIdx < 0) {
        endIdx = removedIdx;
      }
      if (prevIdx >= 0 && removedIdx !== prevIdx - 1) {
        removed.push(...batchRemove(renderer, prevIdx, endIdx + 1));
        endIdx = removedIdx;
        prevIdx = removedIdx;
      } else {
        prevIdx = removedIdx;
      }
      prevList.splice(removedIdx, 1);
    });
    removed.push(...batchRemove(renderer, prevIdx, endIdx + 1));
  }
  diffResult.ordered.forEach(([from, to]) => {
    const prevPanel = panels.splice(from, 1)[0];
    panels.splice(to, 0, prevPanel);
  });
  if (diffResult.ordered.length > 0) {
    panels.forEach((panel, idx) => {
      const indexDiff = idx - panel.index;
      if (indexDiff > 0) {
        panel.increaseIndex(indexDiff);
      } else {
        panel.decreaseIndex(-indexDiff);
      }
    });
    panels.sort((panel1, panel2) => panel1.index - panel2.index);
    panels.forEach((panel) => {
      panel.updatePosition();
    });
  }
  if (diffResult.added.length > 0) {
    let startIdx = -1;
    let prevIdx = -1;
    const addedElements = rendered.slice(prevList.length);
    diffResult.added.forEach((addedIdx, idx) => {
      if (startIdx < 0) {
        startIdx = idx;
      }
      if (prevIdx >= 0 && addedIdx !== prevIdx + 1) {
        added.push(...batchInsert(renderer, diffResult, addedElements, startIdx, idx + 1));
        startIdx = -1;
        prevIdx = -1;
      } else {
        prevIdx = addedIdx;
      }
    });
    if (startIdx >= 0) {
      added.push(...batchInsert(renderer, diffResult, addedElements, startIdx));
    }
  }
  if (diffResult.added.length > 0 || diffResult.removed.length > 0) {
    renderer.updateAfterPanelChange(added, removed);
  } else if (diffResult.ordered.length > 0) {
    const camera = flicking.camera;
    camera.updateRange();
    camera.updateOffset();
    camera.updateAnchors();
    camera.resetNeedPanelHistory();
  }
};
const batchInsert = (renderer, diffResult, addedElements, startIdx, endIdx) => {
  return renderer.batchInsertDefer(
    ...diffResult.added.slice(startIdx, endIdx).map((index, elIdx) => ({ index, elements: [addedElements[elIdx]], hasDOMInElements: false }))
  );
};
const batchRemove = (renderer, startIdx, endIdx) => {
  const removed = renderer.panels.slice(startIdx, endIdx);
  return renderer.batchRemoveDefer({ index: startIdx, deleteCount: removed.length, hasDOMInElements: false });
};
const withFlickingMethods = (prototype, flickingName) => {
  [Component.prototype, Flicking.prototype].forEach((proto) => {
    Object.getOwnPropertyNames(proto).filter((name) => !prototype[name] && name.indexOf("_") !== 0 && name !== "constructor").forEach((name) => {
      const descriptor = Object.getOwnPropertyDescriptor(proto, name);
      if (descriptor.value) {
        Object.defineProperty(prototype, name, {
          value: function(...args) {
            return descriptor.value.call(this[flickingName], ...args);
          }
        });
      } else {
        const getterDescriptor = {};
        if (descriptor.get) {
          getterDescriptor.get = function() {
            var _a;
            const flicking = this[flickingName];
            return flicking && ((_a = descriptor.get) == null ? void 0 : _a.call(flicking));
          };
        }
        if (descriptor.set) {
          getterDescriptor.set = function(...args) {
            var _a;
            return (_a = descriptor.set) == null ? void 0 : _a.call(this[flickingName], ...args);
          };
        }
        Object.defineProperty(prototype, name, getterDescriptor);
      }
    });
  });
};
function keys(obj) {
  return Object.keys(obj);
}
function isObject(val) {
  return typeof val === "object";
}
function isFunction(val) {
  return typeof val === "function";
}
var OBSERVERS_PATH = "__observers__";
var COMPUTED_PATH = "__computed__";
var CFCS_DETECTED_DEPENDENCIES_VERSION = 1;
var CFCS_DETECTED_DEPENDENCIES = "__CFCS_DETECTED_DEPENDENCIES__";
function __spreadArray(to, from, pack) {
  if (arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
    if (ar || !(i in from)) {
      if (!ar) ar = Array.prototype.slice.call(from, 0, i);
      ar[i] = from[i];
    }
  }
  return to.concat(ar || Array.prototype.slice.call(from));
}
function getDetectedStack() {
  Object[CFCS_DETECTED_DEPENDENCIES] = Object[CFCS_DETECTED_DEPENDENCIES] || {};
  var versionList = Object[CFCS_DETECTED_DEPENDENCIES];
  versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] = versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] || [];
  return versionList[CFCS_DETECTED_DEPENDENCIES_VERSION];
}
function getCurrentDetected() {
  var stack = getDetectedStack();
  return stack[stack.length - 1];
}
var Observer = /* @__PURE__ */ (function() {
  function Observer2(value) {
    this._emitter = new Component();
    this._current = value;
  }
  var __proto = Observer2.prototype;
  Object.defineProperty(__proto, "current", {
    /**
     * return the current value.
     */
    get: function() {
      var currentDetected = getCurrentDetected();
      currentDetected === null || currentDetected === void 0 ? void 0 : currentDetected.push(this);
      return this._current;
    },
    set: function(value) {
      this._setCurrent(value);
    },
    enumerable: false,
    configurable: true
  });
  __proto.subscribe = function(callback) {
    this.current;
    this._emitter.on("update", callback);
    return this;
  };
  __proto.unsubscribe = function(callback) {
    this._emitter.off("update", callback);
    return this;
  };
  __proto._setCurrent = function(value) {
    var prevValue = this._current;
    var isUpdate = value !== prevValue;
    this._current = value;
    if (isUpdate) {
      this._emitter.trigger("update", value, prevValue);
    }
  };
  __proto.toString = function() {
    return "".concat(this.current);
  };
  __proto.valueOf = function() {
    return this.current;
  };
  return Observer2;
})();
function injectObserve(prototype, memberName, publicName) {
  if (publicName === void 0) {
    publicName = memberName;
  }
  var nextAttributes = {
    configurable: true,
    get: function() {
      return getObserver(this, publicName).current;
    },
    set: function(value) {
      getObserver(this, publicName, value).current = value;
    }
  };
  Object.defineProperty(prototype, memberName, nextAttributes);
  if (publicName !== memberName) {
    Object.defineProperty(prototype, publicName, {
      configurable: true,
      get: function() {
        return getObserver(this, publicName).current;
      }
    });
  }
}
function Observe() {
  var args = [];
  for (var _i = 0; _i < arguments.length; _i++) {
    args[_i] = arguments[_i];
  }
  if (args.length > 1) {
    return injectObserve(args[0], args[1]);
  }
  return function(prototype, memberName) {
    return injectObserve(prototype, memberName, args[0]);
  };
}
function injectReactiveSubscribe(object) {
  object["subscribe"] = function(name, callback) {
    this[name];
    getObserver(this, name).subscribe(callback);
  };
  object["unsubscribe"] = function(name, callback) {
    var _this = this;
    if (!name) {
      keys(getObservers(this)).forEach(function(observerName) {
        _this.unsubscribe(observerName);
      });
      return;
    }
    if (!(name in this)) {
      return;
    }
    getObserver(this, name).unsubscribe(callback);
  };
}
function makeReactiveObject(setup, all) {
  var result = isFunction(setup) ? setup() : setup;
  var reactiveObject = {};
  defineObservers(reactiveObject);
  keys(result).forEach(function(name) {
    var value = result[name];
    if (isObserver(value)) {
      setObserver(reactiveObject, name, value);
    } else {
      setObserver(reactiveObject, name, observe(value));
    }
    Observe(name)(reactiveObject, name);
  });
  injectReactiveSubscribe(reactiveObject);
  return reactiveObject;
}
function reactive(setup) {
  return makeReactiveObject(setup);
}
function observe(defaultValue) {
  return new Observer(defaultValue);
}
function withReactiveMethods(ref, methods) {
  var obj = {};
  if (!methods) {
    return obj;
  }
  methods.forEach(function(name) {
    obj[name] = function() {
      var args = [];
      for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
      }
      var current = ref.current || ref.value;
      return current[name].apply(current, args);
    };
  });
  return obj;
}
function defineObservers(instance) {
  var observers = {};
  Object.defineProperty(instance, OBSERVERS_PATH, {
    get: function() {
      return observers;
    }
  });
  return observers;
}
function getObservers(instance, isComputed) {
  var _a, _b;
  if (!instance[OBSERVERS_PATH]) {
    defineObservers(instance);
  }
  var observers = instance[OBSERVERS_PATH];
  {
    var computedList = (_b = (_a = instance === null || instance === void 0 ? void 0 : instance.constructor) === null || _a === void 0 ? void 0 : _a.prototype) === null || _b === void 0 ? void 0 : _b[COMPUTED_PATH];
    if (computedList) {
      computedList.forEach(function(name) {
        if (!(name in observers) && name in instance) {
          instance[name];
        }
      });
    }
  }
  return observers;
}
function getObserver(instance, name, defaultValue) {
  var observers = getObservers(instance);
  if (!observers[name]) {
    observers[name] = observe(defaultValue);
  }
  return observers[name];
}
function setObserver(instance, name, observer) {
  var observers = getObservers(instance);
  observers[name] = observer;
}
function isObserver(val) {
  return val && isObject(val) && "current" in val && "subscribe" in val && "unsubscribe" in val;
}
function adaptReactive(adapter, props) {
  var objectAdapter = isFunction(adapter) ? {
    setup: adapter
  } : adapter;
  function getProps() {
    var _a, _b, _c, _d, _e;
    return (_e = (_c = (_a = props === null || props === void 0 ? void 0 : props()) !== null && _a !== void 0 ? _a : (_b = objectAdapter.props) === null || _b === void 0 ? void 0 : _b.call(objectAdapter)) !== null && _c !== void 0 ? _c : (_d = objectAdapter.data) === null || _d === void 0 ? void 0 : _d.call(objectAdapter)) !== null && _e !== void 0 ? _e : {};
  }
  var eventEmitter = new Component();
  var mountedHooks = [];
  var initHooks = [];
  var destroyHooks = [];
  var onHooks = [];
  var instanceRef = {
    current: null
  };
  var offHooksList = [];
  var initialState = null;
  var eventNames = [];
  var methodNames = [];
  var onMounted = function(callback) {
    mountedHooks.push(callback);
  };
  var onInit = function(callback) {
    initHooks.push(callback);
  };
  var onDestroy = function(callback) {
    destroyHooks.push(callback);
  };
  var on = function(callback) {
    onHooks.push(callback);
  };
  var emit = function(eventName) {
    var params = [];
    for (var _i = 1; _i < arguments.length; _i++) {
      params[_i - 1] = arguments[_i];
    }
    eventEmitter.trigger.apply(eventEmitter, __spreadArray([eventName], params, false));
  };
  var setInitialState = function(state) {
    initialState = state;
  };
  var setEvents = function(events) {
    eventNames = events;
  };
  var setMethods = function(methods) {
    methodNames = methods;
  };
  if (objectAdapter.setup) {
    instanceRef.current = objectAdapter.setup({
      getProps,
      setInitialState,
      setEvents,
      setMethods,
      onMounted,
      onDestroy,
      onInit,
      emit,
      on
    }) || null;
  }
  if (objectAdapter.created) {
    instanceRef.current = objectAdapter.created(getProps()) || null;
  }
  if (objectAdapter.events) {
    setEvents(objectAdapter.events);
  }
  if (objectAdapter.state) {
    setInitialState(objectAdapter.state);
  }
  if (objectAdapter.methods) {
    setMethods(objectAdapter.methods);
  }
  if (objectAdapter.mounted) {
    onMounted(objectAdapter.mounted);
  }
  if (objectAdapter.destroy) {
    destroyHooks.push(objectAdapter.destroy);
  }
  if (objectAdapter.init) {
    initHooks.push(objectAdapter.init);
  }
  if (objectAdapter.on) {
    onHooks.push(function(instance, eventName, listener) {
      var off = objectAdapter.on(instance, eventName, listener);
      return function() {
        var _a;
        off && off();
        (_a = objectAdapter.off) === null || _a === void 0 ? void 0 : _a.call(objectAdapter, instance, eventName, listener);
      };
    });
  }
  return {
    events: function() {
      return eventNames;
    },
    state: function() {
      var inst = instanceRef.current;
      if (initialState) {
        return initialState;
      }
      if (inst) {
        var observers_1 = getObservers(inst);
        setInitialState(keys(observers_1).reduce(function(prev, cur) {
          prev[cur] = observers_1[cur].current;
          return prev;
        }, {}));
      }
      return initialState || {};
    },
    instance: function() {
      return instanceRef.current;
    },
    mounted: function() {
      var props2 = getProps();
      mountedHooks.forEach(function(hook) {
        instanceRef.current = hook(props2, instanceRef.current) || instanceRef.current;
      });
    },
    init: function() {
      var instance = instanceRef.current;
      var props2 = getProps();
      offHooksList = eventNames.map(function(eventName) {
        var listener = function() {
          var _a;
          var params = [];
          for (var _i = 0; _i < arguments.length; _i++) {
            params[_i] = arguments[_i];
          }
          (_a = eventEmitter).trigger.apply(_a, __spreadArray([eventName], params, false));
        };
        var instance2 = instanceRef.current;
        return onHooks.map(function(hook) {
          return hook(instance2, eventName, listener);
        }).filter(Boolean);
      });
      initHooks.forEach(function(hook) {
        hook(instance, props2);
      });
    },
    destroy: function() {
      offHooksList.forEach(function(offHooks) {
        offHooks.forEach(function(hook) {
          hook();
        });
      });
      eventEmitter.off();
      var instance = instanceRef.current;
      var props2 = getProps();
      destroyHooks.forEach(function(hook) {
        hook(instance, props2);
      });
    },
    methods: function() {
      return withReactiveMethods(instanceRef, methodNames);
    },
    on: function(eventName, listener) {
      eventEmitter.on(eventName, listener);
    },
    off: function(eventName, listener) {
      eventEmitter.off(eventName, listener);
    }
  };
}
const getIsReachStart = (flicking) => !flicking.circular && flicking.index === 0;
const getIsReachEnd = (flicking) => !flicking.circular && flicking.index === flicking.panelCount - 1;
const getTotalPanelCount = (flicking) => flicking.panelCount;
const getCurrentPanelIndex = (flicking) => flicking.index;
const getProgress = (flicking) => {
  const cam = flicking.camera;
  const progressRatio = (cam.position - cam.range.min) / (cam.range.max - cam.range.min);
  const percent = Math.min(Math.max(progressRatio, 0), 1) * 100;
  return percent;
};
const getIndexProgress = (flicking) => {
  const cam = flicking.camera;
  const anchorPoints = cam.anchorPoints;
  const length = anchorPoints.length;
  const cameraPosition = cam.position;
  const isCircular = flicking.circularEnabled;
  let indexProgress = 0;
  const { min, max } = cam.range;
  const firstAnchorPoint = anchorPoints[0];
  const lastAnchorPoint = anchorPoints[length - 1];
  const distanceLastToFirst = max - lastAnchorPoint.position + (firstAnchorPoint.position - min);
  anchorPoints.some((anchorPoint, index) => {
    const anchorPosition = anchorPoint.position;
    const nextAnchorPoint = anchorPoints[index + 1];
    if (index === 0 && cameraPosition <= anchorPosition) {
      if (isCircular) {
        indexProgress = (cameraPosition - anchorPosition) / distanceLastToFirst;
      } else {
        indexProgress = (cameraPosition - anchorPosition) / anchorPoint.panel.size;
      }
    } else if (index === length - 1 && cameraPosition >= anchorPosition) {
      if (isCircular) {
        indexProgress = index + (cameraPosition - anchorPosition) / distanceLastToFirst;
      } else {
        indexProgress = index + (cameraPosition - anchorPosition) / anchorPoint.panel.size;
      }
    } else if (nextAnchorPoint && anchorPosition <= cameraPosition && cameraPosition <= nextAnchorPoint.position) {
      indexProgress = index + (cameraPosition - anchorPosition) / (nextAnchorPoint.position - anchorPosition);
    } else {
      return false;
    }
    return true;
  });
  return indexProgress;
};
const flickingReactiveAPIAdapter = ({ onInit, onDestroy, setMethods, getProps }) => {
  var _a, _b, _c;
  let flicking;
  const moveTo = (i) => {
    if (flicking == null) {
      return Promise.reject(new Error("Flicking instance is not available"));
    }
    if (flicking == null ? void 0 : flicking.animating) {
      return Promise.resolve();
    }
    return flicking.moveTo(i);
  };
  setMethods(["moveTo"]);
  const options = getProps().options;
  const reactiveObj = reactive({
    isReachStart: (options == null ? void 0 : options.defaultIndex) ? (options == null ? void 0 : options.defaultIndex) === 0 : true,
    isReachEnd: (options == null ? void 0 : options.totalPanelCount) && (options == null ? void 0 : options.defaultIndex) ? options.defaultIndex === options.totalPanelCount - 1 : false,
    totalPanelCount: (_a = options == null ? void 0 : options.totalPanelCount) != null ? _a : 0,
    currentPanelIndex: (_b = options == null ? void 0 : options.defaultIndex) != null ? _b : 0,
    progress: 0,
    indexProgress: (_c = options == null ? void 0 : options.defaultIndex) != null ? _c : 0,
    moveTo
  });
  const onChanged = () => {
    if (flicking === void 0) return;
    reactiveObj.isReachStart = getIsReachStart(flicking);
    reactiveObj.isReachEnd = getIsReachEnd(flicking);
    reactiveObj.currentPanelIndex = getCurrentPanelIndex(flicking);
  };
  const onPanelChange = () => {
    if (flicking === void 0) return;
    onChanged();
    reactiveObj.totalPanelCount = getTotalPanelCount(flicking);
  };
  const onMove = () => {
    if (flicking === void 0) return;
    reactiveObj.progress = getProgress(flicking);
    reactiveObj.indexProgress = getIndexProgress(flicking);
  };
  onInit((inst, data) => {
    flicking = data.flicking;
    if (flicking === void 0) return;
    reactiveObj.isReachStart = getIsReachStart(flicking);
    reactiveObj.isReachEnd = getIsReachEnd(flicking);
    reactiveObj.currentPanelIndex = getCurrentPanelIndex(flicking);
    reactiveObj.progress = getProgress(flicking);
    reactiveObj.totalPanelCount = getTotalPanelCount(flicking);
    flicking == null ? void 0 : flicking.on("changed", onChanged);
    flicking == null ? void 0 : flicking.on("panelChange", onPanelChange);
    flicking == null ? void 0 : flicking.on("move", onMove);
  });
  onDestroy(() => {
    flicking == null ? void 0 : flicking.off("changed", onChanged);
    flicking == null ? void 0 : flicking.off("panelChange", onPanelChange);
    flicking == null ? void 0 : flicking.off("move", onMove);
  });
  return reactiveObj;
};
const connectFlickingReactiveAPI = (flicking, options) => {
  const obj = adaptReactive(flickingReactiveAPIAdapter, () => ({ flicking, options }));
  obj.mounted();
  const instance = obj.instance();
  obj.init();
  return instance;
};
const modules = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  ALIGN,
  AnchorPoint,
  AnimatingState,
  AxesController,
  BoundCameraMode,
  CIRCULAR_FALLBACK,
  CLASS,
  CODE,
  Camera,
  CircularCameraMode,
  Control,
  CrossFlicking,
  DIRECTION,
  DisabledState,
  DraggingState,
  ERROR_CODE: CODE,
  EVENTS,
  ExternalRenderer,
  FlickingError,
  FreeControl,
  HoldingState,
  IdleState,
  LinearCameraMode,
  MESSAGE,
  MOVE_DIRECTION,
  MOVE_TYPE,
  NormalRenderingStrategy,
  ORDER,
  Panel,
  Renderer,
  SIDE_EVENTS,
  SnapControl,
  State,
  StateMachine,
  StrictControl,
  VanillaElementProvider,
  VanillaRenderer,
  Viewport,
  VirtualElementProvider,
  VirtualManager,
  VirtualPanel,
  VirtualRenderingStrategy,
  camelize,
  checkExistence,
  circulateIndex,
  circulatePosition,
  clamp,
  connectFlickingReactiveAPI,
  default: Flicking,
  find,
  findIndex,
  findRight,
  flickingReactiveAPIAdapter,
  getDataAttributes,
  getDefaultCameraTransform,
  getDirection,
  getElement,
  getElementSize,
  getFlickingAttached,
  getMinusCompensatedIndex,
  getProgress: getProgress$1,
  getRenderingPanels,
  getStyle,
  includes,
  isBetween,
  isString,
  merge,
  parseAlign: parseAlign$1,
  parseArithmeticExpression,
  parseArithmeticSize,
  parseBounce,
  parseCSSSizeValue,
  parseElement,
  parsePanelAlign,
  range,
  setPrototypeOf,
  setSize,
  sync,
  toArray,
  withFlickingMethods
}, Symbol.toStringTag, { value: "Module" }));
for (const name in modules) {
  Flicking[name] = modules[name];
}
module.exports = Flicking;
exports.ALIGN = ALIGN;
exports.AnchorPoint = AnchorPoint;
exports.AnimatingState = AnimatingState;
exports.AxesController = AxesController;
exports.BoundCameraMode = BoundCameraMode;
exports.CIRCULAR_FALLBACK = CIRCULAR_FALLBACK;
exports.CLASS = CLASS;
exports.CODE = CODE;
exports.Camera = Camera;
exports.CircularCameraMode = CircularCameraMode;
exports.Control = Control;
exports.CrossFlicking = CrossFlicking;
exports.DIRECTION = DIRECTION;
exports.DisabledState = DisabledState;
exports.DraggingState = DraggingState;
exports.ERROR_CODE = CODE;
exports.EVENTS = EVENTS;
exports.ExternalRenderer = ExternalRenderer;
exports.FlickingError = FlickingError;
exports.FreeControl = FreeControl;
exports.HoldingState = HoldingState;
exports.IdleState = IdleState;
exports.LinearCameraMode = LinearCameraMode;
exports.MESSAGE = MESSAGE;
exports.MOVE_DIRECTION = MOVE_DIRECTION;
exports.MOVE_TYPE = MOVE_TYPE;
exports.NormalRenderingStrategy = NormalRenderingStrategy;
exports.ORDER = ORDER;
exports.Panel = Panel;
exports.Renderer = Renderer;
exports.SIDE_EVENTS = SIDE_EVENTS;
exports.SnapControl = SnapControl;
exports.State = State;
exports.StateMachine = StateMachine;
exports.StrictControl = StrictControl;
exports.VanillaElementProvider = VanillaElementProvider;
exports.VanillaRenderer = VanillaRenderer;
exports.Viewport = Viewport;
exports.VirtualElementProvider = VirtualElementProvider;
exports.VirtualManager = VirtualManager;
exports.VirtualPanel = VirtualPanel;
exports.VirtualRenderingStrategy = VirtualRenderingStrategy;
exports.camelize = camelize;
exports.checkExistence = checkExistence;
exports.circulateIndex = circulateIndex;
exports.circulatePosition = circulatePosition;
exports.clamp = clamp;
exports.connectFlickingReactiveAPI = connectFlickingReactiveAPI;
exports.default = Flicking;
exports.find = find;
exports.findIndex = findIndex;
exports.findRight = findRight;
exports.flickingReactiveAPIAdapter = flickingReactiveAPIAdapter;
exports.getDataAttributes = getDataAttributes;
exports.getDefaultCameraTransform = getDefaultCameraTransform;
exports.getDirection = getDirection;
exports.getElement = getElement;
exports.getElementSize = getElementSize;
exports.getFlickingAttached = getFlickingAttached;
exports.getMinusCompensatedIndex = getMinusCompensatedIndex;
exports.getProgress = getProgress$1;
exports.getRenderingPanels = getRenderingPanels;
exports.getStyle = getStyle;
exports.includes = includes;
exports.isBetween = isBetween;
exports.isString = isString;
exports.merge = merge;
exports.parseAlign = parseAlign$1;
exports.parseArithmeticExpression = parseArithmeticExpression;
exports.parseArithmeticSize = parseArithmeticSize;
exports.parseBounce = parseBounce;
exports.parseCSSSizeValue = parseCSSSizeValue;
exports.parseElement = parseElement;
exports.parsePanelAlign = parsePanelAlign;
exports.range = range;
exports.setPrototypeOf = setPrototypeOf;
exports.setSize = setSize;
exports.sync = sync;
exports.toArray = toArray;
exports.withFlickingMethods = withFlickingMethods;
//# sourceMappingURL=flicking.cjs.js.map