Skip to content

Commit bddff7f

Browse files
committed
Spinner: Prevent double mousewheel & wheel event handling
As of gh-2338, if one has loaded the jQuery MouseWheel plugin, the `mousewheel` handler would fire the `wheel` one, but the `wheel` one would also run in response to the native `wheel` event, resulting in double the distance handled by the spinner. To prevent the issue, only fire the `wheel` handler from inside the `mousewheel` on if the event was triggered by jQuery - jQuery will not care that the underlying event is `wheel` and will only fire handlers for `mousewheel`. Also, add an iframe test using jQuery MouseWheel to not affect all the other tests. Ref gh-2338
1 parent 6843ced commit bddff7f

File tree

6 files changed

+389
-0
lines changed

6 files changed

+389
-0
lines changed

Gruntfile.js

+3
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ grunt.initConfig( {
247247

248248
"requirejs/require.js": "requirejs/require.js",
249249

250+
"jquery-mousewheel/jquery.mousewheel.js": "jquery-mousewheel/jquery.mousewheel.js",
251+
"jquery-mousewheel/LICENSE.txt": "jquery-mousewheel/LICENSE.txt",
252+
250253
"jquery-simulate/jquery.simulate.js": "jquery-simulate/jquery.simulate.js",
251254
"jquery-simulate/LICENSE.txt": "jquery-simulate/LICENSE.txt",
252255

bower.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"devDependencies": {
1515
"jquery-color": "3.0.0",
16+
"jquery-mousewheel": "3.2.2",
1617
"jquery-simulate": "1.1.1",
1718
"qunit": "2.19.4",
1819
"requirejs": "2.1.14",
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
2+
3+
This software consists of voluntary contributions made by many
4+
individuals. For exact contribution history, see the revision history
5+
available at https://github.com/jquery/jquery-mousewheel
6+
7+
The following license applies to all parts of this software except as
8+
documented below:
9+
10+
====
11+
12+
Permission is hereby granted, free of charge, to any person obtaining
13+
a copy of this software and associated documentation files (the
14+
"Software"), to deal in the Software without restriction, including
15+
without limitation the rights to use, copy, modify, merge, publish,
16+
distribute, sublicense, and/or sell copies of the Software, and to
17+
permit persons to whom the Software is furnished to do so, subject to
18+
the following conditions:
19+
20+
The above copyright notice and this permission notice shall be
21+
included in all copies or substantial portions of the Software.
22+
23+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30+
31+
====
32+
33+
All files located in the node_modules and external directories are
34+
externally maintained libraries used by this software which have their
35+
own licenses; we recommend you read them, as their terms may differ from
36+
the terms above.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*!
2+
* jQuery Mousewheel 3.2.2
3+
* Copyright OpenJS Foundation and other contributors
4+
*/
5+
6+
( function( factory ) {
7+
"use strict";
8+
9+
if ( typeof define === "function" && define.amd ) {
10+
11+
// AMD. Register as an anonymous module.
12+
define( [ "jquery" ], factory );
13+
} else if ( typeof exports === "object" ) {
14+
15+
// Node/CommonJS style for Browserify
16+
module.exports = factory;
17+
} else {
18+
19+
// Browser globals
20+
factory( jQuery );
21+
}
22+
} )( function( $ ) {
23+
"use strict";
24+
25+
var nullLowestDeltaTimeout, lowestDelta,
26+
modernEvents = !!$.fn.on,
27+
toFix = [ "wheel", "mousewheel", "DOMMouseScroll", "MozMousePixelScroll" ],
28+
toBind = ( "onwheel" in window.document || window.document.documentMode >= 9 ) ?
29+
[ "wheel" ] : [ "mousewheel", "DomMouseScroll", "MozMousePixelScroll" ],
30+
slice = Array.prototype.slice;
31+
32+
if ( $.event.fixHooks ) {
33+
for ( var i = toFix.length; i; ) {
34+
$.event.fixHooks[ toFix[ --i ] ] = $.event.mouseHooks;
35+
}
36+
}
37+
38+
var special = $.event.special.mousewheel = {
39+
version: "3.2.2",
40+
41+
setup: function() {
42+
if ( this.addEventListener ) {
43+
for ( var i = toBind.length; i; ) {
44+
this.addEventListener( toBind[ --i ], handler, false );
45+
}
46+
} else {
47+
this.onmousewheel = handler;
48+
}
49+
50+
// Store the line height and page height for this particular element
51+
$.data( this, "mousewheel-line-height", special.getLineHeight( this ) );
52+
$.data( this, "mousewheel-page-height", special.getPageHeight( this ) );
53+
},
54+
55+
teardown: function() {
56+
if ( this.removeEventListener ) {
57+
for ( var i = toBind.length; i; ) {
58+
this.removeEventListener( toBind[ --i ], handler, false );
59+
}
60+
} else {
61+
this.onmousewheel = null;
62+
}
63+
64+
// Clean up the data we added to the element
65+
$.removeData( this, "mousewheel-line-height" );
66+
$.removeData( this, "mousewheel-page-height" );
67+
},
68+
69+
getLineHeight: function( elem ) {
70+
var $elem = $( elem ),
71+
$parent = $elem[ "offsetParent" in $.fn ? "offsetParent" : "parent" ]();
72+
if ( !$parent.length ) {
73+
$parent = $( "body" );
74+
}
75+
return parseInt( $parent.css( "fontSize" ), 10 ) ||
76+
parseInt( $elem.css( "fontSize" ), 10 ) || 16;
77+
},
78+
79+
getPageHeight: function( elem ) {
80+
return $( elem ).height();
81+
},
82+
83+
settings: {
84+
adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
85+
normalizeOffset: true // calls getBoundingClientRect for each event
86+
}
87+
};
88+
89+
$.fn.extend( {
90+
mousewheel: function( fn ) {
91+
return fn ?
92+
this[ modernEvents ? "on" : "bind" ]( "mousewheel", fn ) :
93+
this.trigger( "mousewheel" );
94+
},
95+
96+
unmousewheel: function( fn ) {
97+
return this[ modernEvents ? "off" : "unbind" ]( "mousewheel", fn );
98+
}
99+
} );
100+
101+
102+
function handler( event ) {
103+
var orgEvent = event || window.event,
104+
args = slice.call( arguments, 1 ),
105+
delta = 0,
106+
deltaX = 0,
107+
deltaY = 0,
108+
absDelta = 0;
109+
event = $.event.fix( orgEvent );
110+
event.type = "mousewheel";
111+
112+
// Old school scrollwheel delta
113+
if ( "detail" in orgEvent ) {
114+
deltaY = orgEvent.detail * -1;
115+
}
116+
if ( "wheelDelta" in orgEvent ) {
117+
deltaY = orgEvent.wheelDelta;
118+
}
119+
if ( "wheelDeltaY" in orgEvent ) {
120+
deltaY = orgEvent.wheelDeltaY;
121+
}
122+
if ( "wheelDeltaX" in orgEvent ) {
123+
deltaX = orgEvent.wheelDeltaX * -1;
124+
}
125+
126+
// Firefox < 17 horizontal scrolling related to DOMMouseScroll event
127+
if ( "axis" in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
128+
deltaX = deltaY * -1;
129+
deltaY = 0;
130+
}
131+
132+
// Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatability
133+
delta = deltaY === 0 ? deltaX : deltaY;
134+
135+
// New school wheel delta (wheel event)
136+
if ( "deltaY" in orgEvent ) {
137+
deltaY = orgEvent.deltaY * -1;
138+
delta = deltaY;
139+
}
140+
if ( "deltaX" in orgEvent ) {
141+
deltaX = orgEvent.deltaX;
142+
if ( deltaY === 0 ) {
143+
delta = deltaX * -1;
144+
}
145+
}
146+
147+
// No change actually happened, no reason to go any further
148+
if ( deltaY === 0 && deltaX === 0 ) {
149+
return;
150+
}
151+
152+
// Need to convert lines and pages to pixels if we aren't already in pixels
153+
// There are three delta modes:
154+
// * deltaMode 0 is by pixels, nothing to do
155+
// * deltaMode 1 is by lines
156+
// * deltaMode 2 is by pages
157+
if ( orgEvent.deltaMode === 1 ) {
158+
var lineHeight = $.data( this, "mousewheel-line-height" );
159+
delta *= lineHeight;
160+
deltaY *= lineHeight;
161+
deltaX *= lineHeight;
162+
} else if ( orgEvent.deltaMode === 2 ) {
163+
var pageHeight = $.data( this, "mousewheel-page-height" );
164+
delta *= pageHeight;
165+
deltaY *= pageHeight;
166+
deltaX *= pageHeight;
167+
}
168+
169+
// Store lowest absolute delta to normalize the delta values
170+
absDelta = Math.max( Math.abs( deltaY ), Math.abs( deltaX ) );
171+
172+
if ( !lowestDelta || absDelta < lowestDelta ) {
173+
lowestDelta = absDelta;
174+
175+
// Adjust older deltas if necessary
176+
if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) {
177+
lowestDelta /= 40;
178+
}
179+
}
180+
181+
// Adjust older deltas if necessary
182+
if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) {
183+
184+
// Divide all the things by 40!
185+
delta /= 40;
186+
deltaX /= 40;
187+
deltaY /= 40;
188+
}
189+
190+
// Get a whole, normalized value for the deltas
191+
delta = Math[ delta >= 1 ? "floor" : "ceil" ]( delta / lowestDelta );
192+
deltaX = Math[ deltaX >= 1 ? "floor" : "ceil" ]( deltaX / lowestDelta );
193+
deltaY = Math[ deltaY >= 1 ? "floor" : "ceil" ]( deltaY / lowestDelta );
194+
195+
// Normalise offsetX and offsetY properties
196+
if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
197+
var boundingRect = this.getBoundingClientRect();
198+
event.offsetX = event.clientX - boundingRect.left;
199+
event.offsetY = event.clientY - boundingRect.top;
200+
}
201+
202+
// Add information to the event object
203+
event.deltaX = deltaX;
204+
event.deltaY = deltaY;
205+
event.deltaFactor = lowestDelta;
206+
207+
// Go ahead and set deltaMode to 0 since we converted to pixels
208+
// Although this is a little odd since we overwrite the deltaX/Y
209+
// properties with normalized deltas.
210+
event.deltaMode = 0;
211+
212+
// Add event and delta to the front of the arguments
213+
args.unshift( event, delta, deltaX, deltaY );
214+
215+
// Clear out lowestDelta after sometime to better
216+
// handle multiple device types that give different
217+
// a different lowestDelta
218+
// Ex: trackpad = 3 and mouse wheel = 120
219+
if ( nullLowestDeltaTimeout ) {
220+
window.clearTimeout( nullLowestDeltaTimeout );
221+
}
222+
nullLowestDeltaTimeout = window.setTimeout( function() {
223+
lowestDelta = null;
224+
}, 200 );
225+
226+
return ( $.event.dispatch || $.event.handle ).apply( this, args );
227+
}
228+
229+
function shouldAdjustOldDeltas( orgEvent, absDelta ) {
230+
231+
// If this is an older event and the delta is divisible by 120,
232+
// then we are assuming that the browser is treating this as an
233+
// older mouse wheel event and that we should divide the deltas
234+
// by 40 to try and get a more usable deltaFactor.
235+
// Side note, this actually impacts the reported scroll distance
236+
// in older browsers and can cause scrolling to be slower than native.
237+
// Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
238+
return special.settings.adjustOldDeltas && orgEvent.type === "mousewheel" &&
239+
absDelta % 120 === 0;
240+
}
241+
242+
} );

0 commit comments

Comments
 (0)