UNPKG

@egjs/flicking

Version:

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

1,521 lines (1,505 loc) 327 kB
/* Copyright (c) 2015-present NAVER Corp. name: @egjs/flicking license: MIT author: NAVER Corp. repository: https://github.com/naver/egjs-flicking version: 4.13.1 */ import Component, { ComponentEvent } from '@egjs/component'; import Axes, { PanInput } from '@egjs/axes'; import ImReady from '@egjs/imready'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; } || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } /** @deprecated */ function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; /* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /** * Error codes of {@link FlickingError}. Below are the conditions where each error code occurs. * @ko {@link FlickingError}의 에러 코드. 아래는 각각의 에러 코드가 발생하는 조건입니다. * @name ERROR_CODE * @constant * @type object * @property {number} WRONG_TYPE Parameter type is wrong<ko>패러미터의 타입이 잘못되었을 경우</ko> * @property {number} ELEMENT_NOT_FOUND Element is not found inside page with the given CSS selector<ko>주어진 CSS selector로 페이지 내에서 해당 엘리먼트를 찾지 못했을 경우</ko> * @property {number} VAL_MUST_NOT_NULL Expected non-null value, but given `null` or `undefined`<ko>값을 기대했으나, `null`이나 `undefined`를 받은 경우</ko> * @property {number} NOT_ATTACHED_TO_FLICKING When Flicking's component is not initialized (i.e. {@link Flicking#init} is not called)<ko>Flicking 내부 컴포넌트가 초기화되지 않은 경우 ({@link Flicking#init}이 호출되지 않은 경우)</ko> * @property {number} WRONG_OPTION One of the options is wrong<ko>옵션들 중 잘못된 값이 있을 때</ko> * @property {number} INDEX_OUT_OF_RANGE When the given index is out of possible range<ko>인덱스가 주어진 범위를 벗어난 경우</ko> * @property {number} POSITION_NOT_REACHABLE When {@link Control#moveToPosition}'s position parameter is out of possible range.<ko>{@link Control#moveToPosition}의 `position` 패러미터가 도달 가능한 범위를 벗어난 경우</ko> * @property {number} TRANSFORM_NOT_SUPPORTED CSS `transform` property is not available(<=IE8) <ko>CSS `transform` 속성을 사용할 수 없는 경우(<=IE8)</ko> * @property {number} STOP_CALLED_BY_USER When the event's `stop()` is called by user.<ko>사용자에 의해 이벤트의 `stop()`이 호출된 경우</ko> * @property {number} ANIMATION_INTERRUPTED When the animation is interrupted by user.<ko>사용자에 의해 애니메이션이 중단된 경우</ko> * @property {number} ANIMATION_ALREADY_PLAYING When the animation is already playing.<ko>현재 애니메이션이 이미 진행중인 경우</ko> * @property {number} NOT_ALLOWED_IN_FRAMEWORK When the non-allowed method is called from frameworks (React, Angular, Vue...) * <ko>프레임워크(React, Angular, Vue ...)에서 사용 불가능한 메소드를 호출했을 경우</ko> * @property {number} NOT_INITIALIZED When the {@link Flicking#init} is not called before but is needed<ko>{@link Flicking#init}의 호출이 필요하나, 아직 호출되지 않았을 경우</ko> * @property {number} NO_ACTIVE When there're no active panel that flicking has selected. This may be due to the absence of any panels<ko>현재 Flicking이 선택한 패널이 없을 경우. 일반적으로 패널이 하나도 없는 경우에 발생할 수 있습니다</ko> * @property {number} NOT_ALLOWED_IN_VIRTUAL When the non-allowed method is called while the virtual option is enabled<ko>virtual 옵션이 활성화된 상태에서 사용 불가능한 메소드가 호출되었을 경우</ko> */ var CODE = { WRONG_TYPE: 0, ELEMENT_NOT_FOUND: 1, VAL_MUST_NOT_NULL: 2, NOT_ATTACHED_TO_FLICKING: 3, WRONG_OPTION: 4, INDEX_OUT_OF_RANGE: 5, POSITION_NOT_REACHABLE: 6, TRANSFORM_NOT_SUPPORTED: 7, STOP_CALLED_BY_USER: 8, ANIMATION_INTERRUPTED: 9, ANIMATION_ALREADY_PLAYING: 10, NOT_ALLOWED_IN_FRAMEWORK: 11, NOT_INITIALIZED: 12, NO_ACTIVE: 13, NOT_ALLOWED_IN_VIRTUAL: 14 }; var MESSAGE = { WRONG_TYPE: function (wrongVal, correctTypes) { return wrongVal + "(" + typeof wrongVal + ") is not a " + correctTypes.map(function (type) { return "\"" + type + "\""; }).join(" or ") + "."; }, ELEMENT_NOT_FOUND: function (selector) { return "Element with selector \"" + selector + "\" not found."; }, VAL_MUST_NOT_NULL: function (val, name) { return name + " should be provided. Given: " + val; }, NOT_ATTACHED_TO_FLICKING: "This module is not attached to the Flicking instance. \"init()\" should be called first.", WRONG_OPTION: function (optionName, val) { return "Option \"" + optionName + "\" is not in correct format, given: " + val; }, INDEX_OUT_OF_RANGE: function (val, min, max) { return "Index \"" + val + "\" is out of range: should be between " + min + " and " + max + "."; }, POSITION_NOT_REACHABLE: function (position) { return "Position \"" + position + "\" is not reachable."; }, TRANSFORM_NOT_SUPPORTED: "Browser does not support CSS transform.", STOP_CALLED_BY_USER: "Event stop() is called by user.", ANIMATION_INTERRUPTED: "Animation is interrupted by user input.", ANIMATION_ALREADY_PLAYING: "Animation is already playing.", NOT_ALLOWED_IN_FRAMEWORK: "This behavior is not allowed in the frameworks like React, Vue, or Angular.", NOT_INITIALIZED: "Flicking is not initialized yet, call init() first.", NO_ACTIVE: "There's no active panel that Flicking has selected. This may be due to the absence of any panels.", NOT_ALLOWED_IN_VIRTUAL: "This behavior is not allowed when the virtual option is enabled" }; /* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ /** * Event type object with event name strings of {@link Flicking} * @ko {@link Flicking}의 이벤트 이름 문자열들을 담은 객체 * @type {object} * @property {"holdStart"} HOLD_START holdStart event<ko>holdStart 이벤트</ko> * @property {"holdEnd"} HOLD_END holdEnd event<ko>holdEnd 이벤트</ko> * @property {"moveStart"} MOVE_START moveStart event<ko>moveStart 이벤트</ko> * @property {"move"} MOVE move event<ko>move 이벤트</ko> * @property {"moveEnd"} MOVE_END moveEnd event<ko>moveEnd 이벤트</ko> * @property {"willChange"} WILL_CHANGE willChange event<ko>willChange 이벤트</ko> * @property {"changed"} CHANGED changed event<ko>changed 이벤트</ko> * @property {"willRestore"} WILL_RESTORE willRestore event<ko>willRestore 이벤트</ko> * @property {"restored"} RESTORED restored event<ko>restored 이벤트</ko> * @property {"select"} SELECT select event<ko>select 이벤트</ko> * @property {"needPanel"} NEED_PANEL needPanel event<ko>needPanel 이벤트</ko> * @property {"panelChange"} PANEL_CHANGE panelChange event<ko>panelChange 이벤트</ko> * @example * ```ts * import { EVENTS } from "@egjs/flicking"; * EVENTS.MOVE_START; // "moveStart" * ``` */ var EVENTS = { READY: "ready", BEFORE_RESIZE: "beforeResize", AFTER_RESIZE: "afterResize", HOLD_START: "holdStart", HOLD_END: "holdEnd", MOVE_START: "moveStart", MOVE: "move", MOVE_END: "moveEnd", WILL_CHANGE: "willChange", CHANGED: "changed", WILL_RESTORE: "willRestore", RESTORED: "restored", SELECT: "select", NEED_PANEL: "needPanel", VISIBLE_CHANGE: "visibleChange", REACH_EDGE: "reachEdge", PANEL_CHANGE: "panelChange" }; /** * An object with all possible predefined literal string for the {@link Flicking#align align} option * @ko {@link Flicking#align align} 옵션에 사용되는 미리 정의된 리터럴 상수들을 담고 있는 객체 * @type {object} * @property {"prev"} PREV left/top align<ko>좌/상 정렬</ko> * @property {"center"} CENTER center align<ko>중앙 정렬</ko> * @property {"next"} NEXT right/bottom align<ko>우/하 정렬</ko> */ var ALIGN = { PREV: "prev", CENTER: "center", NEXT: "next" }; /** * An object of directions * @ko 방향을 나타내는 값들을 담고 있는 객체 * @type {object} * @property {"PREV"} PREV "left" when {@link Flicking#horizontal horizontal} is true, and "top" when {@link Flicking#horizontal horizontal} is false * <ko>{@link Flicking#horizontal horizontal}가 `true`일 경우 왼쪽, {@link Flicking#horizontal horizontal}가 `false`일 경우 위쪽을 의미합니다</ko> * @property {"NEXT"} NEXT "right" when {@link Flicking#horizontal horizontal} is true, and "bottom" when {@link Flicking#horizontal horizontal} is false * <ko>{@link Flicking#horizontal horizontal}가 `true`일 경우 오른쪽, {@link Flicking#horizontal horizontal}가 `false`일 경우 아래쪽을 의미합니다</ko> * @property {null} NONE This value usually means it's the same position<ko>주로 제자리인 경우를 의미합니다</ko> */ var DIRECTION = { PREV: "PREV", NEXT: "NEXT", NONE: null }; /** * An object with all possible {@link Flicking#moveType moveType}s * @ko Flicking이 제공하는 {@link Flicking#moveType moveType}들을 담고 있는 객체 * @type {object} * @property {"snap"} SNAP Flicking's {@link Flicking#moveType moveType} that enables {@link SnapControl} as a Flicking's {@link Flicking#control control} * <ko>Flicking의 {@link Flicking#control control}을 {@link SnapControl}로 설정하게 하는 {@link Flicking#moveType moveType}</ko> * @property {"freeScroll"} FREE_SCROLL Flicking's {@link Flicking#moveType moveType} that enables {@link FreeControl} as a Flicking's {@link Flicking#control control} * <ko>Flicking의 {@link Flicking#control control}을 {@link FreeControl}로 설정하게 하는 {@link Flicking#moveType moveType}</ko> * @property {"strict"} STRICT Flicking's {@link Flicking#moveType moveType} that enables {@link StrictControl} as a Flicking's {@link Flicking#control control} * <ko>Flicking의 {@link Flicking#control control}을 {@link StrictControl}로 설정하게 하는 {@link Flicking#moveType moveType}</ko> */ var MOVE_TYPE = { SNAP: "snap", FREE_SCROLL: "freeScroll", STRICT: "strict" }; var CLASS = { VIEWPORT: "flicking-viewport", CAMERA: "flicking-camera", VERTICAL: "vertical", HIDDEN: "flicking-hidden", DEFAULT_VIRTUAL: "flicking-panel" }; /** * An object with all possible {@link Flicking#circularFallback circularFallback}s * @ko Flicking의 {@link Flicking#circularFallback circularFallback}에 설정 가능한 값들을 담고 있는 객체 * @type {object} * @property {string} LINEAR "linear" * @property {string} BOUND "bound" */ var CIRCULAR_FALLBACK = { LINEAR: "linear", BOUND: "bound" }; /** * An object for identifying {@link https://developer.mozilla.org/en-US/docs/Web/CSS/direction direction} CSS property applied to the camera element(`.flicking-camera`) * @ko 카메라 엘리먼트(`.flicking-camera`)에 적용된 {@link https://developer.mozilla.org/en-US/docs/Web/CSS/direction direction} CSS 속성을 구분하기 위한 객체 * @type {object} * @property {string} LTR "ltr" * @property {string} RTL "rtl" */ var ORDER = { LTR: "ltr", RTL: "rtl" }; /** * An object that contains the direction that {@link Flicking} is moving * @ko {@link Flicking}이 움직이는 방향을 담고 있는 객체 * @type {object} * @property {"horizontal"} HORIZONTAL horizontal<ko>수평 방향</ko> * @property {"vertical"} VERTICAL vertical<ko>수직 방향</ko> */ var MOVE_DIRECTION = { HORIZONTAL: "horizontal", VERTICAL: "vertical" }; // eslint-disable-next-line @typescript-eslint/ban-types var merge = function (target) { var sources = []; for (var _i = 1; _i < arguments.length; _i++) { sources[_i - 1] = arguments[_i]; } sources.forEach(function (source) { Object.keys(source).forEach(function (key) { target[key] = source[key]; }); }); return target; }; var getElement = function (el, parent) { var targetEl = null; if (isString(el)) { var parentEl = parent ? parent : document; var 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; }; var checkExistence = function (value, nameOnErrMsg) { if (value == null) { throw new FlickingError(MESSAGE.VAL_MUST_NOT_NULL(value, nameOnErrMsg), CODE.VAL_MUST_NOT_NULL); } }; var clamp = function (x, min, max) { return Math.max(Math.min(x, max), min); }; var getFlickingAttached = function (val) { if (!val) { throw new FlickingError(MESSAGE.NOT_ATTACHED_TO_FLICKING, CODE.NOT_ATTACHED_TO_FLICKING); } return val; }; var toArray = function (iterable) { return [].slice.call(iterable); }; var parseAlign$1 = function (align, size) { var 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; }; var parseBounce = function (bounce, size) { var parsedBounce; if (Array.isArray(bounce)) { parsedBounce = bounce.map(function (val) { return parseArithmeticSize(val, size); }); } else { var parsedVal = parseArithmeticSize(bounce, size); parsedBounce = [parsedVal, parsedVal]; } return parsedBounce.map(function (val) { if (val == null) { throw new FlickingError(MESSAGE.WRONG_OPTION("bounce", bounce), CODE.WRONG_OPTION); } return val; }); }; var parseArithmeticSize = function (cssValue, base) { var parsed = parseArithmeticExpression(cssValue); if (parsed == null) return null; return parsed.percentage * base + parsed.absolute; }; var parseArithmeticExpression = function (cssValue) { var cssRegex = /(?:(\+|\-)\s*)?(\d+(?:\.\d+)?(%|px)?)/g; if (typeof cssValue === "number") { return { percentage: 0, absolute: cssValue }; } var parsed = { percentage: 0, absolute: 0 }; var idx = 0; var matchResult = cssRegex.exec(cssValue); while (matchResult != null) { var sign = matchResult[1]; var value = matchResult[2]; var unit = matchResult[3]; var parsedValue = parseFloat(value); if (idx <= 0) { sign = sign || "+"; } // Return default value for values not in good form if (!sign) { return null; } var signMultiplier = sign === "+" ? 1 : -1; if (unit === "%") { parsed.percentage += signMultiplier * (parsedValue / 100); } else { parsed.absolute += signMultiplier * parsedValue; } // Match next occurrence ++idx; matchResult = cssRegex.exec(cssValue); } // None-matched if (idx === 0) { return null; } return parsed; }; var parseCSSSizeValue = function (val) { return isString(val) ? val : val + "px"; }; var parsePanelAlign = function (align) { return typeof align === "object" ? align.panel : align; }; var getDirection = function (start, end) { if (start === end) return DIRECTION.NONE; return start < end ? DIRECTION.NEXT : DIRECTION.PREV; }; var parseElement = function (element) { if (!Array.isArray(element)) { element = [element]; } var elements = []; element.forEach(function (el) { if (isString(el)) { var tempDiv = document.createElement("div"); tempDiv.innerHTML = el; elements.push.apply(elements, __spread(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; }; var getMinusCompensatedIndex = function (idx, max) { return idx < 0 ? clamp(idx + max, 0, max) : clamp(idx, 0, max); }; var includes = function (array, target) { var e_1, _a; try { for (var array_1 = __values(array), array_1_1 = array_1.next(); !array_1_1.done; array_1_1 = array_1.next()) { var val = array_1_1.value; if (val === target) return true; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (array_1_1 && !array_1_1.done && (_a = array_1.return)) _a.call(array_1); } finally { if (e_1) throw e_1.error; } } return false; }; var isString = function (val) { return typeof val === "string"; }; var circulatePosition = function (pos, min, max) { var size = max - min; if (pos < min) { var offset = (min - pos) % size; pos = max - offset; } else if (pos > max) { var offset = (pos - max) % size; pos = min + offset; } return pos; }; var find = function (array, checker) { var e_2, _a; try { for (var array_2 = __values(array), array_2_1 = array_2.next(); !array_2_1.done; array_2_1 = array_2.next()) { var val = array_2_1.value; if (checker(val)) { return val; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (array_2_1 && !array_2_1.done && (_a = array_2.return)) _a.call(array_2); } finally { if (e_2) throw e_2.error; } } return null; }; var findRight = function (array, checker) { for (var idx = array.length - 1; idx >= 0; idx--) { var val = array[idx]; if (checker(val)) { return val; } } return null; }; var findIndex = function (array, checker) { for (var idx = 0; idx < array.length; idx++) { if (checker(array[idx])) { return idx; } } return -1; }; var getProgress = function (pos, prev, next) { return (pos - prev) / (next - prev); }; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access var getStyle = function (el) { if (!el) { return {}; } return window.getComputedStyle(el) || el.currentStyle; }; var setSize = function (el, _a) { var width = _a.width, height = _a.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"; } } }; var isBetween = function (val, min, max) { return val >= min && val <= max; }; var circulateIndex = function (index, max) { if (index >= max) { return index % max; } else if (index < 0) { return getMinusCompensatedIndex((index + 1) % max - 1, max); } else { return index; } }; var range = function (end) { var arr = new Array(end); for (var i = 0; i < end; i++) { arr[i] = i; } return arr; }; var getElementSize = function (_a) { var el = _a.el, horizontal = _a.horizontal, useFractionalSize = _a.useFractionalSize, useOffset = _a.useOffset, style = _a.style; var size = 0; if (useFractionalSize) { var baseSize = parseFloat(horizontal ? style.width : style.height) || 0; var isBorderBoxSizing = style.boxSizing === "border-box"; var 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 { var 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 { var sizeStr = horizontal ? "Width" : "Height"; size = useOffset ? el["offset" + sizeStr] : el["client" + sizeStr]; } return Math.max(size, 0); }; var setPrototypeOf = Object.setPrototypeOf || function (obj, proto) { obj.__proto__ = proto; return obj; }; var camelize = function (str) { return str.replace(/[\s-_]([a-z])/g, function (all, letter) { return letter.toUpperCase(); }); }; var getDataAttributes = function (element, attributePrefix) { var dataAttributes = {}; var attributes = element.attributes; var length = attributes.length; for (var i = 0; i < length; ++i) { var attribute = attributes[i]; var name_1 = attribute.name, value = attribute.value; if (name_1.indexOf(attributePrefix) === -1) { continue; } dataAttributes[camelize(name_1.replace(attributePrefix, ""))] = value; } return dataAttributes; }; /* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ /** * Special type of known error that {@link Flicking} throws. * @ko Flicking 내부에서 알려진 오류 발생시 throw되는 에러 * @property {number} code Error code<ko>에러 코드</ko> * @property {string} message Error message<ko>에러 메시지</ko> * @see {@link ERROR_CODE ERROR_CODE} * @example * ```ts * import Flicking, { FlickingError, ERROR_CODES } from "@egjs/flicking"; * try { * const flicking = new Flicking(".flicking-viewport") * } catch (e) { * if (e instanceof FlickingError && e.code === ERROR_CODES.ELEMENT_NOT_FOUND) { * console.error("Element not found") * } * } * ``` */ var FlickingError = /*#__PURE__*/function (_super) { __extends(FlickingError, _super); /** * @param message Error message<ko>에러 메시지</ko> * @param code Error code<ko>에러 코드</ko> */ function FlickingError(message, code) { var _this = _super.call(this, message) || this; setPrototypeOf(_this, FlickingError.prototype); _this.name = "FlickingError"; _this.code = code; return _this; } return FlickingError; }(Error); /** * A component that manages viewport size * @ko 뷰포트 크기 정보를 담당하는 컴포넌트 */ var Viewport = /*#__PURE__*/function () { /** * @param el A viewport element<ko>뷰포트 엘리먼트</ko> */ function Viewport(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; } var __proto = Viewport.prototype; Object.defineProperty(__proto, "element", { /** * A viewport(root) element * @ko 뷰포트(root) 엘리먼트 * @type {HTMLElement} * @readonly */ get: function () { return this._el; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "width", { /** * Viewport width, without paddings * @ko 뷰포트 너비 * @type {number} * @readonly */ get: function () { return this._width - this._padding.left - this._padding.right; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "height", { /** * Viewport height, without paddings * @ko 뷰포트 높이 * @type {number} * @readonly */ get: function () { return this._height - this._padding.top - this._padding.bottom; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "padding", { /** * Viewport paddings * @ko 뷰포트 CSS padding 값 * @type {object} * @property {number} left CSS `padding-left` * @property {number} right CSS `padding-right` * @property {number} top CSS `padding-top` * @property {number} bottom CSS `padding-bottom` * @readonly */ get: function () { return this._padding; }, enumerable: false, configurable: true }); /** * Change viewport's size. * This will change the actual size of `.flicking-viewport` element by changing its CSS width/height property * @ko 뷰포트 크기를 변경합니다. * `.flicking-viewport` 엘리먼트에 해당 크기의 CSS width/height를 적용합니다 * @param {object} [size] New viewport size<ko>새 뷰포트 크기</ko> * @param {number|string} [size.width] CSS string or number(in px)<ko>CSS 문자열 또는 숫자(px)</ko> * @param {number|string} [size.height] CSS string or number(in px)<ko>CSS 문자열 또는 숫자(px)</ko> */ __proto.setSize = function (_a) { var width = _a.width, height = _a.height; var el = this._el; var padding = this._padding; var isBorderBoxSizing = this._isBorderBoxSizing; if (width != null) { if (isString(width)) { el.style.width = width; } else { var newWidth = isBorderBoxSizing ? width + padding.left + padding.right : width; el.style.width = newWidth + "px"; } } if (height != null) { if (isString(height)) { el.style.height = height; } else { var newHeight = isBorderBoxSizing ? height + padding.top + padding.bottom : height; el.style.height = newHeight + "px"; } } this.resize(); }; /** * Update width/height to the current viewport element's size * @ko 현재 뷰포트 엘리먼트의 크기로 너비/높이를 업데이트합니다 */ __proto.resize = function () { var el = this._el; var elStyle = getStyle(el); var useFractionalSize = this._flicking.useFractionalSize; this._width = getElementSize({ el: el, horizontal: true, useFractionalSize: useFractionalSize, useOffset: false, style: elStyle }); this._height = getElementSize({ el: el, horizontal: false, useFractionalSize: useFractionalSize, useOffset: false, style: elStyle }); this._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 }; this._isBorderBoxSizing = elStyle.boxSizing === "border-box"; }; return Viewport; }(); /* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ /** * A component that detects size change and trigger resize method when the autoResize option is used * @ko autoResize 옵션을 사용할 때 크기 변화를 감지하고 Flicking의 resize를 호출하는 컴포넌트 */ var AutoResizer = /*#__PURE__*/function () { function AutoResizer(flicking) { var _this = this; this._onResizeWrapper = function () { _this._onResize([]); }; this._onResize = function (entries) { var flicking = _this._flicking; var resizeDebounce = flicking.resizeDebounce; var maxResizeDebounce = flicking.maxResizeDebounce; var resizedViewportElement = flicking.element; // 현재 구현에서 리사이즈 옵저빙 대상은 패널과 뷰포트 2개만 존재. // 아래는 뷰포트만 변경되었을 때 동작해야하는 로직이 있으므로 아래와 같이 조건문을 건다. // 패널 쪽에서는 리사이즈 감지에 resizeObserver를 사용하지 않는 경우가 없으므로 이 조건은 곧 뷰포트만 리사이즈가 된 경우를 의미한다. var isResizedViewportOnly = entries.find(function (e) { return e.target === flicking.element; }) && entries.length === 1; // 참고: resizeObserver를 사용하지 않은 경우에는 entries.length가 0으로 오는데 이 경우에는 그냥 항상 리사이즈가 진행되도록 한다. // (vw, vh 등을 사용하는 경우 이상 동작이 발생할 여지가 있기 때문이다) if (isResizedViewportOnly) { // resize 이벤트가 발생했으나 이전과 width, height의 변화가 없다면 이후 로직을 진행하지 않는다. var beforeSize = { width: flicking.viewport.width, height: flicking.viewport.height }; var 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 flicking.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 = function () { clearTimeout(_this._resizeTimer); clearTimeout(_this._maxResizeDebounceTimer); _this._maxResizeDebounceTimer = -1; _this._resizeTimer = -1; void _this._flicking.resize(); }; // eslint-disable-next-line @typescript-eslint/member-ordering this._skipFirstResize = function () { var isFirstResize = true; return function (entries) { if (isFirstResize) { isFirstResize = false; return; } _this._onResize(entries); }; }(); this._flicking = flicking; this._enabled = false; this._resizeObserver = null; this._resizeTimer = -1; this._maxResizeDebounceTimer = -1; } var __proto = AutoResizer.prototype; Object.defineProperty(__proto, "enabled", { get: function () { return this._enabled; }, enumerable: false, configurable: true }); __proto.enable = function () { var flicking = this._flicking; var viewport = flicking.viewport; if (this._enabled) { this.disable(); } if (flicking.useResizeObserver && !!window.ResizeObserver) { var viewportSizeNot0 = viewport.width !== 0 || viewport.height !== 0; var 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; }; __proto.observePanels = function () { var _this = this; this._flicking.panels.forEach(function (panel) { _this.observe(panel.element); }); return this; }; __proto.unobservePanels = function () { var _this = this; this._flicking.panels.forEach(function (panel) { _this.unobserve(panel.element); }); return this; }; __proto.observe = function (element) { var resizeObserver = this._resizeObserver; if (!resizeObserver) return this; resizeObserver.observe(element); return this; }; __proto.unobserve = function (element) { var resizeObserver = this._resizeObserver; if (!resizeObserver) return this; resizeObserver.unobserve(element); if (this._flicking.observePanelResize) { this.unobservePanels(); } return this; }; __proto.disable = function () { if (!this._enabled) return this; var resizeObserver = this._resizeObserver; if (resizeObserver) { resizeObserver.disconnect(); this._resizeObserver = null; } else { window.removeEventListener("resize", this._onResizeWrapper); } this._enabled = false; return this; }; return AutoResizer; }(); /** * @internal */ var VanillaElementProvider = /*#__PURE__*/function () { function VanillaElementProvider(element) { this._element = element; this._rendered = true; } var __proto = VanillaElementProvider.prototype; Object.defineProperty(__proto, "element", { get: function () { return this._element; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "rendered", { get: function () { return this._rendered; }, enumerable: false, configurable: true }); __proto.show = function (flicking) { var el = this.element; var cameraEl = flicking.camera.element; if (el.parentElement !== cameraEl) { cameraEl.appendChild(el); this._rendered = true; } }; __proto.hide = function (flicking) { var el = this.element; var cameraEl = flicking.camera.element; if (el.parentElement === cameraEl) { cameraEl.removeChild(el); this._rendered = false; } }; return VanillaElementProvider; }(); /* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ /** * @internal */ var VirtualElementProvider = /*#__PURE__*/function () { function VirtualElementProvider(flicking) { this._flicking = flicking; } var __proto = VirtualElementProvider.prototype; Object.defineProperty(__proto, "element", { get: function () { return this._virtualElement.nativeElement; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "rendered", { get: function () { return this._virtualElement.visible; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "_virtualElement", { get: function () { var flicking = this._flicking; var elIndex = this._panel.elementIndex; var virtualElements = flicking.virtual.elements; return virtualElements[elIndex]; }, enumerable: false, configurable: true }); __proto.init = function (panel) { this._panel = panel; }; __proto.show = function () { // DO_NOTHING // Actual element visibility is controlled by VirtualManager }; __proto.hide = function () { // DO_NOTHING // Actual element visibility is controlled by VirtualManager }; return VirtualElementProvider; }(); /** * A manager class to add / remove virtual panels */ var VirtualManager = /*#__PURE__*/function () { function VirtualManager(flicking, options) { var _a, _b, _c, _d; this._flicking = flicking; this._renderPanel = (_a = options === null || options === void 0 ? void 0 : options.renderPanel) !== null && _a !== void 0 ? _a : function () { return ""; }; this._initialPanelCount = (_b = options === null || options === void 0 ? void 0 : options.initialPanelCount) !== null && _b !== void 0 ? _b : -1; this._cache = (_c = options === null || options === void 0 ? void 0 : options.cache) !== null && _c !== void 0 ? _c : false; this._panelClass = (_d = options === null || options === void 0 ? void 0 : options.panelClass) !== null && _d !== void 0 ? _d : CLASS.DEFAULT_VIRTUAL; this._elements = []; } var __proto = VirtualManager.prototype; Object.defineProperty(__proto, "elements", { get: function () { return this._elements; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "renderPanel", { // Options /** * A rendering function for the panel element's innerHTML * @ko 패널 엘리먼트의 innerHTML을 렌더링하는 함수 * @type {function} * @param {VirtualPanel} panel Instance of the panel<ko>패널 인스턴스</ko> * @param {number} index Index of the panel<ko>패널 인덱스</ko> * @default "() => {}" */ get: function () { return this._renderPanel; }, set: function (val) { this._renderPanel = val; this._flicking.renderer.panels.forEach(function (panel) { return panel.uncacheRenderResult(); }); }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "initialPanelCount", { /** * Initial panel count to render * @ko 최초로 렌더링할 패널의 개수 * @readonly * @type {number} * @default -1 */ get: function () { return this._initialPanelCount; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "cache", { /** * Whether to cache rendered panel's innerHTML * @ko 렌더링된 패널의 innerHTML 정보를 캐시할지 여부 * @type {boolean} * @default false */ get: function () { return this._cache; }, set: function (val) { this._cache = val; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "panelClass", { /** * The class name that will be applied to rendered panel elements * @ko 렌더링되는 패널 엘리먼트에 적용될 클래스 이름 * @type {string} * @default "flicking-panel" */ get: function () { return this._panelClass; }, set: function (val) { this._panelClass = val; }, enumerable: false, configurable: true }); __proto.init = function () { var flicking = this._flicking; if (!flicking.virtualEnabled) return; if (!flicking.externalRenderer && !flicking.renderExternal) { this._initVirtualElements(); } var virtualElements = flicking.camera.children; this._elements = virtualElements.map(function (el) { return { nativeElement: el, visible: true }; }); }; __proto.show = function (index) { var el = this._elements[index]; var nativeEl = el.nativeElement; el.visible = true; if (nativeEl.style.display) { nativeEl.style.display = ""; } }; __proto.hide = function (index) { var el = this._elements[index]; var nativeEl = el.nativeElement; el.visible = false; nativeEl.style.display = "none"; }; /** * Add new virtual panels at the end of the list * @ko 새로운 가상 패널들을 리스트의 끝에 추가합니다 * @param {number} count The number of panels to add<ko>추가할 패널의 개수</ko> * @returns {Array<VirtualPanel>} The new panels added<ko>새롭게 추가된 패널들</ko> */ __proto.append = function (count) { if (count === void 0) { count = 1; } var flicking = this._flicking; return this.insert(flicking.panels.length, count); }; /** * Add new virtual panels at the start of the list * @ko 새로운 가상 패널들을 리스트의 시작에 추가합니다 * @param {number} count The number of panels to add<ko>추가할 패널의 개수</ko> * @returns {Array<VirtualPanel>} The new panels added<ko>새롭게 추가된 패널들</ko> */ __proto.prepend = function (count) { if (count === void 0) { count = 1; } return this.insert(0, count); }; /** * Add new virtual panels at the given index * @ko 새로운 가상 패널들을 주어진 인덱스에 추가합니다 * @param {number} count The number of panels to add<ko>추가할 패널의 개수</ko> * @returns {Array<VirtualPanel>} The new panels added<ko>새롭게 추가된 패널들</ko> */ __proto.insert = function (index, count) { if (count === void 0) { count = 1; } if (count <= 0) return []; var flicking = this._flicking; return flicking.renderer.batchInsert({ index: index, elements: range(count), hasDOMInElements: false }); }; /** * Remove panels at the given index * @ko 주어진 인덱스에서 패널들을 삭제합니다 * @param {number} count The number of panels to remove<ko>삭제할 패널의 개수</ko> * @returns {Array<VirtualPanel>} The panels removed<ko>삭제된 패널들</ko> */ __proto.remove = function (index, count) { if (count <= 0) return []; var flicking = this._flicking; return flicking.renderer.batchRemove({ index: index, deleteCount: count, hasDOMInElements: false }); }; __proto._initVirtualElements = function () { var _this = this; var flicking = this._flicking; var cameraElement = flicking.camera.element; var panelsPerView = flicking.panelsPerView; var fragment = document.createDocumentFragment(); var newElements = range(panelsPerView + 1).map(function (idx) { var panelEl = document.createElement("div"); panelEl.className = _this._panelClass; panelEl.dataset.elementIndex = idx.toString(); return panelEl; }); newElements.forEach(function (el) { fragment.appendChild(el); }); cameraElement.appendChild(fragment); }; return VirtualManager; }(); /** * All possible @egjs/axes event keys * @internal */ var EVENT = { HOLD: "hold", CHANGE: "change", RELEASE: "release", ANIMATION_END: "animationEnd", FINISH: "finish" }; /** * An Axis key that Flicking uses * @internal */ var POSITION_KEY = "flick"; var STATE_TYPE; (function (STATE_TYPE) { STATE_TYPE[STATE_TYPE["IDLE"] = 0] = "IDLE"; STATE_TYPE[STATE_TYPE["HOLDING"] = 1] = "HOLDING"; STATE_TYPE[STATE_TYPE["DRAGGING"] = 2] = "DRAGGING"; STATE_TYPE[STATE_TYPE["ANIMATING"] = 3] = "ANIMATING"; STATE_TYPE[STATE_TYPE["DISABLED"] = 4] = "DISABLED"; })(STATE_TYPE || (STATE_TYPE = {})); /** * A component that shows the current status of the user input or the animation * @ko 현재 사용자 입력 또는 애니메이션 상태를 나타내는 컴포넌트 * @internal */ var State = /*#__PURE__*/function () { function State() { this._delta = 0; this._targetPanel = null; } var __proto = State.prototype; Object.defineProperty(__proto, "delta", { /** * A sum of delta values of change events from the last hold event of Axes * @ko 이전 hold이벤트부터 change에 의해 발생한 이동 delta값의 합산 * @type {number} * @readonly */ get: function () { return this._delta; }, enumerable: false, configurable: true }); Object.defineProperty(__proto, "targetPanel", { /** * A panel to set as {@link Control#activePanel} after the animation is finished * @ko 애니메이션 종료시 {@link Control#activePanel}로 설정할 패널 * @type {number} * @readonly */ get: function () { return this._targetPanel; }, set: function (val) { this._targetPanel = val; }, enumerable: false, configurable: true }); /** * An callback which is called when state has changed to this state * @ko 현재 상태로 돌입했을때 호출되는 콜백 함수 * @param {State} prevState An previous state<ko>이전 상태값</ko> * @return {void} */ __proto.onEnter = function (prevState) { this._delta = prevState._delta; this._targetPanel = prevState._targetPanel; }; /** * An event handler for Axes's {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:hold hold} event * @ko Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:hold hold} 이벤트 핸들러 * @param {object} [ctx] Event context<ko>이벤트 콘텍스트</ko> * @param {Flicking} [ctx.flicking] An instance of Flicking<ko>Flicking 인스턴스</ko> * @param {object} [ctx.axesEvent] A {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:hold hold} event of Axes * <ko>Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:hold hold} 이벤트</ko> * @param {function} [ctx.transitTo] A function for changing current state to other state<ko>다른 상태로 변경하기 위한 함수</ko> * @return {void} */ __proto.onHold = function (ctx) { // DO NOTHING }; /** * An event handler for Axes's {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:change change} event * @ko Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:change change} 이벤트 핸들러 * @param {object} [ctx] Event context<ko>이벤트 콘텍스트</ko> * @param {Flicking} [ctx.flicking] An instance of Flicking<ko>Flicking 인스턴스</ko> * @param {object} [ctx.axesEvent] A {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:change change} event of Axes * <ko>Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:change change} 이벤트</ko> * @param {function} [ctx.transitTo] A function for changing current state to other state<ko>다른 상태로 변경하기 위한 함수</ko> * @return {void} */ __proto.onChange = function (ctx) { // DO NOTHING }; /** * An event handler for Axes's {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} event * @ko Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} 이벤트 핸들러 * @param {object} [ctx] Event context<ko>이벤트 콘텍스트</ko> * @param {Flicking} [ctx.flicking] An instance of Flicking<ko>Flicking 인스턴스</ko> * @param {object} [ctx.axesEvent] A {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} event of Axes * <ko>Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} 이벤트</ko> * @param {function} [ctx.transitTo] A function for changing current state to other state<ko>다른 상태로 변경하기 위한 함수</ko> * @return {void} */ __proto.onRelease = function (ctx) { // DO NOTHING }; /** * An event handler for Axes's {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:animationEnd animationEnd} event * @ko Axes의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:animationEnd animationEnd} 이벤트 핸들러 * @param {object} [ctx] Event context<ko>이벤트 콘텍스트</ko> * @param {Flicking} [ctx.flicking] An inst