PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 1.4.6
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v1.4.6
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / assets / js / jquery.event.move.js

jquery.event.move.js in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 1.4.6, at assets/js/jquery.event.move.js

587 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // jquery.event.move
2 //
3 // 1.3.6
4 //
5 // Stephen Band
6 //
7 // Triggers 'movestart', 'move' and 'moveend' events after
8 // mousemoves following a mousedown cross a distance threshold,
9 // similar to the native 'dragstart', 'drag' and 'dragend' events.
10 // Move events are throttled to animation frames. Move event objects
11 // have the properties:
12 //
13 // pageX:
14 // pageY: Page coordinates of pointer.
15 // startX:
16 // startY: Page coordinates of pointer at movestart.
17 // distX:
18 // distY: Distance the pointer has moved since movestart.
19 // deltaX:
20 // deltaY: Distance the finger has moved since last event.
21 // velocityX:
22 // velocityY: Average velocity over last few events.
23
24
25 (function (module) {
26 if (typeof define === 'function' && define.amd) {
27 // AMD. Register as an anonymous module.
28 define(['jquery'], module);
29 } else {
30 // Browser globals
31 module(jQuery);
32 }
33 })(function(jQuery, undefined){
34
35 var // Number of pixels a pressed pointer travels before movestart
36 // event is fired.
37 threshold = 6,
38
39 add = jQuery.event.add,
40
41 remove = jQuery.event.remove,
42
43 // Just sugar, so we can have arguments in the same order as
44 // add and remove.
45 trigger = function(node, type, data) {
46 jQuery.event.trigger(type, data, node);
47 },
48
49 // Shim for requestAnimationFrame, falling back to timer. See:
50 // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
51 requestFrame = (function(){
52 return (
53 window.requestAnimationFrame ||
54 window.webkitRequestAnimationFrame ||
55 window.mozRequestAnimationFrame ||
56 window.oRequestAnimationFrame ||
57 window.msRequestAnimationFrame ||
58 function(fn, element){
59 return window.setTimeout(function(){
60 fn();
61 }, 25);
62 }
63 );
64 })(),
65
66 ignoreTags = {
67 textarea: true,
68 input: true,
69 select: true,
70 button: true
71 },
72
73 mouseevents = {
74 move: 'mousemove',
75 cancel: 'mouseup dragstart',
76 end: 'mouseup'
77 },
78
79 touchevents = {
80 move: 'touchmove',
81 cancel: 'touchend',
82 end: 'touchend'
83 };
84
85
86 // Constructors
87
88 function Timer(fn){
89 var callback = fn,
90 active = false,
91 running = false;
92
93 function trigger(time) {
94 if (active){
95 callback();
96 requestFrame(trigger);
97 running = true;
98 active = false;
99 }
100 else {
101 running = false;
102 }
103 }
104
105 this.kick = function(fn) {
106 active = true;
107 if (!running) { trigger(); }
108 };
109
110 this.end = function(fn) {
111 var cb = callback;
112
113 if (!fn) { return; }
114
115 // If the timer is not running, simply call the end callback.
116 if (!running) {
117 fn();
118 }
119 // If the timer is running, and has been kicked lately, then
120 // queue up the current callback and the end callback, otherwise
121 // just the end callback.
122 else {
123 callback = active ?
124 function(){ cb(); fn(); } :
125 fn ;
126
127 active = true;
128 }
129 };
130 }
131
132
133 // Functions
134
135 function returnTrue() {
136 return true;
137 }
138
139 function returnFalse() {
140 return false;
141 }
142
143 function preventDefault(e) {
144 e.preventDefault();
145 }
146
147 function preventIgnoreTags(e) {
148 // Don't prevent interaction with form elements.
149 if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; }
150
151 e.preventDefault();
152 }
153
154 function isLeftButton(e) {
155 // Ignore mousedowns on any button other than the left (or primary)
156 // mouse button, or when a modifier key is pressed.
157 return (e.which === 1 && !e.ctrlKey && !e.altKey);
158 }
159
160 function identifiedTouch(touchList, id) {
161 var i, l;
162
163 if (touchList.identifiedTouch) {
164 return touchList.identifiedTouch(id);
165 }
166
167 // touchList.identifiedTouch() does not exist in
168 // webkit yet… we must do the search ourselves...
169
170 i = -1;
171 l = touchList.length;
172
173 while (++i < l) {
174 if (touchList[i].identifier === id) {
175 return touchList[i];
176 }
177 }
178 }
179
180 function changedTouch(e, event) {
181 var touch = identifiedTouch(e.changedTouches, event.identifier);
182
183 // This isn't the touch you're looking for.
184 if (!touch) { return; }
185
186 // Chrome Android (at least) includes touches that have not
187 // changed in e.changedTouches. That's a bit annoying. Check
188 // that this touch has changed.
189 if (touch.pageX === event.pageX && touch.pageY === event.pageY) { return; }
190
191 return touch;
192 }
193
194
195 // Handlers that decide when the first movestart is triggered
196
197 function mousedown(e){
198 var data;
199
200 if (!isLeftButton(e)) { return; }
201
202 data = {
203 target: e.target,
204 startX: e.pageX,
205 startY: e.pageY,
206 timeStamp: e.timeStamp
207 };
208
209 add(document, mouseevents.move, mousemove, data);
210 add(document, mouseevents.cancel, mouseend, data);
211 }
212
213 function mousemove(e){
214 var data = e.data;
215
216 checkThreshold(e, data, e, removeMouse);
217 }
218
219 function mouseend(e) {
220 removeMouse();
221 }
222
223 function removeMouse() {
224 remove(document, mouseevents.move, mousemove);
225 remove(document, mouseevents.cancel, mouseend);
226 }
227
228 function touchstart(e) {
229 var touch, template;
230
231 // Don't get in the way of interaction with form elements.
232 if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; }
233
234 touch = e.changedTouches[0];
235
236 // iOS live updates the touch objects whereas Android gives us copies.
237 // That means we can't trust the touchstart object to stay the same,
238 // so we must copy the data. This object acts as a template for
239 // movestart, move and moveend event objects.
240 template = {
241 target: touch.target,
242 startX: touch.pageX,
243 startY: touch.pageY,
244 timeStamp: e.timeStamp,
245 identifier: touch.identifier
246 };
247
248 // Use the touch identifier as a namespace, so that we can later
249 // remove handlers pertaining only to this touch.
250 add(document, touchevents.move + '.' + touch.identifier, touchmove, template);
251 add(document, touchevents.cancel + '.' + touch.identifier, touchend, template);
252 }
253
254 function touchmove(e){
255 var data = e.data,
256 touch = changedTouch(e, data);
257
258 if (!touch) { return; }
259
260 checkThreshold(e, data, touch, removeTouch);
261 }
262
263 function touchend(e) {
264 var template = e.data,
265 touch = identifiedTouch(e.changedTouches, template.identifier);
266
267 if (!touch) { return; }
268
269 removeTouch(template.identifier);
270 }
271
272 function removeTouch(identifier) {
273 remove(document, '.' + identifier, touchmove);
274 remove(document, '.' + identifier, touchend);
275 }
276
277
278 // Logic for deciding when to trigger a movestart.
279
280 function checkThreshold(e, template, touch, fn) {
281 var distX = touch.pageX - template.startX,
282 distY = touch.pageY - template.startY;
283
284 // Do nothing if the threshold has not been crossed.
285 if ((distX * distX) + (distY * distY) < (threshold * threshold)) { return; }
286
287 triggerStart(e, template, touch, distX, distY, fn);
288 }
289
290 function handled() {
291 // this._handled should return false once, and after return true.
292 this._handled = returnTrue;
293 return false;
294 }
295
296 function flagAsHandled(e) {
297 e._handled();
298 }
299
300 function triggerStart(e, template, touch, distX, distY, fn) {
301 var node = template.target,
302 touches, time;
303
304 touches = e.targetTouches;
305 time = e.timeStamp - template.timeStamp;
306
307 // Create a movestart object with some special properties that
308 // are passed only to the movestart handlers.
309 template.type = 'movestart';
310 template.distX = distX;
311 template.distY = distY;
312 template.deltaX = distX;
313 template.deltaY = distY;
314 template.pageX = touch.pageX;
315 template.pageY = touch.pageY;
316 template.velocityX = distX / time;
317 template.velocityY = distY / time;
318 template.targetTouches = touches;
319 template.finger = touches ?
320 touches.length :
321 1 ;
322
323 // The _handled method is fired to tell the default movestart
324 // handler that one of the move events is bound.
325 template._handled = handled;
326
327 // Pass the touchmove event so it can be prevented if or when
328 // movestart is handled.
329 template._preventTouchmoveDefault = function() {
330 e.preventDefault();
331 };
332
333 // Trigger the movestart event.
334 trigger(template.target, template);
335
336 // Unbind handlers that tracked the touch or mouse up till now.
337 fn(template.identifier);
338 }
339
340
341 // Handlers that control what happens following a movestart
342
343 function activeMousemove(e) {
344 var timer = e.data.timer;
345
346 e.data.touch = e;
347 e.data.timeStamp = e.timeStamp;
348 timer.kick();
349 }
350
351 function activeMouseend(e) {
352 var event = e.data.event,
353 timer = e.data.timer;
354
355 removeActiveMouse();
356
357 endEvent(event, timer, function() {
358 // Unbind the click suppressor, waiting until after mouseup
359 // has been handled.
360 setTimeout(function(){
361 remove(event.target, 'click', returnFalse);
362 }, 0);
363 });
364 }
365
366 function removeActiveMouse(event) {
367 remove(document, mouseevents.move, activeMousemove);
368 remove(document, mouseevents.end, activeMouseend);
369 }
370
371 function activeTouchmove(e) {
372 var event = e.data.event,
373 timer = e.data.timer,
374 touch = changedTouch(e, event);
375
376 if (!touch) { return; }
377
378 // Stop the interface from gesturing
379 e.preventDefault();
380
381 event.targetTouches = e.targetTouches;
382 e.data.touch = touch;
383 e.data.timeStamp = e.timeStamp;
384 timer.kick();
385 }
386
387 function activeTouchend(e) {
388 var event = e.data.event,
389 timer = e.data.timer,
390 touch = identifiedTouch(e.changedTouches, event.identifier);
391
392 // This isn't the touch you're looking for.
393 if (!touch) { return; }
394
395 removeActiveTouch(event);
396 endEvent(event, timer);
397 }
398
399 function removeActiveTouch(event) {
400 remove(document, '.' + event.identifier, activeTouchmove);
401 remove(document, '.' + event.identifier, activeTouchend);
402 }
403
404
405 // Logic for triggering move and moveend events
406
407 function updateEvent(event, touch, timeStamp, timer) {
408 var time = timeStamp - event.timeStamp;
409
410 event.type = 'move';
411 event.distX = touch.pageX - event.startX;
412 event.distY = touch.pageY - event.startY;
413 event.deltaX = touch.pageX - event.pageX;
414 event.deltaY = touch.pageY - event.pageY;
415
416 // Average the velocity of the last few events using a decay
417 // curve to even out spurious jumps in values.
418 event.velocityX = 0.3 * event.velocityX + 0.7 * event.deltaX / time;
419 event.velocityY = 0.3 * event.velocityY + 0.7 * event.deltaY / time;
420 event.pageX = touch.pageX;
421 event.pageY = touch.pageY;
422 }
423
424 function endEvent(event, timer, fn) {
425 timer.end(function(){
426 event.type = 'moveend';
427
428 trigger(event.target, event);
429
430 return fn && fn();
431 });
432 }
433
434
435 // jQuery special event definition
436
437 function setup(data, namespaces, eventHandle) {
438 // Stop the node from being dragged
439 //add(this, 'dragstart.move drag.move', preventDefault);
440
441 // Prevent text selection and touch interface scrolling
442 //add(this, 'mousedown.move', preventIgnoreTags);
443
444 // Tell movestart default handler that we've handled this
445 add(this, 'movestart.move', flagAsHandled);
446
447 // Don't bind to the DOM. For speed.
448 return true;
449 }
450
451 function teardown(namespaces) {
452 remove(this, 'dragstart drag', preventDefault);
453 remove(this, 'mousedown touchstart', preventIgnoreTags);
454 remove(this, 'movestart', flagAsHandled);
455
456 // Don't bind to the DOM. For speed.
457 return true;
458 }
459
460 function addMethod(handleObj) {
461 // We're not interested in preventing defaults for handlers that
462 // come from internal move or moveend bindings
463 if (handleObj.namespace === "move" || handleObj.namespace === "moveend") {
464 return;
465 }
466
467 // Stop the node from being dragged
468 add(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid, preventDefault, undefined, handleObj.selector);
469
470 // Prevent text selection and touch interface scrolling
471 add(this, 'mousedown.' + handleObj.guid, preventIgnoreTags, undefined, handleObj.selector);
472 }
473
474 function removeMethod(handleObj) {
475 if (handleObj.namespace === "move" || handleObj.namespace === "moveend") {
476 return;
477 }
478
479 remove(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid);
480 remove(this, 'mousedown.' + handleObj.guid);
481 }
482
483 jQuery.event.special.movestart = {
484 setup: setup,
485 teardown: teardown,
486 add: addMethod,
487 remove: removeMethod,
488
489 _default: function(e) {
490 var event, data;
491
492 // If no move events were bound to any ancestors of this
493 // target, high tail it out of here.
494 if (!e._handled()) { return; }
495
496 function update(time) {
497 updateEvent(event, data.touch, data.timeStamp);
498 trigger(e.target, event);
499 }
500
501 event = {
502 target: e.target,
503 startX: e.startX,
504 startY: e.startY,
505 pageX: e.pageX,
506 pageY: e.pageY,
507 distX: e.distX,
508 distY: e.distY,
509 deltaX: e.deltaX,
510 deltaY: e.deltaY,
511 velocityX: e.velocityX,
512 velocityY: e.velocityY,
513 timeStamp: e.timeStamp,
514 identifier: e.identifier,
515 targetTouches: e.targetTouches,
516 finger: e.finger
517 };
518
519 data = {
520 event: event,
521 timer: new Timer(update),
522 touch: undefined,
523 timeStamp: undefined
524 };
525
526 if (e.identifier === undefined) {
527 // We're dealing with a mouse
528 // Stop clicks from propagating during a move
529 add(e.target, 'click', returnFalse);
530 add(document, mouseevents.move, activeMousemove, data);
531 add(document, mouseevents.end, activeMouseend, data);
532 }
533 else {
534 // We're dealing with a touch. Stop touchmove doing
535 // anything defaulty.
536 e._preventTouchmoveDefault();
537 add(document, touchevents.move + '.' + e.identifier, activeTouchmove, data);
538 add(document, touchevents.end + '.' + e.identifier, activeTouchend, data);
539 }
540 }
541 };
542
543 jQuery.event.special.move = {
544 setup: function() {
545 // Bind a noop to movestart. Why? It's the movestart
546 // setup that decides whether other move events are fired.
547 add(this, 'movestart.move', jQuery.noop);
548 },
549
550 teardown: function() {
551 remove(this, 'movestart.move', jQuery.noop);
552 }
553 };
554
555 jQuery.event.special.moveend = {
556 setup: function() {
557 // Bind a noop to movestart. Why? It's the movestart
558 // setup that decides whether other move events are fired.
559 add(this, 'movestart.moveend', jQuery.noop);
560 },
561
562 teardown: function() {
563 remove(this, 'movestart.moveend', jQuery.noop);
564 }
565 };
566
567 add(document, 'mousedown.move', mousedown);
568 add(document, 'touchstart.move', touchstart);
569
570 // Make jQuery copy touch event properties over to the jQuery event
571 // object, if they are not already listed. But only do the ones we
572 // really need. IE7/8 do not have Array#indexOf(), but nor do they
573 // have touch events, so let's assume we can ignore them.
574 if (typeof Array.prototype.indexOf === 'function') {
575 (function(jQuery, undefined){
576 var props = ["changedTouches", "targetTouches"],
577 l = props.length;
578
579 while (l--) {
580 if (jQuery.event.props.indexOf(props[l]) === -1) {
581 jQuery.event.props.push(props[l]);
582 }
583 }
584 })(jQuery);
585 };
586 });
587