@signalk/app-dock 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/package.json +1 -1
- package/public/dock.js +36 -20
package/README.md
CHANGED
|
@@ -48,13 +48,23 @@ if (window.self !== window.top) {
|
|
|
48
48
|
document.addEventListener(
|
|
49
49
|
'pointerdown',
|
|
50
50
|
(e) => {
|
|
51
|
-
window.top.postMessage(
|
|
51
|
+
window.top.postMessage(
|
|
52
|
+
{
|
|
53
|
+
type: 'signalk-app-dock:pointerdown',
|
|
54
|
+
x: e.clientX,
|
|
55
|
+
y: e.clientY,
|
|
56
|
+
t: performance.timeOrigin + e.timeStamp
|
|
57
|
+
},
|
|
58
|
+
'*'
|
|
59
|
+
)
|
|
52
60
|
},
|
|
53
61
|
{ passive: true, capture: true }
|
|
54
62
|
)
|
|
55
63
|
}
|
|
56
64
|
```
|
|
57
65
|
|
|
66
|
+
`t` is when the tap happened. It is optional, but a page that is busy, rendering a radar display for example, can post its messages late, and without `t` the dock can only go by when they arrive, which can split a double-tap in two.
|
|
67
|
+
|
|
58
68
|
The dock accepts these messages only from a page of the app on screen that it cannot listen to itself, or a frame inside that page, and all they do is count towards a double-tap. The tab goes away once every such page has sent one. A page that loads again starts over: the tab is back until its first tap.
|
|
59
69
|
|
|
60
70
|
## Install as an app
|
package/package.json
CHANGED
package/public/dock.js
CHANGED
|
@@ -674,6 +674,19 @@
|
|
|
674
674
|
// two taps pair up only within the same one.
|
|
675
675
|
let lastTapSpace = null
|
|
676
676
|
|
|
677
|
+
// Taps are timed by when they happened, not when their handler ran: a page
|
|
678
|
+
// busy rendering can hold a pointerdown back long enough to split a
|
|
679
|
+
// double-tap. Times are on the clock every document in the browser shares,
|
|
680
|
+
// performance.timeOrigin plus the page's own high-resolution time.
|
|
681
|
+
function nowTime() {
|
|
682
|
+
return performance.timeOrigin + performance.now()
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function eventTime(doc, e) {
|
|
686
|
+
const perf = doc.defaultView && doc.defaultView.performance
|
|
687
|
+
return perf ? perf.timeOrigin + e.timeStamp : nowTime()
|
|
688
|
+
}
|
|
689
|
+
|
|
677
690
|
function triggerDock() {
|
|
678
691
|
if (DEBUG) console.log('[Dock] triggerDock called, dockVisible=', dockVisible)
|
|
679
692
|
if (dockVisible) return
|
|
@@ -687,15 +700,14 @@
|
|
|
687
700
|
|
|
688
701
|
const DEBUG = new URLSearchParams(location.search).has('debugDock')
|
|
689
702
|
|
|
690
|
-
function handleTap(clientX, clientY, space = null) {
|
|
703
|
+
function handleTap(clientX, clientY, space = null, time = nowTime()) {
|
|
691
704
|
if (dockVisible) return
|
|
692
|
-
const now = Date.now()
|
|
693
705
|
const dx = clientX - lastTapX
|
|
694
706
|
const dy = clientY - lastTapY
|
|
695
707
|
const dist = Math.round(Math.sqrt(dx * dx + dy * dy))
|
|
696
708
|
const close = dist <= DOUBLE_TAP_SLOP && space === lastTapSpace
|
|
697
|
-
const gap =
|
|
698
|
-
const match = gap < DOUBLE_TAP_MS && close
|
|
709
|
+
const gap = time - lastTapTime
|
|
710
|
+
const match = gap >= 0 && gap < DOUBLE_TAP_MS && close
|
|
699
711
|
if (DEBUG) {
|
|
700
712
|
console.log('[Dock] tap', { x: Math.round(clientX), y: Math.round(clientY), gap, dist, close, match })
|
|
701
713
|
}
|
|
@@ -703,7 +715,7 @@
|
|
|
703
715
|
lastTapTime = 0
|
|
704
716
|
triggerDock()
|
|
705
717
|
} else {
|
|
706
|
-
lastTapTime =
|
|
718
|
+
lastTapTime = time
|
|
707
719
|
lastTapX = clientX
|
|
708
720
|
lastTapY = clientY
|
|
709
721
|
lastTapSpace = space
|
|
@@ -722,22 +734,23 @@
|
|
|
722
734
|
|
|
723
735
|
// Dedupe: touchstart and pointerdown often both fire for the same gesture.
|
|
724
736
|
// We accept whichever arrives first and ignore the other if it's within
|
|
725
|
-
// 50 ms (the browser's synthesized-event coalescence window).
|
|
726
|
-
|
|
737
|
+
// 50 ms (the browser's synthesized-event coalescence window). The window is
|
|
738
|
+
// measured in event time: after the page stalls, two real taps can be
|
|
739
|
+
// handled back to back, and neither may be taken for the other's twin.
|
|
740
|
+
let lastEventAt = -Infinity
|
|
727
741
|
const COALESCE_MS = 50
|
|
728
742
|
|
|
729
|
-
const onDown = (x, y) => {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
handleTap(x, y)
|
|
743
|
+
const onDown = (x, y, time) => {
|
|
744
|
+
if (Math.abs(time - lastEventAt) < COALESCE_MS) return
|
|
745
|
+
lastEventAt = time
|
|
746
|
+
handleTap(x, y, null, time)
|
|
734
747
|
}
|
|
735
748
|
|
|
736
749
|
doc.addEventListener(
|
|
737
750
|
'pointerdown',
|
|
738
751
|
(e) => {
|
|
739
752
|
const o = offset()
|
|
740
|
-
onDown(e.clientX + o.x, e.clientY + o.y)
|
|
753
|
+
onDown(e.clientX + o.x, e.clientY + o.y, eventTime(doc, e))
|
|
741
754
|
},
|
|
742
755
|
{ passive: true, capture: true }
|
|
743
756
|
)
|
|
@@ -752,7 +765,7 @@
|
|
|
752
765
|
if (!e.touches || e.touches.length === 0) return
|
|
753
766
|
const t = e.touches[0]
|
|
754
767
|
const o = offset()
|
|
755
|
-
onDown(t.clientX + o.x, t.clientY + o.y)
|
|
768
|
+
onDown(t.clientX + o.x, t.clientY + o.y, eventTime(doc, e))
|
|
756
769
|
},
|
|
757
770
|
{ passive: true, capture: true }
|
|
758
771
|
)
|
|
@@ -823,7 +836,7 @@
|
|
|
823
836
|
// loaded.
|
|
824
837
|
const unreachableFrames = new WeakMap()
|
|
825
838
|
const forwardingFrames = new WeakSet()
|
|
826
|
-
let lastForwardedAt =
|
|
839
|
+
let lastForwardedAt = -Infinity
|
|
827
840
|
|
|
828
841
|
// The app frame a frame belongs to: the frame itself, or the one holding
|
|
829
842
|
// the document it sits in, found through same-origin parents.
|
|
@@ -913,16 +926,19 @@
|
|
|
913
926
|
forwardingFrames.add(from)
|
|
914
927
|
updateHandle()
|
|
915
928
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
929
|
+
// The page can say when the tap happened. Without that, or with a time
|
|
930
|
+
// that has not come yet, the message's arrival stands in for it.
|
|
931
|
+
const received = nowTime()
|
|
932
|
+
const time = Number.isFinite(data.t) && data.t <= received ? data.t : received
|
|
933
|
+
if (Math.abs(time - lastForwardedAt) < FORWARD_COALESCE_MS) return
|
|
934
|
+
lastForwardedAt = time
|
|
919
935
|
// A frame inside the page sits somewhere the dock cannot see, so its
|
|
920
936
|
// taps keep their own coordinates and pair up only with each other.
|
|
921
937
|
if (e.source === from.contentWindow) {
|
|
922
938
|
const o = offsetInDock(from)
|
|
923
|
-
handleTap(data.x + o.x, data.y + o.y)
|
|
939
|
+
handleTap(data.x + o.x, data.y + o.y, null, time)
|
|
924
940
|
} else {
|
|
925
|
-
handleTap(data.x, data.y, e.source)
|
|
941
|
+
handleTap(data.x, data.y, e.source, time)
|
|
926
942
|
}
|
|
927
943
|
})
|
|
928
944
|
|