1/*! 2 * jquery.event.drag - v 2.2 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: 2012-05-21 8// REQUIRES: jquery 1.7.x 9 10;(function( $ ){ 11 12// add the jquery instance method 13$.fn.drag = function( str, arg, opts ){ 14 // figure out the event type 15 var type = typeof str == "string" ? str : "", 16 // figure out the event handler... 17 fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; 18 // fix the event type 19 if ( type.indexOf("drag") !== 0 ) 20 type = "drag"+ type; 21 // were options passed 22 opts = ( str == fn ? arg : opts ) || {}; 23 // trigger or bind event handler 24 return fn ? this.bind( type, opts, fn ) : this.trigger( type ); 25}; 26 27// local refs (increase compression) 28var $event = $.event, 29$special = $event.special, 30// configure the drag special event 31drag = $special.drag = { 32 33 // these are the default settings 34 defaults: { 35 which: 1, // mouse button pressed to start drag sequence 36 distance: 0, // distance dragged before dragstart 37 not: ':input', // selector to suppress dragging on target elements 38 handle: null, // selector to match handle target elements 39 relative: false, // true to use "position", false to use "offset" 40 drop: true, // false to suppress drop events, true or selector to allow 41 click: false // false to suppress click events after dragend (no proxy) 42 }, 43 44 // the key name for stored drag data 45 datakey: "dragdata", 46 47 // prevent bubbling for better performance 48 noBubble: true, 49 50 // count bound related events 51 add: function( obj ){ 52 // read the interaction data 53 var data = $.data( this, drag.datakey ), 54 // read any passed options 55 opts = obj.data || {}; 56 // count another realted event 57 data.related += 1; 58 // extend data options bound with this event 59 // don't iterate "opts" in case it is a node 60 $.each( drag.defaults, function( key, def ){ 61 if ( opts[ key ] !== undefined ) 62 data[ key ] = opts[ key ]; 63 }); 64 }, 65 66 // forget unbound related events 67 remove: function(){ 68 $.data( this, drag.datakey ).related -= 1; 69 }, 70 71 // configure interaction, capture settings 72 setup: function(){ 73 // check for related events 74 if ( $.data( this, drag.datakey ) ) 75 return; 76 // initialize the drag data with copied defaults 77 var data = $.extend({ related:0 }, drag.defaults ); 78 // store the interaction data 79 $.data( this, drag.datakey, data ); 80 // bind the mousedown event, which starts drag interactions 81 $event.add( this, "touchstart mousedown", drag.init, data ); 82 // prevent image dragging in IE... 83 if ( this.attachEvent ) 84 this.attachEvent("ondragstart", drag.dontstart ); 85 }, 86 87 // destroy configured interaction 88 teardown: function(){ 89 var data = $.data( this, drag.datakey ) || {}; 90 // check for related events 91 if ( data.related ) 92 return; 93 // remove the stored data 94 $.removeData( this, drag.datakey ); 95 // remove the mousedown event 96 $event.remove( this, "touchstart mousedown", drag.init ); 97 // enable text selection 98 drag.textselect( true ); 99 // un-prevent image dragging in IE... 100 if ( this.detachEvent ) 101 this.detachEvent("ondragstart", drag.dontstart ); 102 }, 103 104 // initialize the interaction 105 init: function( event ){ 106 // sorry, only one touch at a time 107 if ( drag.touched ) 108 return; 109 // the drag/drop interaction data 110 var dd = event.data, results; 111 // check the which directive 112 if ( event.which != 0 && dd.which > 0 && event.which != dd.which ) 113 return; 114 // check for suppressed selector 115 if ( $( event.target ).is( dd.not ) ) 116 return; 117 // check for handle selector 118 if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length ) 119 return; 120 121 drag.touched = event.type == 'touchstart' ? this : null; 122 dd.propagates = 1; 123 dd.mousedown = this; 124 dd.interactions = [ drag.interaction( this, dd ) ]; 125 dd.target = event.target; 126 dd.pageX = event.pageX; 127 dd.pageY = event.pageY; 128 dd.dragging = null; 129 // handle draginit event... 130 results = drag.hijack( event, "draginit", dd ); 131 // early cancel 132 if ( !dd.propagates ) 133 return; 134 // flatten the result set 135 results = drag.flatten( results ); 136 // insert new interaction elements 137 if ( results && results.length ){ 138 dd.interactions = []; 139 $.each( results, function(){ 140 dd.interactions.push( drag.interaction( this, dd ) ); 141 }); 142 } 143 // remember how many interactions are propagating 144 dd.propagates = dd.interactions.length; 145 // locate and init the drop targets 146 if ( dd.drop !== false && $special.drop ) 147 $special.drop.handler( event, dd ); 148 // disable text selection 149 drag.textselect( false ); 150 // bind additional events... 151 if ( drag.touched ) 152 $event.add( drag.touched, "touchmove touchend", drag.handler, dd ); 153 else 154 $event.add( document, "mousemove mouseup", drag.handler, dd ); 155 // helps prevent text selection or scrolling 156 if ( !drag.touched || dd.live ) 157 return false;
158 }, 159 160 // returns an interaction object 161 interaction: function( elem, dd ){ 162 var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 }; 163 return { 164 drag: elem, 165 callback: new drag.callback(), 166 droppable: [], 167 offset: offset 168 }; 169 }, 170 171 // handle drag-releatd DOM events 172 handler: function( event ){ 173 // read the data before hijacking anything 174 var dd = event.data; 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 default: 207 if ( drag.touched ) 208 $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events 209 else 210 $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events 211 if ( dd.dragging ){ 212 if ( dd.drop !== false && $special.drop ) 213 $special.drop.handler( event, dd ); // "drop" 214 drag.hijack( event, "dragend", dd ); // trigger "dragend" 215 } 216 drag.textselect( true ); // enable text selection 217 // if suppressing click events... 218 if ( dd.click === false && dd.dragging ) 219 $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 ); 220 dd.dragging = drag.touched = false; // deactivate element 221 break; 222 } 223 }, 224 225 // re-use event object for custom events 226 hijack: function( event, type, dd, x, elem ){ 227 // not configured 228 if ( !dd ) 229 return; 230 // remember the original event and type 231 var orig = { event:event.originalEvent, type:event.type }, 232 // is the event drag related or drog related? 233 mode = type.indexOf("drop") ? "drag" : "drop", 234 // iteration vars 235 result, i = x || 0, ia, $elems, callback, 236 len = !isNaN( x ) ? x : dd.interactions.length; 237 // modify the event type 238 event.type = type; 239 // remove the original event 240 event.originalEvent = null; 241 // initialize the results 242 dd.results = []; 243 // handle each interacted element 244 do if ( ia = dd.interactions[ i ] ){ 245 // validate the interaction 246 if ( type !== "dragend" && ia.cancelled ) 247 continue; 248 // set the dragdrop properties on the event object 249 callback = drag.properties( event, dd, ia ); 250 // prepare for more results 251 ia.results = []; 252 // handle each element 253 $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){ 254 // identify drag or drop targets individually
255 callback.target = subject; 256 // force propagtion of the custom event 257 event.isPropagationStopped = function(){ return false; }; 258 // handle the event 259 result = subject ? $event.dispatch.call( subject, event, callback ) : null; 260 // stop the drag interaction for this element 261 if ( result === false ){ 262 if ( mode == "drag" ){ 263 ia.cancelled = true; 264 dd.propagates -= 1; 265 } 266 if ( type == "drop" ){ 267 ia[ mode ][p] = null; 268 } 269 } 270 // assign any dropinit elements 271 else if ( type == "dropinit" ) 272 ia.droppable.push( drag.element( result ) || subject ); 273 // accept a returned proxy element 274 if ( type == "dragstart" ) 275 ia.proxy = $( drag.element( result ) || ia.drag )[0]; 276 // remember this result 277 ia.results.push( result ); 278 // forget the event result, for recycling 279 delete event.result; 280 // break on cancelled handler 281 if ( type !== "dropinit" ) 282 return result; 283 }); 284 // flatten the results 285 dd.results[ i ] = drag.flatten( ia.results ); 286 // accept a set of valid drop targets 287 if ( type == "dropinit" ) 288 ia.droppable = drag.flatten( ia.droppable ); 289 // locate drop targets 290 if ( type == "dragstart" && !ia.cancelled ) 291 callback.update(); 292 } 293 while ( ++i < len ) 294 // restore the original event & type 295 event.type = orig.type; 296 event.originalEvent = orig.event; 297 // return all handler results 298 return drag.flatten( dd.results ); 299 }, 300 301 // extend the callback object with drag/drop properties... 302 properties: function( event, dd, ia ){ 303 var obj = ia.callback; 304 // elements 305 obj.drag = ia.drag; 306 obj.proxy = ia.proxy || ia.drag; 307 // starting mouse position 308 obj.startX = dd.pageX; 309 obj.startY = dd.pageY; 310 // current distance dragged 311 obj.deltaX = event.pageX - dd.pageX; 312 obj.deltaY = event.pageY - dd.pageY; 313 // original element position 314 obj.originalX = ia.offset.left; 315 obj.originalY = ia.offset.top; 316 // adjusted element position 317 obj.offsetX = obj.originalX + obj.deltaX; 318 obj.offsetY = obj.originalY + obj.deltaY; 319 // assign the drop targets information 320 obj.drop = drag.flatten( ( ia.drop || [] ).slice() ); 321 obj.available = drag.flatten( ( ia.droppable || [] ).slice() ); 322 return obj; 323 }, 324 325 // determine is the argument is an element or jquery instance 326 element: function( arg ){ 327 if ( arg && ( arg.jquery || arg.nodeType == 1 ) ) 328 return arg; 329 }, 330 331 // flatten nested jquery objects and arrays into a single dimension array 332 flatten: function( arr ){ 333 return $.map( arr, function( member ){ 334 return member && member.jquery ? $.makeArray( member ) : 335 member && member.length ? drag.flatten( member ) : member; 336 }); 337 }, 338 339 // toggles text selection attributes ON (true) or OFF (false) 340 textselect: function( bool ){ 341 $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart ) 342 .css("MozUserSelect", bool ? "" : "none" ); 343 // .attr("unselectable", bool ? "off" : "on" ) 344 document.unselectable = bool ? "off" : "on"; 345 }, 346 347 // suppress "selectstart" and "ondragstart" events 348 dontstart: function(){ 349 return false; 350 }, 351 352 // a callback instance contructor 353 callback: function(){} 354 355}; 356 357// callback methods 358drag.callback.prototype = { 359 update: function(){ 360 if ( $special.drop && this.available.length ) 361 $.each( this.available, function( i ){ 362 $special.drop.locate( this, i ); 363 }); 364 } 365}; 366 367// patch $.event.$dispatch to allow suppressing clicks 368var $dispatch = $event.dispatch; 369$event.dispatch = function( event ){ 370 if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){ 371 $.removeData( this, "suppress."+ event.type ); 372 return; 373 } 374 return $dispatch.apply( this, arguments ); 375}; 376 377// event fix hooks for touch events... 378var touchHooks = 379$event.fixHooks.touchstart = 380$event.fixHooks.touchmove = 381$event.fixHooks.touchend = 382$event.fixHooks.touchcancel = { 383 props: "clientX clientY pageX pageY screenX screenY".split( " " ), 384 filter: function( event, orig ) { 385 if ( orig ){ 386 var touched = ( orig.touches && orig.touches[0] ) 387 || ( orig.changedTouches && orig.changedTouches[0] ) 388 || null; 389 // iOS webkit: touchstart, touchmove, touchend 390 if ( touched ) 391 $.each( touchHooks.props, function( i, prop ){ 392 event[ prop ] = touched[ prop ]; 393 }); 394 } 395 return event; 396 } 397}; 398 399// share the same special event configuration with related events... 400$special.draginit = $special.dragstart = $special.dragend = drag; 401 402})( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.