PageRenderTime 68ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 1ms

/static/scripts/jquery.event.drag.js

https://bitbucket.org/cistrome/cistrome-harvard/
JavaScript | 394 lines | 259 code | 26 blank | 109 comment | 90 complexity | 85dbdc2a2dbe5883317696986b5f0f83 MD5 | raw file
  1. /*!
  2. * jquery.event.drag - v 2.1.0
  3. * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
  4. * Open Source MIT License - http://threedubmedia.com/code/license
  5. */
  6. // Created: 2008-06-04
  7. // Updated: 2010-09-15
  8. // REQUIRES: jquery 1.4.2+
  9. ;(function( $ ){
  10. // add the jquery instance method
  11. $.fn.drag = function( str, arg, opts ){
  12. // figure out the event type
  13. var type = typeof str == "string" ? str : "",
  14. // figure out the event handler...
  15. fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
  16. // fix the event type
  17. if ( type.indexOf("drag") !== 0 )
  18. type = "drag"+ type;
  19. // were options passed
  20. opts = ( str == fn ? arg : opts ) || {};
  21. // trigger or bind event handler
  22. return fn ? this.bind( type, opts, fn ) : this.trigger( type );
  23. };
  24. // local refs (increase compression)
  25. var $event = $.event,
  26. $special = $event.special,
  27. // configure the drag special event
  28. drag = $special.drag = {
  29. // these are the default settings
  30. defaults: {
  31. which: 1, // mouse button pressed to start drag sequence
  32. distance: 0, // distance dragged before dragstart
  33. not: ':input', // selector to suppress dragging on target elements
  34. handle: null, // selector to match handle target elements
  35. relative: false, // true to use "position", false to use "offset"
  36. drop: true, // false to suppress drop events, true or selector to allow
  37. click: false // false to suppress click events after dragend (no proxy)
  38. },
  39. // the key name for stored drag data
  40. datakey: "dragdata",
  41. // count bound related events
  42. add: function( obj ){
  43. // read the interaction data
  44. var data = $.data( this, drag.datakey ),
  45. // read any passed options
  46. opts = obj.data || {};
  47. // count another realted event
  48. data.related += 1;
  49. // extend data options bound with this event
  50. // don't iterate "opts" in case it is a node
  51. $.each( drag.defaults, function( key, def ){
  52. if ( opts[ key ] !== undefined )
  53. data[ key ] = opts[ key ];
  54. });
  55. },
  56. // forget unbound related events
  57. remove: function(){
  58. $.data( this, drag.datakey ).related -= 1;
  59. },
  60. // configure interaction, capture settings
  61. setup: function(){
  62. // check for related events
  63. if ( $.data( this, drag.datakey ) )
  64. return;
  65. // initialize the drag data with copied defaults
  66. var data = $.extend({ related:0 }, drag.defaults );
  67. // store the interaction data
  68. $.data( this, drag.datakey, data );
  69. // bind the mousedown event, which starts drag interactions
  70. $event.add( this, "touchstart mousedown", drag.init, data );
  71. // prevent image dragging in IE...
  72. if ( this.attachEvent )
  73. this.attachEvent("ondragstart", drag.dontstart );
  74. },
  75. // destroy configured interaction
  76. teardown: function(){
  77. var data = $.data( this, drag.datakey ) || {};
  78. // check for related events
  79. if ( data.related )
  80. return;
  81. // remove the stored data
  82. $.removeData( this, drag.datakey );
  83. // remove the mousedown event
  84. $event.remove( this, "touchstart mousedown", drag.init );
  85. // enable text selection
  86. drag.textselect( true );
  87. // un-prevent image dragging in IE...
  88. if ( this.detachEvent )
  89. this.detachEvent("ondragstart", drag.dontstart );
  90. },
  91. // initialize the interaction
  92. init: function( event ){
  93. // sorry, only one touch at a time
  94. if ( drag.touched )
  95. return;
  96. // the drag/drop interaction data
  97. var dd = event.data, results;
  98. // check the which directive
  99. if ( event.which != 0 && dd.which > 0 && event.which != dd.which )
  100. return;
  101. // check for suppressed selector
  102. if ( $( event.target ).is( dd.not ) )
  103. return;
  104. // check for handle selector
  105. if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length )
  106. return;
  107. // store/reset some initial attributes
  108. if ( event.type == "touchstart" ){
  109. drag.touched = this;
  110. drag.touchFix( event, dd );
  111. }
  112. dd.propagates = 1;
  113. dd.mousedown = this;
  114. dd.interactions = [ drag.interaction( this, dd ) ];
  115. dd.target = event.target;
  116. dd.pageX = event.pageX;
  117. dd.pageY = event.pageY;
  118. dd.dragging = null;
  119. // handle draginit event...
  120. results = drag.hijack( event, "draginit", dd );
  121. // early cancel
  122. if ( !dd.propagates )
  123. return;
  124. // flatten the result set
  125. results = drag.flatten( results );
  126. // insert new interaction elements
  127. if ( results && results.length ){
  128. dd.interactions = [];
  129. $.each( results, function(){
  130. dd.interactions.push( drag.interaction( this, dd ) );
  131. });
  132. }
  133. // remember how many interactions are propagating
  134. dd.propagates = dd.interactions.length;
  135. // locate and init the drop targets
  136. if ( dd.drop !== false && $special.drop )
  137. $special.drop.handler( event, dd );
  138. // disable text selection
  139. drag.textselect( false );
  140. // bind additional events...
  141. if ( drag.touched )
  142. $event.add( drag.touched, "touchmove touchend", drag.handler, dd );
  143. else
  144. $event.add( document, "mousemove mouseup", drag.handler, dd );
  145. // helps prevent text selection or scrolling
  146. if ( !drag.touched || dd.live )
  147. return false;
  148. },
  149. // fix event properties for touch events
  150. touchFix: function( event, dd ){
  151. var orig = event.originalEvent, i = 0;
  152. // iOS webkit: touchstart, touchmove, touchend
  153. if ( orig && orig.changedTouches ){
  154. event.pageX = orig.changedTouches[0].pageX;
  155. event.pageY = orig.changedTouches[0].pageY;
  156. }
  157. //console.log( event.type, event );
  158. },
  159. // returns an interaction object
  160. interaction: function( elem, dd ){
  161. var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 };
  162. return {
  163. drag: elem,
  164. callback: new drag.callback(),
  165. droppable: [],
  166. offset: offset
  167. };
  168. },
  169. // handle drag-releatd DOM events
  170. handler: function( event ){
  171. // read the data before hijacking anything
  172. var dd = event.data;
  173. if ( drag.touched )
  174. drag.touchFix( event, dd );
  175. // handle various events
  176. switch ( event.type ){
  177. // mousemove, check distance, start dragging
  178. case !dd.dragging && 'touchmove':
  179. event.preventDefault();
  180. case !dd.dragging && 'mousemove':
  181. // drag tolerance, x? + y? = distance?
  182. if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) )
  183. break; // distance tolerance not reached
  184. event.target = dd.target; // force target from "mousedown" event (fix distance issue)
  185. drag.hijack( event, "dragstart", dd ); // trigger "dragstart"
  186. if ( dd.propagates ) // "dragstart" not rejected
  187. dd.dragging = true; // activate interaction
  188. // mousemove, dragging
  189. case 'touchmove':
  190. event.preventDefault();
  191. case 'mousemove':
  192. if ( dd.dragging ){
  193. // trigger "drag"
  194. drag.hijack( event, "drag", dd );
  195. if ( dd.propagates ){
  196. // manage drop events
  197. if ( dd.drop !== false && $special.drop )
  198. $special.drop.handler( event, dd ); // "dropstart", "dropend"
  199. break; // "drag" not rejected, stop
  200. }
  201. event.type = "mouseup"; // helps "drop" handler behave
  202. }
  203. // mouseup, stop dragging
  204. case 'touchend':
  205. case 'mouseup':
  206. if ( drag.touched )
  207. $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events
  208. else
  209. $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events
  210. if ( dd.dragging ){
  211. if ( dd.drop !== false && $special.drop )
  212. $special.drop.handler( event, dd ); // "drop"
  213. drag.hijack( event, "dragend", dd ); // trigger "dragend"
  214. } else {
  215. drag.hijack( event, "dragclickonly", dd ); // trigger "dragclickonly"
  216. }
  217. drag.textselect( true ); // enable text selection
  218. // if suppressing click events...
  219. if ( dd.click === false && dd.dragging ){
  220. $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 );
  221. }
  222. dd.dragging = drag.touched = false; // deactivate element
  223. break;
  224. }
  225. },
  226. // re-use event object for custom events
  227. hijack: function( event, type, dd, x, elem ){
  228. // not configured
  229. if ( !dd )
  230. return;
  231. // remember the original event and type
  232. var orig = { event:event.originalEvent, type: event.type },
  233. // is the event drag related or drog related?
  234. mode = type.indexOf("drop") ? "drag" : "drop",
  235. // iteration vars
  236. result, i = x || 0, ia, $elems, callback,
  237. len = !isNaN( x ) ? x : dd.interactions.length;
  238. // modify the event type
  239. event.type = type;
  240. // remove the original event
  241. event.originalEvent = null;
  242. // initialize the results
  243. dd.results = [];
  244. // handle each interacted element
  245. do if ( ia = dd.interactions[ i ] ){
  246. // validate the interaction
  247. if ( type !== "dragend" && ia.cancelled )
  248. continue;
  249. // set the dragdrop properties on the event object
  250. callback = drag.properties( event, dd, ia );
  251. // prepare for more results
  252. ia.results = [];
  253. // handle each element
  254. $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){
  255. // identify drag or drop targets individually
  256. callback.target = subject;
  257. // handle the event
  258. result = subject ? $event.handle.call( subject, event, callback ) : null;
  259. // stop the drag interaction for this element
  260. if ( result === false ){
  261. if ( mode == "drag" ){
  262. ia.cancelled = true;
  263. dd.propagates -= 1;
  264. }
  265. if ( type == "drop" ){
  266. ia[ mode ][p] = null;
  267. }
  268. }
  269. // assign any dropinit elements
  270. else if ( type == "dropinit" )
  271. ia.droppable.push( drag.element( result ) || subject );
  272. // accept a returned proxy element
  273. if ( type == "dragstart" )
  274. ia.proxy = $( drag.element( result ) || ia.drag )[0];
  275. // remember this result
  276. ia.results.push( result );
  277. // forget the event result, for recycling
  278. delete event.result;
  279. // break on cancelled handler
  280. if ( type !== "dropinit" )
  281. return result;
  282. });
  283. // flatten the results
  284. dd.results[ i ] = drag.flatten( ia.results );
  285. // accept a set of valid drop targets
  286. if ( type == "dropinit" )
  287. ia.droppable = drag.flatten( ia.droppable );
  288. // locate drop targets
  289. if ( type == "dragstart" && !ia.cancelled )
  290. callback.update();
  291. }
  292. while ( ++i < len )
  293. // restore the original event & type
  294. event.type = orig.type;
  295. event.originalEvent = orig.event;
  296. // return all handler results
  297. return drag.flatten( dd.results );
  298. },
  299. // extend the callback object with drag/drop properties...
  300. properties: function( event, dd, ia ){
  301. var obj = ia.callback;
  302. // elements
  303. obj.drag = ia.drag;
  304. obj.proxy = ia.proxy || ia.drag;
  305. // starting mouse position
  306. obj.startX = dd.pageX;
  307. obj.startY = dd.pageY;
  308. // current distance dragged
  309. obj.deltaX = event.pageX - dd.pageX;
  310. obj.deltaY = event.pageY - dd.pageY;
  311. // original element position
  312. obj.originalX = ia.offset.left;
  313. obj.originalY = ia.offset.top;
  314. // adjusted element position
  315. obj.offsetX = obj.originalX + obj.deltaX;
  316. obj.offsetY = obj.originalY + obj.deltaY;
  317. // assign the drop targets information
  318. obj.drop = drag.flatten( ( ia.drop || [] ).slice() );
  319. obj.available = drag.flatten( ( ia.droppable || [] ).slice() );
  320. return obj;
  321. },
  322. // determine is the argument is an element or jquery instance
  323. element: function( arg ){
  324. if ( arg && ( arg.jquery || arg.nodeType == 1 ) )
  325. return arg;
  326. },
  327. // flatten nested jquery objects and arrays into a single dimension array
  328. flatten: function( arr ){
  329. return $.map( arr, function( member ){
  330. return member && member.jquery ? $.makeArray( member ) :
  331. member && member.length ? drag.flatten( member ) : member;
  332. });
  333. },
  334. // toggles text selection attributes ON (true) or OFF (false)
  335. textselect: function( bool ){
  336. $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart )
  337. .css("MozUserSelect", bool ? "" : "none" );
  338. // .attr("unselectable", bool ? "off" : "on" )
  339. document.unselectable = bool ? "off" : "on";
  340. },
  341. // suppress "selectstart" and "ondragstart" events
  342. dontstart: function(){
  343. return false;
  344. },
  345. // a callback instance contructor
  346. callback: function(){}
  347. };
  348. // callback methods
  349. drag.callback.prototype = {
  350. update: function(){
  351. if ( $special.drop && this.available.length )
  352. $.each( this.available, function( i ){
  353. $special.drop.locate( this, i );
  354. });
  355. }
  356. };
  357. // patch $.event.handle to allow suppressing clicks
  358. var orighandle = $event.handle;
  359. $event.handle = function( event ){
  360. if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){
  361. $.removeData( this, "suppress."+ event.type );
  362. return;
  363. }
  364. return orighandle.apply( this, arguments );
  365. };
  366. // share the same special event configuration with related events...
  367. $special.draginit = $special.dragstart = $special.dragend = drag;
  368. })( jQuery );