Source: flicking.js

  1. // jscs:disable validateLineBreaks, maximumLineLength
  2. /**
  3. * Copyright (c) 2015 NAVER Corp.
  4. * egjs projects are licensed under the MIT license
  5. */
  6. eg.module("flicking", ["jQuery", eg, window, document, eg.MovableCoord], function ($, ns, global, doc, MC) {
  7. "use strict";
  8. // jscs:enable validateLineBreaks, maximumLineLength
  9. /**
  10. * A module used to implement flicking interactions. With this module, you can make flicking gestures, which are ways to navigate left and right to move between panels arranged side by side.
  11. * @group egjs
  12. * @ko 플리킹 UI를 구현하는 모듈. 나란히 배치한 패널을 쓸어 넘겨 다음 패널이나 이전 패널로 이동하는 플리킹 UI를 만들 수 있다.
  13. * @class
  14. * @name eg.Flicking
  15. * @extends eg.Component
  16. *
  17. * @param {HTMLElement|String|jQuery} element A base element for the eg.Flicking module <ko>eg.Flicking 모듈을 사용할 기준 엘리먼트</ko>
  18. * @param {Object} options The option object of the eg.Flicking module<ko>eg.Flicking 모듈의 옵션 객체</ko>
  19. * @param {Boolean} [options.hwAccelerable=eg.isHWAccelerable()] Force hardware compositing <ko>하드웨어 가속 사용 여부</ko>
  20. * @param {String} [options.prefix=eg-flick] A prefix for class names of the panel elements <ko>패널 엘리먼트의 클래스 이름에 설정할 접두사</ko>
  21. * @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>
  22. * @param {Boolean} [options.horizontal=true] Direction of the panel movement (true: horizontal, false: vertical) <ko>패널 이동 방향 (true 가로방향, false 세로방향)</ko>
  23. * @param {Boolean} [options.circular=false] Indicates whether a circular panel is available <ko>패널 순환 여부</ko>
  24. * @param {Number|Array} [options.previewPadding=[0,0]] The preview size for the previous or next panel. If direction is set to "horizontal", the preview section will be displayed on the left and right of the panel. If direction is set to "vertical", it will be displayed on the top and bottom of the panel <ko>이전 패널과 다음 패널을 미리 보는 영역의 크기. 패널 이동 방향이 가로 방향이면 패널 왼쪽과 오른쪽에 미리 보는 영역이 나타난다. 패널 이동 방향이 세로 방향이면 패널 위쪽과 아래쪽에 미리 보는 영역이 나타난다</ko>
  25. * @param {Number|Array} [options.bounce=[10,10]] − The size of bouncing area. If a panel is set to "non-circulable", the start and end panels can exceed the base element area and move further as much as the bouncing area. If a panel is dragged to the bouncing area and then dropped, the panel where bouncing effects are applied is retuned back into the base element area. <ko>바운스 영역의 크기. 패널이 순환하지 않도록 설정됐다면 시작 패널과 마지막 패널은 기준 엘리먼트 영역을 넘어 바운스 영역의 크기만큼 더 이동할 수 있다. 패널을 바운스 영역까지 끌었다가 놓으면, 바운스 효과가 적용된 패널이 다시 기준 엘리먼트 영역 안으로 들어온다</ko>
  26. * @param {Number} [options.threshold=40] Distance threshold. If the drag exceeds the threshold value, it will be changed to the next panel <ko>다음 패널로 바뀌는 기준 이동 거리. 패널을 기준 이동 거리 이상 끌었다 놓으면 패널이 다음 패널로 바뀐다</ko>
  27. * @param {Number} [options.duration=100] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
  28. * @param {Function} [options.panelEffect=easeOutCubic] The easing function to apply to a panel moving animation <ko>패널 이동 애니메이션에 적용할 easing 함수</ko>
  29. * @param {Number} [options.defaultIndex=0] The index number of a panel to be selected upon module initialization <ko>모듈이 초기화될 때 선택할 패널의 인덱스 번호</ko>
  30. * @param {Array} [options.inputType] Types of input devices.<br>- touch: A touch screen can be used to move a panel.<br>- mouse: A mouse can be used to move a panel. <ko>입력 장치 종류.<br>- touch: 터치 입력 장치로 패널을 이동할 수 있다.<br>- mouse: 마우스로 패널을 이동할 수 있다.</ko>
  31. * @param {Number} [options.thresholdAngle=45] The threshold value that determines whether user action is horizontal or vertical (0~90) <ko>사용자의 동작이 가로 방향인지 세로 방향인지 판단하는 기준 각도(0~90)</ko>
  32. * @param {Boolean} [options.adaptiveHeight=false] Set container's height be adaptive according panel's height.<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>
  33. *
  34. * @codepen {"id":"rVOpPK", "ko":"플리킹 UI 기본 예제", "en":"Flicking UI default example", "collectionId":"ArxyLK", "height" : 403}
  35. * @support {"ie": "10+", "ch" : "latest", "ff" : "latest", "sf" : "latest" , "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"}
  36. *
  37. * @see Easing Functions Cheat Sheet {@link http://easings.net/}
  38. * @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>
  39. * @example
  40. <!-- HTML -->
  41. <div id="mflick">
  42. <div>
  43. <p>Layer 0</p>
  44. </div>
  45. <div>
  46. <p>Layer 1</p>
  47. </div>
  48. <div>
  49. <p>Layer 2</p>
  50. </div>
  51. </div>
  52. <script>
  53. var some = new eg.Flicking("#mflick", {
  54. circular : true,
  55. threshold : 50
  56. }).on({
  57. beforeRestore : function(e) { ... },
  58. flickStart : function(e) { ... }
  59. });
  60. </script>
  61. */
  62. // define custom events name
  63. var EVENTS = {
  64. "beforeFlickStart": "beforeFlickStart",
  65. "beforeRestore": "beforeRestore",
  66. "flick": "flick",
  67. "flickEnd": "flickEnd",
  68. "restore": "restore"
  69. };
  70. // check for css transform support
  71. var SUPPORT_TRANSFORM = doc.documentElement.style;
  72. SUPPORT_TRANSFORM = "transform" in SUPPORT_TRANSFORM ||
  73. "webkitTransform" in SUPPORT_TRANSFORM;
  74. // check for will-change support
  75. var SUPPORT_WILLCHANGE = global.CSS && global.CSS.supports &&
  76. global.CSS.supports("will-change", "transform");
  77. // check for Android 2.x
  78. var IS_ANDROID2 = ns.agent().os;
  79. IS_ANDROID2 = IS_ANDROID2.name === "android" && /^2\./.test(IS_ANDROID2.version);
  80. // data-height attribute's name for adaptiveHeight option
  81. var DATA_HEIGHT = "data-height";
  82. ns.Flicking = ns.Class.extend(ns.Component, {
  83. _events: function() {
  84. return EVENTS;
  85. },
  86. /**
  87. * Constructor
  88. * @param {HTMLElement|String|jQuery} element - base element
  89. * @param {Object} options
  90. */
  91. construct: function (element, options, _prefix) {
  92. this.$wrapper = $(element);
  93. var $children = this.$wrapper.children();
  94. if (!$children.length) {
  95. // jscs:disable validateLineBreaks, maximumLineLength
  96. throw new Error("Given base element doesn't exist or it hasn't proper DOM structure to be initialized.");
  97. // jscs:enable validateLineBreaks, maximumLineLength
  98. }
  99. this._setOptions(options);
  100. this._setConfig($children, _prefix);
  101. !ns._hasClickBug() && (this._setPointerEvents = $.noop);
  102. this._build();
  103. this._bindEvents(true);
  104. this._applyPanelsCss();
  105. this._arrangePanels();
  106. this.options.hwAccelerable && SUPPORT_WILLCHANGE && this._setHint();
  107. this.options.adaptiveHeight && this._setAdaptiveHeight();
  108. this._adjustContainerCss("end");
  109. },
  110. /**
  111. * Set options values
  112. * @param {Object} options
  113. */
  114. _setOptions: function(options) {
  115. var arrVal = {
  116. previewPadding: [ 0, 0 ],
  117. bounce: [ 10, 10 ]
  118. };
  119. $.extend(this.options = {
  120. hwAccelerable: ns.isHWAccelerable(), // check weather hw acceleration is available
  121. prefix: "eg-flick", // prefix value of class name
  122. deceleration: 0.0006, // deceleration value
  123. horizontal: true, // move direction (true == horizontal, false == vertical)
  124. circular: false, // circular mode. In this mode at least 3 panels are required.
  125. previewPadding: arrVal.previewPadding, // preview padding value in left(up) to right(down) order. In this mode at least 5 panels are required.
  126. bounce: arrVal.bounce, // bounce value in left(up) to right(down) order. Works only in non-circular mode.
  127. threshold: 40, // the distance pixel threshold value for change panel
  128. duration: 100, // duration ms for animation
  129. panelEffect: $.easing.easeOutCubic, // $.easing function for panel change animation
  130. defaultIndex: 0, // initial panel index to be shown
  131. inputType: ["touch", "mouse"], // input type
  132. thresholdAngle: 45, // the threshold value that determines whether user action is horizontal or vertical (0~90)
  133. adaptiveHeight: false // Set container's height be adaptive according panel's height
  134. }, options);
  135. var self = this;
  136. $.each(arrVal, function(i, v) {
  137. var val = self.options[i];
  138. if ($.isNumeric(val)) {
  139. val = [ val, val ];
  140. } else if (!$.isArray(val)) {
  141. val = v;
  142. }
  143. self.options[i] = val;
  144. });
  145. },
  146. /**
  147. * Set config values
  148. * @param {jQuery} $children wrappers' children elements
  149. * @param {String} _prefix event prefix
  150. */
  151. _setConfig: function($children, _prefix) {
  152. var options = this.options;
  153. var padding = options.previewPadding;
  154. var $container = $children.filter("." + options.prefix + "-container:first");
  155. if ($container.length) {
  156. this.$container = $container;
  157. $children = $container.children();
  158. }
  159. // config value
  160. this._conf = {
  161. panel: {
  162. $list: $children, // panel list
  163. index: 0, // dom index used among process
  164. no: 0, // panel no used among process
  165. currIndex: 0, // current physical dom index
  166. currNo: 0, // current logical panel number
  167. size: 0, // panel size
  168. count: 0, // total physical panel count
  169. origCount: 0, // total count of given original panels
  170. changed: false, // if panel changed
  171. animating: false, // current animating status boolean
  172. minCount: padding[0] + padding[1] > 0 ? 5 : 3 // minimum panel count
  173. },
  174. touch: {
  175. holdPos: [0, 0], // hold x,y coordinate
  176. destPos: [0, 0], // destination x,y coordinate
  177. distance: 0, // touch distance pixel of start to end touch
  178. direction: null, // touch direction
  179. lastPos: 0, // to determine move on holding
  180. holding: false
  181. },
  182. customEvent: { // for custom events value
  183. flick: true,
  184. restore: false,
  185. restoreCall: false
  186. },
  187. origPanelStyle: { // remember original class and inline style in case of restoration on destroy()
  188. wrapper: {
  189. className: this.$wrapper.attr("class") || null,
  190. style: this.$wrapper.attr("style") || null
  191. },
  192. list: $children.map(function(i, v) {
  193. return {
  194. className: $(v).attr("class") || null,
  195. style: $(v).attr("style") || null
  196. };
  197. })
  198. },
  199. useLayerHack: options.hwAccelerable && !SUPPORT_WILLCHANGE,
  200. dirData: [], // direction constant value according horizontal or vertical
  201. indexToMove: 0,
  202. eventPrefix: _prefix || "",
  203. // For buggy link highlighting on Android 2.x
  204. $dummyAnchor: null
  205. };
  206. $([["LEFT", "RIGHT"], ["UP", "DOWN"]][+!options.horizontal]).each(
  207. $.proxy(function (i, v) {
  208. this._conf.dirData.push(MC["DIRECTION_" + v]);
  209. }, this));
  210. },
  211. /**
  212. * Build and set panel nodes to make flicking structure
  213. */
  214. _build: function () {
  215. var panel = this._conf.panel;
  216. var options = this.options;
  217. var $children = panel.$list;
  218. var padding = options.previewPadding.concat();
  219. var prefix = options.prefix;
  220. var horizontal = options.horizontal;
  221. var panelCount = panel.count = panel.origCount = $children.length;
  222. var cssValue;
  223. var bounce = options.bounce;
  224. this._setPadding(padding, true);
  225. var sizeValue = this._getDataByDirection([ panel.size, "100%" ]);
  226. // create container element
  227. cssValue = "position:relative;z-index:2000;width:100%;height:100%;" +
  228. (horizontal ? "" : "top:0;");
  229. if (this.$container) {
  230. this.$container.attr("style", cssValue);
  231. } else {
  232. this.$container = $children.wrapAll(
  233. "<div class='" + prefix + "-container' style='" + cssValue + "'>"
  234. ).parent();
  235. }
  236. // panels' css values
  237. $children.addClass(prefix + "-panel").css({
  238. position: "absolute",
  239. width: sizeValue[0],
  240. height: sizeValue[1],
  241. boxSizing: "border-box",
  242. top: 0,
  243. left: 0
  244. });
  245. if (this._addClonePanels()) {
  246. panelCount = panel.count = (
  247. panel.$list = this.$container.children()
  248. ).length;
  249. }
  250. // create MovableCoord instance
  251. this._mcInst = new MC({
  252. min: [0, 0],
  253. max: this._getDataByDirection([panel.size * (panelCount - 1), 0]),
  254. margin: 0,
  255. circular: false,
  256. easing: options.panelEffect,
  257. deceleration: options.deceleration,
  258. bounce: this._getDataByDirection([ 0, bounce[1], 0, bounce[0] ])
  259. });
  260. this._setDefaultPanel(options.defaultIndex);
  261. },
  262. /**
  263. * Set preview padding value
  264. * @param {Array} padding
  265. * @param {Boolean} build
  266. */
  267. _setPadding: function(padding, build) {
  268. var horizontal = this.options.horizontal;
  269. var panel = this._conf.panel;
  270. var paddingSum = padding[0] + padding[1];
  271. var cssValue = {};
  272. if (paddingSum || !build) {
  273. cssValue.padding = (horizontal ?
  274. "0 " + padding.reverse().join("px 0 ") :
  275. padding.join("px 0 ")) + "px";
  276. }
  277. if (build) {
  278. cssValue.overflow = "hidden";
  279. cssValue.boxSizing = "border-box";
  280. }
  281. !$.isEmptyObject(cssValue) &&
  282. this.$wrapper.css(cssValue);
  283. panel.size = this.$wrapper[ horizontal ? "width" : "height" ]();
  284. },
  285. /**
  286. * To fulfill minimum panel count cloning original node when circular or previewPadding option are set
  287. * @return {Boolean} true : added clone node, false : not added
  288. */
  289. _addClonePanels: function () {
  290. var panel = this._conf.panel;
  291. var panelCount = panel.origCount;
  292. var cloneCount = panel.minCount - panelCount;
  293. var list = panel.$list;
  294. var cloneNodes;
  295. // if panels are given less than required when circular option is set, then clone node to apply circular mode
  296. if (this.options.circular && panelCount < panel.minCount) {
  297. cloneNodes = list.clone();
  298. while (cloneNodes.length < cloneCount) {
  299. cloneNodes = cloneNodes.add(list.clone());
  300. }
  301. return this.$container.append(cloneNodes);
  302. }
  303. },
  304. /**
  305. * Move panel's position within array
  306. * @param {Number} count element counts to move
  307. * @param {Boolean} append where the list to be appended(moved) (true: to the end, false: to the beginning)
  308. */
  309. _movePanelPosition: function (count, append) {
  310. var panel = this._conf.panel;
  311. var list = panel.$list.toArray();
  312. var listToMove;
  313. listToMove = list.splice(append ? 0 : panel.count - count, count);
  314. panel.$list = $(append ? list.concat(listToMove) : listToMove.concat(list));
  315. },
  316. /**
  317. * Set default panel to show
  318. * @param {Number} index
  319. */
  320. _setDefaultPanel: function (index) {
  321. var panel = this._conf.panel;
  322. var lastIndex = panel.count - 1;
  323. var coords;
  324. var baseIndex;
  325. if (this.options.circular) {
  326. // if default index is given, then move correspond panel to the first position
  327. if (index > 0 && index <= lastIndex) {
  328. this._movePanelPosition(index, true);
  329. }
  330. // set first panel's position according physical node length
  331. baseIndex = this._getBasePositionIndex();
  332. this._movePanelPosition(baseIndex, false);
  333. this._setPanelNo({
  334. no: index,
  335. currNo: index
  336. });
  337. } else {
  338. // if defaultIndex option is given, then move to that index panel
  339. if (index > 0 && index <= lastIndex) {
  340. this._setPanelNo({
  341. index: index,
  342. no: index,
  343. currIndex: index,
  344. currNo: index
  345. });
  346. coords = [ -(panel.size * index), 0];
  347. this._setTranslate(coords);
  348. this._setMovableCoord("setTo", [
  349. Math.abs(coords[0]), Math.abs(coords[1])
  350. ], true, 0);
  351. }
  352. }
  353. },
  354. /**
  355. * Arrange panels' position
  356. * @param {Boolean} sort Need to sort panel's position
  357. * @param {Number} indexToMove Number to move from current position (negative: left, positive: right)
  358. */
  359. _arrangePanels: function (sort, indexToMove) {
  360. var conf = this._conf;
  361. var panel = conf.panel;
  362. var touch = conf.touch;
  363. var dirData = conf.dirData;
  364. var baseIndex;
  365. if (this.options.circular) {
  366. // when arranging panels, set flag to not trigger flick custom event
  367. conf.customEvent.flick = false;
  368. // move elements according direction
  369. if (sort) {
  370. indexToMove && (touch.direction = dirData[+!Boolean(indexToMove > 0)]);
  371. this._arrangePanelPosition(touch.direction, indexToMove);
  372. }
  373. // set index for base element's position
  374. baseIndex = this._getBasePositionIndex();
  375. this._setPanelNo({
  376. index: baseIndex,
  377. currIndex: baseIndex
  378. });
  379. // arrange MovableCoord's coord position
  380. conf.customEvent.flick = !!this._setMovableCoord("setTo", [
  381. panel.size * panel.index, 0
  382. ], true, 0);
  383. }
  384. this._applyPanelsPos();
  385. },
  386. /**
  387. * Set each panel's position in DOM
  388. */
  389. _applyPanelsPos: function() {
  390. this._conf.panel.$list.each(
  391. $.proxy(this._applyPanelsCss, this)
  392. );
  393. },
  394. /**
  395. * Set CSS style values to move elements
  396. *
  397. * Initialize setting up checking if browser support transform css property.
  398. * If browser doesn't support transform, then use left/top properties instead.
  399. */
  400. _setMoveStyle: (function () {
  401. return SUPPORT_TRANSFORM ?
  402. function ($element, coords) {
  403. $element.css("transform",
  404. ns.translate(coords[0], coords[1], this._conf.useLayerHack)
  405. );
  406. } : function ($element, coords) {
  407. $element.css({ left: coords[0], top: coords[1] });
  408. };
  409. })(),
  410. /**
  411. * Callback function for applying CSS values to each panels
  412. *
  413. * Need to be initialized before use, to set up for Android 2.x browsers or others.
  414. */
  415. _applyPanelsCss: function () {
  416. var conf = this._conf;
  417. var dummyAnchorClassName = "__dummy_anchor";
  418. if (IS_ANDROID2) {
  419. conf.$dummyAnchor = $("." + dummyAnchorClassName);
  420. !conf.$dummyAnchor.length && this.$wrapper.append(
  421. conf.$dummyAnchor = $("<a href='javascript:void(0);' class='" +
  422. dummyAnchorClassName +
  423. "' style='position:absolute;height:0px;width:0px;'>")
  424. );
  425. this._applyPanelsCss = function (i, v) {
  426. var coords = this._getDataByDirection([
  427. (this._conf.panel.size * i) + "px", 0
  428. ]);
  429. $(v).css({
  430. left: coords[0],
  431. top: coords[1]
  432. });
  433. };
  434. } else {
  435. this._applyPanelsCss = function (i, v) {
  436. var coords = this._getDataByDirection([
  437. SUPPORT_TRANSFORM ?
  438. (100 * i) + "%" :
  439. (this._conf.panel.size * i) + "px", 0]);
  440. this._setMoveStyle($(v), coords);
  441. };
  442. }
  443. },
  444. /**
  445. * Adjust container's css value to handle Android 2.x link highlighting bug
  446. *
  447. * @param {String} phase
  448. * start - set left/top value to 0
  449. * end - set translate value to 0
  450. * @param {Array} coords coordinate value
  451. */
  452. _adjustContainerCss: function (phase, coords) {
  453. var conf = this._conf;
  454. var panel = conf.panel;
  455. var options = this.options;
  456. var horizontal = options.horizontal;
  457. var paddingTop = options.previewPadding[0];
  458. var container = this.$container;
  459. var value;
  460. if (IS_ANDROID2) {
  461. if (!coords) {
  462. coords = [-panel.size * panel.index, 0];
  463. }
  464. if (phase === "start") {
  465. container = container[0].style;
  466. value = parseInt(container[horizontal ? "left" : "top"], 10);
  467. if (horizontal) {
  468. value && (container.left = 0);
  469. } else {
  470. value !== paddingTop && (container.top = "0px");
  471. }
  472. this._setTranslate([-coords[+!options.horizontal], 0]);
  473. } else if (phase === "end") {
  474. coords = this._getCoordsValue(coords);
  475. container.css({
  476. left: coords.x,
  477. top: coords.y,
  478. transform: ns.translate(0, 0, conf.useLayerHack)
  479. });
  480. conf.$dummyAnchor[0].focus();
  481. }
  482. }
  483. },
  484. /**
  485. * Set MovableCoord coord value
  486. * @param {String} method
  487. * @param {Array} coord
  488. * @param {Boolean} isDirVal
  489. * @param {Number} duration
  490. * @return {eg.MovableCoord} MovableCoord instance
  491. */
  492. _setMovableCoord: function (method, coord, isDirVal, duration) {
  493. if (isDirVal) {
  494. coord = this._getDataByDirection(coord);
  495. }
  496. return this._mcInst[method](coord[0], coord[1], duration);
  497. },
  498. /**
  499. * Set hint for browser to decide efficient way of doing transform changes(or animation)
  500. * https://dev.opera.com/articles/css-will-change-property/
  501. */
  502. _setHint: function () {
  503. var value = "transform";
  504. this.$container.css("willChange", value);
  505. this._conf.panel.$list.css("willChange", value);
  506. },
  507. /**
  508. * Get data according options.horizontal value
  509. *
  510. * @param {Array} value primary data to handle
  511. * @return {Array}
  512. */
  513. _getDataByDirection: function (value) {
  514. value = value.concat();
  515. !this.options.horizontal && value.reverse();
  516. return value;
  517. },
  518. /**
  519. * Move nodes
  520. * @param {Boolean} direction
  521. * @param {Number} indexToMove
  522. */
  523. _arrangePanelPosition: function (direction, indexToMove) {
  524. var next = direction === this._conf.dirData[0];
  525. this._movePanelPosition(Math.abs(indexToMove || 1), next);
  526. },
  527. /**
  528. * Get the base position index of the panel
  529. */
  530. _getBasePositionIndex: function () {
  531. return Math.floor(this._conf.panel.count / 2 - 0.1);
  532. },
  533. /**
  534. * Bind events
  535. * @param {Boolean} bind
  536. */
  537. _bindEvents: function (bind) {
  538. var options = this.options;
  539. var $wrapper = this.$wrapper;
  540. var mcInst = this._mcInst;
  541. if (bind) {
  542. mcInst.bind($wrapper, {
  543. scale: this._getDataByDirection([-1, 0]),
  544. direction: MC["DIRECTION_" +
  545. (options.horizontal ? "HORIZONTAL" : "VERTICAL")],
  546. interruptable: false,
  547. inputType: options.inputType,
  548. thresholdAngle: options.thresholdAngle
  549. }).on({
  550. hold: $.proxy(this._holdHandler, this),
  551. change: $.proxy(this._changeHandler, this),
  552. release: $.proxy(this._releaseHandler, this),
  553. animationStart: $.proxy(this._animationStartHandler, this),
  554. animationEnd: $.proxy(this._animationEndHandler, this)
  555. });
  556. } else {
  557. mcInst.unbind($wrapper).off();
  558. }
  559. },
  560. /**
  561. * 'hold' event handler
  562. */
  563. _holdHandler: function (e) {
  564. var conf = this._conf;
  565. conf.touch.holdPos = e.pos;
  566. conf.touch.holding = true;
  567. conf.panel.changed = false;
  568. this._adjustContainerCss("start", e.pos);
  569. },
  570. /**
  571. * 'change' event handler
  572. */
  573. _changeHandler: function (e) {
  574. var conf = this._conf;
  575. var touch = conf.touch;
  576. var posIndex = +!this.options.horizontal;
  577. var pos = e.pos[posIndex];
  578. var holdPos = touch.holdPos[posIndex];
  579. var direction;
  580. var eventRes = null;
  581. var movedPx;
  582. this._setPointerEvents(e); // for "click" bug
  583. /**
  584. * This event is fired when panel moves.
  585. * @ko 패널이 이동할 때 발생하는 이벤트
  586. * @name eg.Flicking#flick
  587. * @event
  588. * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
  589. * @param {String} param.eventType The name of the event<ko>이름명</ko>
  590. * @param {Number} param.index Physical index number of the current panel element, which is relative to DOM (@deprecated since 1.3.0)<ko>현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다 (@deprecated since 1.3.0)</ko>
  591. * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content <ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
  592. * @param {Number} param.direction Direction of the movement (see eg.MovableCoord.DIRECTION_* constant) <ko>이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
  593. * @param {Array} param.pos Start coordinate <ko>출발점 좌표</ko>
  594. * @param {Number} param.pos.0 x-coordinate <ko>x 좌표</ko>
  595. * @param {Number} param.pos.1 y-coordinate <ko>y 좌표</ko>
  596. * @param {Boolean} param.holding Indicates whether a user holds an element on the screen of the device. <ko>사용자가 기기의 화면을 누르고 있는지 여부</ko>
  597. * @param {Number} param.distance Distance moved from then starting point. According the move direction, positive on eg.MovableCoord.DIRECTION_LEFT/UP and negative on eg.MovableCoord.DIRECTION_RIGHT/DOWN <ko>시작점부터 이동된 거리의 값. 이동 방향에 따라 eg.MovableCoord.DIRECTION_LEFT/UP의 경우 양수를 eg.MovableCoord.DIRECTION_RIGHT/DOWN의 경우는 음수를 반환</ko>
  598. */
  599. if (e.hammerEvent) {
  600. direction = e.hammerEvent.direction;
  601. // Adjust direction in case of diagonal touch move
  602. movedPx = e.hammerEvent[ this.options.horizontal ? "deltaX" : "deltaY" ];
  603. if (!~$.inArray(direction, conf.dirData)) {
  604. direction = conf.dirData[ +(Math.abs(touch.lastPos) <= movedPx) ];
  605. }
  606. touch.lastPos = movedPx;
  607. } else {
  608. touch.lastPos = null;
  609. }
  610. conf.customEvent.flick && (eventRes = this._triggerEvent(EVENTS.flick, {
  611. pos: e.pos,
  612. holding: e.holding,
  613. direction: direction || touch.direction,
  614. distance: pos - (holdPos || (touch.holdPos[posIndex] = pos))
  615. }));
  616. (eventRes || eventRes === null) && this._setTranslate([ -pos, 0 ]);
  617. },
  618. /**
  619. * 'release' event handler
  620. */
  621. _releaseHandler: function (e) {
  622. var touch = this._conf.touch;
  623. var pos = e.destPos;
  624. var posIndex = +!this.options.horizontal;
  625. var holdPos = touch.holdPos[posIndex];
  626. var panelSize = this._conf.panel.size;
  627. touch.distance = e.depaPos[posIndex] - touch.holdPos[posIndex];
  628. touch.direction = this._conf.dirData[
  629. +!Boolean(touch.holdPos[posIndex] < e.depaPos[posIndex])
  630. ];
  631. pos[posIndex] = Math.max(
  632. holdPos - panelSize, Math.min(holdPos, pos[posIndex])
  633. );
  634. touch.destPos[posIndex] =
  635. pos[posIndex] = Math.round(pos[posIndex] / panelSize) * panelSize;
  636. touch.distance === 0 && this._adjustContainerCss("end");
  637. touch.holding = false;
  638. this._setPointerEvents(); // for "click" bug
  639. },
  640. /**
  641. * 'animationStart' event handler
  642. */
  643. _animationStartHandler: function (e) {
  644. var conf = this._conf;
  645. var panel = conf.panel;
  646. var customEvent = conf.customEvent;
  647. panel.animating = true;
  648. if (!customEvent.restoreCall && e.hammerEvent &&
  649. this._setPhaseValue("start", {
  650. depaPos: e.depaPos,
  651. destPos: e.destPos
  652. }) === false) {
  653. e.stop();
  654. }
  655. if (e.hammerEvent) {
  656. e.duration = this.options.duration;
  657. e.destPos[+!this.options.horizontal] =
  658. panel.size * (
  659. panel.index + conf.indexToMove
  660. );
  661. }
  662. if (this._isMovable()) {
  663. !customEvent.restoreCall && (customEvent.restore = false);
  664. } else {
  665. this._triggerBeforeRestore(e);
  666. }
  667. },
  668. /**
  669. * 'animationEnd' event handler
  670. */
  671. _animationEndHandler: function () {
  672. this._setPhaseValue("end");
  673. this._conf.panel.animating = false;
  674. this._triggerRestore();
  675. },
  676. /**
  677. * Set container's height value according to children's height
  678. * @param {Number} direction
  679. */
  680. _setAdaptiveHeight: function(direction) {
  681. var $panel;
  682. var $first;
  683. var $children;
  684. var height;
  685. var conf = this._conf;
  686. var indexToMove = conf.indexToMove;
  687. $panel = indexToMove === 0 ?
  688. // panel moved by 1
  689. this[ "get" + (
  690. direction === MC.DIRECTION_LEFT && "Next" ||
  691. direction === MC.DIRECTION_RIGHT && "Prev" || ""
  692. ) + "Element" ]() :
  693. // panel moved by .moveTo()
  694. conf.panel.$list.eq(
  695. conf.panel.currIndex + indexToMove
  696. );
  697. $first = $panel.find(":first");
  698. height = $first.attr(DATA_HEIGHT);
  699. if (!height) {
  700. $children = $panel.children();
  701. height = ($children.length > 1 ? $panel.css("height", "auto") : $first)
  702. .outerHeight(true);
  703. $first.attr(DATA_HEIGHT, height);
  704. }
  705. this.$wrapper.height(height);
  706. },
  707. /**
  708. * Trigger beforeRestore event
  709. * @param {Object} e event object
  710. */
  711. _triggerBeforeRestore: function(e) {
  712. var conf = this._conf;
  713. var touch = conf.touch;
  714. // reverse direction value when restore
  715. touch.direction = ~~conf.dirData.join("").replace(touch.direction, "");
  716. /**
  717. * This event is fired before an element is restored to its original position when user action is done while the element is not dragged until a certain distance threshold is reached
  718. * @ko 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원되기 전에 발생하는 이벤트
  719. * @name eg.Flicking#beforeRestore
  720. * @event
  721. * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
  722. * @param {String} param.eventType The name of the event <ko>이름명</ko>
  723. * @param {Number} param.index Physical index number of the current panel element, which is relative to DOM. (@deprecated since 1.3.0)<ko>현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다. (@deprecated since 1.3.0)</ko>
  724. * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content.<ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
  725. * @param {Number} param.direction Direction of the movement (see eg.MovableCoord.DIRECTION_* constant) <ko>이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
  726. * @param {Array} param.depaPos Start coordinate <ko>출발점 좌표</ko>
  727. * @param {Number} param.depaPos.0 x-coordinate <ko>x 좌표</ko>
  728. * @param {Number} param.depaPos.1 y-coordinate <ko>y 좌표</ko>
  729. * @param {Array} param.destPos End coordinate <ko>도착점 좌표</ko>
  730. * @param {Number} param.destPos.0 x-coordinate <ko>x 좌표</ko>
  731. * @param {Number} param.destPos.1 y-coordinate <ko>y 좌표</ko>
  732. */
  733. conf.customEvent.restore = this._triggerEvent(EVENTS.beforeRestore, {
  734. depaPos: e.depaPos,
  735. destPos: e.destPos
  736. });
  737. if (!conf.customEvent.restore) {
  738. "stop" in e && e.stop();
  739. conf.panel.animating = false;
  740. }
  741. },
  742. /**
  743. * Trigger restore event
  744. */
  745. _triggerRestore: function() {
  746. var customEvent = this._conf.customEvent;
  747. /**
  748. * This event is fired after an element is restored to its original position when user action is done while the element is not dragged until a certain distance threshold is reached.
  749. * @ko 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원된 다음 발생하는 이벤트
  750. * @name eg.Flicking#restore
  751. * @event
  752. * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
  753. * @param {String} param.eventType The name of the event <ko>이름명</ko>
  754. * @param {Number} param.index Physical index number of the current panel element, which is relative to DOM(@deprecated since 1.3.0)<ko>현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다 (@deprecated since 1.3.0)</ko>
  755. * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content. <ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
  756. * @param {Number} param.direction Direction of the panel move (see eg.MovableCoord.DIRECTION_* constant) <ko>이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
  757. */
  758. customEvent.restore && this._triggerEvent(EVENTS.restore);
  759. customEvent.restoreCall = false;
  760. },
  761. /**
  762. * Set value when panel changes
  763. * @param {String} phase - [start|end]
  764. * @param {Object} pos
  765. */
  766. _setPhaseValue: function (phase, pos) {
  767. var conf = this._conf;
  768. var options = this.options;
  769. var panel = conf.panel;
  770. if (phase === "start" && (panel.changed = this._isMovable())) {
  771. /**
  772. * This event is fired before flicking starts
  773. * @ko 플리킹이 시작하기 전에 발생하는 이벤트
  774. * @name eg.Flicking#beforeFlickStart
  775. * @event
  776. * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
  777. * @param {String} param.eventType The name of the event <ko>이름명</ko>
  778. * @param {Number} param.index Physical index number of the current panel element, which is relative to DOM. (@deprecated since 1.3.0)<ko>현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다 (@deprecated since 1.3.0)</ko>
  779. * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content.<ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
  780. * @param {Number} param.direction Direction of the movement (see eg.MovableCoord.DIRECTION_* constant) <ko>− 이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
  781. * @param {Array} param.depaPos Start coordinate <ko>출발점 좌표</ko>
  782. * @param {Number} param.depaPos.0 x-coordinate <ko>x 좌표</ko>
  783. * @param {Number} param.depaPos.1 y-coordinate <ko>y 좌표</ko>
  784. * @param {Array} param.destPos End coordinate <ko>도착점 좌표</ko>
  785. * @param {Number} param.destPos.0 x-coordinate <ko>x 좌표</ko>
  786. * @param {Number} param.destPos.1 y-coordinate <ko>y 좌표</ko>
  787. */
  788. if (!this._triggerEvent(EVENTS.beforeFlickStart, pos)) {
  789. return panel.changed = panel.animating = false;
  790. } else {
  791. options.adaptiveHeight && this._setAdaptiveHeight(conf.touch.direction);
  792. }
  793. conf.indexToMove === 0 && this._setPanelNo();
  794. } else if (phase === "end") {
  795. if (options.circular && panel.changed) {
  796. this._arrangePanels(true, conf.indexToMove);
  797. }
  798. !IS_ANDROID2 && this._setTranslate([-panel.size * panel.index, 0]);
  799. conf.touch.distance = conf.indexToMove = 0;
  800. /**
  801. * This event is fired after panel moves.
  802. * @ko 패널이 이동한 다음 발생하는 이벤트
  803. * @name eg.Flicking#flickEnd
  804. * @event
  805. * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
  806. * @param {String} param.eventType The name of the event <ko>이름명</ko>
  807. * @param {Number} param.index Physical index number of the current panel element, which is relative to DOM (@deprecated since 1.3.0)<ko>현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다 (@deprecated since 1.3.0)</ko>
  808. * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content. <ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.</ko>
  809. * @param {Number} param.direction Direction of the movemen (see eg.MovableCoord.DIRECTION_* constant) <ko>− 이동 방향(eg.MovableCoord.DIRECTION_* constant 참고</ko>
  810. */
  811. panel.changed && this._triggerEvent(EVENTS.flickEnd);
  812. }
  813. this._adjustContainerCss(phase);
  814. },
  815. /**
  816. * Get positive or negative according direction
  817. */
  818. _getNumByDirection: function() {
  819. var conf = this._conf;
  820. return conf.touch.direction === conf.dirData[0] ? 1 : -1;
  821. },
  822. /**
  823. * Revert panel number
  824. */
  825. _revertPanelNo: function() {
  826. var panel = this._conf.panel;
  827. var num = this._getNumByDirection();
  828. var index = panel.currIndex >= 0 ? panel.currIndex : panel.index - num;
  829. var no = panel.currNo >= 0 ? panel.currNo : panel.no - num;
  830. this._setPanelNo({
  831. index: index,
  832. no: no
  833. });
  834. },
  835. /**
  836. * Set the panel number
  837. * @param {Object} obj number object
  838. */
  839. _setPanelNo: function (obj) {
  840. var panel = this._conf.panel;
  841. var count = panel.origCount - 1;
  842. var num = this._getNumByDirection();
  843. if ($.isPlainObject(obj)) {
  844. $.each(obj, function(i, v) {
  845. panel[i] = v;
  846. });
  847. } else {
  848. // remember current value
  849. panel.currIndex = panel.index;
  850. panel.currNo = panel.no;
  851. panel.index += num;
  852. panel.no += num;
  853. }
  854. if (panel.no > count) {
  855. panel.no = 0;
  856. } else if (panel.no < 0) {
  857. panel.no = count;
  858. }
  859. },
  860. /**
  861. * Set pointerEvents css property on container element due to the iOS click bug
  862. * @param {Event} e
  863. */
  864. _setPointerEvents: function (e) {
  865. var pointer = this.$container.css("pointerEvents");
  866. var val;
  867. if (e && e.holding &&
  868. e.hammerEvent && e.hammerEvent.preventSystemEvent &&
  869. pointer !== "none"
  870. ) {
  871. val = "none";
  872. } else if (!e && pointer !== "auto") {
  873. val = "auto";
  874. }
  875. val && this.$container.css("pointerEvents", val);
  876. },
  877. /**
  878. * Get coordinate value with unit
  879. * @param coords {Array} x,y numeric value
  880. * @return {Object} x,y coordinate value with unit
  881. */
  882. _getCoordsValue: function (coords) {
  883. // the param comes as [ val, 0 ], whatever the direction. So reorder the value depend the direction.
  884. coords = this._getDataByDirection(coords);
  885. return {
  886. x: this._getUnitValue(coords[0]),
  887. y: this._getUnitValue(coords[1])
  888. };
  889. },
  890. /**
  891. * Set translate property value
  892. * @param {Array} coords coordinate x,y value
  893. */
  894. _setTranslate: function (coords) {
  895. coords = this._getCoordsValue(coords);
  896. this._setMoveStyle(this.$container, [ coords.x, coords.y ]);
  897. },
  898. /**
  899. * Return unit formatted value
  900. * @param {Number|String} val
  901. * @return {String} val Value formatted with unit
  902. */
  903. _getUnitValue: function (val) {
  904. var rx = /(?:[a-z]{2,}|%)$/;
  905. return (parseInt(val, 10) || 0) + (String(val).match(rx) || "px");
  906. },
  907. /**
  908. * Check if panel passed through threshold pixel
  909. */
  910. _isMovable: function () {
  911. var options = this.options;
  912. var conf = this._conf;
  913. var mcInst = this._mcInst;
  914. var panel = conf.panel;
  915. var isMovable = Math.abs(this._conf.touch.distance) >= options.threshold;
  916. var max;
  917. var currPos;
  918. var touchDirection;
  919. if (!options.circular && isMovable) {
  920. touchDirection = conf.touch.direction;
  921. max = this._getDataByDirection(mcInst.options.max)[0];
  922. currPos = this._getDataByDirection(mcInst.get())[0];
  923. // if current position out of range
  924. if (( panel.currNo === 0 && touchDirection === conf.dirData[1] || // first panel
  925. panel.count - 1 === panel.currNo && touchDirection === conf.dirData[0] // last panel
  926. ) && (currPos < 0 || currPos > max)) {
  927. return false;
  928. }
  929. }
  930. return isMovable;
  931. },
  932. /**
  933. * Trigger custom events
  934. * @param {String} name - event name
  935. * @param {Object} param - additional event value
  936. * @return {Boolean}
  937. */
  938. _triggerEvent: function (name, param) {
  939. var conf = this._conf;
  940. var panel = conf.panel;
  941. // pass changed panel no only on 'flickEnd' event
  942. if (name === EVENTS.flickEnd) {
  943. panel.currNo = panel.no;
  944. panel.currIndex = panel.index;
  945. }
  946. return this.trigger(conf.eventPrefix + name, $.extend({
  947. eventType: name,
  948. index: panel.currIndex,
  949. no: panel.currNo,
  950. direction: conf.touch.direction
  951. }, param));
  952. },
  953. /**
  954. * Get next/prev panel element/index.
  955. * @param {Boolean} direction
  956. * @param {Boolean} element - true:to get element, false:to get index
  957. * @param {Number} physical - true : physical, false : logical
  958. * @return {jQuery|Number}
  959. */
  960. _getElement: function (direction, element, physical) {
  961. var panel = this._conf.panel;
  962. var circular = this.options.circular;
  963. var pos = panel.currIndex;
  964. var next = direction === this._conf.dirData[0];
  965. var result = null;
  966. var total;
  967. var index;
  968. var currentIndex;
  969. if (physical) {
  970. total = panel.count;
  971. index = pos;
  972. } else {
  973. total = panel.origCount;
  974. index = panel.currNo;
  975. }
  976. currentIndex = index;
  977. if (next) {
  978. if (index < total - 1) {
  979. index++;
  980. } else if (circular) {
  981. index = 0;
  982. }
  983. } else {
  984. if (index > 0) {
  985. index--;
  986. } else if (circular) {
  987. index = total - 1;
  988. }
  989. }
  990. if (currentIndex !== index) {
  991. result = element ? $(panel.$list[next ? pos + 1 : pos - 1]) : index;
  992. }
  993. return result;
  994. },
  995. /**
  996. * Set value to force move panels when duration is 0
  997. * @param {Boolean} next
  998. */
  999. _setValueToMove: function (next) {
  1000. var conf = this._conf;
  1001. conf.touch.distance = this.options.threshold + 1;
  1002. conf.touch.direction = conf.dirData[ +!next ];
  1003. },
  1004. /**
  1005. * Check and parse value to number
  1006. * @param {Number|String} val
  1007. * @param {Number} defVal
  1008. * @return {Number}
  1009. */
  1010. _getNumValue: function (val, defVal) {
  1011. return isNaN(val = parseInt(val, 10)) ? defVal : val;
  1012. },
  1013. /**
  1014. * Returns the index number of the current panel element.
  1015. * @ko 현재 패널 엘리먼트의 인덱스 번호를 반환한다
  1016. * @method eg.Flicking#getIndex
  1017. * @param {Boolean} [physical=false] Types of index numbers<br>- true: Indicates physical index numbers relative to DOM.<br>- false: Indicates logical index numbers relative to the panel content. <ko>− 인덱스 번호의 종류<br>- true: 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다.<br>- false: 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.</ko>
  1018. * @return {Number} Index number of the current panel element <ko>현재 패널의 인덱스 번호</ko>
  1019. */
  1020. getIndex: function (physical) {
  1021. return this._conf.panel[ physical ? "currIndex" : "currNo" ];
  1022. },
  1023. /**
  1024. * Returns the reference of the current panel element.
  1025. * @ko 현재 패널 엘리먼트의 레퍼런스를 반환한다
  1026. * @method eg.Flicking#getElement
  1027. * @return {jQuery} Current element <ko>현재 엘리먼트</ko>
  1028. */
  1029. getElement: function () {
  1030. var panel = this._conf.panel;
  1031. return $(panel.$list[ panel.currIndex ]);
  1032. },
  1033. /**
  1034. * Returns the reference of the next panel element.
  1035. * @ko 다음 패널 엘리먼트의 레퍼런스를 반환한다.
  1036. * @method eg.Flicking#getNextElement
  1037. * @return {jQuery|null} Next panel element or null if it does not exist.<ko>다음 패널 엘리먼트. 패널이 없으면 'null'을 반환한다.</ko>
  1038. */
  1039. getNextElement: function () {
  1040. return this._getElement(this._conf.dirData[0], true);
  1041. },
  1042. /**
  1043. * Returns the index number of the next panel element.
  1044. * @ko 다음 패널 엘리먼트의 인덱스 번호를 반환한다
  1045. * @method eg.Flicking#getNextIndex
  1046. * @param {Boolean} [physical=false] Types of index numbers<br>- true: Indicates physical index numbers relative to DOM.<br>- false: Indicates logical index numbers relative to the panel content. <ko>− 인덱스 번호의 종류<br>- true: 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다.<br>- false: 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.</ko>
  1047. * @return {Number|null} Index number of the next panel element or null if it does not exist. <ko>다음 패널 엘리먼트의 인덱스 번호. 패널이 없으면 'null'을 반환한다</ko>
  1048. */
  1049. getNextIndex: function (physical) {
  1050. return this._getElement(this._conf.dirData[0], false, physical);
  1051. },
  1052. /**
  1053. * Returns the references of whole panel elements.
  1054. * @ko 패널을 구성하는 모든 엘리먼트의 레퍼런스를 반환한다
  1055. * @method eg.Flicking#getAllElements
  1056. * @return {jQuery} Whole panel elements <ko>모든 패널 엘리먼트</ko>
  1057. */
  1058. getAllElements: function () {
  1059. return this._conf.panel.$list;
  1060. },
  1061. /**
  1062. * Returns the reference of the previous panel element.
  1063. * @ko 이전 패널 엘리먼트의 레퍼런스를 반환한다.
  1064. * @method eg.Flicking#getPrevElement
  1065. * @return {jQuery|null} Previous panel element or null if it does not exist. <ko>이전 패널 엘리먼트. 패널이 없으면 'null'을 반환한다</ko>
  1066. */
  1067. getPrevElement: function () {
  1068. return this._getElement(this._conf.dirData[1], true);
  1069. },
  1070. /**
  1071. * Returns the index number of the previous panel element.
  1072. * @ko 이전 패널 엘리먼트의 인덱스 번호를 반환한다
  1073. * @method eg.Flicking#getPrevIndex
  1074. * @param {Boolean} [physical=false] Types of index numbers<br>- true: Indicates physical index numbers relative to DOM.<br>- false: Indicates logical index numbers relative to the panel content. <ko>− 인덱스 번호의 종류<br>- true: 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다.<br>- false: 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.</ko>
  1075. * @return {Number|null} Previous element index value or null if no more element exist<ko>이전 패널 인덱스 번호. 패널이 없는 경우에는 null</ko>
  1076. */
  1077. getPrevIndex: function (physical) {
  1078. return this._getElement(this._conf.dirData[1], false, physical);
  1079. },
  1080. /**
  1081. * Returns the total number of whole panel elements.
  1082. * @ko 전체 패널 엘리먼트의 개수를 반환한다
  1083. * @method eg.Flicking#getTotalCount
  1084. * @deprecated since 1.3.0
  1085. * @param {Boolean} [physical=false] Number of elements relative to (true: DOM, false: panel content)<ko>엘리먼트 개수의 기준(true: DOM 엘리먼트 기준, false: 패널 콘텐츠 기준)</ko>
  1086. * @return {Number} Total number of whole panel elements <ko>모든 패널 엘리먼트의 개수</ko>
  1087. */
  1088. getTotalCount: function (physical) {
  1089. return this._conf.panel[ physical ? "count" : "origCount" ];
  1090. },
  1091. /**
  1092. * Checks whether the animated panel is playing.
  1093. * @ko 패널 이동 애니메이션이 진행 중인지 확인한다.
  1094. * @method eg.Flicking#isPlaying
  1095. * @return {Boolean} Indicates whether the animated panel is playing <ko>패널 이동 애니메이션 진행 중 여부</ko>
  1096. */
  1097. isPlaying: function () {
  1098. return this._conf.panel.animating;
  1099. },
  1100. /**
  1101. * Move panel to the given direction
  1102. * @param {Boolean} next
  1103. * @param {Number} duration
  1104. */
  1105. _movePanel: function (next, duration) {
  1106. var conf = this._conf;
  1107. var panel = conf.panel;
  1108. var options = this.options;
  1109. if (panel.animating || conf.touch.holding) {
  1110. return;
  1111. }
  1112. this._setValueToMove(next);
  1113. if (options.circular ||
  1114. this[next ? "getNextIndex" : "getPrevIndex"]() != null
  1115. ) {
  1116. this._movePanelByPhase("setBy", [
  1117. panel.size * (next ? 1 : -1), 0
  1118. ], duration);
  1119. }
  1120. return this;
  1121. },
  1122. /**
  1123. * Move panel applying start/end phase value
  1124. * @param {String} method movableCoord method name
  1125. * @param {Object} coords coordinate array value
  1126. * @param {Number} duration duration value
  1127. */
  1128. _movePanelByPhase: function(method, coords, duration) {
  1129. duration = this._getNumValue(duration, this.options.duration);
  1130. if (this._setPhaseValue("start") !== false) {
  1131. this._setMovableCoord(method, coords, true, duration);
  1132. !duration && this._setPhaseValue("end");
  1133. }
  1134. },
  1135. /**
  1136. * Moves an element to the next panel.
  1137. * @ko 다음 패널로 이동한다.
  1138. * @method eg.Flicking#next
  1139. * @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
  1140. * @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
  1141. */
  1142. next: function (duration) {
  1143. return this._movePanel(true, duration);
  1144. },
  1145. /**
  1146. * Moves an element to the previous panel.
  1147. * @ko 이전 패널로 이동한다.
  1148. * @method eg.Flicking#prev
  1149. * @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
  1150. * @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
  1151. */
  1152. prev: function (duration) {
  1153. return this._movePanel(false, duration);
  1154. },
  1155. /**
  1156. * Moves an element to the indicated panel.
  1157. * @ko 지정한 패널로 이동한다.
  1158. * @method eg.Flicking#moveTo
  1159. * @param {Number} no Logical index number of the target panel element, which is relative to the panel content. <ko>이동할 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
  1160. * @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
  1161. * @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
  1162. */
  1163. moveTo: function (no, duration) {
  1164. var conf = this._conf;
  1165. var panel = conf.panel;
  1166. var circular = this.options.circular;
  1167. var currentIndex = panel.index;
  1168. var indexToMove;
  1169. var isPositive;
  1170. no = this._getNumValue(no, -1);
  1171. if (no < 0 || no >= panel.origCount || no === panel.no ||
  1172. panel.animating || conf.touch.holding) {
  1173. return this;
  1174. }
  1175. indexToMove = no - (circular ? panel.no : currentIndex);
  1176. isPositive = indexToMove > 0;
  1177. // check for real panel count which can be moved on each sides in circular mode
  1178. if (circular &&
  1179. Math.abs(indexToMove) >
  1180. (isPositive ? panel.count - (currentIndex + 1) : currentIndex)) {
  1181. indexToMove = indexToMove + (isPositive ? -1 : 1) * panel.count;
  1182. isPositive = indexToMove > 0;
  1183. }
  1184. this._setPanelNo(circular ? { no: no } : { no: no, index: no });
  1185. this._conf.indexToMove = indexToMove;
  1186. this._setValueToMove(isPositive);
  1187. this._movePanelByPhase(
  1188. circular ? "setBy" : "setTo",
  1189. [ panel.size * (circular ? indexToMove : no), 0 ],
  1190. duration
  1191. );
  1192. return this;
  1193. },
  1194. /**
  1195. * Update panel's previewPadding size according options.previewPadding
  1196. */
  1197. _checkPadding: function () {
  1198. var options = this.options;
  1199. var previewPadding = options.previewPadding.concat();
  1200. var padding = this.$wrapper.css("padding").split(" ");
  1201. options.horizontal && padding.reverse();
  1202. // get current padding value
  1203. padding = padding.length === 2 ?
  1204. [ padding[0], padding[0] ] : [ padding[0], padding[2] ];
  1205. padding = $.map(padding, function(num) {
  1206. return parseInt(num, 10);
  1207. });
  1208. // update padding when current and given are different
  1209. if (previewPadding.length === 2 &&
  1210. previewPadding[0] !== padding[0] || previewPadding[1] !== padding[1]) {
  1211. this._setPadding(previewPadding);
  1212. }
  1213. },
  1214. /**
  1215. * Updates the size of the panel.
  1216. * @ko 패널의 크기를 갱신한다
  1217. * @method eg.Flicking#resize
  1218. * @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
  1219. * @example
  1220. var some = new eg.Flicking("#mflick", {
  1221. previewPadding: [10,10]
  1222. });
  1223. // when device orientaion changes
  1224. some.resize();
  1225. // or when changes previewPadding option from its original value
  1226. some.options.previewPadding = [20, 30];
  1227. some.resize();
  1228. */
  1229. resize: function () {
  1230. var conf = this._conf;
  1231. var options = this.options;
  1232. var panel = conf.panel;
  1233. var horizontal = options.horizontal;
  1234. var panelSize;
  1235. var maxCoords;
  1236. if (~~options.previewPadding.join("")) {
  1237. this._checkPadding();
  1238. panelSize = panel.size;
  1239. } else if (horizontal) {
  1240. panelSize = panel.size = this.$wrapper.width();
  1241. }
  1242. maxCoords = this._getDataByDirection([panelSize * (panel.count - 1), 0]);
  1243. // resize elements
  1244. horizontal && this.$container.width(maxCoords[0] + panelSize);
  1245. panel.$list.css(horizontal ? "width" : "height", panelSize);
  1246. // remove data-height attribute and re-evaluate panel's height
  1247. if (options.adaptiveHeight) {
  1248. var $panel = this.$container.find("[" + DATA_HEIGHT + "]");
  1249. $panel.size() && $panel.attr(DATA_HEIGHT, null) &&
  1250. this._setAdaptiveHeight();
  1251. }
  1252. this._mcInst.options.max = maxCoords;
  1253. this._setMovableCoord("setTo", [panelSize * panel.index, 0], true, 0);
  1254. if (IS_ANDROID2) {
  1255. this._applyPanelsPos();
  1256. this._adjustContainerCss("end");
  1257. }
  1258. return this;
  1259. },
  1260. /**
  1261. * Restores an element to its original position when it movement stops while the element is not dragged until a certain distance threshold is reached.
  1262. * @ko 다음 패널로 바뀌기 전에 패널 이동이 멈췄을 때 원래 패널로 복원한다
  1263. * @method eg.Flicking#restore
  1264. * @param {Number} [duration=options.duration] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
  1265. * @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
  1266. * @example
  1267. var some = new eg.Flicking("#mflick").on({
  1268. beforeFlickStart : function(e) {
  1269. if(e.no === 2) {
  1270. e.stop(); // stop flicking
  1271. this.restore(100); // restoring to previous position
  1272. }
  1273. }
  1274. );
  1275. */
  1276. restore: function (duration) {
  1277. var conf = this._conf;
  1278. var panel = conf.panel;
  1279. var currPos = this._getDataByDirection(this._mcInst.get());
  1280. var destPos;
  1281. // check if the panel isn't in right position
  1282. if (currPos[0] !== panel.currIndex * panel.size) {
  1283. conf.customEvent.restoreCall = true;
  1284. duration = this._getNumValue(duration, this.options.duration);
  1285. this._revertPanelNo();
  1286. destPos = this._getDataByDirection([panel.size * panel.index, 0]);
  1287. this._triggerBeforeRestore({ depaPos: currPos, destPos: destPos });
  1288. this._setMovableCoord("setTo", destPos, true, duration);
  1289. if (!duration) {
  1290. this._adjustContainerCss("end");
  1291. this._triggerRestore();
  1292. }
  1293. // to handle on api call
  1294. } else if (panel.changed) {
  1295. this._revertPanelNo();
  1296. conf.touch.distance = conf.indexToMove = 0;
  1297. }
  1298. return this;
  1299. },
  1300. /**
  1301. * Enables input devices.
  1302. * @ko 입력 장치를 사용할 수 있게 한다
  1303. * @method eg.Flicking#enableInput
  1304. * @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
  1305. */
  1306. enableInput: function() {
  1307. this._mcInst.enableInput();
  1308. return this;
  1309. },
  1310. /**
  1311. * Disables input devices.
  1312. * @ko 입력 장치를 사용할 수 없게 한다.
  1313. * @method eg.Flicking#disableInput
  1314. * @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
  1315. */
  1316. disableInput: function() {
  1317. this._mcInst.disableInput();
  1318. return this;
  1319. },
  1320. /**
  1321. * Destroys elements, properties, and events used in a panel.
  1322. * @ko 패널에 사용한 엘리먼트와 속성, 이벤트를 해제한다
  1323. * @method eg.Flicking#destroy
  1324. */
  1325. destroy: function() {
  1326. var conf = this._conf;
  1327. var origPanelStyle = conf.origPanelStyle;
  1328. var wrapper = origPanelStyle.wrapper;
  1329. var list = origPanelStyle.list;
  1330. // unwrap container element and restore original inline style
  1331. this.$wrapper.attr("class", wrapper.className)
  1332. .attr("style", wrapper.style);
  1333. this.$container.children().unwrap().each(function(i, v) {
  1334. var $el = $(v);
  1335. if (i > list.length - 1) {
  1336. return !!$el.remove();
  1337. }
  1338. $el.attr("class", list[i].className)
  1339. .attr("style", list[i].style);
  1340. });
  1341. // unbind events
  1342. this._bindEvents(false);
  1343. this.off();
  1344. // release resources
  1345. for (var x in this) {
  1346. this[x] = null;
  1347. }
  1348. }
  1349. });
  1350. });
  1351. /**
  1352. * A jQuery plugin available in the eg.Flicking module.
  1353. *
  1354. * @ko eg.Flicking 모듈의 jQuery 플러그인
  1355. * @method jQuery.flicking
  1356. * @example
  1357. <div id="content">
  1358. <div>
  1359. <p>Layer 0</p>
  1360. </div>
  1361. <div>
  1362. <p>Layer 1</p>
  1363. </div>
  1364. <div>
  1365. <p>Layer 2</p>
  1366. </div>
  1367. </div>
  1368. <script>
  1369. // create
  1370. $("#content").flicking({
  1371. circular : true,
  1372. threshold : 50
  1373. });
  1374. // method
  1375. $("#content").flicking("option","circular",true); //Set option
  1376. $("#content").flicking("instance"); // Return flicking instance
  1377. $("#content").flicking("getNextIndex",1); // Get next panel index
  1378. </script>
  1379. * @see eg.Flicking
  1380. */
  1381. /**
  1382. * A jQuery custom event of the eg.Flicking module. This event is fired before an element is restored to its original position when user action is done while the element is not dragged until a certain distance threshold is reached.
  1383. *
  1384. * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원되기 전에 발생한다
  1385. * @name jQuery#flicking:beforeRestore
  1386. * @event
  1387. * @example
  1388. $("#mflick").on("flicking:beforeRestore",callback);
  1389. $("#mflick").off("flicking:beforeRestore",callback);
  1390. $("#mflick").trigger("flicking:beforeRestore",callback);
  1391. * @see eg.Flicking#event:beforeRestore
  1392. */
  1393. /**
  1394. * A jQuery custom event of the eg.Flicking module, which occurs before the flicking starts.
  1395. *
  1396. * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 플리킹을 시작하기 전에 발생한다
  1397. * @name jQuery#flicking:beforeFlickStart
  1398. * @event
  1399. * @example
  1400. $("#mflick").on("flicking:beforeFlickStart",callback);
  1401. $("#mflick").off("flicking:beforeFlickStart",callback);
  1402. $("#mflick").trigger("flicking:beforeFlickStart",callback);
  1403. * @see eg.Flicking#event:beforeFlickStart
  1404. */
  1405. /**
  1406. * A jQuery custom event of the eg.Flicking module. This event is fired when panel moves.
  1407. *
  1408. * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 패널이 이동될 때 발생한다
  1409. * @name jQuery#flicking:flick
  1410. * @event
  1411. * @example
  1412. $("#mflick").on("flicking:flick",callback);
  1413. $("#mflick").off("flicking:flick",callback);
  1414. $("#mflick").trigger("flicking:flick",callback);
  1415. * @see eg.Flicking#event:flick
  1416. */
  1417. /**
  1418. * A jQuery custom event of the eg.Flicking module. This event is fired after the panel moves.
  1419. *
  1420. * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 패널이 이동된 뒤 발생한다
  1421. * @name jQuery#flicking:flickEnd
  1422. * @event
  1423. * @example
  1424. $("#mflick").on("flicking:flickEnd",callback);
  1425. $("#mflick").off("flicking:flickEnd",callback);
  1426. $("#mflick").trigger("flicking:flickEnd",callback);
  1427. * @see eg.Flicking#event:flickEnd
  1428. */
  1429. /**
  1430. * A jQuery custom event of the eg.Flicking module. This event is fired after an element is restored to its original position when user action is done while the element has not bene dragged until a certain distance threshold is reached.
  1431. *
  1432. * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원된 다음 발생한다
  1433. * @name jQuery#flicking:restore
  1434. * @event
  1435. * @example
  1436. $("#mflick").on("flicking:restore",callback);
  1437. $("#mflick").off("flicking:restore",callback);
  1438. $("#mflick").trigger("flicking:restore",callback);
  1439. * @see eg.Flicking#event:restore
  1440. */
comments powered by Disqus