An instance, an option value, or an option object of a component itself. - If both key and value are used to set an option, it returns an instance of a component itself. - If only a key is specified for the parameter, it returns the option value corresponding to a given key. - If nothing is specified, it returns an option object.
+
컴포넌트 자신의 인스턴스나 옵션값, 옵션 객체. - 키와 값으로 옵션을 설정하면 컴포넌트 자신의 인스턴스를 반환한다. - 파라미터에 키만 설정하면 키에 해당하는 옵션값을 반환한다. - 파라미터에 아무것도 설정하지 않으면 옵션 객체를 반환한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component);
+ var some = new Some({
+ "foo": 1,
+ "bar": 2,
+ });
+ some.option("foo"); // return 1
+ some.option("foo",3); // return some instance
+ some.option(); // return options object.
+ some.option({
+ "foo" : 10,
+ "bar" : 20,
+ "baz" : 30
+ }); // return some instance.
Event data to be sent when triggering a custom event
+
커스텀 이벤트가 발생할 때 전달할 데이터
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether the event has occurred. If the stop() method is called by a custom event handler, it will return false and prevent the event from occurring.
+
이벤트 발생 여부. 커스텀 이벤트 핸들러에서 stop() 메서드를 호출하면 'false'를 반환하고 이벤트 발생을 중단한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component,{
+ "some": function(){
+ this.trigger("hi");// fire hi event.
+ }
+ });
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.
+
+
+
+
+
플리킹 UI를 구현하는 모듈. 나란히 배치한 패널을 쓸어 넘겨 다음 패널이나 이전 패널로 이동하는 플리킹 UI를 만들 수 있다.
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
+
이전 패널과 다음 패널을 미리 보는 영역의 크기. 패널 이동 방향이 가로 방향이면 패널 왼쪽과 오른쪽에 미리 보는 영역이 나타난다. 패널 이동 방향이 세로 방향이면 패널 위쪽과 아래쪽에 미리 보는 영역이 나타난다
− 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.
+
바운스 영역의 크기. 패널이 순환하지 않도록 설정됐다면 시작 패널과 마지막 패널은 기준 엘리먼트 영역을 넘어 바운스 영역의 크기만큼 더 이동할 수 있다. 패널을 바운스 영역까지 끌었다가 놓으면, 바운스 효과가 적용된 패널이 다시 기준 엘리먼트 영역 안으로 들어온다
Types of index numbers - true: Indicates physical index numbers relative to DOM. - false: Indicates logical index numbers relative to the panel content.
+
− 인덱스 번호의 종류 - true: 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다. - false: 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.
Types of index numbers - true: Indicates physical index numbers relative to DOM. - false: Indicates logical index numbers relative to the panel content.
+
− 인덱스 번호의 종류 - true: 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다. - false: 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Number
+ |
+
+ null
+
+
+
+
+
+
Index number of the next panel element or null if it does not exist.
Types of index numbers - true: Indicates physical index numbers relative to DOM. - false: Indicates logical index numbers relative to the panel content.
+
− 인덱스 번호의 종류 - true: 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다. - false: 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Number
+ |
+
+ null
+
+
+
+
+
+
Previous element index value or null if no more element exist
An instance, an option value, or an option object of a component itself. - If both key and value are used to set an option, it returns an instance of a component itself. - If only a key is specified for the parameter, it returns the option value corresponding to a given key. - If nothing is specified, it returns an option object.
+
컴포넌트 자신의 인스턴스나 옵션값, 옵션 객체. - 키와 값으로 옵션을 설정하면 컴포넌트 자신의 인스턴스를 반환한다. - 파라미터에 키만 설정하면 키에 해당하는 옵션값을 반환한다. - 파라미터에 아무것도 설정하지 않으면 옵션 객체를 반환한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component);
+ var some = new Some({
+ "foo": 1,
+ "bar": 2,
+ });
+ some.option("foo"); // return 1
+ some.option("foo",3); // return some instance
+ some.option(); // return options object.
+ some.option({
+ "foo" : 10,
+ "bar" : 20,
+ "baz" : 30
+ }); // return some instance.
var some = new eg.Flicking("#mflick", {
+ previewPadding: [10,10]
+ });
+
+ // when device orientaion changes
+ some.resize();
+
+ // or when changes previewPadding option from its original value
+ some.options.previewPadding = [20, 30];
+ some.resize();
Event data to be sent when triggering a custom event
+
커스텀 이벤트가 발생할 때 전달할 데이터
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether the event has occurred. If the stop() method is called by a custom event handler, it will return false and prevent the event from occurring.
+
이벤트 발생 여부. 커스텀 이벤트 핸들러에서 stop() 메서드를 호출하면 'false'를 반환하고 이벤트 발생을 중단한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component,{
+ "some": function(){
+ this.trigger("hi");// fire hi event.
+ }
+ });
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
+
+
+
+
+
다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원되기 전에 발생하는 이벤트
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ param
+
+
+
+
+
+
+
Type:
+Object
+
+
+
+
+
+
+
+
The object of data to be sent to an event
+
이벤트에 전달되는 데이터 객체
+
+
+
+
+
+
+
+
+
+
+
+
+ eventType
+
+
+
+
+
+
+
Type:
+String
+
+
+
+
+
+
+
+
The name of the event
+
이름명
+
+
+
+
+
+
+
+
+
+
+ index
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Physical index number of the current panel element, which is relative to DOM. (@deprecated since 1.3.0)
+
현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다. (@deprecated since 1.3.0)
+
+
+
+
+
+
+
+
+
+
+ no
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Logical index number of the current panel element, which is relative to the panel content.
+
현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다
+
+
+
+
+
+
+
+
+
+
+ direction
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Direction of the movement (see eg.MovableCoord.DIRECTION* constant)
Physical index number of the current panel element, which is relative to DOM (@deprecated since 1.3.0)
+
현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다 (@deprecated since 1.3.0)
+
+
+
+
+
+
+
+
+
+
+ no
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Logical index number of the current panel element, which is relative to the panel content
+
현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다
+
+
+
+
+
+
+
+
+
+
+ direction
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Direction of the movement (see eg.MovableCoord.DIRECTION* constant)
+
이동 방향(eg.MovableCoord.DIRECTION* constant 참고)
+
+
+
+
+
+
+
+
+
+
+ pos
+
+
+
+
+
+
+
Type:
+Array
+
+
+
+
+
+
+
+
Start coordinate
+
출발점 좌표
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
x-coordinate
+
x 좌표
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
y-coordinate
+
y 좌표
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ holding
+
+
+
+
+
+
+
Type:
+Boolean
+
+
+
+
+
+
+
+
Indicates whether a user holds an element on the screen of the device.
+
사용자가 기기의 화면을 누르고 있는지 여부
+
+
+
+
+
+
+
+
+
+
+ distance
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
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
+
시작점부터 이동된 거리의 값. 이동 방향에 따라 eg.MovableCoord.DIRECTION_LEFT/UP의 경우 양수를 eg.MovableCoord.DIRECTION_RIGHT/DOWN의 경우는 음수를 반환
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.
+
+
+
+
+
다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원된 다음 발생하는 이벤트
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ param
+
+
+
+
+
+
+
Type:
+Object
+
+
+
+
+
+
+
+
The object of data to be sent to an event
+
이벤트에 전달되는 데이터 객체
+
+
+
+
+
+
+
+
+
+
+
+
+ eventType
+
+
+
+
+
+
+
Type:
+String
+
+
+
+
+
+
+
+
The name of the event
+
이름명
+
+
+
+
+
+
+
+
+
+
+ index
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Physical index number of the current panel element, which is relative to DOM(@deprecated since 1.3.0)
+
현재 패널 엘리먼트의 물리적 인덱스 번호. DOM 엘리먼트를 기준으로 하는 인덱스 번호다 (@deprecated since 1.3.0)
+
+
+
+
+
+
+
+
+
+
+ no
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Logical index number of the current panel element, which is relative to the panel content.
+
현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다
+
+
+
+
+
+
+
+
+
+
+ direction
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Direction of the panel move (see eg.MovableCoord.DIRECTION* constant)
A module used to arrange card elements including content infinitely on a grid layout. With this module, you can implement a grid-pattern user interface composed of different card elements whose sizes vary. It guarantees performance by maintaining the number of DOMs the module is handling under any circumstance
+
+
+
+
+
콘텐츠가 있는 카드 엘리먼트를 그리드 레이아웃에 무한으로 배치하는 모듈. 다양한 크기의 카드 엘리먼트를 격자 모양으로 배치하는 UI를 만들 수 있다. 카드 엘리먼트의 개수가 계속 늘어나도 모듈이 처리하는 DOM의 개수를 일정하게 유지해 최적의 성능을 보장한다
The number of DOMs handled by module. If the count value is greater than zero, the number of DOMs is maintained. If the count value is zero or less than zero, the number of DOMs will increase as card elements are added.
+
모듈이 유지할 실제 DOM의 개수. count 값이 0보다 크면 DOM 개수를 일정하게 유지한다. count 값이 0 이하면 카드 엘리먼트가 추가될수록 DOM 개수가 계속 증가한다.
Indicates whether sizes of all card elements are equal to one another. If sizes of card elements to be arranged are all equal and this option is set to "true", the performance of layout arrangement can be improved.
+
카드 엘리먼트의 크기가 동일한지 여부. 배치될 카드 엘리먼트의 크기가 모두 동일할 때 이 옵션을 'true'로 설정하면 레이아웃 배치 성능을 높일 수 있다
The threshold size of an event area where card elements are added to a layout. - append event: If the current vertical position of the scroll bar is greater than "the bottom property value of the card element at the top of the layout" plus "the value of the threshold option", the append event will occur. - prepend event: If the current vertical position of the scroll bar is less than "the bottom property value of the card element at the top of the layout" minus "the value of the threshold option", the prepend event will occur.
+
− 레이아웃에 카드 엘리먼트를 추가하는 이벤트가 발생하는 기준 영역의 크기. - append 이벤트: 현재 스크롤의 y 좌표 값이 '레이아웃의 맨 아래에 있는 카드 엘리먼트의 top 속성의 값 + threshold 옵션의 값'보다 크면 append 이벤트가 발생한다. - prepend 이벤트: 현재 스크롤의 y 좌표 값이 '레이아웃의 맨 위에 있는 카드 엘리먼트의 bottom 속성의 값 - threshold 옵션의 값'보다 작으면 prepend 이벤트가 발생한다
Returns the list of group keys which belongs to card elements currently being maintained. You can use the append() or prepend() method to configure group keys so that multiple card elements can be managed at once. If you do not use these methods to configure group keys, it returns undefined as a group key.
+
+
+
+
+
현재 유지하고 있는 카드 엘리먼트의 그룹 키 목록을 반환한다. 여러 개의 카드 엘리먼트를 묶어서 관리할 수 있도록 append() 메서드나 prepend() 메서드에서 그룹 키를 지정할 수 있다. append() 메서드나 prepend() 메서드에서 그룹 키를 지정하지 않았다면 'undefined'가 그룹 키로 반환된다
Returns the current state of a module such as location information. You can use the setStatus() method to restore the information returned through a call to this method.
+
+
+
+
+
카드의 위치 정보 등 모듈의 현재 상태 정보를 반환한다. 이 메서드가 반환한 정보를 저장해 두었다가 setStatus() 메서드로 복원할 수 있다
Checks whether the total number of added card elements is greater than the value of the count option. Note that the value of the count option is always greater than zero. If it returns true, the number of DOMs won't increase even though card elements are added; instead of adding a new DOM, existing DOMs are recycled to maintain the number of DOMs.
+
+
+
+
+
추가된 카드 엘리먼트의 전체 개수가 count 옵션의 값보다 큰지 확인한다. 단, count 옵션의 값은 0보다 크다. 'true'가 반환되면 카드 엘리먼트가 더 추가돼도 DOM의 개수를 증가하지 않고 기존 DOM을 재활용(recycle)해 DOM의 개수를 일정하게 유지한다
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether the total number of added card elements is greater than the value of the count option.
An instance, an option value, or an option object of a component itself. - If both key and value are used to set an option, it returns an instance of a component itself. - If only a key is specified for the parameter, it returns the option value corresponding to a given key. - If nothing is specified, it returns an option object.
+
컴포넌트 자신의 인스턴스나 옵션값, 옵션 객체. - 키와 값으로 옵션을 설정하면 컴포넌트 자신의 인스턴스를 반환한다. - 파라미터에 키만 설정하면 키에 해당하는 옵션값을 반환한다. - 파라미터에 아무것도 설정하지 않으면 옵션 객체를 반환한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component);
+ var some = new Some({
+ "foo": 1,
+ "bar": 2,
+ });
+ some.option("foo"); // return 1
+ some.option("foo",3); // return some instance
+ some.option(); // return options object.
+ some.option({
+ "foo" : 10,
+ "bar" : 20,
+ "baz" : 30
+ }); // return some instance.
Adds a card element at the top of a grid layout. This method is available only if the isProcessing() method returns false and the isRecycling() method returns true.
+
+
+
+
+
카드 엘리먼트를 그리드 레이아웃의 위에 추가한다. isProcessing() 메서드의 반환값이 'false'이고, isRecycling() 메서드의 반환값이 'true'일 때만 이 메서드를 사용할 수 있다
Event data to be sent when triggering a custom event
+
커스텀 이벤트가 발생할 때 전달할 데이터
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether the event has occurred. If the stop() method is called by a custom event handler, it will return false and prevent the event from occurring.
+
이벤트 발생 여부. 커스텀 이벤트 핸들러에서 stop() 메서드를 호출하면 'false'를 반환하고 이벤트 발생을 중단한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component,{
+ "some": function(){
+ this.trigger("hi");// fire hi event.
+ }
+ });
This event is fired when a card element must be added at the bottom of a grid layout because there is no card to be displayed on screen when a user scrolls near bottom.
+
+
+
+
+
카드 엘리먼트가 그리드 레이아웃의 아래에 추가돼야 할 때 발생하는 이벤트. 사용자가 아래로 스크롤해서 화면에 표시될 카드가 없을 때 발생한다
This event is fired when layout is successfully arranged through a call to the append(), prepend(), or layout() method.
+
+
+
+
+
레이아웃 배치가 완료됐을 때 발생하는 이벤트. append() 메서드나 prepend() 메서드, layout() 메서드 호출 후 카드의 배치가 완료됐을 때 발생한다
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ param
+
+
+
+
+
+
+
Type:
+Object
+
+
+
+
+
+
+
+
The object of data to be sent to an event
+
이벤트에 전달되는 데이터 객체
+
+
+
+
+
+
+
+
+
+
+
+
+ target
+
+
+
+
+
+
+
Type:
+Array
+
+
+
+
+
+
+
+
Rearranged card elements
+
재배치된 카드 엘리먼트들
+
+
+
+
+
+
+
+
+
+
+ isAppend
+
+
+
+
+
+
+
Type:
+Boolean
+
+
+
+
+
+
+
+
Checks whether the append() method is used to add a card element. It returns true even though the layoutComplete event is fired after the layout() method is called.
+
카드 엘리먼트가 append() 메서드로 추가됐는지 확인한다. layout() 메서드가 호출된 후 layoutComplete 이벤트가 발생해도 'true'를 반환한다.
+
+
+
+
+
+
+
+
+
+
+ distance
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
Distance the card element at the top of a grid layout has moved after the layoutComplete event is fired. In other words, it is the same as an increased height with a new card element added using the prepend() method
+
그리드 레이아웃의 맨 위에 있던 카드 엘리먼트가 layoutComplete 이벤트 발생 후 이동한 거리. 즉, prepend() 메서드로 카드 엘리먼트가 추가돼 늘어난 높이다.
+
+
+
+
+
+
+
+
+
+
+ croppedCount
+
+
+
+
+
+
+
Type:
+Number
+
+
+
+
+
+
+
+
The number of deleted card elements to maintain the number of DOMs
This event is fired when a card element must be added at the top of a grid layout because there is no card to be displayed on screen when a user scrolls near top. This event is available only if the isRecycling() method returns true.
+
+
+
+
+
카드가 그리드 레이아웃의 위에 추가돼야 할 때 발생하는 이벤트. 사용자가 위로 스크롤해서 화면에 표시될 카드가 없을 때 발생한다. 이 이벤트는 isRecycling() 메서드의 반환값이 'true'일 때만 발생한다
A module used to change the information of user action entered by various input devices such as touch screen or mouse into logical coordinates within the virtual coordinate system. The coordinate information sorted by time events occurred is provided if animations are made by user actions. You can implement a user interface by applying the logical coordinates provided. For more information on the eg.MovableCoord module, see demos.
+
+
+
+
+
터치 입력 장치나 마우스와 같은 다양한 입력 장치로 전달 받은 사용자의 동작을 가상 좌표계의 논리적 좌표로 변경하는 모듈. 사용자의 동작으로 애니메이션이 일어나면 시간순으로 변경되는 좌표 정보도 제공한다. 변경된 논리적 좌표를 반영해 UI를 구현할 수 있다. eg.MovableCoord 모듈의 자세한 작동 방식은 데모를 참고한다.
The size of bouncing area. The coordinates can exceed the coordinate area as much as the bouncing area based on user action. If the coordinates does not exceed the bouncing area when an element is dragged, the coordinates where bouncing effects are applied are retuned back into the coordinate area
+
바운스 영역의 크기. 사용자의 동작에 따라 좌표가 좌표 영역을 넘어 바운스 영역의 크기만큼 더 이동할 수 있다. 사용자가 끌어다 놓는 동작을 했을 때 좌표가 바운스 영역에 있으면, 바운스 효과가 적용된 좌표가 다시 좌표 영역 안으로 들어온다
The size of accessible space outside the coordinate area. If an element is dragged outside the coordinate area and then dropped, the coordinates of the element are returned back into the coordinate area. The size of margins that can be exceeded
+
− 좌표 영역을 넘어 이동할 수 있는 바깥 영역의 크기. 사용자가 좌표를 바깥 영역까지 끌었다가 놓으면 좌표가 좌표 영역 안으로 들어온다.
Indicates whether a circular element is available. If it is set to "true" and an element is dragged outside the coordinate area, the element will appear on the other side.
+
순환 여부. 'true'로 설정한 방향의 좌표 영역 밖으로 엘리먼트가 이동하면 반대 방향에서 엘리먼트가 나타난다
• Hammer.JS applies specific CSS properties by default when creating an instance (See http://hammerjs.github.io/jsdoc/Hammer.defaults.cssProps.html). The eg.MovableCoord module removes all default CSS properties provided by Hammer.JS Hammer.JS는 인스턴스를 생성할 때 기본으로 특정 CSS 속성을 적용한다(참고: @link{http://hammerjs.github.io/jsdoc/Hammer.defaults.cssProps.html}). 특정한 상황에서는 Hammer.JS의 속성 때문에 사용성에 문제가 있을 수 있다. eg.MovableCoord 모듈은 Hammer.JS의 기본 CSS 속성을 모두 제거했다
Coordinate direction that a user can move - eg.MovableCoord.DIRECTION_ALL: All directions available. - eg.MovableCoord.DIRECTION_HORIZONTAL: Horizontal direction only. - eg.MovableCoord.DIRECTION_VERTICAL: Vertical direction only
+
사용자의 동작으로 움직일 수 있는 좌표의 방향. - eg.MovableCoord.DIRECTION_ALL: 모든 방향으로 움직일 수 있다. - eg.MovableCoord.DIRECTION_HORIZONTAL: 가로 방향으로만 움직일 수 있다. - eg.MovableCoord.DIRECTION_VERTICAL: 세로 방향으로만 움직일 수 있다.
Indicates whether an animation is interruptible. - true: It can be paused or stopped by user action or the API. - false: It cannot be paused or stopped by user action or the API while it is running.
+
진행 중인 애니메이션 중지 가능 여부. - true: 사용자의 동작이나 API로 애니메이션을 중지할 수 있다. - false: 애니메이션이 진행 중일 때는 사용자의 동작이나 API가 적용되지 않는다
An instance, an option value, or an option object of a component itself. - If both key and value are used to set an option, it returns an instance of a component itself. - If only a key is specified for the parameter, it returns the option value corresponding to a given key. - If nothing is specified, it returns an option object.
+
컴포넌트 자신의 인스턴스나 옵션값, 옵션 객체. - 키와 값으로 옵션을 설정하면 컴포넌트 자신의 인스턴스를 반환한다. - 파라미터에 키만 설정하면 키에 해당하는 옵션값을 반환한다. - 파라미터에 아무것도 설정하지 않으면 옵션 객체를 반환한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component);
+ var some = new Some({
+ "foo": 1,
+ "bar": 2,
+ });
+ some.option("foo"); // return 1
+ some.option("foo",3); // return some instance
+ some.option(); // return options object.
+ some.option({
+ "foo" : 10,
+ "bar" : 20,
+ "baz" : 30
+ }); // return some instance.
Event data to be sent when triggering a custom event
+
커스텀 이벤트가 발생할 때 전달할 데이터
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether the event has occurred. If the stop() method is called by a custom event handler, it will return false and prevent the event from occurring.
+
이벤트 발생 여부. 커스텀 이벤트 핸들러에서 stop() 메서드를 호출하면 'false'를 반환하고 이벤트 발생을 중단한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component,{
+ "some": function(){
+ this.trigger("hi");// fire hi event.
+ }
+ });
The size of the expanded area to be checked whether an element is visible. If this value is less than zero, the size of the area is smaller than that of the base element.
+
기준 엘리먼트의 경계를 넘어 엘리먼트가 보이는지 확인할 영역의 크기. 값이 0보다 작으면 엘리먼트가 보이는지 확인할 영역의 크기가 기준 엘리먼트보다 작아진다
An instance, an option value, or an option object of a component itself. - If both key and value are used to set an option, it returns an instance of a component itself. - If only a key is specified for the parameter, it returns the option value corresponding to a given key. - If nothing is specified, it returns an option object.
+
컴포넌트 자신의 인스턴스나 옵션값, 옵션 객체. - 키와 값으로 옵션을 설정하면 컴포넌트 자신의 인스턴스를 반환한다. - 파라미터에 키만 설정하면 키에 해당하는 옵션값을 반환한다. - 파라미터에 아무것도 설정하지 않으면 옵션 객체를 반환한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component);
+ var some = new Some({
+ "foo": 1,
+ "bar": 2,
+ });
+ some.option("foo"); // return 1
+ some.option("foo",3); // return some instance
+ some.option(); // return options object.
+ some.option({
+ "foo" : 10,
+ "bar" : 20,
+ "baz" : 30
+ }); // return some instance.
Event data to be sent when triggering a custom event
+
커스텀 이벤트가 발생할 때 전달할 데이터
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether the event has occurred. If the stop() method is called by a custom event handler, it will return false and prevent the event from occurring.
+
이벤트 발생 여부. 커스텀 이벤트 핸들러에서 stop() 메서드를 호출하면 'false'를 반환하고 이벤트 발생을 중단한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
var Some = eg.Class.extend(eg.Component,{
+ "some": function(){
+ this.trigger("hi");// fire hi event.
+ }
+ });
Indicates whether hardware acceleration is enabled.
+
하드웨어 가속 사용 가능 여부
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
eg.isHWAccelerable(); // Returns 'true' when hardware acceleration is supported
+
// also, you can control return value
+eg.hook.isHWAccelerable = function(defalutVal,agent) {
+if(agent.os.name === "ios") {
+ // if os is 'ios', return value is 'false'
+ return false;
+} else if(agent.browser.name === "chrome" ) {
+ // if browser is 'chrome', return value is 'true'
+ return true;
+}
+return defaultVal;
+}
Checks whether CSS transition properties can be used.
+
+
+
+
+
CSS 트랜지션 속성을 사용할 수 있는 환경인지 확인한다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns:
+
+
+
+
+
+
+
+
Type
+
Description
+
+
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+
Indicates whether CSS transition properties can be used.
+
CSS 트랜지션 속성 사용 가능 여부
+
+
+
+
+
+
+
+
+
+
+
+
+
Example
+
+
+
+
eg.isTransitional(); // Returns 'true' when CSS transition is supported.
+
// also, you can control return value
+eg.hook.isTransitional = function(defaultVal, agent) {
+if(agent.os.name === "ios") {
+ // if os is 'ios', return value is 'false'
+ return false;
+} else if(agent.browser.name === "chrome" ) {
+ // if browser is 'chrome', return value is 'true'
+ return true;
+}
+return defaultVal;
+}
// jscs:disable validateLineBreaks, maximumLineLength
+/**
+* Copyright (c) 2015 NAVER Corp.
+* egjs projects are licensed under the MIT license
+*/
+eg.module("flicking", ["jQuery", eg, window, document, eg.MovableCoord], function ($, ns, global, doc, MC) {
+ "use strict";
+
+ // jscs:enable validateLineBreaks, maximumLineLength
+ /**
+ * 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.
+ * @group egjs
+ * @ko 플리킹 UI를 구현하는 모듈. 나란히 배치한 패널을 쓸어 넘겨 다음 패널이나 이전 패널로 이동하는 플리킹 UI를 만들 수 있다.
+ * @class
+ * @name eg.Flicking
+ * @extends eg.Component
+ *
+ * @param {HTMLElement|String|jQuery} element A base element for the eg.Flicking module <ko>eg.Flicking 모듈을 사용할 기준 엘리먼트</ko>
+ * @param {Object} options The option object of the eg.Flicking module<ko>eg.Flicking 모듈의 옵션 객체</ko>
+ * @param {Boolean} [options.hwAccelerable=eg.isHWAccelerable()] 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] Indicates whether a circular panel is available <ko>패널 순환 여부</ko>
+ * @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>
+ * @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>
+ * @param {Number} [options.threshold=40] Distance threshold. If the drag exceeds the threshold value, it will be changed to the next panel <ko>다음 패널로 바뀌는 기준 이동 거리. 패널을 기준 이동 거리 이상 끌었다 놓으면 패널이 다음 패널로 바뀐다</ko>
+ * @param {Number} [options.duration=100] Duration of the panel movement (unit: ms) <ko>패널 이동 애니메이션 진행 시간(단위: ms)</ko>
+ * @param {Function} [options.panelEffect=easeOutCubic] The easing function to apply to a panel moving animation <ko>패널 이동 애니메이션에 적용할 easing 함수</ko>
+ * @param {Number} [options.defaultIndex=0] The index number of a panel to be selected upon module initialization <ko>모듈이 초기화될 때 선택할 패널의 인덱스 번호</ko>
+ * @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>
+ *
+ * @codepen {"id":"rVOpPK", "ko":"플리킹 UI 기본 예제", "en":"Flicking UI default example", "collectionId":"ArxyLK", "height" : 403}
+ * @support {"ie": "10+", "ch" : "latest", "ff" : "latest", "sf" : "latest" , "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"}
+ *
+ * @see Easing Functions Cheat Sheet {@link http://easings.net/}
+ * @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>
+ * @example
+ <!-- HTML -->
+ <div id="mflick">
+ <div>
+ <p>Layer 0</p>
+ </div>
+ <div>
+ <p>Layer 1</p>
+ </div>
+ <div>
+ <p>Layer 2</p>
+ </div>
+ </div>
+ <script>
+ var some = new eg.Flicking("#mflick", {
+ circular : true,
+ threshold : 50
+ }).on({
+ beforeRestore : function(e) { ... },
+ flickStart : function(e) { ... }
+ });
+ </script>
+ */
+
+ // define custom events name
+ var EVENTS = {
+ "beforeFlickStart": "beforeFlickStart",
+ "beforeRestore": "beforeRestore",
+ "flick": "flick",
+ "flickEnd": "flickEnd",
+ "restore": "restore"
+ };
+
+ // check for css transform support
+ var SUPPORT_TRANSFORM = doc.documentElement.style;
+ SUPPORT_TRANSFORM = "transform" in SUPPORT_TRANSFORM ||
+ "webkitTransform" in SUPPORT_TRANSFORM;
+
+ // check for will-change support
+ var SUPPORT_WILLCHANGE = global.CSS && global.CSS.supports &&
+ global.CSS.supports("will-change", "transform");
+
+ // check for Android 2.x
+ var IS_ANDROID2 = ns.agent().os;
+ IS_ANDROID2 = IS_ANDROID2.name === "android" && /^2\./.test(IS_ANDROID2.version);
+
+ ns.Flicking = ns.Class.extend(ns.Component, {
+ _events: function() {
+ return EVENTS;
+ },
+ /**
+ * Constructor
+ * @param {HTMLElement|String|jQuery} element - base element
+ * @param {Object} options
+ */
+ construct: function (element, options, _prefix) {
+ this.$wrapper = $(element);
+
+ var $children = this.$wrapper.children();
+ if (!$children.length) {
+ // jscs:disable validateLineBreaks, maximumLineLength
+ throw new Error("Given base element doesn't exist or it hasn't proper DOM structure to be initialized.");
+
+ // jscs:enable validateLineBreaks, maximumLineLength
+ }
+
+ this._setOptions(options);
+ this._setConfig($children, _prefix);
+
+ !ns._hasClickBug() && (this._setPointerEvents = $.noop);
+
+ this._build();
+ this._bindEvents(true);
+
+ this._applyPanelsCss();
+ this._arrangePanels();
+
+ this.options.hwAccelerable && SUPPORT_WILLCHANGE && this._setHint();
+ this._adjustContainerCss("end");
+ },
+
+ /**
+ * Set options values
+ * @param {Object} options
+ */
+ _setOptions: function(options) {
+ var arrVal = {
+ previewPadding: [ 0, 0 ],
+ bounce: [ 10, 10 ]
+ };
+
+ $.extend(this.options = {
+ hwAccelerable: ns.isHWAccelerable(), // check weather hw acceleration is available
+ prefix: "eg-flick", // prefix value of class name
+ deceleration: 0.0006, // deceleration value
+ horizontal: true, // move direction (true == horizontal, false == vertical)
+ circular: false, // circular mode. In this mode at least 3 panels are required.
+ previewPadding: arrVal.previewPadding, // preview padding value in left(up) to right(down) order. In this mode at least 5 panels are required.
+ bounce: arrVal.bounce, // bounce value in left(up) to right(down) order. Works only in non-circular mode.
+ threshold: 40, // the distance pixel threshold value for change panel
+ duration: 100, // duration ms for animation
+ panelEffect: $.easing.easeOutCubic, // $.easing function for panel change animation
+ defaultIndex: 0, // initial panel index to be shown
+ inputType: ["touch", "mouse"] // input type
+ }, options);
+
+ var self = this;
+ $.each(arrVal, function(i, v) {
+ var val = self.options[i];
+
+ if ($.isNumeric(val)) {
+ val = [ val, val ];
+ } else if (!$.isArray(val)) {
+ val = v;
+ }
+
+ self.options[i] = val;
+ });
+ },
+
+ /**
+ * Set config values
+ * @param {jQuery} $children wrappers' children elements
+ * @param {String} _prefix event prefix
+ */
+ _setConfig: function($children, _prefix) {
+ var options = this.options;
+ var padding = options.previewPadding;
+
+ if ($children.eq(0).hasClass(options.prefix + "-container")) {
+ this.$container = $children;
+ $children = $children.children();
+ }
+
+ // config value
+ this._conf = {
+ panel: {
+ $list: $children, // panel list
+ index: 0, // dom index used among process
+ no: 0, // panel no used among process
+ currIndex: 0, // current physical dom index
+ currNo: 0, // current logical panel number
+ size: 0, // panel size
+ count: 0, // total physical panel count
+ origCount: 0, // total count of given original panels
+ changed: false, // if panel changed
+ animating: false, // current animating status boolean
+ minCount: padding[0] + padding[1] > 0 ? 5 : 3 // minimum panel count
+ },
+ touch: {
+ holdPos: [0, 0], // hold x,y coordinate
+ destPos: [0, 0], // destination x,y coordinate
+ distance: 0, // touch distance pixel of start to end touch
+ direction: null, // touch direction
+ lastPos: 0, // to determine move on holding
+ holding: false
+ },
+ customEvent: { // for custom events value
+ flick: true,
+ restore: false,
+ restoreCall: false
+ },
+ origPanelStyle: { // remember original class and inline style in case of restoration on destroy()
+ wrapper: {
+ className: this.$wrapper.attr("class") || null,
+ style: this.$wrapper.attr("style") || null
+ },
+ list: $children.map(function(i, v) {
+ return {
+ className: $(v).attr("class") || null,
+ style: $(v).attr("style") || null
+ };
+ })
+ },
+ inputEvent: false, // input event biding status
+ useLayerHack: options.hwAccelerable && !SUPPORT_WILLCHANGE,
+ dirData: [], // direction constant value according horizontal or vertical
+ indexToMove: 0,
+ eventPrefix: _prefix || "",
+
+ // For buggy link highlighting on Android 2.x
+ $dummyAnchor: null
+ };
+
+ $([["LEFT", "RIGHT"], ["UP", "DOWN"]][+!options.horizontal]).each(
+ $.proxy(function (i, v) {
+ this._conf.dirData.push(MC["DIRECTION_" + v]);
+ }, this));
+ },
+
+ /**
+ * Build and set panel nodes to make flicking structure
+ */
+ _build: function () {
+ var panel = this._conf.panel;
+ var options = this.options;
+ var $children = panel.$list;
+ var padding = options.previewPadding.concat();
+ var prefix = options.prefix;
+ var horizontal = options.horizontal;
+ var panelCount = panel.count = panel.origCount = $children.length;
+ var cssValue;
+ var bounce = options.bounce;
+
+ this._setPadding(padding, true);
+ var sizeValue = this._getDataByDirection([ panel.size, "100%" ]);
+
+ // create container element
+ cssValue = "position:relative;z-index:2000;width:100%;height:100%;" +
+ (horizontal ? "" : "top:0;");
+
+ if (this.$container) {
+ this.$container.attr("style", cssValue);
+ } else {
+ this.$container = $children.wrapAll(
+ "<div class='" + prefix + "-container' style='" + cssValue + "'>"
+ ).parent();
+ }
+
+ // panels' css values
+ $children.addClass(prefix + "-panel").css({
+ position: "absolute",
+ width: sizeValue[0],
+ height: sizeValue[1],
+ boxSizing: "border-box",
+ top: 0,
+ left: 0
+ });
+
+ if (this._addClonePanels()) {
+ panelCount = panel.count = (
+ panel.$list = this.$container.children()
+ ).length;
+ }
+
+ // create MovableCoord instance
+ this._mcInst = new MC({
+ min: [0, 0],
+ max: this._getDataByDirection([panel.size * (panelCount - 1), 0]),
+ margin: 0,
+ circular: false,
+ easing: options.panelEffect,
+ deceleration: options.deceleration,
+ bounce: this._getDataByDirection([ 0, bounce[1], 0, bounce[0] ])
+ });
+
+ this._setDefaultPanel(options.defaultIndex);
+ },
+
+ /**
+ * Set preview padding value
+ * @param {Array} padding
+ * @param {Boolean} build
+ */
+ _setPadding: function(padding, build) {
+ var horizontal = this.options.horizontal;
+ var panel = this._conf.panel;
+ var paddingSum = padding[0] + padding[1];
+ var cssValue = {};
+
+ if (paddingSum || !build) {
+ cssValue.padding = (horizontal ?
+ "0 " + padding.reverse().join("px 0 ") :
+ padding.join("px 0 ")) + "px";
+ }
+
+ if (build) {
+ cssValue.overflow = "hidden";
+ cssValue.boxSizing = "border-box";
+ }
+
+ !$.isEmptyObject(cssValue) &&
+ this.$wrapper.css(cssValue);
+
+ panel.size = this.$wrapper[ horizontal ? "width" : "height" ]();
+ },
+
+ /**
+ * To fulfill minimum panel count cloning original node when circular or previewPadding option are set
+ * @return {Boolean} true : added clone node, false : not added
+ */
+ _addClonePanels: function () {
+ var panel = this._conf.panel;
+ var panelCount = panel.origCount;
+ var cloneCount = panel.minCount - panelCount;
+ var list = panel.$list;
+ var 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.clone();
+
+ while (cloneNodes.length < cloneCount) {
+ cloneNodes = cloneNodes.add(list.clone());
+ }
+
+ return this.$container.append(cloneNodes);
+ }
+ },
+
+ /**
+ * Move panel's position within array
+ * @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: function (count, append) {
+ var panel = this._conf.panel;
+ var list = panel.$list.toArray();
+ var listToMove;
+
+ listToMove = list.splice(append ? 0 : panel.count - count, count);
+ panel.$list = $(append ? list.concat(listToMove) : listToMove.concat(list));
+ },
+
+ /**
+ * Set default panel to show
+ * @param {Number} index
+ */
+ _setDefaultPanel: function (index) {
+ var panel = this._conf.panel;
+ var lastIndex = panel.count - 1;
+ var coords;
+ var 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
+ });
+ } else {
+ // if defaultIndex option is given, then move to that index panel
+ if (index > 0 && index <= lastIndex) {
+ this._setPanelNo({
+ index: index,
+ no: index,
+ currIndex: index,
+ currNo: index
+ });
+
+ coords = [ -(panel.size * index), 0];
+
+ this._setTranslate(coords);
+ this._setMovableCoord("setTo", [
+ Math.abs(coords[0]), Math.abs(coords[1])
+ ], true, 0);
+ }
+ }
+ },
+
+ /**
+ * Arrange panels' position
+ * @param {Boolean} sort Need to sort panel's position
+ * @param {Number} indexToMove Number to move from current position (negative: left, positive: right)
+ */
+ _arrangePanels: function (sort, indexToMove) {
+ var conf = this._conf;
+ var panel = conf.panel;
+ var touch = conf.touch;
+ var dirData = conf.dirData;
+ var 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[+!Boolean(indexToMove > 0)]);
+ this._arrangePanelPosition(touch.direction, indexToMove);
+ }
+
+ // set index for base element's position
+ baseIndex = this._getBasePositionIndex();
+
+ this._setPanelNo({
+ index: baseIndex,
+ currIndex: baseIndex
+ });
+
+ // arrange MovableCoord's coord position
+ conf.customEvent.flick = !!this._setMovableCoord("setTo", [
+ panel.size * panel.index, 0
+ ], true, 0);
+ }
+
+ this._applyPanelsPos();
+ },
+
+ /**
+ * Set each panel's position in DOM
+ */
+ _applyPanelsPos: function() {
+ this._conf.panel.$list.each(
+ $.proxy(this._applyPanelsCss, 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.
+ */
+ _setMoveStyle: (function () {
+ return SUPPORT_TRANSFORM ?
+ function ($element, coords) {
+ $element.css("transform",
+ ns.translate(coords[0], coords[1], this._conf.useLayerHack)
+ );
+ } : function ($element, coords) {
+ $element.css({ left: coords[0], top: coords[1] });
+ };
+ })(),
+
+ /**
+ * 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.
+ */
+ _applyPanelsCss: function () {
+ var conf = this._conf;
+ var dummyAnchorClassName = "__dummy_anchor";
+
+ if (IS_ANDROID2) {
+ conf.$dummyAnchor = $("." + dummyAnchorClassName);
+
+ !conf.$dummyAnchor.length && this.$wrapper.append(
+ conf.$dummyAnchor = $("<a href='javascript:void(0);' class='" +
+ dummyAnchorClassName +
+ "' style='position:absolute;height:0px;width:0px;'>")
+ );
+
+ this._applyPanelsCss = function (i, v) {
+ var coords = this._getDataByDirection([
+ (this._conf.panel.size * i) + "px", 0
+ ]);
+
+ $(v).css({
+ left: coords[0],
+ top: coords[1]
+ });
+ };
+ } else {
+ this._applyPanelsCss = function (i, v) {
+ var coords = this._getDataByDirection([
+ SUPPORT_TRANSFORM ?
+ (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
+ *
+ * @param {String} phase
+ * start - set left/top value to 0
+ * end - set translate value to 0
+ * @param {Array} coords coordinate value
+ */
+ _adjustContainerCss: function (phase, coords) {
+ var conf = this._conf;
+ var panel = conf.panel;
+ var options = this.options;
+ var horizontal = options.horizontal;
+ var paddingTop = options.previewPadding[0];
+ var container = this.$container;
+ var value;
+
+ if (IS_ANDROID2) {
+ if (!coords) {
+ coords = [-panel.size * panel.index, 0];
+ }
+
+ if (phase === "start") {
+ container = container[0].style;
+ value = parseInt(container[horizontal ? "left" : "top"], 10);
+
+ if (horizontal) {
+ value && (container.left = 0);
+ } else {
+ value !== paddingTop && (container.top = paddingTop + "px");
+ }
+
+ this._setTranslate([-coords[+!options.horizontal], 0]);
+
+ } else if (phase === "end") {
+ !horizontal && (coords[0] += paddingTop);
+ coords = this._getCoordsValue(coords);
+
+ container.css({
+ left: coords.x,
+ top: coords.y,
+ transform: ns.translate(0, 0, conf.useLayerHack)
+ });
+
+ conf.$dummyAnchor[0].focus();
+ }
+ }
+ },
+
+ /**
+ * Set MovableCoord coord value
+ * @param {String} method
+ * @param {Array} coord
+ * @param {Boolean} isDirVal
+ * @param {Number} duration
+ * @return {eg.MovableCoord} MovableCoord instance
+ */
+ _setMovableCoord: function (method, coord, isDirVal, duration) {
+ if (isDirVal) {
+ coord = this._getDataByDirection(coord);
+ }
+
+ return this._mcInst[method](coord[0], coord[1], duration);
+ },
+
+ /**
+ * Set hint for browser to decide efficient way of doing transform changes(or animation)
+ * https://dev.opera.com/articles/css-will-change-property/
+ */
+ _setHint: function () {
+ var value = "transform";
+ this.$container.css("willChange", value);
+ this._conf.panel.$list.css("willChange", value);
+ },
+
+ /**
+ * Get data according options.horizontal value
+ *
+ * @param {Array} value primary data to handle
+ * @return {Array}
+ */
+ _getDataByDirection: function (value) {
+ value = value.concat();
+ !this.options.horizontal && value.reverse();
+ return value;
+ },
+
+ /**
+ * Move nodes
+ * @param {Boolean} direction
+ * @param {Number} indexToMove
+ */
+ _arrangePanelPosition: function (direction, indexToMove) {
+ var next = direction === this._conf.dirData[0];
+ this._movePanelPosition(Math.abs(indexToMove || 1), next);
+ },
+
+ /**
+ * Get the base position index of the panel
+ */
+ _getBasePositionIndex: function () {
+ return Math.floor(this._conf.panel.count / 2 - 0.1);
+ },
+
+ /**
+ * Bind events
+ * @param {Boolean} bind
+ */
+ _bindEvents: function (bind) {
+ var options = this.options;
+ var $wrapper = this.$wrapper;
+ var mcInst = this._mcInst;
+
+ if (bind) {
+ mcInst.bind($wrapper, {
+ scale: this._getDataByDirection([-1, 0]),
+ direction: MC["DIRECTION_" +
+ (options.horizontal ? "HORIZONTAL" : "VERTICAL")],
+ interruptable: false,
+ inputType: options.inputType
+ }).on({
+ hold: $.proxy(this._holdHandler, this),
+ change: $.proxy(this._changeHandler, this),
+ release: $.proxy(this._releaseHandler, this),
+ animationStart: $.proxy(this._animationStartHandler, this),
+ animationEnd: $.proxy(this._animationEndHandler, this)
+ });
+ } else {
+ mcInst.unbind($wrapper).off();
+ }
+
+ this._conf.inputEvent = !!bind;
+ },
+
+ /**
+ * 'hold' event handler
+ */
+ _holdHandler: function (e) {
+ var conf = this._conf;
+
+ conf.touch.holdPos = e.pos;
+ conf.touch.holding = true;
+ conf.panel.changed = false;
+
+ this._adjustContainerCss("start", e.pos);
+ },
+
+ /**
+ * 'change' event handler
+ */
+ _changeHandler: function (e) {
+ var conf = this._conf;
+ var touch = conf.touch;
+ var posIndex = +!this.options.horizontal;
+ var pos = e.pos[posIndex];
+ var holdPos = touch.holdPos[posIndex];
+ var direction;
+ var eventRes = null;
+ var movedPx;
+
+ this._setPointerEvents(e); // for "click" bug
+
+ /**
+ * This event is fired when panel moves.
+ * @ko 패널이 이동할 때 발생하는 이벤트
+ * @name eg.Flicking#flick
+ * @event
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {String} param.eventType The name of the event<ko>이름명</ko>
+ * @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>
+ * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content <ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
+ * @param {Number} param.direction Direction of the movement (see eg.MovableCoord.DIRECTION_* constant) <ko>이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
+ * @param {Array} param.pos Start coordinate <ko>출발점 좌표</ko>
+ * @param {Number} param.pos.0 x-coordinate <ko>x 좌표</ko>
+ * @param {Number} param.pos.1 y-coordinate <ko>y 좌표</ko>
+ * @param {Boolean} param.holding Indicates whether a user holds an element on the screen of the device. <ko>사용자가 기기의 화면을 누르고 있는지 여부</ko>
+ * @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>
+ */
+ if (e.hammerEvent) {
+ direction = e.hammerEvent.direction;
+
+ // Adjust direction in case of diagonal touch move
+ movedPx = e.hammerEvent[ this.options.horizontal ? "deltaX" : "deltaY" ];
+
+ if (!~$.inArray(direction, conf.dirData)) {
+ direction = conf.dirData[ +(Math.abs(touch.lastPos) <= movedPx) ];
+ }
+
+ touch.lastPos = movedPx;
+ } else {
+ touch.lastPos = null;
+ }
+
+ conf.customEvent.flick && (eventRes = this._triggerEvent(EVENTS.flick, {
+ pos: e.pos,
+ holding: e.holding,
+ direction: direction || touch.direction,
+ distance: pos - (holdPos || (touch.holdPos[posIndex] = pos))
+ }));
+
+ (eventRes || eventRes === null) && this._setTranslate([ -pos, 0 ]);
+ },
+
+ /**
+ * 'release' event handler
+ */
+ _releaseHandler: function (e) {
+ var touch = this._conf.touch;
+ var pos = e.destPos;
+ var posIndex = +!this.options.horizontal;
+ var holdPos = touch.holdPos[posIndex];
+ var panelSize = this._conf.panel.size;
+
+ touch.distance = e.depaPos[posIndex] - touch.holdPos[posIndex];
+
+ touch.direction = this._conf.dirData[
+ +!Boolean(touch.holdPos[posIndex] < e.depaPos[posIndex])
+ ];
+
+ pos[posIndex] = Math.max(
+ holdPos - panelSize, Math.min(holdPos, pos[posIndex])
+ );
+
+ touch.destPos[posIndex] =
+ pos[posIndex] = Math.round(pos[posIndex] / panelSize) * panelSize;
+
+ touch.distance === 0 && this._adjustContainerCss("end");
+ touch.holding = false;
+
+ this._setPointerEvents(); // for "click" bug
+ },
+
+ /**
+ * 'animationStart' event handler
+ */
+ _animationStartHandler: function (e) {
+ var conf = this._conf;
+ var panel = conf.panel;
+ var customEvent = conf.customEvent;
+
+ panel.animating = true;
+
+ if (!customEvent.restoreCall && e.hammerEvent &&
+ this._setPhaseValue("start", {
+ depaPos: e.depaPos,
+ destPos: e.destPos
+ }) === false) {
+ e.stop();
+ }
+
+ if (e.hammerEvent) {
+ e.duration = this.options.duration;
+
+ e.destPos[+!this.options.horizontal] =
+ panel.size * (
+ panel.index + conf.indexToMove
+ );
+ }
+
+ if (this._isMovable()) {
+ !customEvent.restoreCall && (customEvent.restore = false);
+ } else {
+ this._triggerBeforeRestore(e);
+ }
+ },
+
+ /**
+ * 'animationEnd' event handler
+ */
+ _animationEndHandler: function () {
+ this._setPhaseValue("end");
+
+ this._conf.panel.animating = false;
+ this._triggerRestore();
+ },
+
+ /**
+ * Trigger beforeRestore event
+ * @param {Object} e event object
+ */
+ _triggerBeforeRestore: function(e) {
+ var conf = this._conf;
+ var touch = conf.touch;
+
+ // reverse direction value when restore
+ touch.direction = ~~conf.dirData.join("").replace(touch.direction, "");
+
+ /**
+ * 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
+ * @ko 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원되기 전에 발생하는 이벤트
+ * @name eg.Flicking#beforeRestore
+ * @event
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {String} param.eventType The name of the event <ko>이름명</ko>
+ * @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>
+ * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content.<ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
+ * @param {Number} param.direction Direction of the movement (see eg.MovableCoord.DIRECTION_* constant) <ko>이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
+ * @param {Array} param.depaPos Start coordinate <ko>출발점 좌표</ko>
+ * @param {Number} param.depaPos.0 x-coordinate <ko>x 좌표</ko>
+ * @param {Number} param.depaPos.1 y-coordinate <ko>y 좌표</ko>
+ * @param {Array} param.destPos End coordinate <ko>도착점 좌표</ko>
+ * @param {Number} param.destPos.0 x-coordinate <ko>x 좌표</ko>
+ * @param {Number} param.destPos.1 y-coordinate <ko>y 좌표</ko>
+ */
+ conf.customEvent.restore = this._triggerEvent(EVENTS.beforeRestore, {
+ depaPos: e.depaPos,
+ destPos: e.destPos
+ });
+
+ if (!conf.customEvent.restore) {
+ "stop" in e && e.stop();
+ conf.panel.animating = false;
+ }
+ },
+
+ /**
+ * Trigger restore event
+ */
+ _triggerRestore: function() {
+ var customEvent = this._conf.customEvent;
+
+ /**
+ * 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.
+ * @ko 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원된 다음 발생하는 이벤트
+ * @name eg.Flicking#restore
+ * @event
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {String} param.eventType The name of the event <ko>이름명</ko>
+ * @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>
+ * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content. <ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
+ * @param {Number} param.direction Direction of the panel move (see eg.MovableCoord.DIRECTION_* constant) <ko>이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
+ */
+ customEvent.restore && this._triggerEvent(EVENTS.restore);
+ customEvent.restoreCall = false;
+ },
+
+ /**
+ * Set value when panel changes
+ * @param {String} phase - [start|end]
+ * @param {Object} pos
+ */
+ _setPhaseValue: function (phase, pos) {
+ var conf = this._conf;
+ var options = this.options;
+ var panel = conf.panel;
+
+ if (phase === "start" && (panel.changed = this._isMovable())) {
+ /**
+ * This event is fired before flicking starts
+ * @ko 플리킹이 시작하기 전에 발생하는 이벤트
+ * @name eg.Flicking#beforeFlickStart
+ * @event
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {String} param.eventType The name of the event <ko>이름명</ko>
+ * @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>
+ * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content.<ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</ko>
+ * @param {Number} param.direction Direction of the movement (see eg.MovableCoord.DIRECTION_* constant) <ko>− 이동 방향(eg.MovableCoord.DIRECTION_* constant 참고)</ko>
+ * @param {Array} param.depaPos Start coordinate <ko>출발점 좌표</ko>
+ * @param {Number} param.depaPos.0 x-coordinate <ko>x 좌표</ko>
+ * @param {Number} param.depaPos.1 y-coordinate <ko>y 좌표</ko>
+ * @param {Array} param.destPos End coordinate <ko>도착점 좌표</ko>
+ * @param {Number} param.destPos.0 x-coordinate <ko>x 좌표</ko>
+ * @param {Number} param.destPos.1 y-coordinate <ko>y 좌표</ko>
+ */
+ if (!this._triggerEvent(EVENTS.beforeFlickStart, pos)) {
+ return panel.changed = panel.animating = false;
+ }
+
+ conf.indexToMove === 0 && this._setPanelNo();
+ } else if (phase === "end") {
+ if (options.circular && panel.changed) {
+ this._arrangePanels(true, conf.indexToMove);
+ }
+
+ !IS_ANDROID2 && this._setTranslate([-panel.size * panel.index, 0]);
+ conf.touch.distance = conf.indexToMove = 0;
+
+ /**
+ * This event is fired after panel moves.
+ * @ko 패널이 이동한 다음 발생하는 이벤트
+ * @name eg.Flicking#flickEnd
+ * @event
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {String} param.eventType The name of the event <ko>이름명</ko>
+ * @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>
+ * @param {Number} param.no Logical index number of the current panel element, which is relative to the panel content. <ko>현재 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다.</ko>
+ * @param {Number} param.direction Direction of the movemen (see eg.MovableCoord.DIRECTION_* constant) <ko>− 이동 방향(eg.MovableCoord.DIRECTION_* constant 참고</ko>
+ */
+ panel.changed && this._triggerEvent(EVENTS.flickEnd);
+ }
+
+ !(phase === "start" && pos === undefined) && this._adjustContainerCss(phase);
+ },
+
+ /**
+ * Get positive or negative according direction
+ */
+ _getNumByDirection: function() {
+ var conf = this._conf;
+ return conf.touch.direction === conf.dirData[0] ? 1 : -1;
+ },
+
+ /**
+ * Revert panel number
+ */
+ _revertPanelNo: function() {
+ var panel = this._conf.panel;
+ var num = this._getNumByDirection();
+
+ var index = panel.currIndex >= 0 ? panel.currIndex : panel.index - num;
+ var no = panel.currNo >= 0 ? panel.currNo : panel.no - num;
+
+ this._setPanelNo({
+ index: index,
+ no: no
+ });
+ },
+
+ /**
+ * Set the panel number
+ * @param {Object} obj number object
+ */
+ _setPanelNo: function (obj) {
+ var panel = this._conf.panel;
+ var count = panel.origCount - 1;
+ var num = this._getNumByDirection();
+
+ if ($.isPlainObject(obj)) {
+ $.each(obj, function(i, v) {
+ panel[i] = v;
+ });
+
+ } 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
+ * @param {Event} e
+ */
+ _setPointerEvents: function (e) {
+ var pointer = this.$container.css("pointerEvents");
+ var val;
+
+ if (e && e.holding &&
+ e.hammerEvent && e.hammerEvent.preventSystemEvent &&
+ pointer !== "none"
+ ) {
+ val = "none";
+ } else if (!e && pointer !== "auto") {
+ val = "auto";
+ }
+
+ val && this.$container.css("pointerEvents", val);
+ },
+
+ /**
+ * Get coordinate value with unit
+ * @param coords {Array} x,y numeric value
+ * @return {Object} x,y coordinate value with unit
+ */
+ _getCoordsValue: function (coords) {
+ // the param comes as [ val, 0 ], whatever the direction. So reorder the value depend the direction.
+ coords = this._getDataByDirection(coords);
+
+ return {
+ x: this._getUnitValue(coords[0]),
+ y: this._getUnitValue(coords[1])
+ };
+ },
+
+ /**
+ * Set translate property value
+ * @param {Array} coords coordinate x,y value
+ */
+ _setTranslate: function (coords) {
+ coords = this._getCoordsValue(coords);
+ this._setMoveStyle(this.$container, [ coords.x, coords.y ]);
+ },
+
+ /**
+ * Return unit formatted value
+ * @param {Number|String} val
+ * @return {String} val Value formatted with unit
+ */
+ _getUnitValue: function (val) {
+ var rx = /(?:[a-z]{2,}|%)$/;
+ return (parseInt(val, 10) || 0) + (String(val).match(rx) || "px");
+ },
+
+ /**
+ * Check if panel passed through threshold pixel
+ */
+ _isMovable: function () {
+ var options = this.options;
+ var mcInst = this._mcInst;
+ var isMovable = Math.abs(this._conf.touch.distance) >= options.threshold;
+ var max;
+ var currPos;
+
+ if (!options.circular && isMovable) {
+ max = this._getDataByDirection(mcInst.options.max)[0];
+ currPos = this._getDataByDirection(mcInst.get())[0];
+
+ // if current position out of range
+ if (currPos < 0 || currPos > max) {
+ return false;
+ }
+ }
+
+ return isMovable;
+ },
+
+ /**
+ * Trigger custom events
+ * @param {String} name - event name
+ * @param {Object} param - additional event value
+ * @return {Boolean}
+ */
+ _triggerEvent: function (name, param) {
+ var conf = this._conf;
+ var 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, $.extend({
+ eventType: name,
+ index: panel.currIndex,
+ no: panel.currNo,
+ direction: conf.touch.direction
+ }, param));
+ },
+
+ /**
+ * Get next/prev panel element/index.
+ * @param {Boolean} direction
+ * @param {Boolean} element - true:to get element, false:to get index
+ * @param {Number} physical - true : physical, false : logical
+ * @return {jQuery|Number}
+ */
+ _getElement: function (direction, element, physical) {
+ var panel = this._conf.panel;
+ var circular = this.options.circular;
+ var pos = panel.currIndex;
+ var next = direction === this._conf.dirData[0];
+ var result = null;
+ var total;
+ var index;
+ var currentIndex;
+
+ if (physical) {
+ total = panel.count;
+ index = pos;
+ } else {
+ total = panel.origCount;
+ index = panel.currNo;
+ }
+
+ 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
+ * @param {Boolean} next
+ */
+ _setValueToMove: function (next) {
+ var conf = this._conf;
+
+ conf.touch.distance = this.options.threshold + 1;
+ conf.touch.direction = conf.dirData[ +!next ];
+ },
+
+ /**
+ * Check and parse value to number
+ * @param {Number|String} val
+ * @param {Number} defVal
+ * @return {Number}
+ */
+ _getNumValue: function (val, defVal) {
+ return isNaN(val = parseInt(val, 10)) ? defVal : val;
+ },
+
+ /**
+ * Returns the index number of the current panel element.
+ * @ko 현재 패널 엘리먼트의 인덱스 번호를 반환한다
+ * @method eg.Flicking#getIndex
+ * @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>
+ * @return {Number} Index number of the current panel element <ko>현재 패널의 인덱스 번호</ko>
+ */
+ getIndex: function (physical) {
+ return this._conf.panel[ physical ? "currIndex" : "currNo" ];
+ },
+
+ /**
+ * Returns the reference of the current panel element.
+ * @ko 현재 패널 엘리먼트의 레퍼런스를 반환한다
+ * @method eg.Flicking#getElement
+ * @return {jQuery} Current element <ko>현재 엘리먼트</ko>
+ */
+ getElement: function () {
+ var panel = this._conf.panel;
+ return $(panel.$list[ panel.currIndex ]);
+ },
+
+ /**
+ * Returns the reference of the next panel element.
+ * @ko 다음 패널 엘리먼트의 레퍼런스를 반환한다.
+ * @method eg.Flicking#getNextElement
+ * @return {jQuery|null} Next panel element or null if it does not exist.<ko>다음 패널 엘리먼트. 패널이 없으면 'null'을 반환한다.</ko>
+ */
+ getNextElement: function () {
+ 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] 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>
+ * @return {Number|null} Index number of the next panel element or null if it does not exist. <ko>다음 패널 엘리먼트의 인덱스 번호. 패널이 없으면 'null'을 반환한다</ko>
+ */
+ getNextIndex: function (physical) {
+ return this._getElement(this._conf.dirData[0], false, physical);
+ },
+
+ /**
+ * Returns the references of whole panel elements.
+ * @ko 패널을 구성하는 모든 엘리먼트의 레퍼런스를 반환한다
+ * @method eg.Flicking#getAllElements
+ * @return {jQuery} Whole panel elements <ko>모든 패널 엘리먼트</ko>
+ */
+ getAllElements: function () {
+ return this._conf.panel.$list;
+ },
+
+ /**
+ * Returns the reference of the previous panel element.
+ * @ko 이전 패널 엘리먼트의 레퍼런스를 반환한다.
+ * @method eg.Flicking#getPrevElement
+ * @return {jQuery|null} Previous panel element or null if it does not exist. <ko>이전 패널 엘리먼트. 패널이 없으면 'null'을 반환한다</ko>
+ */
+ getPrevElement: function () {
+ 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] 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>
+ * @return {Number|null} Previous element index value or null if no more element exist<ko>이전 패널 인덱스 번호. 패널이 없는 경우에는 null</ko>
+ */
+ getPrevIndex: function (physical) {
+ return this._getElement(this._conf.dirData[1], false, physical);
+ },
+
+ /**
+ * Returns the total number of whole panel elements.
+ * @ko 전체 패널 엘리먼트의 개수를 반환한다
+ * @method eg.Flicking#getTotalCount
+ * @deprecated since 1.3.0
+ * @param {Boolean} [physical=false] Number of elements relative to (true: DOM, false: panel content)<ko>엘리먼트 개수의 기준(true: DOM 엘리먼트 기준, false: 패널 콘텐츠 기준)</ko>
+ * @return {Number} Total number of whole panel elements <ko>모든 패널 엘리먼트의 개수</ko>
+ */
+ getTotalCount: function (physical) {
+ return this._conf.panel[ physical ? "count" : "origCount" ];
+ },
+
+ /**
+ * Checks whether the animated panel is playing.
+ * @ko 패널 이동 애니메이션이 진행 중인지 확인한다.
+ * @method eg.Flicking#isPlaying
+ * @return {Boolean} Indicates whether the animated panel is playing <ko>패널 이동 애니메이션 진행 중 여부</ko>
+ */
+ isPlaying: function () {
+ return this._conf.panel.animating;
+ },
+
+ /**
+ * Move panel to the given direction
+ * @param {Boolean} next
+ * @param {Number} duration
+ */
+ _movePanel: function (next, duration) {
+ var conf = this._conf;
+ var panel = conf.panel;
+ var options = this.options;
+
+ if (panel.animating || conf.touch.holding) {
+ return;
+ }
+
+ this._setValueToMove(next);
+
+ if (options.circular ||
+ this[next ? "getNextIndex" : "getPrevIndex"]() != null
+ ) {
+ this._movePanelByPhase("setBy", [
+ panel.size * (next ? 1 : -1), 0
+ ], duration);
+ }
+
+ return this;
+ },
+
+ /**
+ * Move panel applying start/end phase value
+ * @param {String} method movableCoord method name
+ * @param {Object} coords coordinate array value
+ * @param {Number} duration duration value
+ */
+ _movePanelByPhase: function(method, coords, duration) {
+ duration = this._getNumValue(duration, this.options.duration);
+
+ if (this._setPhaseValue("start") !== false) {
+ this._setMovableCoord(method, coords, true, duration);
+ !duration && this._setPhaseValue("end");
+ }
+ },
+
+ /**
+ * Moves an element to the next panel.
+ * @ko 다음 패널로 이동한다.
+ * @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>
+ */
+ next: function (duration) {
+ return this._movePanel(true, duration);
+ },
+
+ /**
+ * Moves an element to the previous panel.
+ * @ko 이전 패널로 이동한다.
+ * @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>
+ */
+ prev: function (duration) {
+ return this._movePanel(false, duration);
+ },
+
+ /**
+ * Moves an element to the indicated panel.
+ * @ko 지정한 패널로 이동한다.
+ * @method eg.Flicking#moveTo
+ * @param {Number} no Logical index number of the target panel element, which is relative to the panel content. <ko>이동할 패널 엘리먼트의 논리적 인덱스 번호. 패널 콘텐츠를 기준으로 하는 인덱스 번호다</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>
+ */
+ moveTo: function (no, duration) {
+ var conf = this._conf;
+ var panel = conf.panel;
+ var circular = this.options.circular;
+ var currentIndex = panel.index;
+ var indexToMove;
+ var isPositive;
+
+ no = this._getNumValue(no, -1);
+
+ if (no < 0 || no >= panel.origCount || no === panel.no ||
+ panel.animating || conf.touch.holding) {
+ return this;
+ }
+
+ if (circular) {
+ indexToMove = no - panel.no;
+ isPositive = indexToMove > 0;
+
+ // check for real panel count which can be moved on each sides
+ if (Math.abs(indexToMove) > (isPositive ?
+ panel.count - (currentIndex + 1) : currentIndex)) {
+ indexToMove = indexToMove + (isPositive ? -1 : 1) * panel.count;
+ }
+
+ this._setPanelNo({ no: no });
+ } else {
+ indexToMove = no - currentIndex;
+ this._setPanelNo({ index: no, no: no });
+ }
+
+ this._conf.indexToMove = indexToMove;
+ this._setValueToMove(isPositive);
+
+ this._movePanelByPhase(
+ circular ? "setBy" : "setTo",
+ [ panel.size * (circular ? indexToMove : no), 0 ],
+ duration
+ );
+
+ return this;
+ },
+
+ /**
+ * Update panel's previewPadding size according options.previewPadding
+ */
+ _checkPadding: function () {
+ var options = this.options;
+ var previewPadding = options.previewPadding.concat();
+ var padding = this.$wrapper.css("padding").split(" ");
+
+ options.horizontal && padding.reverse();
+
+ // get current padding value
+ padding = padding.length === 2 ?
+ [ padding[0], padding[0] ] : [ padding[0], padding[2] ];
+
+ padding = $.map(padding, function(num) {
+ return parseInt(num, 10);
+ });
+
+ // update padding when current and given are different
+ if (previewPadding.length === 2 &&
+ previewPadding[0] !== padding[0] || previewPadding[1] !== padding[1]) {
+
+ this._setPadding(previewPadding);
+ }
+ },
+
+ /**
+ * Updates the size of the panel.
+ * @ko 패널의 크기를 갱신한다
+ * @method eg.Flicking#resize
+ * @return {eg.Flicking} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
+ * @example
+ var some = new eg.Flicking("#mflick", {
+ previewPadding: [10,10]
+ });
+
+ // when device orientaion changes
+ some.resize();
+
+ // or when changes previewPadding option from its original value
+ some.options.previewPadding = [20, 30];
+ some.resize();
+ */
+ resize: function () {
+ var conf = this._conf;
+ var options = this.options;
+ var panel = conf.panel;
+ var horizontal = options.horizontal;
+ var panelSize;
+ var maxCoords;
+
+ if (~~options.previewPadding.join("")) {
+ this._checkPadding();
+ panelSize = panel.size;
+ } else if (horizontal) {
+ panelSize = panel.size = this.$wrapper.width();
+ }
+
+ maxCoords = this._getDataByDirection([panelSize * (panel.count - 1), 0]);
+
+ // resize elements
+ horizontal && this.$container.width(maxCoords[0] + panelSize);
+ panel.$list.css(horizontal ? "width" : "height", panelSize);
+
+ this._mcInst.options.max = maxCoords;
+ this._setMovableCoord("setTo", [panelSize * panel.index, 0], true, 0);
+
+ if (IS_ANDROID2) {
+ this._applyPanelsPos();
+ this._adjustContainerCss("end");
+ }
+
+ return this;
+ },
+
+ /**
+ * Restores an element to its original position when it movement stops while the element is not dragged until a certain distance threshold is reached.
+ * @ko 다음 패널로 바뀌기 전에 패널 이동이 멈췄을 때 원래 패널로 복원한다
+ * @method eg.Flicking#restore
+ * @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>
+ * @example
+ var some = new eg.Flicking("#mflick").on({
+ beforeFlickStart : function(e) {
+ if(e.no === 2) {
+ e.stop(); // stop flicking
+ this.restore(100); // restoring to previous position
+ }
+ }
+ );
+ */
+ restore: function (duration) {
+ var conf = this._conf;
+ var panel = conf.panel;
+ var currPos = this._getDataByDirection(this._mcInst.get());
+ var destPos;
+
+ // check if the panel isn't in right position
+ if (currPos[0] !== panel.currIndex * panel.size) {
+ conf.customEvent.restoreCall = true;
+ duration = this._getNumValue(duration, this.options.duration);
+
+ this._revertPanelNo();
+ destPos = this._getDataByDirection([panel.size * panel.index, 0]);
+
+ this._triggerBeforeRestore({ depaPos: currPos, destPos: destPos });
+ this._setMovableCoord("setTo", destPos, true, 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;
+ },
+
+ /**
+ * Set input event biding
+ * @param {Boolean} bind - true: bind, false: unbind
+ * @return {eg.Flicking} instance of itself
+ */
+ _setInputEvent: function(bind) {
+ var inputEvent = this._conf.inputEvent;
+
+ if (bind ^ inputEvent) {
+ this._bindEvents(bind);
+ }
+
+ return this;
+ },
+
+ /**
+ * Enables input devices.
+ * @ko 입력 장치를 사용할 수 있게 한다
+ * @method eg.Flicking#enableInput
+ * @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
+ */
+ enableInput: function() {
+ return this._setInputEvent(true);
+ },
+
+ /**
+ * Disables input devices.
+ * @ko 입력 장치를 사용할 수 없게 한다.
+ * @method eg.Flicking#disableInput
+ * @return {eg.Flicking} An instance of a module itself <ko>모듈 자신의 인스턴스</ko>
+ */
+ disableInput: function() {
+ return this._setInputEvent();
+ },
+
+ /**
+ * Destroys elements, properties, and events used in a panel.
+ * @ko 패널에 사용한 엘리먼트와 속성, 이벤트를 해제한다
+ * @method eg.Flicking#destroy
+ */
+ destroy: function() {
+ var conf = this._conf;
+ var origPanelStyle = conf.origPanelStyle;
+ var wrapper = origPanelStyle.wrapper;
+ var list = origPanelStyle.list;
+
+ // unwrap container element and restore original inline style
+ this.$wrapper.attr("class", wrapper.className)
+ .attr("style", wrapper.style);
+
+ this.$container.children().unwrap().each(function(i, v) {
+ var $el = $(v);
+
+ if (i > list.length - 1) {
+ return !!$el.remove();
+ }
+
+ $el.attr("class", list[i].className)
+ .attr("style", list[i].style);
+ });
+
+ // unbind events
+ this.disableInput();
+ this.off();
+
+ // release resources
+ for (var x in this) {
+ this[x] = null;
+ }
+ }
+ });
+});
+/**
+ * A jQuery plugin available in the eg.Flicking module.
+ *
+ * @ko eg.Flicking 모듈의 jQuery 플러그인
+ * @method jQuery.flicking
+ * @example
+ <div id="content">
+ <div>
+ <p>Layer 0</p>
+ </div>
+ <div>
+ <p>Layer 1</p>
+ </div>
+ <div>
+ <p>Layer 2</p>
+ </div>
+ </div>
+ <script>
+ // create
+ $("#content").flicking({
+ circular : true,
+ threshold : 50
+ });
+ // method
+ $("#content").flicking("option","circular",true); //Set option
+ $("#content").flicking("instance"); // Return flicking instance
+ $("#content").flicking("getNextIndex",1); // Get next panel index
+ </script>
+ * @see eg.Flicking
+ */
+/**
+ * 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.
+ *
+ * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원되기 전에 발생한다
+ * @name jQuery#flicking:beforeRestore
+ * @event
+ * @example
+ $("#mflick").on("flicking:beforeRestore",callback);
+ $("#mflick").off("flicking:beforeRestore",callback);
+ $("#mflick").trigger("flicking:beforeRestore",callback);
+ * @see eg.Flicking#event:beforeRestore
+ */
+/**
+ * A jQuery custom event of the eg.Flicking module, which occurs before the flicking starts.
+ *
+ * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 플리킹을 시작하기 전에 발생한다
+ * @name jQuery#flicking:beforeFlickStart
+ * @event
+ * @example
+ $("#mflick").on("flicking:beforeFlickStart",callback);
+ $("#mflick").off("flicking:beforeFlickStart",callback);
+ $("#mflick").trigger("flicking:beforeFlickStart",callback);
+ * @see eg.Flicking#event:beforeFlickStart
+ */
+/**
+ * A jQuery custom event of the eg.Flicking module. This event is fired when panel moves.
+ *
+ * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 패널이 이동될 때 발생한다
+ * @name jQuery#flicking:flick
+ * @event
+ * @example
+ $("#mflick").on("flicking:flick",callback);
+ $("#mflick").off("flicking:flick",callback);
+ $("#mflick").trigger("flicking:flick",callback);
+ * @see eg.Flicking#event:flick
+ */
+/**
+ * A jQuery custom event of the eg.Flicking module. This event is fired after the panel moves.
+ *
+ * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 패널이 이동된 뒤 발생한다
+ * @name jQuery#flicking:flickEnd
+ * @event
+ * @example
+ $("#mflick").on("flicking:flickEnd",callback);
+ $("#mflick").off("flicking:flickEnd",callback);
+ $("#mflick").trigger("flicking:flickEnd",callback);
+ * @see eg.Flicking#event:flickEnd
+ */
+/**
+ * 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.
+ *
+ * @ko eg.Flicking 모듈의 jQuery 커스텀 이벤트. 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원된 다음 발생한다
+ * @name jQuery#flicking:restore
+ * @event
+ * @example
+ $("#mflick").on("flicking:restore",callback);
+ $("#mflick").off("flicking:restore",callback);
+ $("#mflick").trigger("flicking:restore",callback);
+ * @see eg.Flicking#event:restore
+ */
+
egjs is a jQuery-based JavaScript library consisting of UI interactions, effects, and utilities, which brings easiest and fastest way to build a web application in your way.
Clone the egjs depository and install the Bower and npm dependency modules.
+
# Create and move a folder.
+$ mkdir egjs && cd egjs
+
+# Clone a repository.
+$ git clone https://github.com/naver/egjs.git
+
+# Install the Bower dependency module.
+$ bower install
+
+# Install the node dependency module.
+$ npm install
+
3. Build
+
Use Grunt to build egjs.
+
$ grunt build
+
Two folders will be created after complete build is completed.
+
+
dist folder: Includes the eg.js and eg.min.js files.
+
doc folder: Includes API documentation. The home page for the documentation is doc/index.html.
+
+
Test
+
Once you create a branch and done with development, you must perform a test using the "grunt test" command before you push code to a remote repository.
+
$ grunt test
+
Running a "grunt test" command will start JShint, JSCS, QUnit, and istanbul.
+ JShint and JSCS: Performs static checking and code style checking.
+ QUnit: Performs unit tests of egjs.
+ istanbul: Measures test coverage. The test results can be verified once the Grunt task is completed. Or you can use the *./report/index.html file to verify them.
+
Bug Report
+
If you find a bug, please report it to us using the Issues page on GitHub.
Copyright (c) 2015 NAVER Corp.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
/**
+* Copyright (c) 2015 NAVER Corp.
+* egjs projects are licensed under the MIT license
+*/
+
+// jscs:disable validateLineBreaks, maximumLineLength
+eg.module("infiniteGrid", ["jQuery", eg, window, document], function($, ns, global, doc) {
+ "use strict";
+
+ /**
+ * A module used to arrange card elements including content infinitely on a grid layout. With this module, you can implement a grid-pattern user interface composed of different card elements whose sizes vary. It guarantees performance by maintaining the number of DOMs the module is handling under any circumstance
+ * @group egjs
+ * @ko 콘텐츠가 있는 카드 엘리먼트를 그리드 레이아웃에 무한으로 배치하는 모듈. 다양한 크기의 카드 엘리먼트를 격자 모양으로 배치하는 UI를 만들 수 있다. 카드 엘리먼트의 개수가 계속 늘어나도 모듈이 처리하는 DOM의 개수를 일정하게 유지해 최적의 성능을 보장한다
+ * @class
+ * @name eg.InfiniteGrid
+ * @extends eg.Component
+ *
+ * @param {HTMLElement|String|jQuery} element A base element for a module <ko>모듈을 적용할 기준 엘리먼트</ko>
+ * @param {Object} [options] The option object of the eg.InfiniteGrid module <ko>eg.InfiniteGrid 모듈의 옵션 객체</ko>
+ * @param {String} [options.itemSelector] A selector to select card elements that make up the layout (@deprecated since 1.3.0)<ko>레이아웃을 구성하는 카드 엘리먼트를 선택할 선택자(selector) (@deprecated since 1.3.0)</ko>
+ * @param {Number} [options.count=30] The number of DOMs handled by module. If the count value is greater than zero, the number of DOMs is maintained. If the count value is zero or less than zero, the number of DOMs will increase as card elements are added. <ko>모듈이 유지할 실제 DOM의 개수. count 값이 0보다 크면 DOM 개수를 일정하게 유지한다. count 값이 0 이하면 카드 엘리먼트가 추가될수록 DOM 개수가 계속 증가한다.</ko>
+ * @param {String} [options.defaultGroupKey=null] The default group key configured in a card element contained in the markup upon initialization of a module object <ko>모듈 객체를 초기화할 때 마크업에 있는 카드 엘리먼트에 설정할 그룹 키 </ko>
+ * @param {Boolean} [options.isEqualSize=false] Indicates whether sizes of all card elements are equal to one another. If sizes of card elements to be arranged are all equal and this option is set to "true", the performance of layout arrangement can be improved. <ko>카드 엘리먼트의 크기가 동일한지 여부. 배치될 카드 엘리먼트의 크기가 모두 동일할 때 이 옵션을 'true'로 설정하면 레이아웃 배치 성능을 높일 수 있다</ko>
+ * @param {Number} [options.threshold=300] The threshold size of an event area where card elements are added to a layout.<br>- append event: If the current vertical position of the scroll bar is greater than "the bottom property value of the card element at the top of the layout" plus "the value of the threshold option", the append event will occur.<br>- prepend event: If the current vertical position of the scroll bar is less than "the bottom property value of the card element at the top of the layout" minus "the value of the threshold option", the prepend event will occur. <ko>− 레이아웃에 카드 엘리먼트를 추가하는 이벤트가 발생하는 기준 영역의 크기.<br>- append 이벤트: 현재 스크롤의 y 좌표 값이 '레이아웃의 맨 아래에 있는 카드 엘리먼트의 top 속성의 값 + threshold 옵션의 값'보다 크면 append 이벤트가 발생한다.<br>- prepend 이벤트: 현재 스크롤의 y 좌표 값이 '레이아웃의 맨 위에 있는 카드 엘리먼트의 bottom 속성의 값 - threshold 옵션의 값'보다 작으면 prepend 이벤트가 발생한다</ko>
+ *
+ * @codepen {"id":"zvrbap", "ko":"InfiniteGrid 데모", "en":"InfiniteGrid example", "collectionId":"DPYEww", "height": 403}
+ * @support {"ie": "8+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.1+ (except 3.x)"}
+ *
+ * @example
+ <!-- HTML -->
+ <ul id="grid">
+ <li class="card">
+ <div>test1</div>
+ </li>
+ <li class="card">
+ <div>test2</div>
+ </li>
+ <li class="card">
+ <div>test3</div>
+ </li>
+ <li class="card">
+ <div>test4</div>
+ </li>
+ <li class="card">
+ <div>test5</div>
+ </li>
+ <li class="card">
+ <div>test6</div>
+ </li>
+ </ul>
+ <script>
+ var some = new eg.InfiniteGrid("#grid").on("layoutComplete", function(e) {
+ // ...
+ });
+ </script>
+ */
+ var EVENTS = {
+ "layoutComplete": "layoutComplete",
+ "append": "append",
+ "prepend": "prepend"
+ };
+ ns.InfiniteGrid = ns.Class.extend(ns.Component, {
+ _events: function() {
+ return EVENTS;
+ },
+ construct: function(el, options, _prefix) {
+ this.options = $.extend({
+ isEqualSize: false,
+ defaultGroupKey: null,
+ count: 30,
+ threshold: 300
+ }, options);
+
+ // if el is jQuery instance, el should change to HTMLElement.
+ this.$el = el instanceof $ ? el : $(el);
+ this.el = this.$el.get(0);
+ this.el.style.position = "relative";
+ this._prefix = _prefix || "";
+ this._isIos = /iPhone|iPad/.test(global.navigator.userAgent);
+ this._isIE10lower = !!(doc.documentMode && doc.documentMode < 10);
+ this._appendCols = this._prependCols = [];
+ this.$view = $(global);
+ this._reset();
+ this._refreshViewport();
+ if (this.el.children.length > 0) {
+ this.items = this._itemize($.makeArray(this.el.children), this.options.defaultGroupKey, true);
+ this.layout(this.items, true);
+ }
+
+ this._onScroll = $.proxy(this._onScroll, this);
+ this._onResize = $.proxy(this._onResize, this);
+ this.$view.on("scroll", this._onScroll)
+ .on("resize", this._onResize);
+ },
+ _getScrollTop: function() {
+ return doc.body.scrollTop || doc.documentElement.scrollTop;
+ },
+ _onScroll: function() {
+ if (this.isProcessing()) {
+ return;
+ }
+ var scrollTop = this._getScrollTop();
+ var prevScrollTop = this._prevScrollTop;
+
+ if (this._isIos && scrollTop === 0 || prevScrollTop === scrollTop) {
+ return;
+ }
+ var ele;
+ var rect;
+ if (prevScrollTop < scrollTop) {
+ if ($.isEmptyObject(this._bottomElement)) {
+ this._bottomElement = this.getBottomElement();
+ if (this._bottomElement == null) {
+ return;
+ }
+ }
+ ele = this._bottomElement;
+ rect = ele.getBoundingClientRect();
+ if (rect.top <= this._clientHeight + this.options.threshold) {
+ /**
+ * This event is fired when a card element must be added at the bottom of a grid layout because there is no card to be displayed on screen when a user scrolls near bottom.
+ * @ko 카드 엘리먼트가 그리드 레이아웃의 아래에 추가돼야 할 때 발생하는 이벤트. 사용자가 아래로 스크롤해서 화면에 표시될 카드가 없을 때 발생한다
+ * @name eg.InfiniteGrid#append
+ * @event
+ *
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {Number} param.scrollTop Current vertical position of the scroll bar<ko>현재 스크롤의 y 좌표 값</ko>
+ */
+ this.trigger(this._prefix + EVENTS.append, {
+ scrollTop: scrollTop
+ });
+ }
+ } else {
+ if (this.isRecycling() && this._removedContent > 0) {
+ if ($.isEmptyObject(this._topElement)) {
+ this._topElement = this.getTopElement();
+ if (this._topElement == null) {
+ return;
+ }
+ }
+ ele = this._topElement;
+ rect = ele.getBoundingClientRect();
+ if (rect.bottom >= -this.options.threshold) {
+ /**
+ * This event is fired when a card element must be added at the top of a grid layout because there is no card to be displayed on screen when a user scrolls near top. This event is available only if the isRecycling() method returns true.
+ * @ko 카드가 그리드 레이아웃의 위에 추가돼야 할 때 발생하는 이벤트. 사용자가 위로 스크롤해서 화면에 표시될 카드가 없을 때 발생한다. 이 이벤트는 isRecycling() 메서드의 반환값이 'true'일 때만 발생한다
+ * @name eg.InfiniteGrid#prepend
+ * @event
+ *
+ * @param {Object} param The object of data to be sent to an event<ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {Number} param.scrollTop Current vertical position of the scroll bar<ko>현재 스크롤의 y 좌표 값</ko>
+ */
+ var croppedDistance = this.fit();
+ if (croppedDistance > 0) {
+ scrollTop -= croppedDistance;
+ this.$view.scrollTop(scrollTop);
+ }
+ this.trigger(this._prefix + EVENTS.prepend, {
+ scrollTop: scrollTop
+ });
+ }
+ }
+ }
+ this._prevScrollTop = scrollTop;
+ },
+ _onResize: function() {
+ if (this._resizeTimeout) {
+ clearTimeout(this._resizeTimeout);
+ }
+ var self = this;
+ this._resizeTimeout = setTimeout(function() {
+ self._refreshViewport();
+ (self.$el.innerWidth() !== self._containerWidth) && self.layout(self.items, true);
+ self._resizeTimeout = null;
+ }, 100);
+ },
+ _refreshViewport: function() {
+ this._clientHeight = this.$view.height();
+ },
+ /**
+ * Returns the current state of a module such as location information. You can use the setStatus() method to restore the information returned through a call to this method.
+ * @ko 카드의 위치 정보 등 모듈의 현재 상태 정보를 반환한다. 이 메서드가 반환한 정보를 저장해 두었다가 setStatus() 메서드로 복원할 수 있다
+ * @method eg.InfiniteGrid#getStatue
+ * @return {Object} State object of the eg.InfiniteGrid module<ko>eg.InfiniteGrid 모듈의 상태 객체</ko>
+ */
+ getStatus: function() {
+ var data = {};
+ var p;
+ for (p in this) {
+ if (this.hasOwnProperty(p) && /^_/.test(p) &&
+ typeof this[p] !== "function" && !(this[p] instanceof Element)) {
+ data[p] = this[p];
+ }
+ }
+ return {
+ prop: data,
+ options: $.extend({}, this.options),
+ items: $.map(this.items, function(v) {
+ var clone = $.extend({}, v);
+ delete clone.el;
+ return clone;
+ }),
+ html: this.el.innerHTML,
+ cssText: this.el.style.cssText
+ };
+ },
+ /**
+ * Sets the state of the eg.InfiniteGrid module with the information returned through a call to the getStatue() method.
+ * @ko getStatue() 메서드가 저장한 정보로 eg.InfiniteGrid 모듈의 상태를 설정한다.
+ * @method eg.InfiniteGrid#setStatus
+ * @param {Object} status State object of the eg.InfiniteGrid module <ko>eg.InfiniteGrid 모듈의 상태 객체</ko>
+ * @return {eg.InfiniteGrid} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
+ */
+ setStatus: function(status) {
+ if (!status || !status.cssText || !status.html ||
+ !status.prop || !status.items) {
+ return this;
+ }
+ this.el.style.cssText = status.cssText;
+ this.el.innerHTML = status.html;
+ $.extend(this, status.prop);
+ this._topElement = this._bottomElement = null;
+ this.items = $.map(this.el.children, function(v, i) {
+ status.items[i].el = v;
+ return status.items[i];
+ });
+ return this;
+ },
+ /**
+ * Checks whether a card element is being added.
+ * @ko 카드 엘리먼트 추가가 진행 중인지 확인한다
+ * @method eg.InfiniteGrid#isProcessing
+ * @return {Boolean} Indicates whether a card element is being added <ko>카드 엘리먼트 추가 진행 중 여부</ko>
+ */
+ isProcessing: function() {
+ return this._isProcessing;
+ },
+ /**
+ * Checks whether the total number of added card elements is greater than the value of the count option. Note that the value of the count option is always greater than zero. If it returns true, the number of DOMs won't increase even though card elements are added; instead of adding a new DOM, existing DOMs are recycled to maintain the number of DOMs.
+ * @ko 추가된 카드 엘리먼트의 전체 개수가 count 옵션의 값보다 큰지 확인한다. 단, count 옵션의 값은 0보다 크다. 'true'가 반환되면 카드 엘리먼트가 더 추가돼도 DOM의 개수를 증가하지 않고 기존 DOM을 재활용(recycle)해 DOM의 개수를 일정하게 유지한다
+ * @method eg.InfiniteGrid#isRecycling
+ * @return {Boolean} Indicates whether the total number of added card elements is greater than the value of the count option. <ko>추가된 카드 엘리먼트의 전체 개수가 count 옵션의 값보다 큰지 여부</ko>
+ */
+ isRecycling: function() {
+ return (this.options.count > 0) && this._isRecycling;
+ },
+ /**
+ * Returns the list of group keys which belongs to card elements currently being maintained. You can use the append() or prepend() method to configure group keys so that multiple card elements can be managed at once. If you do not use these methods to configure group keys, it returns undefined as a group key.
+ * @ko 현재 유지하고 있는 카드 엘리먼트의 그룹 키 목록을 반환한다. 여러 개의 카드 엘리먼트를 묶어서 관리할 수 있도록 append() 메서드나 prepend() 메서드에서 그룹 키를 지정할 수 있다. append() 메서드나 prepend() 메서드에서 그룹 키를 지정하지 않았다면 'undefined'가 그룹 키로 반환된다
+ * @method eg.InfiniteGrid#getGroupKeys
+ * @return {Array} List of group keys <ko>그룹 키의 목록</ko>
+ */
+ getGroupKeys: function() {
+ return $.map(this.items, function(v) {
+ return v.groupKey;
+ });
+ },
+ /**
+ * Rearranges a layout.
+ * @ko 레이아웃을 다시 배치한다.
+ * @method eg.InfiniteGrid#layout
+ * @return {eg.InfiniteGrid} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
+ */
+ layout: function(items, isRefresh) {
+ items = items || this.items;
+ isRefresh = typeof isRefresh === "undefined" ? true : isRefresh;
+ this._isProcessing = true;
+ isRefresh && (items = $.map(items, function(v) {
+ v.isAppend = true;
+ return v;
+ }));
+ this._waitResource(items, isRefresh);
+ return this;
+ },
+ _layoutItems: function(items) {
+ var self = this;
+
+ // for performance
+ $.each(
+ $.map(items, function(v) {
+ v.position = self._getItemLayoutPosition(v);
+ return v;
+ }),
+ function(i, v) {
+ if (v.el) {
+ var style = v.el.style;
+ style.left = v.position.x + "px";
+ style.top = v.position.y + "px";
+ }
+ });
+ },
+ /**
+ * Adds a card element at the bottom of a grid layout. This method is available only if the isProcessing() method returns false.
+ * @ko 카드 엘리먼트를 그리드 레이아웃의 아래에 추가한다. isProcessing() 메서드의 반환값이 'false'일 때만 이 메서드를 사용할 수 있다
+ * 이 메소드는 isProcessing()의 반환값이 false일 경우에만 사용 가능하다.
+ * @method eg.InfiniteGrid#append
+ * @param {Array|String|jQuery} elements Array of the card elements to be added <ko>추가할 카드 엘리먼트의 배열</ko>
+ * @param {Number|String} [groupKey] The group key to be configured in a card element. It is set to "undefined" by default.<ko>추가할 카드 엘리먼트에 설정할 그룹 키. 생략하면 값이 'undefined'로 설정된다</ko>
+ * @return {Number} The number of added card elements <ko>추가된 카드 엘리먼트의 개수</ko>
+ */
+ append: function($elements, groupKey) {
+ if (this._isProcessing || $elements.length === 0) {
+ return;
+ }
+
+ // convert jQuery instance
+ $elements = $($elements);
+ this._isProcessing = true;
+ if (!this.isRecycling()) {
+ this._isRecycling =
+ (this.items.length + $elements.length) >= this.options.count;
+ }
+ this._insert($elements, groupKey, true);
+ return $elements.length;
+ },
+ /**
+ * Adds a card element at the top of a grid layout. This method is available only if the isProcessing() method returns false and the isRecycling() method returns true.
+ * @ko 카드 엘리먼트를 그리드 레이아웃의 위에 추가한다. isProcessing() 메서드의 반환값이 'false'이고, isRecycling() 메서드의 반환값이 'true'일 때만 이 메서드를 사용할 수 있다
+ * @method eg.InfiniteGrid#prepend
+ * @param {Array|String|jQuery} elements Array of the card elements to be added <ko>추가할 카드 엘리먼트 배열</ko>
+ * @param {Number|String} [groupKey] The group key to be configured in a card element. It is set to "undefined" by default.<ko>추가할 카드 엘리먼트에 설정할 그룹 키. 생략하면 값이 'undefined'로 설정된다</ko>
+ * @return {Number} The number of added card elements <ko>추가된 카드 엘리먼트의 개수</ko>
+ */
+ prepend: function($elements, groupKey) {
+ if (!this.isRecycling() || this._removedContent === 0 ||
+ this._isProcessing || $elements.length === 0) {
+ return;
+ }
+
+ // convert jQuery instance
+ $elements = $($elements);
+
+ this._isProcessing = true;
+ this._fit();
+ if ($elements.length > this._removedContent) {
+ $elements = $elements.slice(0, this._removedContent);
+ }
+ this._insert($elements, groupKey, false);
+ return $elements.length;
+ },
+ /**
+ * Clears added card elements and data.
+ * @ko 추가된 카드 엘리먼트와 데이터를 모두 지운다.
+ * @method eg.InfiniteGrid#clear
+ * @return {eg.InfiniteGrid} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
+ */
+ clear: function() {
+ this.el.innerHTML = "";
+ this.el.style.height = "";
+ this._reset();
+ return this;
+ },
+
+ _getTopItem: function() {
+ var item = null;
+ var min = Infinity;
+ $.each(this._getColItems(false), function(i, v) {
+ if (v && v.position.y < min) {
+ min = v.position.y;
+ item = v;
+ }
+ });
+ return item;
+ },
+
+ /**
+ * Returns a card element at the top of a layout.
+ * @ko 레이아웃의 맨 위에 있는 카드 엘리먼트를 반환한다.
+ * @method eg.InfiniteGrid#getTopElement
+ *
+ * @return {HTMLElement} Card element at the top of a layout <ko>레이아웃의 맨 위에 있는 카드 엘리먼트</ko>
+ */
+ getTopElement: function() {
+ var item = this._getTopItem();
+ return item && item.el;
+ },
+
+ _getBottomItem: function() {
+ var item = null;
+ var max = -Infinity;
+ $.each(this._getColItems(true), function(i, v) {
+ if (v && v.position.y + v.size.height > max) {
+ max = v.position.y + v.size.height;
+ item = v;
+ }
+ });
+ return item;
+ },
+
+ /**
+ * Returns a card element at the bottom of a layout.
+ * @ko 레이아웃의 맨 아래에 있는 카드 엘리먼트를 반환한다.
+ * @method eg.InfiniteGrid#getBottomElement
+ *
+ * @return {HTMLElement} Card element at the bottom of a layout <ko>레이아웃의 맨 아래에 있는 카드 엘리먼트</ko>
+ */
+ getBottomElement: function() {
+ var item = this._getBottomItem();
+ return item && item.el;
+ },
+
+ _postLayout: function(items) {
+ if (!this._isProcessing || items.length <= 0) {
+ return;
+ }
+
+ var size = this._getContainerSize();
+ this.el.style.width = size.width + "px";
+ this.el.style.height = size.height + "px";
+
+ // refresh element
+ this._topElement = this.getTopElement();
+ this._bottomElement = this.getBottomElement();
+
+ var distance = 0;
+ var isAppend = items[0].isAppend;
+ if (!isAppend) {
+ this._isFitted = false;
+ this._fit(true);
+ distance = items.length >= this.items.length ?
+ 0 : this.items[items.length].position.y;
+ if (distance > 0) {
+ this._prevScrollTop = this._getScrollTop() + distance;
+ this.$view.scrollTop(this._prevScrollTop);
+ }
+ }
+
+ // reset flags
+ this._isProcessing = false;
+
+ /**
+ * This event is fired when layout is successfully arranged through a call to the append(), prepend(), or layout() method.
+ * @ko 레이아웃 배치가 완료됐을 때 발생하는 이벤트. append() 메서드나 prepend() 메서드, layout() 메서드 호출 후 카드의 배치가 완료됐을 때 발생한다
+ * @name eg.InfiniteGrid#layoutComplete
+ * @event
+ *
+ * @param {Object} param The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
+ * @param {Array} param.target Rearranged card elements<ko>재배치된 카드 엘리먼트들</ko>
+ * @param {Boolean} param.isAppend Checks whether the append() method is used to add a card element. It returns true even though the layoutComplete event is fired after the layout() method is called. <ko>카드 엘리먼트가 append() 메서드로 추가됐는지 확인한다. layout() 메서드가 호출된 후 layoutComplete 이벤트가 발생해도 'true'를 반환한다.</ko>
+ * @param {Number} param.distance Distance the card element at the top of a grid layout has moved after the layoutComplete event is fired. In other words, it is the same as an increased height with a new card element added using the prepend() method <ko>그리드 레이아웃의 맨 위에 있던 카드 엘리먼트가 layoutComplete 이벤트 발생 후 이동한 거리. 즉, prepend() 메서드로 카드 엘리먼트가 추가돼 늘어난 높이다.</ko>
+ * @param {Number} param.croppedCount The number of deleted card elements to maintain the number of DOMs<ko>일정한 DOM 개수를 유지하기 위해, 삭제한 카드 엘리먼트들의 개수</ko>
+ */
+ this.trigger(this._prefix + EVENTS.layoutComplete, {
+ target: items.concat(),
+ isAppend: isAppend,
+ distance: distance,
+ croppedCount: this._removedContent
+ });
+ },
+
+ // $elements => $([HTMLElement, HTMLElement, ...])
+ _insert: function($elements, groupKey, isAppend) {
+ if ($elements.length === 0) {
+ return;
+ }
+ var elements = $elements.toArray();
+ var $cloneElements = $(elements);
+ var dummy = -this._clientHeight + "px";
+ $.each(elements, function(i, v) {
+ v.style.position = "absolute";
+ v.style.top = dummy;
+ });
+ var items = this._itemize(elements, groupKey, isAppend);
+ if (isAppend) {
+ this.items = this.items.concat(items);
+ } else {
+ this.items = items.concat(this.items);
+ items = items.reverse();
+ }
+ this.isRecycling() && this._adjustRange(isAppend, $cloneElements);
+
+ this.$el[isAppend ? "append" : "prepend"]($cloneElements);
+ this.layout(items, false);
+ },
+ _waitResource: function(items, isRefresh) {
+ var needCheck = this._checkImageLoaded();
+ var self = this;
+ var callback = function() {
+ if (self._isProcessing) {
+ if (isRefresh || !self._appendCols.length) {
+ $.each(items, function(i, v) {
+ v.el.style.position = "absolute";
+ });
+ self._measureColumns();
+ }
+ self._layoutItems(items);
+ self._postLayout(items);
+ }
+ };
+ if (needCheck.length > 0) {
+ this._waitImageLoaded(needCheck, callback);
+ } else {
+ // convert to async
+ setTimeout(function() {
+ callback && callback();
+ }, 0);
+ }
+ },
+ _adjustRange: function (isTop, $elements) {
+ var diff = this.items.length - this.options.count;
+ var targets;
+ var idx;
+ if (diff <= 0 || (idx = this._getDelimiterIndex(isTop, diff)) < 0) {
+ return;
+ }
+ if (isTop) {
+ targets = this.items.splice(0, idx);
+ this._isFitted = false;
+ } else {
+ targets = this.items.splice(idx, this.items.length - idx);
+ }
+
+ // @todo improve performance
+ $.each(targets, function(i, v) {
+ idx = $elements.index(v.el);
+ if (idx !== -1) {
+ $elements.splice(idx, 1);
+ } else {
+ v.el.parentNode.removeChild(v.el);
+ }
+ });
+ this._removedContent += isTop ? targets.length : -targets.length;
+ },
+ _getDelimiterIndex: function(isTop, removeCount) {
+ var len = this.items.length;
+ var i;
+ var idx = 0;
+ var baseIdx = isTop ? removeCount - 1 : len - removeCount;
+ var targetIdx = baseIdx + (isTop ? 1 : -1);
+ var groupKey = this.items[baseIdx].groupKey;
+ if (groupKey != null && groupKey === this.items[targetIdx].groupKey) {
+ if (isTop) {
+ for (i = baseIdx; i > 0; i--) {
+ if (groupKey !== this.items[i].groupKey) {
+ break;
+ }
+ }
+ idx = i === 0 ? -1 : i + 1;
+ } else {
+ for (i = baseIdx; i < len; i++) {
+ if (groupKey !== this.items[i].groupKey) {
+ break;
+ }
+ }
+ idx = i === len ? -1 : i;
+ }
+ } else {
+ idx = isTop ? targetIdx : baseIdx;
+ }
+ return idx;
+ },
+
+ // fit size
+ _fit: function(applyDom) {
+ // for caching
+ if (this.options.count <= 0) {
+ this._fit = function() {
+ return false;
+ };
+ this._isFitted = true;
+ return false;
+ }
+
+ if (this._isFitted) {
+ return false;
+ }
+ var y = this._updateCols(); // for prepend
+ $.each(this.items, function(i, v) {
+ v.position.y -= y;
+ applyDom && (v.el.style.top = v.position.y + "px");
+ });
+ this._updateCols(true); // for append
+ var height = this._getContainerSize().height;
+ applyDom && (this.el.style.height = height + "px");
+ this._isFitted = true;
+ return true;
+ },
+
+ /**
+ * Removes extra space caused by adding card elements.
+ * @ko 카드 엘리먼트를 추가한 다음 생긴 빈 공간을 제거한다
+ * @method eg.InfiniteGrid#fit
+ * @deprecated since version 1.3.0
+ * @return {Number} Actual length of space removed (unit: px) <ko>빈 공간이 제거된 실제 길이(단위: px)</ko>
+ */
+ fit: function() {
+ var item = this._getTopItem();
+ var distance = item ? item.position.y : 0;
+ this._fit(true);
+ return distance;
+ },
+ _reset: function() {
+ this._isProcessing = false;
+ this._topElement = null;
+ this._bottomElement = null;
+ this._isFitted = true;
+ this._isRecycling = false;
+ this._removedContent = 0;
+ this._prevScrollTop = 0;
+ this._equalItemSize = 0;
+ this._resizeTimeout = null;
+ this._resetCols(this._appendCols.length || 0);
+ this.items = [];
+ },
+ _checkImageLoaded: function() {
+ return this.$el.find("img").filter(function(k, v) {
+ if (v.nodeType && ($.inArray(v.nodeType, [1,9,11]) !== -1)) {
+ return !v.complete;
+ }
+ }).toArray();
+ },
+ _waitImageLoaded: function(needCheck, callback) {
+ var checkCount = needCheck.length;
+ var onCheck = function(e) {
+ checkCount--;
+ $(e.target).off("load error");
+ checkCount <= 0 && callback && callback();
+ };
+ var $el;
+ var self = this;
+ $.each(needCheck, function(i, v) {
+ $el = $(v);
+
+ // for IE10 lower
+ if (self._isIE10lower) {
+ var url = v.getAttribute("src");
+ v.setAttribute("src", "");
+ v.setAttribute("src", url);
+ }
+ $el.on("load error", onCheck);
+ });
+ },
+ _measureColumns: function() {
+ this.el.style.width = null;
+ this._containerWidth = this.$el.innerWidth();
+ this._columnWidth = this._getColumnWidth() || this._containerWidth;
+ var cols = this._containerWidth / this._columnWidth;
+ var excess = this._columnWidth - this._containerWidth % this._columnWidth;
+
+ // if overshoot is less than a pixel, round up, otherwise floor it
+ cols = Math.max(Math[ excess && excess <= 1 ? "round" : "floor" ](cols), 1);
+
+ // reset column Y
+ this._resetCols(cols || 0);
+ },
+ _resetCols: function(count) {
+ count = typeof count === "undefined" ? 0 : count;
+ var arr = [];
+ while (count--) {
+ arr.push(0);
+ }
+ this._appendCols = arr.concat();
+ this._prependCols = arr.concat();
+ },
+ _getContainerSize: function() {
+ return {
+ height: Math.max.apply(Math, this._appendCols),
+ width: this._containerWidth
+ };
+ },
+ _getColumnWidth: function() {
+ var el = this.items[0] && this.items[0].el;
+ var width = 0;
+ if (el) {
+ var $el = $(el);
+ width = $el.innerWidth();
+ if (this.options.isEqualSize) {
+ this._equalItemSize = {
+ width: width,
+ height: $el.innerHeight()
+ };
+ }
+ }
+ return width;
+ },
+ _updateCols: function(isAppend) {
+ var col = isAppend ? this._appendCols : this._prependCols;
+ var items = this._getColItems(isAppend);
+ var base = this._isFitted || isAppend ? 0 : this._getMinY(items);
+ var i = 0;
+ var len = col.length;
+ var item;
+ for (; i < len; i++) {
+ if (item = items[i]) {
+ col[i] = item.position.y + (isAppend ? item.size.height : -base);
+ } else {
+ col[i] = 0;
+ }
+ }
+ return base;
+ },
+ _getMinY: function(items) {
+ return Math.min.apply(Math, $.map(items, function(v) {
+ return v ? v.position.y : 0;
+ }));
+ },
+ _getColIdx: function(item) {
+ return parseInt(item.position.x / parseInt(this._columnWidth, 10), 10);
+ },
+ _getColItems: function(isTail) {
+ var len = this._appendCols.length;
+ var colItems = new Array(len);
+ var item;
+ var idx;
+ var count = 0;
+ var i = isTail ? this.items.length - 1 : 0;
+ while (item = this.items[i]) {
+ idx = this._getColIdx(item);
+ if (!colItems[idx]) {
+ colItems[idx] = item;
+ if (++count === len) {
+ return colItems;
+ }
+ }
+ i += isTail ? -1 : 1;
+ }
+ return colItems;
+ },
+ _itemize: function(elements, groupKey, isAppend) {
+ return $.map(elements, function(v) {
+ return {
+ el: v,
+ position: {
+ x: 0,
+ y: 0
+ },
+ isAppend: typeof isAppend === "undefined" ? true : isAppend,
+ groupKey: typeof groupKey === "undefined" ? null : groupKey
+ };
+ });
+ },
+ _getItemLayoutPosition: function(item) {
+ if (!item.el) {
+ return;
+ }
+ var $el = $(item.el);
+ item.size = this._equalItemSize || {
+ width: $el.innerWidth(),
+ height: $el.innerHeight()
+ };
+ var isAppend = item.isAppend;
+ var cols = isAppend ? this._appendCols : this._prependCols;
+ var y = Math[isAppend ? "min" : "max"].apply(Math, cols);
+ var shortColIndex;
+ if (isAppend) {
+ shortColIndex = $.inArray(y, cols);
+ } else {
+ var i = cols.length;
+ while (i-- >= 0) {
+ if (cols[i] === y) {
+ shortColIndex = i;
+ break;
+ }
+ }
+ }
+ cols[shortColIndex] = y + (isAppend ? item.size.height : -item.size.height);
+
+ return {
+ x: this._columnWidth * shortColIndex,
+ y: isAppend ? y : y - item.size.height
+ };
+ },
+ /**
+ * Destroys elements, properties, and events used on a grid layout.
+ * @ko 그리드 레이아웃에 사용한 엘리먼트와 속성, 이벤트를 해제한다
+ * @method eg.InfiniteGrid#destroy
+ */
+ destroy: function() {
+ this.off();
+ this.$view.off("resize", this._onResize)
+ .off("scroll", this._onScroll);
+ this._reset();
+ }
+ });
+});
+/**
+ * A jQuery plugin available in the eg.InfiniteGrid module.
+ * @ko eg.InfiniteGrid 모듈의 jQuery 플러그인
+ * @method jQuery.infiniteGrid
+ * @example
+ <ul id="grid">
+ <li class="item">
+ <div>test1</div>
+ </li>
+ <li class="item">
+ <div>test3</div>
+ </li>
+ </ul>
+ <script>
+ // create
+ $("#grid").infiniteGrid();
+ // method
+ $("#grid").infiniteGrid("option","count","60"); //Set option
+ $("#grid").infiniteGrid("instance"); // Return infiniteGrid instance
+ $("#grid").infiniteGrid("getBottomElement"); // Get bottom element
+ </script>
+ * @see eg.InfiniteGrid
+ */
+/**
+ * A jQuery custom event of the eg.InfiniteGrid module. This event is fired when a layout is successfully arranged.
+ *
+ * @ko eg.InfiniteGrid 모듈의 jQuery 커스텀 이벤트. 레이아웃 배치가 완료됐을 때 발생한다
+ * @name jQuery#infiniteGrid:layoutComplete
+ * @event
+ * @example
+ <ul id="grid">
+ <li class="item">
+ <div>test1</div>
+ </li>
+ <li class="item">
+ <div>test3</div>
+ </li>
+ </ul>
+ <script>
+ // create
+ $("#grid").infiniteGrid();
+ // event
+ $("#grid").on("infiniteGrid:layoutComplete",callback);
+ $("#grid").off("infiniteGrid:layoutComplete",callback);
+ $("#grid").trigger("infiniteGrid:layoutComplete",callback);
+ </script>
+ * @see eg.InfiniteGrid#event:layoutComplete
+ */
+/**
+ * A jQuery custom event of the eg.InfiniteGrid module. This event is fired when a card element must be added at the bottom of a grid layout
+ *
+ * @ko eg.InfiniteGrid 모듈의 jQuery 커스텀 이벤트. 그리드 레이아웃 아래에 카드 엘리먼트가 추가돼야 할 때 발생한다.
+ * @name jQuery#infiniteGrid:append
+ * @event
+ * @example
+ <ul id="grid">
+ <li class="item">
+ <div>test1</div>
+ </li>
+ <li class="item">
+ <div>test3</div>
+ </li>
+ </ul>
+ <script>
+ // create
+ $("#grid").infiniteGrid();
+ // event
+ $("#grid").on("infiniteGrid:append",callback);
+ $("#grid").off("infiniteGrid:append",callback);
+ $("#grid").trigger("infiniteGrid:append",callback);
+ </script>
+ * @see eg.InfiniteGrid#event:append
+ */
+/**
+ * A jQuery custom event of the eg.InfiniteGrid module. This event is fired when a card element must be added at the top of a grid layout
+ *
+ * @ko eg.InfiniteGrid 모듈의 jQuery 커스텀 이벤트. 그리드 레이아웃 위에 카드 엘리먼트가 추가돼야 할 때 발생한다
+ * @name jQuery#infiniteGrid:prepend
+ * @event
+ * @example
+ <ul id="grid">
+ <li class="item">
+ <div>test1</div>
+ </li>
+ <li class="item">
+ <div>test3</div>
+ </li>
+ </ul>
+ <script>
+ // create
+ $("#grid").infiniteGrid();
+ // event
+ $("#grid").on("infiniteGrid:prepend",callback);
+ $("#grid").off("infiniteGrid:prepend",callback);
+ $("#grid").trigger("infiniteGrid:prepend",callback);
+ </script>
+ * @see eg.InfiniteGrid#event:prepend
+ */
+
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.
+
+
+
+
+
eg.Flicking 모듈의 jQuery 커스텀 이벤트. 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원되기 전에 발생한다
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.
+
+
+
+
+
eg.Flicking 모듈의 jQuery 커스텀 이벤트. 다음 패널로 바뀌는 기준 이동 거리만큼 이동하기 전에 사용자의 동작이 끝났을 때 원래 패널로 복원된 다음 발생한다
A jQuery custom event of the eg.Visible module. This event is fired when the event is compared with the last verified one and there is an element of which the visibility property has changed.
+
+
+
+
+
eg.Visible 모듈의 jQuery 커스텀 이벤트. 마지막으로 확인한 결과와 비교해 visibility 속성이 변경된 엘리먼트가 있을 때 발생한다
/**
+* Copyright (c) 2015 NAVER Corp.
+* egjs projects are licensed under the MIT license
+*/
+
+eg.module("visible", ["jQuery", eg, document], function($, ns, doc) {
+ "use strict";
+
+ /**
+ * A module used to check whether an element is visible in the base element or viewport.
+ * @ko 엘리먼트가 기준 엘리먼트나 뷰포트 안에 보이는지 확인하는 모듈
+ * @class
+ * @name eg.Visible
+ * @extends eg.Component
+ * @group egjs
+ *
+ * @param {HTMLElement|String|jQuery} [element=document] A base element that detects if another element is visible<ko>엘리먼트가 보이는 기준 엘리먼트</ko>
+ * @param {Object} options The option object of the eg.Visible module<ko>eg.Visible 모듈의 옵션 객체</ko>
+ * @param {String} [options.targetClass="check_visible"] The class name of the element to be checked<ko>보이는지 확인할 엘리먼트의 클래스 이름</ko>
+ * @param {Number} [options.expandSize=0] The size of the expanded area to be checked whether an element is visible. If this value is less than zero, the size of the area is smaller than that of the base element. <ko>기준 엘리먼트의 경계를 넘어 엘리먼트가 보이는지 확인할 영역의 크기. 값이 0보다 작으면 엘리먼트가 보이는지 확인할 영역의 크기가 기준 엘리먼트보다 작아진다</ko>
+ * @support {"ie": "7+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.1+ (except 3.x)"}
+ *
+ * @codepen {"id":"WbWzqq", "ko":"Visible 기본 예제", "en":"Visible basic example", "collectionId":"Ayrabj", "height" : 403}
+ */
+ var EVENTS = {
+ "change": "change"
+ };
+ ns.Visible = ns.Class.extend(ns.Component, {
+ _events: function() {
+ return EVENTS;
+ },
+ construct: function(element, options, _prefix) {
+ this._prefix = _prefix || "";
+ this.options = {
+ targetClass: "check_visible",
+ expandSize: 0
+ };
+ $.extend(this.options, options);
+
+ this._wrapper = $(element)[0] || doc;
+
+ // this._wrapper is Element, or may be Window
+ if (this._wrapper.nodeType && this._wrapper.nodeType === 1) {
+ this._getAreaRect = this._getWrapperRect;
+ } else {
+ this._getAreaRect = this._getWindowRect;
+ }
+
+ this._targets = [];
+ this._timer = null;
+ this._supportElementsByClassName = (function() {
+ var dummy = doc.createElement("div");
+ var dummies;
+ if (!dummy.getElementsByClassName) {
+ return false;
+ }
+ dummies = dummy.getElementsByClassName("dummy");
+ dummy.innerHTML = "<span class='dummy'></span>";
+ return dummies.length === 1;
+ })();
+
+ this.refresh();
+ },
+ /**
+ * Updates the list of elements where the visibility property is to be checked
+ * @ko visibility 속성을 검사할 엘리먼트의 목록을 갱신한다
+ * @method eg.Visible#refresh
+ * @return {eg.Visible} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
+ *
+ * @remark
+ * If targets was added or removed from DOM tree, must call refresh method to update internal target list.
+ * <ko>확인 대상이 영역 안에 추가되거나 삭제된 경우, 모듈내부에서 사용하는 확인 대상 목록을 이 메소드를 호출하여 갱신해야한다.<ko>
+ */
+ refresh: function() {
+ if (this._supportElementsByClassName) {
+ this._targets = this._wrapper
+ .getElementsByClassName(this.options.targetClass);
+ this.refresh = function() {
+ return this;
+ };
+ } else {
+ this.refresh = function() {
+ this._targets = $(this._wrapper)
+ .find("." + this.options.targetClass)
+ .get();
+ return this;
+ };
+ }
+ return this.refresh();
+ },
+ /**
+ * Checks whether the visibility property of the target elements has changed. The change event is fired when the property has changed.
+ * @ko 보이는지 확인할 대상 엘리먼트 목록의 visibility 속성이 변경됐는지 확인한다. 속성이 변경됐으면 change 이벤트가 발생한다
+ * @method eg.Visible#check
+ * @param {Number} [delay=-1] Delay time. It is used to check the property after a method is called and a period of time has passed.<ko>속성 확인 지연 시간. 메서드를 호출하고 일정 시간이 지난 후에 속성을 확인할 때 사용한다</ko>
+ * @return {eg.Visible} An instance of a module itself<ko>모듈 자신의 인스턴스</ko>
+ */
+ check: function(delay) {
+ if (typeof delay === "undefined") {
+ delay = -1;
+ }
+ clearTimeout(this._timer);
+ if (delay < 0) {
+ this._check();
+ } else {
+ this._timer = setTimeout($.proxy(function() {
+ this._check();
+ this._timer = null;
+ }, this), delay);
+ }
+ return this;
+ },
+ _getWrapperRect: function() {
+ return this._wrapper.getBoundingClientRect();
+ },
+ _getWindowRect: function() {
+ // [IE7] document.documentElement.clientHeight has always value 0 (bug)
+ return {
+ top: 0,
+ left: 0,
+ bottom: doc.documentElement.clientHeight ||
+ doc.body.clientHeight,
+ right: doc.documentElement.clientWidth ||
+ doc.body.clientWidth
+ };
+ },
+ _reviseElements: function(target, i) {
+ if (this._supportElementsByClassName) {
+ this._reviseElements = function() {
+ return true;
+ };
+ } else {
+ this._reviseElements = function(target, i) {
+ if (!$(target).hasClass(this.options.targetClass)) {
+ target.__VISIBLE__ = null;
+ this._targets.splice(i, 1);
+ return false;
+ }
+ return true;
+ };
+ }
+ return this._reviseElements(target, i);
+ },
+ _check: function() {
+ var expandSize = parseInt(this.options.expandSize, 10);
+ var visibles = [];
+ var invisibles = [];
+ var area = this._getAreaRect();
+
+ // Error Fix: Cannot set property top of #<ClientRect> which has only a getter
+ area = $.extend({}, area);
+
+ area.top -= expandSize;
+ area.left -= expandSize;
+ area.bottom += expandSize;
+ area.right += expandSize;
+ for (var i = this._targets.length - 1, target, targetArea, after, before;
+ target = this._targets[i] ; i--) {
+ targetArea = target.getBoundingClientRect();
+ if (targetArea.width === 0 && targetArea.height === 0) {
+ continue;
+ }
+ if (this._reviseElements(target, i)) {
+ before = !!target.__VISIBLE__;
+ target.__VISIBLE__ = after = !(
+ targetArea.bottom < area.top ||
+ area.bottom < targetArea.top ||
+ targetArea.right < area.left ||
+ area.right < targetArea.left
+ );
+ (before !== after) && (after ? visibles : invisibles).unshift(target);
+ }
+ }
+ /**
+ * This event is fired when the event is compared with the last verified one and there is an element of which the visibility property has changed.
+ * @ko 마지막으로 확인한 결과와 비교해 visibility 속성이 변경된 엘리먼트가 있을 때 발생하는 이벤트
+ * @name eg.Visible#change
+ * @event
+ * @param {Array} visible Visible elements (the element type is `HTMLElement`) <ko>보이게 된 엘리먼트들</ko>
+ * @param {Array} invisible Invisible elements (the element type is `HTMLElement`) <ko>안 보이게 된 엘리먼트들</ko>
+ */
+ this.trigger(this._prefix + EVENTS.change, {
+ visible: visibles,
+ invisible: invisibles
+ });
+ },
+ destroy: function() {
+ this.off();
+ this._targets = [];
+ this._wrapper = this._timer = null;
+ }
+ });
+});
+/**
+ * A jQuery custom event of the eg.Visible module. This event is fired when the event is compared with the last verified one and there is an element of which the visibility property has changed.
+ *
+ * @ko eg.Visible 모듈의 jQuery 커스텀 이벤트. 마지막으로 확인한 결과와 비교해 visibility 속성이 변경된 엘리먼트가 있을 때 발생한다
+ * @name jQuery#visible:change
+ * @event
+ * @example
+ // create
+ $("body").visible();
+
+ // event
+ $("body").on("visible:change",callback);
+ $("body").off("visible:change",callback);
+ $("body").trigger("visible:change",callback);
+ * @see eg.Visble
+ */
+/**
+ * A jQuery plugin available in the eg.Visible module.
+ * @ko eg.Visible 모듈의 jQuery 플러그인
+ * @method jQuery.visible
+ * @example
+ // create
+ $("body").visible();
+
+ // event
+ $("body").on("visible:change",callback);
+ $("body").off("visible:change",callback);
+ $("body").trigger("visible:change",callback);
+
+ // method
+ $("body").visible("option","circular",true); //Set option
+ $("body").visible("instance"); // Return flicking instance
+ $("body").visible("check",10); // Check to change target elements.
+ * @see eg.Visble#event:change
+ */
+