unidragger.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*!
  2. * Unidragger v2.3.1
  3. * Draggable base class
  4. * MIT license
  5. */
  6. /*jshint browser: true, unused: true, undef: true, strict: true */
  7. ( function( window, factory ) {
  8. // universal module definition
  9. /*jshint strict: false */ /*globals define, module, require */
  10. if ( typeof define == 'function' && define.amd ) {
  11. // AMD
  12. define( [
  13. 'unipointer/unipointer'
  14. ], function( Unipointer ) {
  15. return factory( window, Unipointer );
  16. });
  17. } else if ( typeof module == 'object' && module.exports ) {
  18. // CommonJS
  19. module.exports = factory(
  20. window,
  21. require('unipointer')
  22. );
  23. } else {
  24. // browser global
  25. window.Unidragger = factory(
  26. window,
  27. window.Unipointer
  28. );
  29. }
  30. }( window, function factory( window, Unipointer ) {
  31. 'use strict';
  32. // -------------------------- Unidragger -------------------------- //
  33. function Unidragger() {}
  34. // inherit Unipointer & EvEmitter
  35. var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
  36. // ----- bind start ----- //
  37. proto.bindHandles = function() {
  38. this._bindHandles( true );
  39. };
  40. proto.unbindHandles = function() {
  41. this._bindHandles( false );
  42. };
  43. /**
  44. * Add or remove start event
  45. * @param {Boolean} isAdd
  46. */
  47. proto._bindHandles = function( isAdd ) {
  48. // munge isAdd, default to true
  49. isAdd = isAdd === undefined ? true : isAdd;
  50. // bind each handle
  51. var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
  52. var touchAction = isAdd ? this._touchActionValue : '';
  53. for ( var i=0; i < this.handles.length; i++ ) {
  54. var handle = this.handles[i];
  55. this._bindStartEvent( handle, isAdd );
  56. handle[ bindMethod ]( 'click', this );
  57. // touch-action: none to override browser touch gestures. metafizzy/flickity#540
  58. if ( window.PointerEvent ) {
  59. handle.style.touchAction = touchAction;
  60. }
  61. }
  62. };
  63. // prototype so it can be overwriteable by Flickity
  64. proto._touchActionValue = 'none';
  65. // ----- start event ----- //
  66. /**
  67. * pointer start
  68. * @param {Event} event
  69. * @param {Event or Touch} pointer
  70. */
  71. proto.pointerDown = function( event, pointer ) {
  72. var isOkay = this.okayPointerDown( event );
  73. if ( !isOkay ) {
  74. return;
  75. }
  76. // track start event position
  77. // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
  78. this.pointerDownPointer = {
  79. pageX: pointer.pageX,
  80. pageY: pointer.pageY,
  81. };
  82. event.preventDefault();
  83. this.pointerDownBlur();
  84. // bind move and end events
  85. this._bindPostStartEvents( event );
  86. this.emitEvent( 'pointerDown', [ event, pointer ] );
  87. };
  88. // nodes that have text fields
  89. var cursorNodes = {
  90. TEXTAREA: true,
  91. INPUT: true,
  92. SELECT: true,
  93. OPTION: true,
  94. };
  95. // input types that do not have text fields
  96. var clickTypes = {
  97. radio: true,
  98. checkbox: true,
  99. button: true,
  100. submit: true,
  101. image: true,
  102. file: true,
  103. };
  104. // dismiss inputs with text fields. flickity#403, flickity#404
  105. proto.okayPointerDown = function( event ) {
  106. var isCursorNode = cursorNodes[ event.target.nodeName ];
  107. var isClickType = clickTypes[ event.target.type ];
  108. var isOkay = !isCursorNode || isClickType;
  109. if ( !isOkay ) {
  110. this._pointerReset();
  111. }
  112. return isOkay;
  113. };
  114. // kludge to blur previously focused input
  115. proto.pointerDownBlur = function() {
  116. var focused = document.activeElement;
  117. // do not blur body for IE10, metafizzy/flickity#117
  118. var canBlur = focused && focused.blur && focused != document.body;
  119. if ( canBlur ) {
  120. focused.blur();
  121. }
  122. };
  123. // ----- move event ----- //
  124. /**
  125. * drag move
  126. * @param {Event} event
  127. * @param {Event or Touch} pointer
  128. */
  129. proto.pointerMove = function( event, pointer ) {
  130. var moveVector = this._dragPointerMove( event, pointer );
  131. this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
  132. this._dragMove( event, pointer, moveVector );
  133. };
  134. // base pointer move logic
  135. proto._dragPointerMove = function( event, pointer ) {
  136. var moveVector = {
  137. x: pointer.pageX - this.pointerDownPointer.pageX,
  138. y: pointer.pageY - this.pointerDownPointer.pageY
  139. };
  140. // start drag if pointer has moved far enough to start drag
  141. if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
  142. this._dragStart( event, pointer );
  143. }
  144. return moveVector;
  145. };
  146. // condition if pointer has moved far enough to start drag
  147. proto.hasDragStarted = function( moveVector ) {
  148. return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
  149. };
  150. // ----- end event ----- //
  151. /**
  152. * pointer up
  153. * @param {Event} event
  154. * @param {Event or Touch} pointer
  155. */
  156. proto.pointerUp = function( event, pointer ) {
  157. this.emitEvent( 'pointerUp', [ event, pointer ] );
  158. this._dragPointerUp( event, pointer );
  159. };
  160. proto._dragPointerUp = function( event, pointer ) {
  161. if ( this.isDragging ) {
  162. this._dragEnd( event, pointer );
  163. } else {
  164. // pointer didn't move enough for drag to start
  165. this._staticClick( event, pointer );
  166. }
  167. };
  168. // -------------------------- drag -------------------------- //
  169. // dragStart
  170. proto._dragStart = function( event, pointer ) {
  171. this.isDragging = true;
  172. // prevent clicks
  173. this.isPreventingClicks = true;
  174. this.dragStart( event, pointer );
  175. };
  176. proto.dragStart = function( event, pointer ) {
  177. this.emitEvent( 'dragStart', [ event, pointer ] );
  178. };
  179. // dragMove
  180. proto._dragMove = function( event, pointer, moveVector ) {
  181. // do not drag if not dragging yet
  182. if ( !this.isDragging ) {
  183. return;
  184. }
  185. this.dragMove( event, pointer, moveVector );
  186. };
  187. proto.dragMove = function( event, pointer, moveVector ) {
  188. event.preventDefault();
  189. this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
  190. };
  191. // dragEnd
  192. proto._dragEnd = function( event, pointer ) {
  193. // set flags
  194. this.isDragging = false;
  195. // re-enable clicking async
  196. setTimeout( function() {
  197. delete this.isPreventingClicks;
  198. }.bind( this ) );
  199. this.dragEnd( event, pointer );
  200. };
  201. proto.dragEnd = function( event, pointer ) {
  202. this.emitEvent( 'dragEnd', [ event, pointer ] );
  203. };
  204. // ----- onclick ----- //
  205. // handle all clicks and prevent clicks when dragging
  206. proto.onclick = function( event ) {
  207. if ( this.isPreventingClicks ) {
  208. event.preventDefault();
  209. }
  210. };
  211. // ----- staticClick ----- //
  212. // triggered after pointer down & up with no/tiny movement
  213. proto._staticClick = function( event, pointer ) {
  214. // ignore emulated mouse up clicks
  215. if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
  216. return;
  217. }
  218. this.staticClick( event, pointer );
  219. // set flag for emulated clicks 300ms after touchend
  220. if ( event.type != 'mouseup' ) {
  221. this.isIgnoringMouseUp = true;
  222. // reset flag after 300ms
  223. setTimeout( function() {
  224. delete this.isIgnoringMouseUp;
  225. }.bind( this ), 400 );
  226. }
  227. };
  228. proto.staticClick = function( event, pointer ) {
  229. this.emitEvent( 'staticClick', [ event, pointer ] );
  230. };
  231. // ----- utils ----- //
  232. Unidragger.getPointerPoint = Unipointer.getPointerPoint;
  233. // ----- ----- //
  234. return Unidragger;
  235. }));