From f0e9037ecfba7b090b394d9c39ea5873dfb06c98 Mon Sep 17 00:00:00 2001 From: poongaloo Date: Mon, 8 Jun 2026 23:22:16 +0200 Subject: [PATCH] =?UTF-8?q?modernisation=20des=20=C3=A9v=C3=A9nements=20d?= =?UTF-8?q?=E2=80=99entr=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.fr.md | 1 + readme.md | 1 + tests/unit/turn.unit.test.js | 66 +++++++++++++++++++++++++++++++++--- turn.js | 29 +++++++++++----- turn.min.js | 2 +- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/README.fr.md b/README.fr.md index 79c8fd3..c83aa2b 100644 --- a/README.fr.md +++ b/README.fr.md @@ -15,6 +15,7 @@ La compatibilité avec tous les navigateurs et appareils modernes n'est pas enco ## Modifications réalisées - Migration des enregistrements d'événements internes depuis les anciens patterns jQuery `.bind()` / `.unbind()` vers `.on()` / `.off()`. +- Modernisation des événements d'entrée pour privilégier Pointer Events quand ils sont disponibles, tout en conservant les chemins touch et souris en fallback. - Ajout de namespaces d'événements : - `.turn` pour les événements du livre et du document ; - `.turnFlip` pour les événements internes des pages pliées. diff --git a/readme.md b/readme.md index efdb1ae..80bc5ee 100644 --- a/readme.md +++ b/readme.md @@ -15,6 +15,7 @@ Compatibility is not yet guaranteed for every modern browser or device. Safari/W ## What Changed - Updated internal event registration from deprecated jQuery `.bind()` / `.unbind()` patterns to `.on()` / `.off()`. +- Modernized input events to prefer Pointer Events when available, with the existing touch and mouse paths kept as fallbacks. - Added event namespaces: - `.turn` for book-level and document-level turn.js events. - `.turnFlip` for internal flip-page events. diff --git a/tests/unit/turn.unit.test.js b/tests/unit/turn.unit.test.js index 959089f..03acfda 100644 --- a/tests/unit/turn.unit.test.js +++ b/tests/unit/turn.unit.test.js @@ -23,6 +23,11 @@ function createFixture(pageCount = 4, windowOptions = {}) { }); Object.entries(windowOptions).forEach(([key, value]) => { + if (value === undefined) { + delete dom.window[key]; + return; + } + Object.defineProperty(dom.window, key, { value, configurable: true, @@ -718,16 +723,67 @@ describe('turn.js jQuery plugin', () => { .map(call => call[0]) .filter(name => typeof name === 'string'); - const pointerEvents = $.isTouch - ? ['touchstart.turn', 'touchmove.turn', 'touchend.turn'] - : ['mousedown.turn', 'mousemove.turn', 'mouseup.turn']; - expect(eventNames).toContain('turned.turn'); - expect(eventNames).toEqual(expect.arrayContaining(pointerEvents)); + expect(eventNames).toContain('pointerdown.turn'); + expect(eventNames).toContain('pointermove.turn'); + expect(eventNames).toContain('pointerup.turn pointercancel.turn'); expect(eventNames).toContain('pressed.turnFlip'); expect(eventNames).toContain('released.turnFlip'); expect(eventNames).toContain('start.turnFlip'); expect(eventNames).toContain('end.turnFlip'); expect(eventNames).toContain('flip.turnFlip'); }); + + it('falls back to touch events when Pointer Events are unavailable', () => { + fixture.window.close(); + fixture = createFixture(4, { + PointerEvent: undefined + }); + + const { $ } = fixture; + const onSpy = vi.spyOn($.fn, 'on'); + + $('#book').turn({ + width: 600, + height: 400, + display: 'double', + gradients: false, + acceleration: false + }); + + const eventNames = onSpy.mock.calls + .map(call => call[0]) + .filter(name => typeof name === 'string'); + + expect(eventNames).toContain('touchstart.turn'); + expect(eventNames).toContain('touchmove.turn'); + expect(eventNames).toContain('touchend.turn'); + }); + + it('removes pointer event handlers on destroy', () => { + fixture.window.close(); + fixture = createFixture(); + + const { $ } = fixture; + const offSpy = vi.spyOn($.fn, 'off'); + const $book = $('#book'); + + $book.turn({ + width: 600, + height: 400, + display: 'double', + gradients: false, + acceleration: false + }); + + $book.turn('destroy'); + + const eventNames = offSpy.mock.calls + .map(call => call[0]) + .filter(name => typeof name === 'string'); + + expect(eventNames).toContain('pointerdown.turn'); + expect(eventNames).toContain('pointermove.turn'); + expect(eventNames).toContain('pointerup.turn pointercancel.turn'); + }); }); diff --git a/turn.js b/turn.js index 02ee0ec..603b8a8 100644 --- a/turn.js +++ b/turn.js @@ -29,9 +29,14 @@ var has3d, A90 = PI/2, + hasPointer = 'PointerEvent' in window, + isTouch = 'ontouchstart' in window, - events = (isTouch) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'} + usesTouchEvents = !hasPointer && isTouch, + + events = (hasPointer) ? {start: 'pointerdown', move: 'pointermove', end: 'pointerup pointercancel'} + : (usesTouchEvents) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'} : {start: 'mousedown', move: 'mousemove', end: 'mouseup'}, frameTime = (window.performance && window.performance.now) ? @@ -207,6 +212,12 @@ var has3d, return Object.prototype.hasOwnProperty.call(object, property); }, + namespacedEvents = function(names, namespace) { + return $.map(names.split(' '), function(name) { + return name + namespace; + }).join(' '); + }, + clamp = function(value, min, max) { value = parseFloat(value); return Math.max(min, Math.min(max, isNaN(value) ? min : value)); @@ -474,10 +485,10 @@ turnMethods = { } }; - $(this).on(events.start + turnEventNamespace, data.eventHandlers.start); + $(this).on(namespacedEvents(events.start, turnEventNamespace), data.eventHandlers.start); - $(document).on(events.move + turnEventNamespace, data.eventHandlers.move). - on(events.end + turnEventNamespace, data.eventHandlers.end); + $(document).on(namespacedEvents(events.move, turnEventNamespace), data.eventHandlers.move). + on(namespacedEvents(events.end, turnEventNamespace), data.eventHandlers.end); data.done = true; @@ -500,10 +511,10 @@ turnMethods = { this.off(turnEventNamespace); if (data.eventHandlers) { - this.off(events.start + turnEventNamespace, data.eventHandlers.start); + this.off(namespacedEvents(events.start, turnEventNamespace), data.eventHandlers.start); $(document). - off(events.move + turnEventNamespace, data.eventHandlers.move). - off(events.end + turnEventNamespace, data.eventHandlers.end); + off(namespacedEvents(events.move, turnEventNamespace), data.eventHandlers.move). + off(namespacedEvents(events.end, turnEventNamespace), data.eventHandlers.end); } for (page in data.pageObjs) { @@ -1569,7 +1580,7 @@ flipMethods = { return false; } - e = (isTouch) ? e.originalEvent.touches : [e]; + e = (usesTouchEvents) ? e.originalEvent.touches : [e]; var data = flipData(this), pos = data.parent.offset(), @@ -2066,7 +2077,7 @@ flipMethods = { var data = this.data().f; if (!data.disabled) { - e = (isTouch) ? e.originalEvent.touches : [e]; + e = (usesTouchEvents) ? e.originalEvent.touches : [e]; if (data.corner) { diff --git a/turn.min.js b/turn.min.js index f244709..a046754 100644 --- a/turn.min.js +++ b/turn.min.js @@ -1 +1 @@ -!function(t){"use strict";var e,a="",r=Math.PI,i=r/2,n="ontouchstart"in window,s=n?{start:"touchstart",move:"touchmove",end:"touchend"}:{start:"mousedown",move:"mousemove",end:"mouseup"},o=window.performance&&window.performance.now?function(){return window.performance.now()}:function(){return(new Date).getTime()},p=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(t){return window.setTimeout(function(){t(o())},16)},l=window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(t){window.clearTimeout(t)},h=".turn",d=".turnFlip",g={backward:["bl","tl"],forward:["br","tr"],all:["tl","bl","tr","br"]},f=["single","double"],c={page:1,gradients:!0,duration:600,acceleration:!0,display:"double",when:null},u={folding:null,corners:"forward",cornerSize:100,gradients:!0,duration:600,acceleration:!0},v={0:{top:0,left:0,right:"auto",bottom:"auto"},1:{top:0,right:0,left:"auto",bottom:"auto"}},w="turn-js-base-css",m=[".turn-page{position:absolute;}",".turn-page-wrapper,.turn-flip-wrapper,.turn-fold-wrapper,.turn-shadow{position:absolute;overflow:hidden;}",".turn-fold-parent{position:absolute;overflow:visible;pointer-events:none;}",".turn-fold-inner{position:absolute;overflow:visible;}",".turn-fold-page{cursor:default;}",".turn-back-shadow{overflow:hidden;}"].join(""),b=function(){document.getElementById(w)||t("