2 * jquery.event.linger - v 1.0.0
3 * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
4 * Open Source MIT License - http://threedubmedia.com/code/license
8 // REQUIRES: jquery 1.4.2+
10 ;(function($){ // secure $ jQuery alias
12 // add the jquery instance method
13 $.fn.linger = 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;
19 if ( type.indexOf("linger") !== 0 )
20 type = "linger"+ 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 );
28 // local refs (increase compression)
30 $special = $event.special,
31 // special event configuration
32 linger = $special.linger = {
36 speed: 100, // speed limit (pixels per second)
37 delay: 100, // milliseconds per speed check
38 persist: 400 // milliseconds after mouseleave
41 // the key name for stored data
42 datakey: "lingerdata",
44 // count bound related events
46 // read the interaction data
47 var data = $.data( this, linger.datakey ),
48 // read any passed options
49 opts = obj.data || {};
50 // count another realted event
52 // extend data options bound with this event
53 // don't iterate "opts" in case it is a node
54 $.each( linger.defaults, function( key, def ){
55 if ( opts[ key ] !== undefined )
56 data[ key ] = opts[ key ];
60 // forget unbound related events
62 $.data( this, linger.datakey ).related -= 1;
65 // configure interaction
67 // check for related events
68 if ( $.data( this, linger.datakey ) )
70 // initialize the drag data with copied defaults
71 var data = $.extend({ related:0 }, linger.defaults );
72 // store the interaction data
73 $.data( this, linger.datakey, data );
74 // bind the mouse events with data
75 $event.add( this, "mouseenter mouseleave", linger.handler, data );
78 // destroy configured interaction
80 // check for related events
81 if ( $.data( this, linger.datakey ).related )
83 // remove the stored data
84 $.removeData( this, linger.datakey );
85 // remove the mouse events
86 $event.remove( this, "mouseenter mousemove mouseleave", linger.handler );
89 // handle mouse events
90 handler: function( event ){
91 var data = event.data || {};
92 // initialize props for new interaction
93 if ( event.type == "mouseenter" && !data.lingered ){
94 // mouse distance squared
96 // ( speed * time ) squared
97 data.limit = Math.pow( data.speed * ( data.delay/1e3 ), 2 );
98 // the interacted element
100 // handle the start event (handler may return false to cancel)
101 if ( linger.hijack( event, "lingerstart", this ) ){
102 // store the event, to compare later
104 // begin tracking the mouse movement
105 $event.add( this, "mousemove", linger.handler, data );
106 // start comparing mouse speed at fixed intervals
107 data.timer = setTimeout(function(){
108 // check the current speed against the limit
109 if ( data.dist2 <= data.limit )
110 // handle the linger event (handler may return false to cancel)
111 data.lingered = linger.hijack( data.event, "linger", data.elem );
112 // stop tracking the mouse
114 $event.remove( data.elem, "mousemove", linger.handler );
117 data.timer = setTimeout( arguments.callee, data.delay );
118 // reset distance for next comparison
123 // stop if not properly initialized
126 // handle other events
127 switch ( event.type ){
129 case data.lingered && 'mouseenter':
130 // stop the current timer
131 clearTimeout( data.timer );
133 // track mouse movement
135 // distance² = x² + y²
136 data.dist2 += Math.pow( event.pageX - data.event.pageX, 2 )
137 + Math.pow( event.pageY - data.event.pageY, 2 );
138 // store current event
141 // handle leaving after lingering
142 case data.lingered && 'mouseleave':
143 // optionally delay the end event
144 data.timer = setTimeout(function(){
145 // handle the end event, flip flag for lingering
146 data.lingered = !linger.hijack( event, "lingerend", data.elem );
147 // if still lingering, recurse
149 setTimeout( arguments.callee, data.persist );
152 // handle leaving after no lingering
153 case !data.lingered && 'mouseleave':
154 // stop the current timer
155 clearTimeout( data.timer );
156 // stop tracking the mouse movement
157 $event.remove( data.elem, "mousemove", linger.handler );
158 // clean the data for next interaction
164 // re-use event object for custom events
165 hijack: function( event, type, elem ){
166 // remember the original event and type
168 event: event.originalEvent,
171 // modify the event type
173 // remove the original event
174 event.originalEvent = null;
175 // remove any previous event result
177 // handle the custom event
178 result = $event.handle.call( elem, event );
179 // restore the original event & type
180 event.type = orig.type;
181 event.originalEvent = orig.event;
182 // return handled result
183 return ( result !== false );
187 // share the same special event configuration with related events...
188 $special.lingerstart = $special.lingerend = linger;
190 })(jQuery); // confine scope