PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2.9
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2.9
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 2.2.9, at assets/js/jquery.event.move.js

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