/**
* Copyright (c) 2015 NAVER Corp.
* egjs projects are licensed under the MIT license
*/
import Component from "@egjs/component";
import Axes, {PanInput} from "@egjs/axes";
import {utils, Mixin} from "./utils";
import {
EVENTS,
TRANSFORM,
SUPPORT_WILLCHANGE,
IS_ANDROID2,
DATA_HEIGHT
} from "./consts";
import {CONFIG, OPTIONS} from "./config";
import {document} from "./browser";
import eventHandler from "./eventHandler";
/**
* Create an instance of the eg.Flicking class. Create a flicking UI that sweeps a side-by-side panel with mouse move or touch move input and moves to the next or previous panel.
* @ko eg.Flicking 클래스의 인스턴스를 생성한다. 나란히 배치한 패널을 마우스 이동(move) 혹은 터치 이동(move) 입력을 받아 쓸어 넘겨 다음 패널이나 이전 패널로 이동하는 UI를 만든다.
* @alias eg.Flicking
* @extends eg.Component
* @requires {@link https://github.com/naver/egjs-component|eg.Component}
* @requires {@link https://github.com/naver/egjs-axes|eg.Axes}
* @see Easing Functions Cheat Sheet {@link http://easings.net/} <ko>이징 함수 Cheat Sheet {@link http://easings.net/}</ko>
* @see If you want to try a different easing function, use the jQuery easing plugin ({@link http://gsgd.co.uk/sandbox/jquery/easing}) or the jQuery UI easing library ({@link https://jqueryui.com/easing}). <ko>다른 easing 함수를 사용하려면 jQuery easing 플러그인({@link http://gsgd.co.uk/sandbox/jquery/easing})이나, jQuery UI easing 라이브러리({@link https://jqueryui.com/easing})를 사용한다</ko>
* @throws {Error} An Error occur when given base element doesn't exist or it hasn't proper DOM structure to be initialized. <ko>주어진 기본 요소가 존재하지 않거나 초기화 할 적절한 DOM 구조가없는 경우 오류가 발생한다.</ko>
* @support {"ie": "10+", "ch" : "latest", "ff" : "latest", "sf" : "latest" , "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"}
* @example
* A common example.
* 일반적인 예.
* ```html
* <div id="flick">
* <div><p>panel 0</p></div>
* <div><p>panel 1</p></div>
* <div><p>panel 2</p></div>
* </div>
* ```
* ```javascript
* // Examples to omit and omit optional options.
* // 생략가능한 옵션은 생략하고 생성하는 예.
* new eg.Flicking("#flick");
*
* // An example of specifying and generating values for all optional parameters.
* // 모든 옵션의 값을 지정하고 생성하는 예.
* new eg.Flicking("#flick", {
* hwAccelerable: true,
* prefix: "eg-flick",
* deceleration: 0.0006,
* horizontal: true,
* circular: false,
* previewPadding: [10, "15%"], // also as "10px", 15 or "15%" can be applied.
* bounce: [10, 10],
* threshold: 40,
* duration: 100,
* panelEffect: x => 1 - Math.pow(1 - x, 3),
* defaultIndex: 0,
* inputType: ["touch", "mouse"],
* thresholdAngle: 45,
* adaptiveHeight: false
* });
* ```
* @example
* Example of constructor element parameter value specification.
* 생성자 element 파라미터 값 지정 예.
* ```javascript
* // An example of assigning HTMLElement to an element parameter.
* // element 파라미터에 HTMLElement를 지정하는 예.
* new eg.Flicking(document.getElementById("flick"));
*
* // An example of assigning a jQuery object to an element parameter.
* // element 파라미터에 jQuery객체를 지정하는 예.
* new eg.Flicking($("#flick")[0]);
*
* // An example of assigning a css selector string to an element parameter.
* // element 파라미터에 css 선택자 문자열을 지정하는 예.
* new eg.Flicking("#flick");
* ```
* @example
* Panel element definition location example.
* 패널 요소 정의 위치 예.
* ```html
* <!--An example of defining a panel element as a child of a base element.-->
* <!--패널 요소를 기준 요소의 자식으로 정의한 예.-->
* <div id="flick">
* <div><p>panel 0</p></div>
* <div><p>panel 1</p></div>
* <div><p>panel 2</p></div>
* </div>
*
* <!--An example of defining a panel element as a child of a container element.-->
* <!--패널 요소를 컨테이너 요소의 자식으로 정의한 예.-->
* <div id="flick2">
* <div class="eg-flick-container">
* <div><p>panel 0</p></div>
* <div><p>panel 1</p></div>
* <div><p>panel 2</p></div>
* <div>
* </div>
* ```
* @example
* An example where only one panel is defined and created with a circular.
* 패널을 하나만 정의하고 순환으로 생성하는 예.
* ```html
* <div id="flick">
* <div><p>panel 0</p></div>
* </div>
* ```
* ```javascript
* // If the number of defined panels is less than the minimum number required for the circulation operation, the necessary number of panel elements are generated.
* // 정의된 패널의 수가 순환동작에 필요한 최소 개수보다 적으면 필요한 수만큼의 패널 요소가 생성된다.
* new eg.Flicking("#flick", {
* circular: true
* })
* ```
* @example
* For error occurrence example. There is no panel element.
* 오류 발생 예. 패널 요소가 하나도 없는 경우.
* ```html
* <div id="flick"></div>
* ```
* ```javascript
* try{
* new eg.Flicking("#flick");
* } catch(e) {
* // An error occurs because there are no child elements in the reference element.
* // 기준 요소안에 자식 요소가 하나도 없으므로 에러가 발생한다.
* }
* ```
*/
export default class Flicking extends Mixin(Component).with(eventHandler) {
/**
* Constructor
* @param {HTMLElement|String} element A base element for the eg.Flicking module. When specifying a value as a `String` type, you must specify a css selector string to select the element.<ko>eg.Flicking 모듈을 사용할 기준 요소. `String`타입으로 값 지정시 요소를 선택하기 위한 css 선택자 문자열을 지정해야 한다.</ko>
* @param {Object} [options] The option object of the eg.Flicking module<ko>eg.Flicking 모듈의 옵션 객체</ko>
* @param {Boolean} [options.hwAccelerable=true] Force hardware compositing.<ko>하드웨어 가속 사용 여부.</ko>
* @param {String} [options.prefix="eg-flick"] A prefix for class names of the panel elements.<ko>패널 요소의 클래스 이름 접두사.</ko>
* @param {Number} [options.deceleration=0.0006] Deceleration of the animation where acceleration is manually enabled by user. A higher value indicates shorter running time.<ko>사용자의 동작으로 가속도가 적용된 애니메이션의 감속도. 값이 높을수록 애니메이션 실행 시간이 짧아진다.</ko>
* @param {Boolean} [options.horizontal=true] Direction of the panel movement. (true: horizontal, false: vertical)<ko>패널 이동 방향. (true 가로방향, false 세로방향)</ko>
* @param {Boolean} [options.circular=false] Whether to let the first panel flick right to the end panel (let the left panel flick from the end panel to move to the first panel). (The term 'circulation')<ko>첫 패널에서 우 액션 입력하여 끝 패널로 이동하게 할지와 끝 패널에서 우 액션 입력하여 첫 패널로 이동할하게 할지 여부. (통칭 '순환')</ko>
* @param {Number|String|Array} [options.previewPadding=[0,0]] The preview size value(if no unit is given, defaults to `px`) for the previous or next panel.<br>- If the direction is set to "horizontal", the preview section will be displayed on the left and right of the panel.<br>- If the direction is set to "vertical", it will be displayed on the top and bottom of the panel.<ko>이전 패널과 다음 패널을 미리 보는 영역의 크기 값(단위가 지정되지 않는 경우, `px`로 지정).<br>패널 이동 방향이 가로 방향이면 패널 좌우에, 세로 방향이면 패널 상하에 미리 보는 영역이 나타난다.</ko>
* @param {Number|Array} [options.bounce=[10,10]] The size value(unit: pixel) of the bounce area. If `circular=false`, the panel can be moved by this value when inputting a right gesture in the first panel or inputting a left gesture in the end panel. When the input is completed while moving, it returns to its original position.<ko>바운스 영역의 크기값(단위: 픽셀). `circular=false`인 경우, 첫 패널에서 우 액션 입력시, 끝 패널에서 좌 액션 입력시 이 값 만큼만 패널이 이동할 수 있고 이동한 상태에서 입력을 마치면 원래 자리로 돌아온다.</ko>
* @param {Number} [options.threshold=40] Movement threshold to destination panel(unit: pixel). A panel element must be dragged beyond the threshold to move to the destination panel.<ko>목적 패널로의 이동 임계값 (단위: 픽셀). 패널 요소를 임계값 이상으로 끌어다 놓아야만이 목적 패널로 이동한다.</ko>
* @param {Number} [options.duration=100] Duration of the panel movement. (unit: ms)<ko>패널 이동 애니메이션 진행 시간.(단위: ms)</ko>
* @param {Function} [options.panelEffect=x => 1 - Math.pow(1 - x, 3)] The easing function to apply to a panel moving animation. The default function is easeOutCubic.<ko>패널 이동 애니메이션에 적용할 `easing`함수. 기본값은 `easeOutCubic`이다.</ko>
* @param {Number} [options.defaultIndex=0] The panel index number to specify when initializing the module. A zero-based integer.<ko>모듈 초기화시 지정할 패널 인덱스 번호. 0부터 시작하는 정수.</ko>
* @param {Array} [options.inputType=["touch,"mouse"]] Types of input devices. ({@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.PanInput.html|eg.Axes.PanInput Reference})<br>- "touch": A touch input device.<br>- "mouse": A mouse.<ko>입력 장치 종류. ({@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.PanInput.html|eg.Axes.PanInput 참고})<br>- "touch": 터치 입력 장치.<br>- "mouse": 마우스.</ko>
* @param {Number} [options.thresholdAngle=45] The threshold value that determines whether user input is horizontal or vertical. (0 ~ 90)<ko>사용자의 입력이 가로 방향인지 세로 방향인지 판단하는 기준 각도 (0 ~ 90)</ko>
* @param {Boolean} [options.adaptiveHeight=false] Whether the height of the container element reflects the height value of the panel after completing the movement.<br>(Note: on Android 4.1.x stock browser, has rendering bug which not correctly render height value on panel with single node. To avoid just append another empty node at the end.)<ko>목적 패널로 이동한 후 그 패널의 높이값을 컨테이너 요소의 높이값에 반영할지 여부.<br>(참고: Android 4.1.x 스톡 브라우저에서 단일 노드로 구성된 패널의 높이값 변경이 제대로 렌더링 되지 않는 버그가 있음. 비어있는 노드를 추가하면 해결이 가능하다.)</ko>
* @param {Number} [options.zIndex=2000] z-index value for container element<ko>컨테이너 요소의 z-index 값</ko>
* @param {Boolean} [options.useTranslate=true] Use css translate method on panel moves. When set to 'false', it'll use top/left instead.<ko>패널 이동시 CSS translate 사용 여부. 'false'로 설정 시, top/left 속성을 사용</ko>
*/
constructor(element, options, _prefix) {
super();
this.$wrapper = utils.$(element);
this.plugins = [];
const $children = this.$wrapper && this.$wrapper.children;
if (!this.$wrapper || !$children || !$children.length) {
// eslint-disable validateLineBreaks, maximumLineLength
throw new Error("Given base element doesn't exist or it hasn't proper DOM structure to be initialized.");
// eslint-enable validateLineBreaks, maximumLineLength
}
this._setOptions(options);
this._setConfig($children, _prefix);
!utils.hasClickBug() && (this._setPointerEvents = () => {});
this._build();
this._bindEvents(true);
this._applyPanelsCss();
this._arrangePanels();
this.options.hwAccelerable && SUPPORT_WILLCHANGE && this._setHint();
this.options.adaptiveHeight && this._setAdaptiveHeight();
this._adjustContainerCss("end");
}
/**
* Set options values
* @private
* @param {Object} options
*/
_setOptions(options) {
// default value of previewPadding and bounce
const arrVal = {
previewPadding: [0, 0],
bounce: [10, 10]
};
this.options = utils.extend(utils.extend({}, OPTIONS), arrVal, options);
for (const key in arrVal) {
let val = this.options[key];
if (/(number|string)/.test(typeof val)) {
val = [val, val];
} else if (!utils.isArray(val)) {
val = arrVal[key];
}
this.options[key] = val;
}
// block use of translate for Android2 environment
if (IS_ANDROID2) {
this.options.useTranslate = false;
}
}
/**
* Set config values
* @private
* @param {HTMLCollection} $children wrappers' children elements
* @param {String} _prefix event prefix
* @return {HTMLElement}
*/
_setConfig($children, _prefix) {
const options = this.options;
const padding = options.previewPadding;
let $nodes = $children;
if (utils.classList($nodes[0], `${options.prefix}-container`)) {
$nodes = $nodes[0];
this.$container = $nodes;
$nodes = $nodes.children;
}
// convert to array
$nodes = utils.toArray($nodes);
// config value
const conf = this._conf = utils.extend(utils.extend({}, CONFIG), {
panel: {
$list: $nodes,
minCount: padding[0] + padding[1] > 0 ? 5 : 3 // minimum panel count
},
// remember original class and inline style in case of restoration on destroy()
origPanelStyle: {
wrapper: {
className: this.$wrapper.getAttribute("class") || null,
style: this.$wrapper.getAttribute("style") || null
},
container: {
className: (this.$container && this.$container.getAttribute("class")) || null,
style: (this.$container && this.$container.getAttribute("style")) || null
},
list: $nodes.map(v => ({
className: v.getAttribute("class") || null,
style: v.getAttribute("style") || null
}))
},
useLayerHack: options.hwAccelerable && !SUPPORT_WILLCHANGE,
eventPrefix: _prefix || ""
});
[["LEFT", "RIGHT"], ["UP", "DOWN"]][+!options.horizontal]
.forEach(v => conf.dirData.push(Axes[`DIRECTION_${v}`]));
}
/**
* Build and set panel nodes to make flicking structure
* @private
*/
_build() {
const panel = this._conf.panel;
const options = this.options;
const $children = panel.$list;
const padding = options.previewPadding.concat();
const prefix = options.prefix;
const horizontal = options.horizontal;
let panelCount = panel.count = panel.origCount = $children.length;
const bounce = options.bounce;
this._setPadding(padding, true);
// container element style
const cssValue = {
position: "relative",
zIndex: options.zIndex || 2000,
width: "100%",
height: "100%"
};
horizontal && (cssValue.top = "0px");
if (this.$container) {
utils.css(this.$container, cssValue);
} else {
const $parent = $children[0].parentNode;
const $container = document.createElement("div");
$container.className = `${prefix}-container`;
utils.css($container, cssValue);
$children.forEach(v => $container.appendChild(v));
$parent.appendChild($container);
this.$container = $container;
}
// panels' css values
this._initOriginalPanelStyle($children);
if (this._addClonePanels()) {
panelCount = panel.count = (
panel.$list = utils.toArray(this.$container.children)
).length;
}
// create Axes instance
this._axesInst = new Axes({
flick: {
range: [0, panel.size * (panelCount - 1)],
bounce
}
}, {
easing: options.panelEffect,
deceleration: options.deceleration,
interruptable: false
});
this._setDefaultPanel(options.defaultIndex);
}
_initOriginalPanelStyle(panels) {
const panel = this._conf.panel;
const sizeValue = this._getDataByDirection([panel.size, "100%"]);
panels.forEach(v => {
utils.classList(v, `${this.options.prefix}-panel`, true);
utils.css(v, {
position: "absolute",
width: utils.getUnitValue(sizeValue[0]),
height: utils.getUnitValue(sizeValue[1]),
boxSizing: "border-box",
top: 0,
left: 0
});
});
}
/**
* Set preview padding value
* @private
* @param {Array} padding
* @param {Boolean} build
*/
_setPadding(padding, build) {
const $wrapper = this.$wrapper;
const horizontal = this.options.horizontal;
const panel = this._conf.panel;
const paddingSum = padding.reduce((a, c) => parseFloat(a) + parseFloat(c));
const cssValue = {};
if (paddingSum || !build) {
horizontal && padding.reverse();
cssValue.padding = `${horizontal ? "0 " : ""}${
// add 'px' unit if not present
padding.map(v => (isNaN(v) ? v : `${v}px`))
.join(" 0 ")
}`;
}
if (build) {
cssValue.overflow = "hidden";
cssValue.boxSizing = "border-box";
}
Object.keys(cssValue).length && utils.css($wrapper, cssValue);
const paddingType = horizontal ? ["Left", "Right"] : ["Top", "Bottom"];
const wrapperSize = Math.max(
$wrapper[`offset${horizontal ? "Width" : "Height"}`],
utils.css($wrapper, horizontal ? "width" : "height", true)
);
panel.size = wrapperSize - (
utils.css($wrapper, `padding${paddingType[0]}`, true) +
utils.css($wrapper, `padding${paddingType[1]}`, true)
);
}
_clonePanel(v) {
const clone = v.cloneNode(true);
utils.classList(clone, `${this.options.prefix}-clone`, true);
return clone;
}
/**
* To fulfill minimum panel count cloning original node when circular or previewPadding option are set
* @private
* @return {Boolean} true : added clone node, false : not added
*/
_addClonePanels() {
const panel = this._conf.panel;
const panelCount = panel.origCount;
const cloneCount = panel.minCount - panelCount;
const list = panel.$list;
let cloneNodes;
// if panels are given less than required when circular option is set, then clone node to apply circular mode
if (this.options.circular && panelCount < panel.minCount) {
cloneNodes = list.map(v => this._clonePanel(v));
while (cloneNodes.length < cloneCount) {
cloneNodes = cloneNodes.concat(
list.map(v => this._clonePanel(v))
);
}
cloneNodes.forEach(v => this.$container.appendChild(v));
return !!cloneNodes.length;
}
return false;
}
/**
* Move panel's position within array
* @private
* @param {Number} count element counts to move
* @param {Boolean} append where the list to be appended(moved) (true: to the end, false: to the beginning)
*/
_movePanelPosition(count, append) {
const panel = this._conf.panel;
const list = panel.$list;
const listToMove = list.splice(append ? 0 : panel.count - count, count);
panel.$list = append ?
list.concat(listToMove) :
listToMove.concat(list);
}
/**
* Set default panel to show
* @private
* @param {Number} index
*/
_setDefaultPanel(index) {
const panel = this._conf.panel;
const lastIndex = panel.count - 1;
let coords;
let baseIndex;
if (this.options.circular) {
// if default index is given, then move correspond panel to the first position
if (index > 0 && index <= lastIndex) {
this._movePanelPosition(index, true);
}
// set first panel's position according physical node length
baseIndex = this._getBasePositionIndex();
this._movePanelPosition(baseIndex, false);
this._setPanelNo({
no: index,
currNo: index
});
// if defaultIndex option is given, then move to that index panel
} else if (index > 0 && index <= lastIndex) {
this._setPanelNo({
index,
no: index,
currIndex: index,
currNo: index
});
coords = [-(panel.size * index), 0];
this._setTranslate(coords);
this._setAxes("setTo", Math.abs(coords[0]), 0);
}
}
/**
* Arrange panels' position
* @private
* @param {Boolean} sort Need to sort panel's position
* @param {Number} indexToMove Number to move from current position (negative: left, positive: right)
*/
_arrangePanels(sort, indexToMove) {
const conf = this._conf;
const panel = conf.panel;
const touch = conf.touch;
const dirData = conf.dirData;
let baseIndex;
if (this.options.circular) {
// when arranging panels, set flag to not trigger flick custom event
conf.customEvent.flick = false;
// move elements according direction
if (sort) {
indexToMove && (touch.direction = dirData[+!(indexToMove > 0)]);
this._arrangePanelPosition(touch.direction, indexToMove);
}
// set index for base element's position
baseIndex = this._getBasePositionIndex();
this._setPanelNo({
index: baseIndex,
currIndex: baseIndex
});
// arrange Axes' coord position
conf.customEvent.flick = !!this._setAxes("setTo", panel.size * panel.index, 0);
}
this._applyPanelsPos();
}
/**
* Set each panel's position in DOM
* @private
*/
_applyPanelsPos() {
this._conf.panel.$list.forEach(this._applyPanelsCss.bind(this));
}
/**
* Set CSS style values to move elements
*
* Initialize setting up checking if browser support transform css property.
* If browser doesn't support transform, then use left/top properties instead.
* @private
* @param {HTMLElement} $el
* @param {Array} coordsValue
*/
_setMoveStyle($el, coordsValue) {
const transform = TRANSFORM;
const useLayerHack = this._conf.useLayerHack;
this._setMoveStyle = transform.support ?
($element, coords) => {
utils.css($element, {
[transform.name]: utils.translate(coords[0], coords[1], useLayerHack)
});
} : ($element, coords) => {
utils.css($element, {left: coords[0], top: coords[1]});
};
this._setMoveStyle($el, coordsValue);
}
/**
* Callback function for applying CSS values to each panels
* Need to be initialized before use, to set up for Android 2.x browsers or others.
* @private
*/
_applyPanelsCss() {
const conf = this._conf;
const dummyAnchorClassName = "__dummy_anchor";
const useTranslate = this.options.useTranslate;
if (!useTranslate) {
if (IS_ANDROID2) {
conf.$dummyAnchor = utils.$(`.${dummyAnchorClassName}`);
!conf.$dummyAnchor && this.$wrapper.appendChild(
conf.$dummyAnchor = utils.$(`<a href="javascript:void(0)" class="${dummyAnchorClassName}" style="position:absolute;height:0px;width:0px">`)
);
}
this._applyPanelsCss = function(v, i) {
const coords = this._getDataByDirection(
[`${this._conf.panel.size * i}px`, 0]
);
utils.css(v, {
left: coords[0],
top: coords[1]
});
};
} else {
this._applyPanelsCss = function(v, i) {
const coords = this._getDataByDirection([
TRANSFORM.support ?
`${100 * i}%` :
`${this._conf.panel.size * i}px`, 0
]);
this._setMoveStyle(v, coords);
};
}
}
/**
* Adjust container's css value to handle Android 2.x link highlighting bug
* @private
* @param {String} phase
* start - set left/top value to 0
* end - set translate value to 0
* @param {Array} toValue coordinate value
*/
_adjustContainerCss(phase, toValue) {
const conf = this._conf;
const panel = conf.panel;
const options = this.options;
const useTranslate = options.useTranslate;
const horizontal = options.horizontal;
const paddingTop = options.previewPadding[0];
let container = this.$container;
let to = toValue;
let value;
if (!useTranslate) {
if (!to) {
to = -panel.size * panel.index;
}
if (phase === "start") {
container = container.style;
value = parseFloat(container[horizontal ? "left" : "top"]);
if (horizontal) {
value && (container.left = "0px");
} else {
value !== paddingTop && (container.top = "0px");
}
this._setTranslate([-to, 0]);
} else if (phase === "end") {
to = this._getCoordsValue([to, 0]);
utils.css(container, {
left: to.x,
top: to.y,
[TRANSFORM.name]: utils.translate(0, 0, conf.useLayerHack)
});
conf.$dummyAnchor && conf.$dummyAnchor.focus();
}
}
}
/**
* Set Axes coord value
* @private
* @param {String} method
* @param {Number} flick destination value
* @param {Number} duration
* @return {eg.Axes} Axes instance
*/
_setAxes(method, flick, duration) {
return this._axesInst[method]({flick}, duration);
}
/**
* Set hint for browser to decide efficient way of doing transform changes(or animation)
* https://dev.opera.com/articles/css-will-change-property/
* @private
*/
_setHint() {
const style = {willChange: "transform"};
utils.css(this.$container, style);
utils.css(this._conf.panel.$list, style);
}
/**
* Get data according options.horizontal value
* @private
* @param {Array} value primary data to handle
* @return {Array}
*/
_getDataByDirection(value) {
const data = value.concat();
!this.options.horizontal && data.reverse();
return data;
}
/**
* Move nodes
* @private
* @param {Boolean} direction
* @param {Number} indexToMove
*/
_arrangePanelPosition(direction, indexToMove) {
const next = direction === this._conf.dirData[0];
this._movePanelPosition(Math.abs(indexToMove || 1), next);
}
/**
* Get the base position index of the panel
* @private
*/
_getBasePositionIndex() {
return Math.floor(this._conf.panel.count / 2 - 0.1);
}
/**
* Bind events
* @private
* @param {Boolean} bind
*/
_bindEvents(bind) {
const options = this.options;
const $wrapper = this.$wrapper;
const axesInst = this._axesInst;
if (bind) {
this._panInput = new PanInput($wrapper, {
inputType: options.inputType,
thresholdAngle: options.thresholdAngle,
scale: this._getDataByDirection([-1, 0])
});
axesInst.on({
hold: this._holdHandler.bind(this),
change: this._changeHandler.bind(this),
release: this._releaseHandler.bind(this),
animationStart: this._animationStartHandler.bind(this),
animationEnd: this._animationEndHandler.bind(this)
}).connect(this._getDataByDirection(["flick", ""]), this._panInput);
} else {
this.disableInput();
axesInst.off();
}
}
/**
* Set container's height value according to children's height
* @private
* @param {Number} direction
*/
_setAdaptiveHeight(direction) {
const conf = this._conf;
const indexToMove = conf.indexToMove;
let $children;
let height;
const $panel = indexToMove === 0 ?
// panel moved by 1
this[`get${
(direction === Axes.DIRECTION_LEFT && "Next") ||
(direction === Axes.DIRECTION_RIGHT && "Prev") || ""
}Element`]() :
// panel moved by .moveTo()
conf.panel.$list[
conf.panel.currIndex + indexToMove
];
const $first = $panel.querySelector(":first-child");
if ($first) {
height = $first.getAttribute(DATA_HEIGHT);
if (!height) {
$children = $panel.children;
height = utils.outerHeight(
$children.length > 1 ? ($panel.style.height = "auto", $panel) : $first
);
height > 0 && $first.setAttribute(DATA_HEIGHT, height);
}
height > 0 && (this.$wrapper.style.height = `${height}px`);
}
}
/**
* Trigger beforeRestore event
* @private
* @param {Object} e event object
*/
_triggerBeforeRestore(e) {
const conf = this._conf;
const touch = conf.touch;
// reverse direction value when restore
touch.direction = +conf.dirData.join("").replace(touch.direction, "");
/**
* This event occurs before the current panel starts to return to its original position. Followes [flick]{@link eg.Flicking#event:flick} and [restore]{@link eg.Flicking#event:restore} events. The conditions of occurrence are as follows.<br><br>1. The user has finished input but does not exceed the panel movement threshold.<br>2. Call the [restore()]{@link eg.Flicking#restore} method. (Prevent the default behavior of the [beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart} event.)
* @ko 현재 패널이 원래 위치로 되돌아가기 시작전에 발생하는 이벤트이다. 뒤이어 [flick]{@link eg.Flicking#event:flick}과 [restore]{@link eg.Flicking#event:restore}이벤트가 발생한다. 발생조건은 아래와 같다.<br><br>1. 사용자 입력이 끝났는데 패널 이동 임계점을 넘지 않은 경우.<br>2. [restore()]{@link eg.Flicking#restore} 메서드 호출.([beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart} 이벤트의 기본동작 방지 전제)
* @name eg.Flicking#beforeRestore
* @event
* @property {String} eventType The name of the event <ko>이벤트 명</ko>
* @property {Boolean} isTrusted `true` when the event was generated by a user action("mouse" or "touch") otherwise `false`.<ko>사용자 액션("mouse" 또는 "touch")에 의해 이벤트가 생성된 경우 `true`. 그 외는 `false`.</ko>
* @property {Number} no Index number of the current panel element. See the [getIndex()]{@link eg.Flicking#getIndex} method.<ko>현재 패널 요소의 인덱스 번호. [getIndex()]{@link eg.Flicking#getIndex}메서드 참조.</ko>
* @property {Number} direction of the panel movement. If `horizontal=true` is {@link eg.Flicking.DIRECTION_LEFT} or {@link eg.Flicking.DIRECTION_RIGHT}. If `horizontal=false` is {@link eg.Flicking.DIRECTION_UP} or {@link eg.Flicking.DIRECTION_DOWN}.<ko>패널 이동 방향. `horizontal=true` 이면 {@link eg.Flicking.DIRECTION_LEFT} 혹은 {@link eg.Flicking.DIRECTION_RIGHT}. `horizontal=false` 이면 {@link eg.Flicking.DIRECTION_UP} 혹은 {@link eg.Flicking.DIRECTION_DOWN}.</ko>
* @property {Number} depaPos Starting coordinate. <ko>출발점 좌표.</ko>
* @property {Number} destPos Destination coordinate. <ko>도착점 좌표.</ko>
* @see eg.Flicking#event:flick
* @see eg.Flicking#event:restore
* @see eg.Flicking#restore
* @example
* // The order of event occurrence.
* // 이벤트 발생 순서
* beforeRestore (once) > flick (many times) > restore (once)
*/
conf.customEvent.restore = this._triggerEvent(EVENTS.beforeRestore, {
depaPos: e.depaPos.flick,
destPos: e.destPos.flick
});
if (!conf.customEvent.restore) {
"stop" in e && e.stop();
conf.panel.animating = false;
}
}
/**
* Trigger restore event
* @private
*/
_triggerRestore() {
const customEvent = this._conf.customEvent;
/**
* The event that occurs after completing the move by [restore()]{@link eg.Flicking#restore} method.
* @ko [restore()]{@link eg.Flicking#restore} 메서드에 의해 패널이 원래 위치로 이동을 완료한 다음 발생하는 이벤트.
* @name eg.Flicking#restore
* @event
* @property {String} eventType The name of the event <ko>이벤트 명</ko>
* @property {Boolean} isTrusted `true` when the event was generated by a user action("mouse" or "touch") otherwise `false`.<ko>사용자 액션("mouse" 또는 "touch")에 의해 이벤트가 생성된 경우 `true`. 그 외는 `false`.</ko>
* @property {Number} no Index number of the current panel element. See the [getIndex()]{@link eg.Flicking#getIndex} method.<ko>현재 패널 요소의 인덱스 번호. [getIndex()]{@link eg.Flicking#getIndex}메서드 참조.</ko>
* @property {Number} direction of the panel movement. If `horizontal=true` is {@link eg.Flicking.DIRECTION_LEFT} or {@link eg.Flicking.DIRECTION_RIGHT}. If `horizontal=false` is {@link eg.Flicking.DIRECTION_UP} or {@link eg.Flicking.DIRECTION_DOWN}.<ko>패널 이동 방향. `horizontal=true` 이면 {@link eg.Flicking.DIRECTION_LEFT} 혹은 {@link eg.Flicking.DIRECTION_RIGHT}. `horizontal=false` 이면 {@link eg.Flicking.DIRECTION_UP} 혹은 {@link eg.Flicking.DIRECTION_DOWN}.</ko>
* @see eg.Flicking#event:beforeRestore
* @see eg.Flicking#event:flick
* @see eg.Flicking#restore
* @example
* // The order of event occurrence.
* // 이벤트 발생 순서
* beforeRestore (once) > flick (many times) > restore (once)
*/
customEvent.restore && this._triggerEvent(EVENTS.restore);
customEvent.restore = customEvent.restoreCall = false;
}
/**
* Set value when panel changes
* @private
* @param {String} phase - [start|end]
* @param {Object} pos
*/
_setPhaseValue(phase, pos) {
const conf = this._conf;
const options = this.options;
const panel = conf.panel;
const useTranslate = options.useTranslate;
if (phase === "start" && (panel.changed = this._isMovable())) {
/**
* An event that occurs before a user action or [moveTo()]{@link eg.Flicking#moveTo}, [prev()]{@link eg.Flicking#prev}, [next()]{@link eg.Flicking#next} method initiates a move to the destination panel. If you do not prevent the default behavior, then many [flick]{@link eg.Flicking#event:flick} events and one [flickEnd]{@link eg.Flicking#event:flickEnd} event will occur.
* @ko 사용자 액션 혹은 [moveTo()]{@link eg.Flicking#moveTo}, [prev()]{@link eg.Flicking#prev}, [next()]{@link eg.Flicking#next} 메서드에 의해 목적 패널로의 이동 시작전 발생하는 이벤트. 기본동작을 막지 않는다면 뒤이어 다수의 [flick]{@link eg.Flicking#event:flick}이벤트와 그 뒤 한 번의 [flickEnd]{@link eg.Flicking#event:flickEnd}이벤트가 발생한다.
* @name eg.Flicking#beforeFlickStart
* @event
* @property {String} eventType The name of the event <ko>이벤트 명</ko>
* @property {Boolean} isTrusted `true` when the event was generated by a user action("mouse" or "touch") otherwise `false`.<ko>사용자 액션("mouse" 또는 "touch")에 의해 이벤트가 생성된 경우 `true`. 그 외는 `false`</ko>
* @property {Number} no Index number of the current panel element. See the [getIndex()]{@link eg.Flicking#getIndex} method.<ko>현재 패널 요소의 인덱스 번호. [getIndex()]{@link eg.Flicking#getIndex}메서드 참조.</ko>
* @property {Number} direction of the panel movement. If `horizontal=true` is {@link eg.Flicking.DIRECTION_LEFT} or {@link eg.Flicking.DIRECTION_RIGHT}. If `horizontal=false` is {@link eg.Flicking.DIRECTION_UP} or {@link eg.Flicking.DIRECTION_DOWN}.<ko>패널 이동 방향. `horizontal=true` 이면 {@link eg.Flicking.DIRECTION_LEFT} 혹은 {@link eg.Flicking.DIRECTION_RIGHT}. `horizontal=false` 이면 {@link eg.Flicking.DIRECTION_UP} 혹은 {@link eg.Flicking.DIRECTION_DOWN}.</ko>
* @property {Number} depaPos Starting coordinate. <ko>출발점 좌표.</ko>
* @property {Number} destPos Destination coordinate. <ko>도착점 좌표.</ko>
* @property {Function} stop Cancels the default action. (Default action: Move to destination panel.) The panel element stays at the position of the call to `stop()`. To return to the original position, you must call the [restore()]{@link eg.Flicking#restore} method.<ko>기본동작을 취소한다. (기본동작: 목적 패널로의 이동.) 패널 요소가 `stop()`호출시점의 위치에 머물러 있는다. 원래 자리로 되돌리려면 [restore()]{@link eg.Flicking#restore} 메서드를 호출해야 한다.</ko>
* @see eg.Flicking#event:flick
* @see eg.Flicking#event:flickEnd
* @see eg.Flicking#moveTo
* @see eg.Flicking#prev
* @see eg.Flicking#next
* @example
* // The order of event occurrence.
* // 이벤트 발생 순서
* beforeFlickStart (once) > flick (many times) > flickEnd (once)
* @example
* // An example to prevent the default behavior.
* // 기본동작을 막는 예.
* new eg.Flicking("#flick").on("beforeFlickStart", e => {
* e.stop();
* });
*/
if (!this._triggerEvent(EVENTS.beforeFlickStart, pos)) {
panel.changed = panel.animating = false;
return false;
} else {
options.adaptiveHeight && this._setAdaptiveHeight(conf.touch.direction);
}
conf.indexToMove === 0 && this._setPanelNo();
} else if (phase === "end") {
if (options.circular && panel.changed) {
this._arrangePanels(true, conf.indexToMove);
}
useTranslate && this._setTranslate([-panel.size * panel.index, 0]);
conf.touch.distance = conf.indexToMove = 0;
/**
* The event that occurs after completing the move to the destination panel. It occurs in the following cases.<br><br>- After completing the movement to the destination panel by user's move input.<br>- `moveTo()`, `prev()`, `next()` method call. (It does not occur if you have disabled the default behavior of the [beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart} event.)
* @ko 목적 패널로의 이동을 완료한 다음 발생하는 이벤트. 아래의 경우에 발생한다.<br><br>- 사용자의 이동(move) 액션 입력에 의한 목적 패널로의 이동완료 후.<br>- `moveTo()`, `prev()`, `next()` 메서드 호출.([beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart}이벤트의 기본동작을 막았다면 발생하지 않는다.)
* @name eg.Flicking#flickEnd
* @event
* @property {String} eventType The name of the event.<ko>이벤트 명</ko>
* @property {Boolean} isTrusted `true` when the event was generated by a user action("mouse" or "touch") otherwise `false`.<ko>사용자 액션("mouse" 또는 "touch")에 의해 이벤트가 생성된 경우 `true`. 그 외는 `false`.</ko>
* @property {Number} no Index number of the current panel element. See the [getIndex()]{@link eg.Flicking#getIndex} method.<ko>현재 패널 요소의 인덱스 번호. [getIndex()]{@link eg.Flicking#getIndex}메서드 참조.</ko>
* @property {Number} direction of the panel movement. If `horizontal=true` is {@link eg.Flicking.DIRECTION_LEFT} or {@link eg.Flicking.DIRECTION_RIGHT}. If `horizontal=false` is {@link eg.Flicking.DIRECTION_UP} or {@link eg.Flicking.DIRECTION_DOWN}.<ko>패널 이동 방향. `horizontal=true` 이면 {@link eg.Flicking.DIRECTION_LEFT} 혹은 {@link eg.Flicking.DIRECTION_RIGHT}. `horizontal=false` 이면 {@link eg.Flicking.DIRECTION_UP} 혹은 {@link eg.Flicking.DIRECTION_DOWN}.</ko>
* @see eg.Flicking#event:beforeFlickStart
* @see eg.Flicking#event:flick
* @see eg.Flicking#moveTo
* @see eg.Flicking#prev
* @see eg.Flicking#next
* @example
* // The order of event occurrence.
* // 이벤트 발생 순서
* beforeFlickStart (once) > flick (many times) > flickEnd (once)
*/
panel.changed && this._triggerEvent(EVENTS.flickEnd);
}
this._adjustContainerCss(phase);
return true;
}
/**
* Get positive or negative according direction
* @private
*/
_getNumByDirection() {
const conf = this._conf;
return conf.touch.direction === conf.dirData[0] ? 1 : -1;
}
/**
* Revert panel number
* @private
*/
_revertPanelNo() {
const panel = this._conf.panel;
const num = this._getNumByDirection();
const index = panel.currIndex >= 0 ? panel.currIndex : panel.index - num;
const no = panel.currNo >= 0 ? panel.currNo : panel.no - num;
this._setPanelNo({
index,
no
});
}
/**
* Set the panel number
* @private
* @param {Object} obj number object
*/
_setPanelNo(obj) {
const panel = this._conf.panel;
const count = panel.origCount - 1;
const num = this._getNumByDirection();
if (utils.isObject(obj)) {
for (const key in obj) {
panel[key] = obj[key];
}
} else {
// remember current value
panel.currIndex = panel.index;
panel.currNo = panel.no;
panel.index += num;
panel.no += num;
}
if (panel.no > count) {
panel.no = 0;
} else if (panel.no < 0) {
panel.no = count;
}
}
/**
* Set pointerEvents css property on container element due to the iOS click bug
* @private
* @param {Event} e
*/
_setPointerEvents(e) {
const $container = this.$container;
const pointer = utils.css($container, "pointerEvents");
let pointerEvents;
if (e && e.holding &&
e.inputEvent && e.inputEvent.preventSystemEvent &&
pointer !== "none"
) {
pointerEvents = "none";
} else if (!e && pointer !== "auto") {
pointerEvents = "auto";
}
pointerEvents && utils.css($container, {pointerEvents});
}
/**
* Get coordinate value with unit
* @private
* @param coordsValue {Array} x,y numeric value
* @return {Object} x,y coordinate value with unit
*/
_getCoordsValue(coordsValue) {
// the param comes as [ val, 0 ], whatever the direction. So reorder the value depend the direction.
const coords = this._getDataByDirection(coordsValue);
return {
x: utils.getUnitValue(coords[0]),
y: utils.getUnitValue(coords[1])
};
}
/**
* Set translate property value
* @private
* @param {Array} coordsValue coordinate x,y value
*/
_setTranslate(coordsValue) {
const coords = this._getCoordsValue(coordsValue);
this._setMoveStyle(this.$container, [coords.x, coords.y]);
}
/**
* Check if panel passed through threshold pixel
* @private
*/
_isMovable() {
const options = this.options;
const axesInst = this._axesInst;
const isMovable = Math.abs(this._conf.touch.distance) >= options.threshold;
let max;
let currPos;
if (!options.circular && isMovable) {
max = axesInst.axis.flick.range[1];
currPos = axesInst.get().flick;
// if current position out of range
if (currPos < 0 || currPos > max) {
return false;
}
}
return isMovable;
}
/**
* Trigger custom events
* @private
* @param {String} name - event name
* @param {Object} param - additional event value
* @return {Boolean}
*/
_triggerEvent(name, param) {
const conf = this._conf;
const panel = conf.panel;
// pass changed panel no only on 'flickEnd' event
if (name === EVENTS.flickEnd) {
panel.currNo = panel.no;
panel.currIndex = panel.index;
}
return this.trigger(conf.eventPrefix + name, utils.extend({
eventType: name,
no: panel.currNo,
direction: conf.touch.direction,
isTrusted: conf.touch.isTrusted
}, param));
}
/**
* Get next/prev panel element/index.
* @private
* @param {Boolean} direction
* @param {Boolean} element - true:to get element, false:to get index
* @param {Number} physical - true : physical, false : logical (@deprecated since 2.2.0)
* @return {HTMLElement|Number}
*/
_getElement(direction, element, physical) {
const panel = this._conf.panel;
const circular = this.options.circular;
const pos = panel.currIndex;
const next = direction === this._conf.dirData[0];
let result = null;
let total;
let index;
if (physical) {
total = panel.count;
index = pos;
} else {
total = panel.origCount;
index = panel.currNo;
}
const currentIndex = index;
if (next) {
if (index < total - 1) {
index++;
} else if (circular) {
index = 0;
}
} else {
if (index > 0) {
index--;
} else if (circular) {
index = total - 1;
}
}
if (currentIndex !== index) {
result = element ? panel.$list[next ? pos + 1 : pos - 1] : index;
}
return result;
}
/**
* Set value to force move panels when duration is 0
* @private
* @param {Boolean} next
*/
_setValueToMove(next) {
const conf = this._conf;
conf.touch.distance = this.options.threshold + 1;
conf.touch.direction = conf.dirData[+!next];
}
/**
* Returns the index number of the current panel element.
* @ko 현재 패널 요소의 인덱스 번호를 반환한다.
* @method eg.Flicking#getIndex
* @param {Boolean} [physical=false] @deprecated since 2.2.0<br><br>Types of index numbers.<br>- true (Physical): Math.floor({Total number of panels} / 2 - 0.1) value. (Increase by 1 for every two panels.) If the circular option is false, it equals physical=false.<br>- false (Logical): The value of how the content(innerHTML) of the current panel element is in the defined order of the panel elements.<ko>@deprecated since 2.2.0<br><br>인덱스 번호의 종류.<br>- true (물리적): `Math.floor({패널 총 개수} / 2 - 0.1)` 값. (패널이 2개 늘어날 때마다 1씩 증가) `circular`옵션이 `false`이면 `physical=false`와 동일한 값.<br>- false (논리적): 현재 패널 요소의 컨텐트(innerHTML)가 '패널 요소들의 정의된 순서'에서 몇 번째인지에 대한 값.</ko>
* @return {Number} Index number of the current panel element. A zero-based integer.<ko>현재 패널의 인덱스 번호. 0부터 시작하는 정수.</ko>
* @see eg.Flicking#getPrevIndex
* @see eg.Flicking#getNextIndex
* @example
* ```html
* <div id="flick">
* <div><p>panel 0</p></div>
* <div><p>panel 1</p></div>
* <div><p>panel 2</p></div>
* <div><p>panel 3</p></div>
* </div>
* ```
* ```javascript
* // circular off and left flicking.
* // 순환을 끄고 좌 플리킹.
* new eg.Flicking("#flick").on("flickEnd", {currentTarget} => {
* console.log(currentTarget.getIndex()); // 1 > 2 > 3
* console.log(currentTarget.getIndex(true)); // 1 > 2 > 3
* };
*
* // circular on and left flicking.
* // 순환을 켜고 좌 플리킹.
* new eg.Flicking("#flick", {circular: true}).on("flickEnd", {currentTarget} => {
* console.log(currentTarget.getIndex()); // 1 > 2 > 3 > 0 > 1 > 2 > 3 > 0 ...
* console.log(currentTarget.getIndex(true)); // 1 > 1 > 1 > 1 > 1 > 1 > 1 > 1 ...
* };
* ```
* @example
* ```html
* <!--Define only two panels.-->
* <!--패널을 두 개만 정의한다.-->
* <div id="flick2">
* <div><p>panel 0</p></div>
* <div><p>panel 1</p></div>
* </div>
* ```
* ```javascript
* // (In the case of circulation) If the number of defined panel elements is less than the minimum number required, the number of panels is created.
* // Therefore, it is described as 'the number of panel definitions of the contents of the panel.'
* // (순환인 경우) 정의된 패널 요소의 개수가 필요 최소개수보다 적으면 그 수만큼의 패널을 생성한다.
* // 그렇기 때문에 '패널이 담고 있는 컨텐트의 패널 정의 순성상의 번호'라고 설명한다.
* const flick = new eg.Flicking("flick2", {
* circular: true
* });
*
* // The content of the current panel is the first in the panel definition order.
* // 현재 패널이 담고 있는 컨텐트는 패널 정의 순서상 첫 번째이다.
* flick.getIndex(); // 0
*
* // The content of the next panel is the second in the panel definition order.
* // 다음 패널이 담고 있는 컨텐트는 패널 정의 순서상 두 번째이다.
* flick.getNextIndex(); // 1
*
* // The content of the previous panel is the second in the panel definition order.
* // 이전 패널이 담고 있는 컨텐트는 패널 정의 순서상 두 번째이다.
* flick.getPrevIndex(); // 1
* ```
*/
getIndex(physical) {
return this._conf.panel[physical ? "currIndex" : "currNo"];
}
/**
* Returns the reference of the current panel element.
* @ko 현재 패널 요소의 레퍼런스를 반환한다.
* @method eg.Flicking#getElement
* @return {HTMLElement} Current panel element.<ko>현재 패널 요소.</ko>
* @see eg.Flicking#getPrevElement
* @see eg.Flicking#getNextElement
*/
getElement() {
const panel = this._conf.panel;
return panel.$list[panel.currIndex];
}
/**
* Returns the reference of the next panel element.
* @ko 다음 패널 요소의 레퍼런스를 반환한다.
* @method eg.Flicking#getNextElement
* @return {HTMLElement|null} Next panel element or `null` if it does not exist.<ko>다음 패널 요소. 패널이 없으면 `null`을 반환한다.</ko>
* @see eg.Flicking#getElement
* @see eg.Flicking#getPrevElement
*/
getNextElement() {
return this._getElement(this._conf.dirData[0], true);
}
/**
* Returns the index number of the next panel element.
* @ko 다음 패널 요소의 인덱스 번호를 반환한다.
* @method eg.Flicking#getNextIndex
* @param {Boolean} [physical=false] @deprecated since 2.2.0<br><br>Types of index numbers<br>- true (Physical): Plus one of [getIndex()]{@link eg.Flicking#getIndex} return value.<br>- false (Logical): The value of how the content(innerHTML) of the next panel element is in the defined order of the panel elements.<ko>@deprecated since 2.2.0<br><br>인덱스 번호의 종류.<br>- true (물리적): [getIndex()]{@link eg.Flicking#getIndex} 반환값에 1을 더한 값.<br>- false (논리적): 다음 패널 요소의 컨텐트(innerHTML)가 '패널 요소들의 정의된 순서'에서 몇 번째인지에 대한 값.</ko>
* @return {Number|null} Index number of the next panel element or null if it does not exist. A zero-based integer.<ko>다음 패널 요소의 인덱스 번호. 0부터 시작하는 정수. 패널이 없으면 `null`을 반환한다.</ko>
* @see eg.Flicking#getIndex
* @see eg.Flicking#getPrevIndex
*/
getNextIndex(physical) {
return this._getElement(this._conf.dirData[0], false, physical);
}
/**
* Returns a reference to all panel elements.
* @ko 모든 패널 요소의 레퍼런스를 반환한다.
* @method eg.Flicking#getAllElements
* @return {HTMLElement[]} Whole panel elements.<ko>모든 패널 요소.</ko>
*/
getAllElements() {
return this._conf.panel.$list;
}
/**
* Returns the reference of the previous panel element.
* @ko 이전 패널 요소의 레퍼런스를 반환한다.
* @method eg.Flicking#getPrevElement
* @return {HTMLElement|null} Previous panel element or `null` if it does not exist.<ko>이전 패널 요소. 패널이 없으면 `null`을 반환한다.</ko>
* @see eg.Flicking#getElement
* @see eg.Flicking#getNextElement
*/
getPrevElement() {
return this._getElement(this._conf.dirData[1], true);
}
/**
* Returns the index number of the previous panel element.
* @ko 이전 패널 요소의 인덱스 번호를 반환한다.
* @method eg.Flicking#getPrevIndex
* @param {Boolean} [physical=false] @deprecated since 2.2.0<br><br>Types of index numbers<br>- true (Physical): Minus one of [getIndex()]{@link eg.Flicking#getIndex} return value.<br>- false (Logical): The value of how the content(innerHTML) of the current panel element is in the defined order of the panel elements.<ko>@deprecated since 2.2.0<br><br>인덱스 번호의 종류<br>- true (물리적): [getIndex()]{@link eg.Flicking#getIndex} 반환값에 1을 뺀 값.<br>- false (논리적): 이전 패널 요소의 컨텐트(innerHTML)가 '패널 요소들의 정의된 순서'에서 몇 번째인지에 대한 값.</ko>
* @return {Number|null} Previous element index value or null if no more element exist. A zero-based integer.<ko>이전 패널 요소의 인덱스 번호. 0부터 시작하는 정수. 패널이 없는 경우는 `null`.</ko>
* @see eg.Flicking#getIndex
* @see eg.Flicking#getNextIndex
*/
getPrevIndex(physical) {
return this._getElement(this._conf.dirData[1], false, physical);
}
/**
* Checks whether the animated panel is playing.
* @ko 패널 이동 애니메이션이 진행 중인지 확인한다.
* @method eg.Flicking#isPlaying
* @return {Boolean} Indicates whether the animated panel is playing <ko>패널 이동 애니메이션 진행 중 여부</ko>
*/
isPlaying() {
return this._conf.panel.animating;
}
/**
* Move panel applying start/end phase value
* @private
* @param {String} method Axes' method name
* @param {Number} to destination value
* @param {Number} durationValue duration value
*/
_movePanelByPhase(method, to, durationValue) {
const duration = utils.getNumValue(durationValue, this.options.duration);
if (this._setPhaseValue("start") !== false) {
this._setAxes(method, to, duration);
!duration && this._setPhaseValue("end");
}
}
/**
* Moves an element to the next panel. If `horizontal=true`is right panel. If `horizontal=false`is lower panel.
* @ko 다음 패널로 이동한다. `horizontal=true`이면 우측 패널. `horizontal=false`이면 하측 패널.
* @method eg.Flicking#next
* @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
* @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
* @fires eg.Flicking#beforeFlickStart
* @fires eg.Flicking#flick
* @fires eg.Flicking#flickEnd
* @see eg.Flicking#moveTo
* @see eg.Flicking#prev
*/
next(duration) {
const index = this.getNextIndex();
if (typeof index !== "number") {
return this;
}
return this._moveTo(index, duration, Axes.DIRECTION_RIGHT);
}
/**
* Moves an element to the previous panel. If `horizontal=true`is left panel. If `horizontal=false`is upper panel.
* @ko 이전 패널로 이동한다. `horizontal=true`이면 좌측 패널. `horizontal=false`이면 상측 패널.
* @method eg.Flicking#prev
* @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
* @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
* @fires eg.Flicking#beforeFlickStart
* @fires eg.Flicking#flick
* @fires eg.Flicking#flickEnd
* @see eg.Flicking#moveTo
* @see eg.Flicking#next
*/
prev(duration) {
const index = this.getPrevIndex();
if (typeof index !== "number") {
return this;
}
return this._moveTo(index, duration, Axes.DIRECTION_LEFT);
}
/**
* Moves to the panel in the order specified in `noValue`. If noValue is equal to the current logical index numbering, no action is taken. [beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart}, [flick]{@link eg.Flicking#event:flick}, [flickEnd]{@link eg.Flicking#event:flickEnd} events occur one after the other.
* @ko `noValue`에 지정한 순서의 패널로 이동한다. `noValue`값이 현재의 논리적 인덱스 번호와 같다면 아무동작 하지 않는다. [beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart}, [flick]{@link eg.Flicking#event:flick}, [flickEnd]{@link eg.Flicking#event:flickEnd} 이벤트가 차례로 발생한다.
* @method eg.Flicking#moveTo
* @param {Number} noValue The logical index number of the panel element to be moved. (Based on the defined order of the panel elements.)<ko>이동할 패널 요소의 논리적 인덱스 번호. ([getIndex()]{@link eg.Flicking#getIndex}메서드 참조.)</ko>
* @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
* @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
* @fires eg.Flicking#beforeFlickStart
* @fires eg.Flicking#flick
* @fires eg.Flicking#flickEnd
* @see eg.Flicking#prev
* @see eg.Flicking#next
*/
moveTo(noValue, duration) {
return this._moveTo(noValue, duration);
}
_moveTo(noValue, duration, direction) {
const conf = this._conf;
const panel = conf.panel;
const circular = this.options.circular;
const currentIndex = panel.index;
let indexToMove;
let isPositive;
let no = noValue;
no = utils.getNumValue(no, -1);
if (no < 0 || no >= panel.origCount || no === panel.no ||
panel.animating || conf.touch.holding) {
return this;
}
indexToMove = no - (circular ? panel.no : currentIndex);
if (direction === Axes.DIRECTION_RIGHT && indexToMove < 0) {
indexToMove += panel.origCount;
} else if (direction === Axes.DIRECTION_LEFT && indexToMove > 0) {
indexToMove -= panel.origCount;
}
isPositive = indexToMove > 0;
// check for real panel count which can be moved on each sides in circular mode
if (circular &&
Math.abs(indexToMove) >
(isPositive ? panel.count - (currentIndex + 1) : currentIndex)) {
indexToMove += (isPositive ? -1 : 1) * panel.count;
isPositive = indexToMove > 0;
}
this._setPanelNo(circular ? {no} : {no, index: no});
this._conf.indexToMove = indexToMove;
this._setValueToMove(isPositive);
this._movePanelByPhase(
circular ? "setBy" : "setTo",
panel.size * (circular ? indexToMove : no),
duration
);
return this;
}
/**
* The horizontal or vertical length of the panel is updated according to the base element. If `horizontal=true` is horizontal. If `horizontal=false` is vertical.
* @ko 패널의 가로 혹은 세로 길이를 기준요소에 맞춰 갱신한다. `horizontal=true`이면 가로, `horizontal=false`이면 세로.
* @method eg.Flicking#resize
* @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
* @example
* const flick = new eg.Flicking("#flick", {
* previewPadding: [10, 10]
* });
*
* // When device orientaion changes.
* // 단말기를 회전했을 때.
* flick.resize();
*
* // Or when changes previewPadding option from its original value.
* // 또는 previewPadding옵션값을 변경했을 때.
* flick.options.previewPadding = [20, 30];
* flick.resize();
*/
resize() {
const conf = this._conf;
const options = this.options;
const panel = conf.panel;
const horizontal = options.horizontal;
const useTranslate = options.useTranslate;
let panelSize;
if (!this.isPlaying()) {
if (utils.isArray(options.previewPadding) && typeof(+options.previewPadding.join("")) === "number") {
this._setPadding(options.previewPadding.concat());
panelSize = panel.size;
} else if (horizontal) {
panelSize = panel.size = utils.css(this.$wrapper, "width", true);
}
// resize panel elements
utils.css(panel.$list, {
[horizontal ? "width" : "height"]: utils.getUnitValue(panelSize)
});
// remove data-height attribute and re-evaluate panel's height
if (options.adaptiveHeight) {
const $panel = this.$container.querySelectorAll(`[${DATA_HEIGHT}]`);
if ($panel.length) {
utils.toArray($panel)
.forEach(v => v.removeAttribute(DATA_HEIGHT));
this._setAdaptiveHeight();
}
}
this._axesInst.axis.flick.range = [0, panelSize * (panel.count - 1)];
this._setAxes("setTo", panelSize * panel.index, 0);
if (!useTranslate) {
this._applyPanelsPos();
this._adjustContainerCss("end");
}
}
return this;
}
/**
* Return the panel to its original position. (It only works when the default behavior of the [beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart} event is canceled.) [beforeRestore]{@link eg.Flicking#event:beforeRestore}, [flick]{@link eg.Flicking#event:flick}, [restore]{@link eg.Flicking#event:restore} events are occur in order.
* @ko 패널의 위치를 원래 자리로 되돌린다. ([beforeFlickStart]{@link eg.Flicking#event:beforeFlickStart} 이벤트의 기본동작을 취소한 경우에만 동작함.) [beforeRestore]{@link eg.Flicking#event:beforeRestore}, [flick]{@link eg.Flicking#event:flick}, [restore]{@link eg.Flicking#event:restore} 이벤트가 차례로 발생한다.
* @method eg.Flicking#restore
* @param {Number} [durationValue=options.duration] Duration of the panel movement (unit: ms)<ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
* @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
* @fires eg.Flicking#event:beforeRestore
* @fires eg.Flicking#event:flick
* @fires eg.Flicking#event:restore
* @example
* new eg.Flicking("#flick").on("beforeFlickStart", e => {
* if (e.no === 2) {
* // Cancels the default behavior of the 'beforeFlickStart' event.
* // 'beforeFlickStart' 이벤트 기본동작 취소.
* e.stop();
*
* // Return to original position.
* // 원래 자리로 되돌림.
* this.restore(100);
* }
* });
*/
restore(durationValue) {
const conf = this._conf;
const panel = conf.panel;
const currPos = this._axesInst.get().flick;
let duration = durationValue;
let destPos;
// check if the panel isn't in right position
if (currPos !== panel.currIndex * panel.size) {
conf.customEvent.restoreCall = true;
duration = utils.getNumValue(duration, this.options.duration);
this._revertPanelNo();
destPos = panel.size * panel.index;
this._triggerBeforeRestore({depaPos: currPos, destPos});
this._setAxes("setTo", destPos, duration);
if (!duration) {
this._adjustContainerCss("end");
this._triggerRestore();
}
// to handle on api call
} else if (panel.changed) {
this._revertPanelNo();
conf.touch.distance = conf.indexToMove = 0;
}
return this;
}
/**
* The input from the input device is not blocked so that the panel can be moved by the input device.
* @ko 막았던 입력 장치로부터의 입력을 푼다.
* @method eg.Flicking#enableInput
* @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
* @see eg.Flicking#disableInput
*/
enableInput() {
this._panInput.enable();
return this;
}
/**
* The input from the input device is blocked so that the panel is not moved by the input device.
* @ko 패널이 입력 장치에 의해 움직이지 않도록 입력 장치로부터의 입력을 막는다.
* @method eg.Flicking#disableInput
* @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
* @see eg.Flicking#enableInput
*/
disableInput() {
this._panInput.disable();
return this;
}
/**
* Get current flicking status. If the returned value is specified as a [setStatus()]{@link eg.Flicking#setStatus} method argument, it can be returned to its value state.
* @ko 현재 상태 값을 반환한다. 반환받은 값을 [setStatus()]{@link eg.Flicking#setStatus} 메서드 인자로 지정하면 그 값 상태로 되돌릴 수 있다.
* @method eg.Flicking#getStatus
* @param {Boolean} [stringify] Set true if want get stringified status value.<ko>true 지정시 json문자열 형태로 반환한다.</ko>
* @return {Status|String} An object with current state value information.<ko>현재 상태값 정보를 가진 객체.</ko>
* @see eg.Flicking#setStatus
* @example
* const flick = new eg.Flicking("#flick");
* const status = flick.getStatus();
* const jsonStaus = flick.getStatus(true);
*
* console.log(status); // {panel: {...}, $list: Array(7)}
* console.log(jsonStatus); // "{\"panel\":{\"index\":3,\"no\":6,\"currIndex\":3,\"currNo\":6},\"$list\":[{\"style\":\"background-color: rgb(155, 49, 137); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 3</p>\n\t\t\t\t\t\t\"},{\"style\":\"background-color: rgb(51, 172, 91); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 4</p>\n\t\t\t\t\t\t\"},{\"style\":\"background-color: rgb(116, 38, 241); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 5</p>\n\t\t\t\t\t\t\"},{\"style\":\"background-color: rgb(141, 139, 24); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 6</p>\n\t\t\t\t\t\t\"},{\"style\":\"background-color: rgb(204, 102, 204); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 0</p>\n\t\t\t\t\t\t\"},{\"style\":\"background-color: rgb(54, 53, 156); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 1</p>\n\t\t\t\t\t\t\"},{\"style\":\"background-color: rgb(196, 218, 72); position: absolute; height: 100%;\",\"className\":\"eg-flick-panel\",\"html\":\"\n\t\t\t\t\t\t\t\t<p>panel 2</p>\n\t\t\t\t\t\t\"}]}"
*/
/**
* The return value specification of the getStatus () method.
* @ko getStatus() 메서드의 반환값 명세.
* @typedef {Object} Status
* @property {Object} panel current panel position<ko>현재 패널 위치</ko>
* @property {Number} panel.index Physical index number.<ko>물리적 인덱스 번호.</ko>
* @property {Number} panel.currIndex Current physical index number.<ko>현재 물리적 인덱스 번호.</ko>
* @property {Number} panel.no Logical index number.<ko>논리적 인덱스 번호.</ko>
* @property {Number} panel.currNo Current logical index number.<ko>현재 논리적 인덱스 번호.</ko>
* @property {Array.<{style: String, className: String, html: String}>} $list panel's html<ko>패널 정보</ko>
* @property {Object} $list.obj For convenience, the element is denoted by obj.<ko>편의상 원소를 obj로 표기함</ko>
* @property {String} $list.obj.style The value of the style attribute of the panel element. ('transform', 'left', 'top', 'will-change', 'box-sizing', 'width' style has been deleted.)<ko>패널 요소의 style 속성 값. ('transform', 'left', 'top', 'will-change', 'box-sizing', 'width' style은 삭제됨)</ko>
* @property {String} $list.obj.className The class name of the panel element.<ko>패널 요소의 class 이름.</ko>
* @property {String} $list.obj.html The innerHTML value of the panel element.<ko>패널 요소의 innerHTML 값.</ko>
* @see eg.Flicking#getIndex
*/
getStatus(stringify) {
const panel = this._conf.panel;
const rxStyle = /((?:-webkit-)?transform|left|top|will-change|box-sizing|width):[^;]*;/g;
const status = {
// current panel position
panel: {
index: panel.index,
no: panel.no,
currIndex: panel.currIndex,
currNo: panel.currNo
},
// panel's html
$list: panel.$list.map(v => ({
style: v.style.cssText.replace(rxStyle, "").trim(),
className: v.className,
html: v.innerHTML
}))
};
return stringify ?
JSON.stringify(status) : status;
}
/**
* Restore to the state of the `statusValue`.
* @ko `statusValue`의 상태로 복원한다.
* @method eg.Flicking#setStatus
* @param {Status|String} statusValue Status value to be restored. You can specify the return value of the [getStatus()]{@link eg.Flicking#getStatus} method.<ko>복원할 상태 값. [getStatus()]{@link eg.Flicking#getStatus}메서드 반환값을 지정하면 된다.</ko>
* @see eg.Flicking#getStatus
* @example
* const flick = new eg.Flicking("#flick");
* const status = flick.getStatus();
*
* // Move to arbitrary panel.
* // 임의 패널로 이동
* flick.moveTo(2);
*
* // Restore to status.
* // status 상태로 복원
* flick.setStatus(status);
*/
setStatus(statusValue) {
const panel = this._conf.panel;
const isAdaptiveHeight = this.options.adaptiveHeight;
const status = typeof statusValue === "string" ?
JSON.parse(statusValue) : statusValue;
if (status) {
for (const x in status.panel) {
x in panel && (panel[x] = status.panel[x]);
}
panel.$list.forEach((v, i) => {
const data = status.$list[i];
const style = data.style;
const className = data.className;
const html = data.html;
style && (v.style.cssText += style);
className && (v.className = className);
html && (v.innerHTML = html);
});
isAdaptiveHeight && this._setAdaptiveHeight();
}
}
/**
* Returns the reference element and its children to the state they were in before the instance was created. Remove all attached event handlers. Specify `null` for all attributes of the instance (including inherited attributes).<br>If plugin isn't empty, also reset all plugins registered.
* @ko 기준 요소와 그 하위 요소를 인스턴스 생성전의 상태로 되돌린다. 부착된 모든 이벤트 핸들러를 탈거한다. 인스턴스의 모든 속성(상속받은 속성포함)에 `null`을 지정한다.<br>플러그인이 비어있지 않다면, 플러그인도 모두 리셋한다.
* @method eg.Flicking#destroy
* @example
* const flick = new eg.Flicking("#flick");
* flick.destroy();
* console.log(flick.moveTo); // null
*/
destroy() {
const conf = this._conf;
const origPanelStyle = conf.origPanelStyle;
const wrapper = origPanelStyle.wrapper;
const container = origPanelStyle.container;
const list = origPanelStyle.list;
// unbind events
this._bindEvents(false);
this.off();
// destroy eg.Axes instance
this._axesInst.destroy();
this._panInput.destroy();
// unwrap container element and restore original inline style
// restore wrapper style
const $wrapper = this.$wrapper;
$wrapper.setAttribute("class", wrapper.className);
$wrapper[wrapper.style ? "setAttribute" : "removeAttribute"]("style", wrapper.style);
// restore container style
const $container = this.$container;
const $children = []
.slice.call($container.children);
if (origPanelStyle.container.className) {
$container.setAttribute("class", container.className);
$container[container.style ? "setAttribute" : "removeAttribute"]("style", container.style);
} else {
$children.forEach(v => $wrapper.appendChild(v));
$container.parentNode.removeChild($container);
}
for (let i = 0, $el; ($el = $children[i]); i++) {
if (i > list.length - 1) {
$el.parentNode.removeChild($el);
} else {
const className = list[i].className;
const style = list[i].style;
$el[className ? "setAttribute" : "removeAttribute"]("class", className);
$el[style ? "setAttribute" : "removeAttribute"]("style", style);
}
}
// release plugin resources
this.plugins.forEach(v => {
this.plugins[v].$componentWillUnmount();
});
// release resources
for (const x in this) {
this[x] = null;
}
}
/**
* Register plugin to be used.
* @ko 사용될 플러그인을 등록한다.
* @method eg.Flicking#plugin
* @example
* new eg.Flicking("#flick").plugin([
* new eg.Flicking.plugin.OpacityEffect("span"),
* ...
* ]);
* @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
*/
plugin(list) {
list.forEach(p => {
/**
* A list of plugins used.
* @ko 사용된 플러그인 목록
* @property {Array} plugins An array of plugin instances <ko>플러그인 인스턴스 배열</ko>
* @name plugins
* @type {Array}
* @instance
* @example
* const flick = new eg.Flicking( ... ).plugin([ ... ]);
*
* flick.plugins; // [ ... ] - array of plugins
* @memberof eg.Flicking
*/
if (this.plugins.filter(v => v.constructor === p.constructor).length === 0) {
this.plugins.push(p.$componentWillMount(this));
}
});
return this;
}
/**
* Rebuild/Initialize panels by current DOM of panels.
* @ko 현재 패널 DOM 을 기준으로 패널을 재구성/초기화한다.
* @method eg.Flicking#rebuild
* @example
* flicking.rebuild();
*
* @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
*/
rebuild(param = {defaultIndex: this.options.defaultIndex}) {
const container = this.$container;
const panel = this._conf.panel;
const options = this.options;
const prefix = options.prefix;
// filter original panel (remove clones)
utils.toArray(container.querySelectorAll(`.${prefix}-clone`)).forEach(el => {
container.removeChild(el);
});
panel.$list = utils.toArray(container.children);
// panels' css values
this._initOriginalPanelStyle(panel.$list);
// Add clones
if (this._addClonePanels()) {
panel.$list = utils.toArray(container.children);
}
this._setDefaultPanel(param.defaultIndex);
this._arrangePanels();
return this;
}
/**
* Collection of utilities used internally
* @ko 내부에서 사용되는 유틸리티 모음
* @name utils
* @memberof eg.Flicking
* @static
* @constant
* @private
* @type {Object}
*/
static utils = utils;
/**
* Version info string
* @ko 버전정보 문자열
* @name VERSION
* @static
* @type {String}
* @example
* eg.Flicking.VERSION; // ex) 2.2.0
* @memberof eg.Flicking
*/
static VERSION = "#__VERSION__#";
/**
* Constant value used internally
* @ko 내부에서 사용되는 상수 값
* @name consts
* @memberof eg.Flicking
* @static
* @constant
* @private
* @type {Object}
*/
static consts = {
EVENTS,
TRANSFORM,
SUPPORT_WILLCHANGE,
IS_ANDROID2
};
/**
* Constant value for none direction.
* @ko none 방향에 대한 상수 값.
* @name DIRECTION_NONE
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 1
*/
static DIRECTION_NONE = Axes.DIRECTION_NONE;
/**
* Constant value for left direction.
* @ko left 방향에 대한 상수 값.
* @name DIRECTION_LEFT
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 2
*/
static DIRECTION_LEFT = Axes.DIRECTION_LEFT;
/**
* Constant value for right direction.
* @ko right 방향에 대한 상수 값.
* @name DIRECTION_RIGHT
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 4
*/
static DIRECTION_RIGHT = Axes.DIRECTION_RIGHT;
/**
* Constant value for up direction.
* @ko up 방향에 대한 상수 값.
* @name DIRECTION_UP
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 8
*/
static DIRECTION_UP = Axes.DIRECTION_UP;
/**
* Constant value for down direction.
* @ko down 방향에 대한 상수 값.
* @name DIRECTION_DOWN
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 16
*/
static DIRECTION_DOWN = Axes.DIRECTION_DOWN;
/**
* Constant value for horizontal direction.
* @ko horizontal 방향에 대한 상수 값.
* @name DIRECTION_HORIZONTAL
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 6
*/
static DIRECTION_HORIZONTAL = Axes.DIRECTION_HORIZONTAL;
/**
* Constant value for vertical direction.
* @ko vertical 방향에 대한 상수 값.
* @name DIRECTION_VERTICAL
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 24
*/
static DIRECTION_VERTICAL = Axes.DIRECTION_VERTICAL;
/**
* Constant value for all direction.
* @ko all 방향에 대한 상수 값.
* @name DIRECTION_ALL
* @memberof eg.Flicking
* @static
* @constant
* @type {Number}
* @default 30
*/
static DIRECTION_ALL = Axes.DIRECTION_ALL;
}