@neuroplastio/xterm-addon-hotty 0.1.0-next.1 → 0.1.0-next.3
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 +32 -7
- package/dist/hotty-xterm.js +286 -55
- package/dist/hotty-xterm.js.map +3 -3
- package/dist/types/hostcss.d.ts +6 -0
- package/dist/types/steps.d.ts +9 -0
- package/dist/types/surface.d.ts +67 -4
- package/dist/types/touch.d.ts +45 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -171,11 +171,33 @@ runs programs for whoever connects.
|
|
|
171
171
|
lose the capture. The browser clicks the root on a captured release, so
|
|
172
172
|
the drag reports the click itself when it ends where it began. The host
|
|
173
173
|
stylesheet makes the elements that opt in unselectable, important in its
|
|
174
|
-
layer, whatever the document's CSS.
|
|
174
|
+
layer, whatever the document's CSS.
|
|
175
|
+
|
|
176
|
+
A touch drags such an element only where its `touch-action` allows no
|
|
177
|
+
pan along the touch's first move past 8 px (the larger delta; a tie is a
|
|
178
|
+
pan). The value is the element's own, met with its ancestors' up to the
|
|
179
|
+
nearest one that scrolls, both included, as Pointer Events have it; the
|
|
180
|
+
host's own `touch-action` on the root (below) is not the document's. The
|
|
181
|
+
touch then reports `press` and `dragstart` at the cell where it began,
|
|
182
|
+
and drags from there as a mouse does; the lift reports `dragend`, and the
|
|
183
|
+
click where it began. A second finger ends it with `dragend` and no
|
|
184
|
+
target, and nothing more is heard until every finger lifts. A long press,
|
|
185
|
+
or Alt held at the touch, never drags. Any other touch pans as below.
|
|
186
|
+
|
|
187
|
+
An element that also has `data-steps="<x> [<y>]"` (whole numbers, 0 for
|
|
188
|
+
none along that axis) says where in it the pointer is: every event of a
|
|
189
|
+
drag of it carries `x` and `y`, the pointer's step along its border box
|
|
190
|
+
(`getBoundingClientRect`, as it is at each move), rounded to the nearest
|
|
191
|
+
and clamped to 0 and the count, wherever the pointer or the finger is.
|
|
192
|
+
A `drag` also goes out each time the step changes. The counts are read
|
|
193
|
+
when the drag starts; once the element has left the document, the step
|
|
194
|
+
stays as it last was, as it does in a `dragend` that ends a drag early.
|
|
195
|
+
The capabilities report `steps: true`.
|
|
175
196
|
- **Presses** (SPEC §5.2, §9: `p=1` on `a=place`). Every primary press of a
|
|
176
197
|
mouse or a pen in the window reports `press` on its pointerdown, before
|
|
177
198
|
the drag and the focus it causes; a tap reports it on the mousedown the
|
|
178
|
-
browser makes for it
|
|
199
|
+
browser makes for it, and a touch that drags when it becomes a drag. A
|
|
200
|
+
touch that pans presses nothing. A press on a hyperlink is the terminal's.
|
|
179
201
|
- **Fit** (SPEC §5.2, §9: `f=1` on `a=place`). The program hears `fit`
|
|
180
202
|
with the rows the document needs at the placement's width whenever they
|
|
181
203
|
differ from the rows it heard last, starting from the placement's own;
|
|
@@ -232,8 +254,8 @@ runs programs for whoever connects.
|
|
|
232
254
|
- It shows no scrollbars, and pans nothing on a touch
|
|
233
255
|
(`touch-action: none`). Any scroll offset the browser sets goes back to
|
|
234
256
|
zero, except a text field's own text.
|
|
235
|
-
- A wheel over a surface goes to the terminal, and so does a touch
|
|
236
|
-
as wheel events at the finger. After the finger lifts, the drag keeps
|
|
257
|
+
- A wheel over a surface goes to the terminal, and so does a touch that
|
|
258
|
+
pans, as wheel events at the finger. After the finger lifts, the drag keeps
|
|
237
259
|
going and slows down.
|
|
238
260
|
- Taps and long presses stay the surface's.
|
|
239
261
|
- On the cells, the addon handles touch too (the `touch` option, on by
|
|
@@ -280,9 +302,12 @@ runs programs for whoever connects.
|
|
|
280
302
|
the pointer that can move that way can (the browser scrolls it), else
|
|
281
303
|
on to the terminal, as over the cells, unless `overscroll-behavior`
|
|
282
304
|
stops it. A gesture begun over the cells stays the terminal's when a
|
|
283
|
-
surface comes under the pointer. A touch
|
|
284
|
-
it has a direction: the browser pans the document, or the
|
|
285
|
-
the terminal as before.
|
|
305
|
+
surface comes under the pointer. A touch that pans decides the same way
|
|
306
|
+
once it has a direction: the browser pans the document, or the touch
|
|
307
|
+
goes to the terminal as before. Where the document's `touch-action`
|
|
308
|
+
would stop the browser's pan (a `none` on a box that doesn't drag), the
|
|
309
|
+
addon pans the box itself, so that only a drag element takes a touch
|
|
310
|
+
from scrolling (SPEC §9.1).
|
|
286
311
|
- **Keys.** While the surface has the keyboard, the keys a browser
|
|
287
312
|
scrolls with that the focused element neither uses nor gives the
|
|
288
313
|
program (SPEC §10.2) scroll the innermost box, from the focused
|
package/dist/hotty-xterm.js
CHANGED
|
@@ -67,14 +67,13 @@ function scrollCss(axes, pageScrolls) {
|
|
|
67
67
|
if (!axes) {
|
|
68
68
|
return [
|
|
69
69
|
"@layer hotty-host {",
|
|
70
|
-
`:root { overflow: hidden; touch-action: ${pageScrolls
|
|
70
|
+
`:root { overflow: hidden; touch-action: ${rootTouchAction(axes, pageScrolls)}; }`,
|
|
71
71
|
"* { scrollbar-width: none !important; }",
|
|
72
72
|
"::-webkit-scrollbar { display: none !important; }",
|
|
73
73
|
"}"
|
|
74
74
|
].join("\n");
|
|
75
75
|
}
|
|
76
|
-
const
|
|
77
|
-
const lines = ["@layer hotty-host {", `:root { touch-action: ${pan}; }`];
|
|
76
|
+
const lines = ["@layer hotty-host {", `:root { touch-action: ${rootTouchAction(axes, pageScrolls)}; }`];
|
|
78
77
|
if (axes !== 3) {
|
|
79
78
|
const clipped = axes === 1 ? "overflow-x" : "overflow-y";
|
|
80
79
|
lines.push(`:root, :root:not([${UNCLIPPED}]) [${CLIP}] { ${clipped}: hidden !important; }`);
|
|
@@ -82,6 +81,10 @@ function scrollCss(axes, pageScrolls) {
|
|
|
82
81
|
lines.push("}");
|
|
83
82
|
return lines.join("\n");
|
|
84
83
|
}
|
|
84
|
+
function rootTouchAction(axes, pageScrolls) {
|
|
85
|
+
if (pageScrolls) return "manipulation";
|
|
86
|
+
return axes === 3 ? "pan-x pan-y" : axes === 1 ? "pan-y" : axes === 2 ? "pan-x" : "none";
|
|
87
|
+
}
|
|
85
88
|
function fontStack(family) {
|
|
86
89
|
const f = family.trim();
|
|
87
90
|
if (!f) return "monospace";
|
|
@@ -515,9 +518,27 @@ var Store = class {
|
|
|
515
518
|
|
|
516
519
|
// src/touch.ts
|
|
517
520
|
var DRAG_SLOP = 8;
|
|
521
|
+
var LONG_PRESS_MS = 500;
|
|
518
522
|
var FLING_DECAY = 0.996;
|
|
519
523
|
var REST_MS = 80;
|
|
524
|
+
function touchPans(value) {
|
|
525
|
+
const v = value.trim();
|
|
526
|
+
if (!v || v === "auto" || v === "manipulation") return [true, true];
|
|
527
|
+
const words = v.split(/\s+/);
|
|
528
|
+
return [words.some((w) => /^pan-(x|left|right)$/.test(w)), words.some((w) => /^pan-(y|up|down)$/.test(w))];
|
|
529
|
+
}
|
|
530
|
+
function panBlocked(pans, dx, dy) {
|
|
531
|
+
const ax = Math.abs(dx), ay = Math.abs(dy);
|
|
532
|
+
return ax > ay ? !pans.x : ay > ax ? !pans.y : false;
|
|
533
|
+
}
|
|
520
534
|
var Touch = class {
|
|
535
|
+
touch = null;
|
|
536
|
+
fling = 0;
|
|
537
|
+
off = [];
|
|
538
|
+
win;
|
|
539
|
+
host;
|
|
540
|
+
exclusive;
|
|
541
|
+
engage;
|
|
521
542
|
/**
|
|
522
543
|
* `exclusive`: every touch here is this Touch's (the cells: xterm.js's own
|
|
523
544
|
* touch handling, which listens on the document, never sees it). Otherwise
|
|
@@ -537,19 +558,12 @@ var Touch = class {
|
|
|
537
558
|
};
|
|
538
559
|
on("touchstart", (e) => this.start(e), true);
|
|
539
560
|
on("touchmove", (e) => this.move(e), false);
|
|
540
|
-
on("touchend", (e) => this.end(e),
|
|
561
|
+
on("touchend", (e) => this.end(e), false);
|
|
541
562
|
on("touchcancel", (e) => {
|
|
542
563
|
this.keep(e);
|
|
543
|
-
this.
|
|
564
|
+
this.drop();
|
|
544
565
|
}, true);
|
|
545
566
|
}
|
|
546
|
-
win;
|
|
547
|
-
host;
|
|
548
|
-
exclusive;
|
|
549
|
-
engage;
|
|
550
|
-
touch = null;
|
|
551
|
-
fling = 0;
|
|
552
|
-
off = [];
|
|
553
567
|
dispose() {
|
|
554
568
|
this.stopFling();
|
|
555
569
|
for (const f of this.off) f();
|
|
@@ -557,32 +571,62 @@ var Touch = class {
|
|
|
557
571
|
keep(e) {
|
|
558
572
|
if (this.exclusive) e.stopPropagation();
|
|
559
573
|
}
|
|
574
|
+
/** The touch under way ends without its lift: a drag the host holds
|
|
575
|
+
* hears it. */
|
|
576
|
+
drop() {
|
|
577
|
+
const s = this.touch;
|
|
578
|
+
this.touch = null;
|
|
579
|
+
if (s?.held) this.host.dragCancel?.();
|
|
580
|
+
}
|
|
560
581
|
start(e) {
|
|
561
582
|
this.keep(e);
|
|
562
583
|
this.stopFling();
|
|
563
584
|
const t = e.touches.length === 1 ? e.touches[0] : void 0;
|
|
564
585
|
if (!t || this.engage && !this.engage(e)) {
|
|
565
|
-
this.
|
|
586
|
+
this.drop();
|
|
566
587
|
return;
|
|
567
588
|
}
|
|
568
589
|
const [x, y] = this.host.toPage(t.clientX, t.clientY);
|
|
569
|
-
|
|
590
|
+
const begin = {
|
|
591
|
+
target: e.target,
|
|
592
|
+
x: t.clientX,
|
|
593
|
+
y: t.clientY,
|
|
594
|
+
time: e.timeStamp,
|
|
595
|
+
shiftKey: e.shiftKey,
|
|
596
|
+
ctrlKey: e.ctrlKey,
|
|
597
|
+
altKey: e.altKey,
|
|
598
|
+
metaKey: e.metaKey
|
|
599
|
+
};
|
|
600
|
+
this.touch = { id: t.identifier, x0: x, y0: y, x, y, t: e.timeStamp, t0: e.timeStamp, vx: 0, vy: 0, dragging: false, held: false, begin };
|
|
570
601
|
}
|
|
571
602
|
move(e) {
|
|
572
603
|
this.keep(e);
|
|
573
604
|
const s = this.touch;
|
|
574
605
|
if (!s) return;
|
|
575
606
|
if (e.touches.length !== 1) {
|
|
576
|
-
this.
|
|
607
|
+
this.drop();
|
|
577
608
|
return;
|
|
578
609
|
}
|
|
579
610
|
const t = [...e.changedTouches].find((c) => c.identifier === s.id);
|
|
580
611
|
if (!t) return;
|
|
612
|
+
if (s.held) {
|
|
613
|
+
e.preventDefault();
|
|
614
|
+
this.host.dragMove?.(t.clientX, t.clientY, e);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
581
617
|
const [x, y] = this.host.toPage(t.clientX, t.clientY);
|
|
582
618
|
if (!s.dragging && Math.hypot(x - s.x0, y - s.y0) < DRAG_SLOP) return;
|
|
583
|
-
if (!s.dragging && this.host.claim
|
|
584
|
-
this.
|
|
585
|
-
|
|
619
|
+
if (!s.dragging && this.host.claim) {
|
|
620
|
+
const claimed = this.host.claim(s.x0 - x, s.y0 - y, e, s.begin);
|
|
621
|
+
if (!claimed) {
|
|
622
|
+
this.touch = null;
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (claimed === "drag") {
|
|
626
|
+
s.held = true;
|
|
627
|
+
e.preventDefault();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
586
630
|
}
|
|
587
631
|
s.dragging = true;
|
|
588
632
|
e.preventDefault();
|
|
@@ -599,13 +643,19 @@ var Touch = class {
|
|
|
599
643
|
end(e) {
|
|
600
644
|
this.keep(e);
|
|
601
645
|
const s = this.touch;
|
|
602
|
-
|
|
646
|
+
const t = s && [...e.changedTouches].find((c) => c.identifier === s.id);
|
|
647
|
+
if (!s || !t) return;
|
|
603
648
|
this.touch = null;
|
|
649
|
+
if (s.held) {
|
|
650
|
+
e.preventDefault();
|
|
651
|
+
this.host.dragEnd?.(t.clientX, t.clientY, e);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
604
654
|
if (s.dragging) {
|
|
605
655
|
if (e.timeStamp - s.t < REST_MS) this.startFling(-s.vx, -s.vy, s.x, s.y);
|
|
606
656
|
return;
|
|
607
657
|
}
|
|
608
|
-
if (this.host.tap && e.timeStamp - s.t0 <
|
|
658
|
+
if (this.host.tap && e.timeStamp - s.t0 < LONG_PRESS_MS) {
|
|
609
659
|
e.preventDefault();
|
|
610
660
|
this.host.tap(s.x, s.y);
|
|
611
661
|
}
|
|
@@ -633,6 +683,21 @@ var Touch = class {
|
|
|
633
683
|
}
|
|
634
684
|
};
|
|
635
685
|
|
|
686
|
+
// src/steps.ts
|
|
687
|
+
function parseSteps(value) {
|
|
688
|
+
if (value === null) return null;
|
|
689
|
+
const words = value.trim().split(/\s+/);
|
|
690
|
+
if (words.length > 2 || !words.every((w) => /^[0-9]+$/.test(w))) return null;
|
|
691
|
+
const [x, y = 0] = words.map(Number);
|
|
692
|
+
if (!Number.isSafeInteger(x) || !Number.isSafeInteger(y) || x === 0 && y === 0) return null;
|
|
693
|
+
return [x, y];
|
|
694
|
+
}
|
|
695
|
+
function stepAt(offset, size, count) {
|
|
696
|
+
if (!(size > 0)) return 0;
|
|
697
|
+
const at = Math.min(Math.max(offset / size, 0), 1) * count;
|
|
698
|
+
return Math.min(Math.floor(at + 0.5), count);
|
|
699
|
+
}
|
|
700
|
+
|
|
636
701
|
// src/resolver.ts
|
|
637
702
|
var DROP = /* @__PURE__ */ new Set(["script", "iframe", "frame", "frameset", "object", "embed", "applet", "base", "meta", "portal", "fencedframe"]);
|
|
638
703
|
var DROP_ATTRS = /* @__PURE__ */ new Set(["srcdoc", "autofocus", "http-equiv", "ping", "nonce", "integrity"]);
|
|
@@ -1371,8 +1436,13 @@ var Surface = class {
|
|
|
1371
1436
|
/** The axes the document asked to scroll along (`scroll`, SPEC §5.1): 1
|
|
1372
1437
|
* vertically, 2 horizontally, 3 both; 0, nothing scrolls. */
|
|
1373
1438
|
axes = 0;
|
|
1374
|
-
/** Where the touch drag under way goes, once it has a direction
|
|
1439
|
+
/** Where the touch drag under way goes, once it has a direction: "self",
|
|
1440
|
+
* the document, but scrolled by the surface, since the document's
|
|
1441
|
+
* `touch-action` would stop the browser's own pan (SPEC §9.1: it changes
|
|
1442
|
+
* nothing there). */
|
|
1375
1443
|
touchRoute = "terminal";
|
|
1444
|
+
/** The box a touch drag scrolls itself ("self"). */
|
|
1445
|
+
touchBox = null;
|
|
1376
1446
|
/** The box the wheel gesture under way scrolls, if it went to the
|
|
1377
1447
|
* document: the gesture moves no other (`scrollWheel`). */
|
|
1378
1448
|
wheelBox = null;
|
|
@@ -1727,6 +1797,11 @@ var Surface = class {
|
|
|
1727
1797
|
setTimeout(() => this.auxPress = false, 0);
|
|
1728
1798
|
return;
|
|
1729
1799
|
}
|
|
1800
|
+
this.pressAt(e);
|
|
1801
|
+
}
|
|
1802
|
+
/** What a click (SPEC §10.1) at `e`'s target does to the keyboard: a
|
|
1803
|
+
* press of the primary button, a tap, or a touch that becomes a drag. */
|
|
1804
|
+
pressAt(e) {
|
|
1730
1805
|
const el = this.detachedState ? null : focusTargetAt(e, this.hyper);
|
|
1731
1806
|
if (el) {
|
|
1732
1807
|
this.pressed = el;
|
|
@@ -2229,25 +2304,49 @@ var Surface = class {
|
|
|
2229
2304
|
if (!id) return;
|
|
2230
2305
|
const [c, r] = this.cellOf(e);
|
|
2231
2306
|
const keys = keysOf(e);
|
|
2232
|
-
|
|
2233
|
-
this.
|
|
2307
|
+
const d = { pointer: e.pointerId, start: id, steps: stepsOf(start), target: id, c, r, keys, step: {} };
|
|
2308
|
+
d.step = this.stepIn(d, e.clientX, e.clientY);
|
|
2309
|
+
this.drag = d;
|
|
2310
|
+
this.emit("dragstart", id, { c, r, keys, ...d.step });
|
|
2234
2311
|
try {
|
|
2235
2312
|
this.doc.documentElement.setPointerCapture(e.pointerId);
|
|
2236
2313
|
} catch {
|
|
2237
2314
|
}
|
|
2238
2315
|
}
|
|
2239
|
-
/** A move during a drag
|
|
2240
|
-
* while it has none, each time its cell does. */
|
|
2316
|
+
/** A move during a drag (`moveDrag`). */
|
|
2241
2317
|
onDragMove(e) {
|
|
2242
2318
|
const d = this.drag;
|
|
2243
2319
|
if (!d || e.pointerId !== d.pointer) return;
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2320
|
+
this.moveDrag(d, e.clientX, e.clientY, keysOf(e));
|
|
2321
|
+
}
|
|
2322
|
+
/** The pointer or the finger of a drag moved to (x, y) of the document:
|
|
2323
|
+
* an event each time its target changes, while it has none each time its
|
|
2324
|
+
* cell does, and each time its step does (SPEC §9.1); one for a move that
|
|
2325
|
+
* changes more than one. */
|
|
2326
|
+
moveDrag(d, x, y, keys) {
|
|
2327
|
+
const [c, r] = this.cellAt(x, y);
|
|
2328
|
+
const target = this.dragTarget(this.elementAtPoint(x, y, c, r));
|
|
2329
|
+
const step = this.stepIn(d, x, y);
|
|
2330
|
+
if (target !== d.target || target === "" && (c !== d.c || r !== d.r) || step.x !== d.step.x || step.y !== d.step.y) {
|
|
2331
|
+
this.emit("drag", target, { c, r, keys, ...step });
|
|
2249
2332
|
}
|
|
2250
|
-
Object.assign(d, { target, c, r, keys });
|
|
2333
|
+
Object.assign(d, { target, c, r, keys, step });
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Where (x, y) of the document is in the element a drag started on, in
|
|
2337
|
+
* its steps (SPEC §9.1): measured against its border box where the user
|
|
2338
|
+
* sees it now, wherever the point is, and as they last were once the
|
|
2339
|
+
* element has left the document. None when it has no steps.
|
|
2340
|
+
*/
|
|
2341
|
+
stepIn(d, x, y) {
|
|
2342
|
+
const s = d.steps;
|
|
2343
|
+
if (!s) return {};
|
|
2344
|
+
if (!s.el.isConnected) return d.step;
|
|
2345
|
+
const box = s.el.getBoundingClientRect();
|
|
2346
|
+
const step = {};
|
|
2347
|
+
if (s.x) step.x = stepAt(x - box.left, box.width, s.x);
|
|
2348
|
+
if (s.y) step.y = stepAt(y - box.top, box.height, s.y);
|
|
2349
|
+
return step;
|
|
2251
2350
|
}
|
|
2252
2351
|
/** The release ends the drag, wherever it is. Where it began, it is the
|
|
2253
2352
|
* click it would have been without the drag; anywhere else, no click. */
|
|
@@ -2258,7 +2357,7 @@ var Surface = class {
|
|
|
2258
2357
|
const [c, r] = this.cellOf(e);
|
|
2259
2358
|
const at = this.elementAt(e, c, r);
|
|
2260
2359
|
const target = this.dragTarget(at);
|
|
2261
|
-
this.emit("dragend", target, { c, r, keys: keysOf(e) });
|
|
2360
|
+
this.emit("dragend", target, { c, r, keys: keysOf(e), ...this.stepIn(d, e.clientX, e.clientY) });
|
|
2262
2361
|
this.dragReleased = true;
|
|
2263
2362
|
setTimeout(() => this.dragReleased = false, 0);
|
|
2264
2363
|
if (target === d.start && at) this.reportClick(ancestry(at));
|
|
@@ -2271,13 +2370,13 @@ var Surface = class {
|
|
|
2271
2370
|
/**
|
|
2272
2371
|
* Ends a drag under way without a release (SPEC §9.1: its placement went
|
|
2273
2372
|
* away, a new document came, or the pointer was lost): `dragend` with no
|
|
2274
|
-
* target, at the last cell the program heard of.
|
|
2373
|
+
* target, at the last cell and step the program heard of.
|
|
2275
2374
|
*/
|
|
2276
2375
|
cancelDrag() {
|
|
2277
2376
|
const d = this.drag;
|
|
2278
2377
|
if (!d) return;
|
|
2279
2378
|
this.dropDrag();
|
|
2280
|
-
this.emit("dragend", "", { c: d.c, r: d.r, keys: d.keys });
|
|
2379
|
+
this.emit("dragend", "", { c: d.c, r: d.r, keys: d.keys, ...d.step });
|
|
2281
2380
|
}
|
|
2282
2381
|
/** Ends a drag under way, silently (a detached or deleted surface). */
|
|
2283
2382
|
dropDrag() {
|
|
@@ -2292,13 +2391,11 @@ var Surface = class {
|
|
|
2292
2391
|
/** The surface's cell under the pointer, from its top left (the frame's
|
|
2293
2392
|
* origin), counting on past its edges. */
|
|
2294
2393
|
cellOf(e) {
|
|
2295
|
-
return
|
|
2394
|
+
return this.cellAt(e.clientX, e.clientY);
|
|
2296
2395
|
}
|
|
2297
2396
|
/** The element under the pointer, if it is in the window (SPEC §5.2). */
|
|
2298
2397
|
elementAt(e, c, r) {
|
|
2299
|
-
|
|
2300
|
-
if (c < w.x || c >= w.x + w.w || r < w.y || r >= w.y + w.h) return null;
|
|
2301
|
-
return this.doc.elementFromPoint(e.clientX, e.clientY);
|
|
2398
|
+
return this.elementAtPoint(e.clientX, e.clientY, c, r);
|
|
2302
2399
|
}
|
|
2303
2400
|
/** A drag's target: the nearest element with an id and `drag` in its
|
|
2304
2401
|
* `data-on`, from `el` outward; "" where there is none. */
|
|
@@ -2309,6 +2406,134 @@ var Surface = class {
|
|
|
2309
2406
|
}
|
|
2310
2407
|
return "";
|
|
2311
2408
|
}
|
|
2409
|
+
// --- Touch drags (SPEC §9.1) ----------------------------------------------
|
|
2410
|
+
/**
|
|
2411
|
+
* Where a touch that has just gone past the slop, scrolling by dx, dy so
|
|
2412
|
+
* far, goes. A drag, where the touched element opts in and the
|
|
2413
|
+
* touch-action that counts for it allows no pan along the touch's first
|
|
2414
|
+
* move (the larger delta; a tie pans). Otherwise a pan, as §5.3 and §9
|
|
2415
|
+
* have it, whatever touch-action says: where the browser would pan the
|
|
2416
|
+
* document but the document's touch-action stops it, the surface scrolls
|
|
2417
|
+
* the box itself.
|
|
2418
|
+
*/
|
|
2419
|
+
claimTouch(dx, dy, e, begin) {
|
|
2420
|
+
const el = begin.target && isElement(begin.target) ? begin.target : null;
|
|
2421
|
+
const pans = this.allowedPans(el);
|
|
2422
|
+
if (panBlocked(pans, dx, dy) && this.touchDragStart(e, begin)) return "drag";
|
|
2423
|
+
if (!this.axes) {
|
|
2424
|
+
this.touchRoute = "terminal";
|
|
2425
|
+
return true;
|
|
2426
|
+
}
|
|
2427
|
+
const to = this.gestureTarget(el, dx, dy);
|
|
2428
|
+
if (typeof to === "string") {
|
|
2429
|
+
this.touchRoute = to;
|
|
2430
|
+
return true;
|
|
2431
|
+
}
|
|
2432
|
+
if (pans.x && pans.y) {
|
|
2433
|
+
this.touchRoute = "doc";
|
|
2434
|
+
return false;
|
|
2435
|
+
}
|
|
2436
|
+
this.touchRoute = "self";
|
|
2437
|
+
this.touchBox = to;
|
|
2438
|
+
return true;
|
|
2439
|
+
}
|
|
2440
|
+
/** Whether the touch-action that counts at `target` allows every pan. */
|
|
2441
|
+
pansFreely(target) {
|
|
2442
|
+
const p = this.allowedPans(target && isElement(target) ? target : null);
|
|
2443
|
+
return p.x && p.y;
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* The pans the document's touch-action allows a touch on `el`, as Pointer
|
|
2447
|
+
* Events determine them: its own, with its ancestors' up to the nearest
|
|
2448
|
+
* element that scrolls (SPEC §5.3), both included. The root's is the
|
|
2449
|
+
* document's only where it differs from the host's (`rootTouchAction`),
|
|
2450
|
+
* which is there for the browser's own panning.
|
|
2451
|
+
*/
|
|
2452
|
+
allowedPans(el) {
|
|
2453
|
+
let x = true;
|
|
2454
|
+
let y = true;
|
|
2455
|
+
const root = this.doc.documentElement;
|
|
2456
|
+
const host = rootTouchAction(this.axes, this.host.pageScrolls);
|
|
2457
|
+
for (let n = el; n; n = n.parentElement) {
|
|
2458
|
+
const s = n.ownerDocument.defaultView.getComputedStyle(n);
|
|
2459
|
+
const value = n === root && s.touchAction === host ? "auto" : s.touchAction;
|
|
2460
|
+
const [px, py] = touchPans(value);
|
|
2461
|
+
x &&= px;
|
|
2462
|
+
y &&= py;
|
|
2463
|
+
if (n !== root && this.scrolls(s)) break;
|
|
2464
|
+
}
|
|
2465
|
+
return { x, y };
|
|
2466
|
+
}
|
|
2467
|
+
/** Whether an element with this style scrolls (SPEC §5.3): its overflow is
|
|
2468
|
+
* `auto` or `scroll` along an axis the document asked to scroll. */
|
|
2469
|
+
scrolls(s) {
|
|
2470
|
+
return !!(this.axes & 1) && /auto|scroll/.test(s.overflowY) || !!(this.axes & 2) && /auto|scroll/.test(s.overflowX);
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* A touch becomes a drag (SPEC §9.1), if it can: one that began without
|
|
2474
|
+
* Alt and was not held still past a long press, on an element that opts
|
|
2475
|
+
* in and has an id, in a surface not detached. It presses at this moment
|
|
2476
|
+
* as a mouse does: `press`, then `dragstart`, both for where it began,
|
|
2477
|
+
* then what the press causes; then a `drag` if the finger is by now over
|
|
2478
|
+
* another element.
|
|
2479
|
+
*/
|
|
2480
|
+
touchDragStart(e, begin) {
|
|
2481
|
+
if (this.detachedState || begin.altKey || e.timeStamp - begin.time >= LONG_PRESS_MS) return false;
|
|
2482
|
+
const el = begin.target && isElement(begin.target) ? begin.target : null;
|
|
2483
|
+
let start = null;
|
|
2484
|
+
for (let n = el; n; n = n.parentElement) {
|
|
2485
|
+
if (listens(n, "drag")) {
|
|
2486
|
+
start = n;
|
|
2487
|
+
break;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
const id = start?.getAttribute("id");
|
|
2491
|
+
if (!id) return false;
|
|
2492
|
+
this.cancelDrag();
|
|
2493
|
+
const [c, r] = this.cellAt(begin.x, begin.y);
|
|
2494
|
+
const keys = keysOf(begin);
|
|
2495
|
+
this.reportPress(e);
|
|
2496
|
+
const d = { pointer: NaN, start: id, steps: stepsOf(start), target: id, c, r, keys, step: {} };
|
|
2497
|
+
d.step = this.stepIn(d, begin.x, begin.y);
|
|
2498
|
+
this.drag = d;
|
|
2499
|
+
this.emit("dragstart", id, { c, r, keys, ...d.step });
|
|
2500
|
+
this.pressAt(e);
|
|
2501
|
+
const t = [...e.changedTouches][0];
|
|
2502
|
+
if (t) this.touchDragMove(t.clientX, t.clientY, e);
|
|
2503
|
+
return true;
|
|
2504
|
+
}
|
|
2505
|
+
/** A touch drag's finger moved to (x, y) of the document: as a pointer's
|
|
2506
|
+
* move (`moveDrag`). */
|
|
2507
|
+
touchDragMove(x, y, e) {
|
|
2508
|
+
const d = this.drag;
|
|
2509
|
+
if (d) this.moveDrag(d, x, y, keysOf(e));
|
|
2510
|
+
}
|
|
2511
|
+
/** A touch drag's finger lifted at (x, y): as a pointer's release
|
|
2512
|
+
* (`onDragUp`), a click where it began. */
|
|
2513
|
+
touchDragEnd(x, y, e) {
|
|
2514
|
+
const d = this.drag;
|
|
2515
|
+
if (!d) return;
|
|
2516
|
+
this.drag = null;
|
|
2517
|
+
const [c, r] = this.cellAt(x, y);
|
|
2518
|
+
const at = this.elementAtPoint(x, y, c, r);
|
|
2519
|
+
const target = this.dragTarget(at);
|
|
2520
|
+
this.emit("dragend", target, { c, r, keys: keysOf(e), ...this.stepIn(d, x, y) });
|
|
2521
|
+
this.dragReleased = true;
|
|
2522
|
+
setTimeout(() => this.dragReleased = false, 0);
|
|
2523
|
+
if (target === d.start && at) this.reportClick(ancestry(at));
|
|
2524
|
+
}
|
|
2525
|
+
/** The surface's cell at (x, y) of the document, counting on past its
|
|
2526
|
+
* edges. */
|
|
2527
|
+
cellAt(x, y) {
|
|
2528
|
+
return [Math.floor(x / this.cellW), Math.floor(y / this.cellH)];
|
|
2529
|
+
}
|
|
2530
|
+
/** The element at (x, y) of the document, if cell (c, r) is in the
|
|
2531
|
+
* window (SPEC §5.2). */
|
|
2532
|
+
elementAtPoint(x, y, c, r) {
|
|
2533
|
+
const w = this.win;
|
|
2534
|
+
if (c < w.x || c >= w.x + w.w || r < w.y || r >= w.y + w.h) return null;
|
|
2535
|
+
return this.doc.elementFromPoint(x, y);
|
|
2536
|
+
}
|
|
2312
2537
|
// --- Presses with Alt (SPEC §9.2) ---------------------------------------
|
|
2313
2538
|
/**
|
|
2314
2539
|
* A press of a mouse's or a pen's primary button with Alt held is the
|
|
@@ -2455,26 +2680,27 @@ var Surface = class {
|
|
|
2455
2680
|
const r = this.frame.getBoundingClientRect();
|
|
2456
2681
|
return [r.left + x, r.top + y];
|
|
2457
2682
|
};
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
{ scroll: (dx, dy) => this.host.scrollPage(dx, dy), toPage },
|
|
2463
|
-
false,
|
|
2464
|
-
(e) => !this.axes && this.inScroller(e.target)
|
|
2465
|
-
);
|
|
2466
|
-
} else {
|
|
2467
|
-
new Touch(d, this.frame.ownerDocument.defaultView, {
|
|
2683
|
+
new Touch(
|
|
2684
|
+
d,
|
|
2685
|
+
this.frame.ownerDocument.defaultView,
|
|
2686
|
+
{
|
|
2468
2687
|
scroll: (dx, dy, x, y) => {
|
|
2469
|
-
if (this.touchRoute === "
|
|
2688
|
+
if (this.touchRoute === "self") this.touchBox?.scrollBy(dx, dy);
|
|
2689
|
+
else if (this.touchRoute !== "terminal") return;
|
|
2690
|
+
else if (this.host.pageScrolls) this.host.scrollPage(dx, dy);
|
|
2691
|
+
else this.host.wheel(new WheelEvent("wheel", { deltaX: dx, deltaY: dy, deltaMode: 0, clientX: x, clientY: y }), x, y);
|
|
2470
2692
|
},
|
|
2471
2693
|
toPage,
|
|
2472
|
-
claim: (dx, dy, e) =>
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2694
|
+
claim: (dx, dy, e, begin) => this.claimTouch(dx, dy, e, begin),
|
|
2695
|
+
dragMove: (x, y, e) => this.touchDragMove(x, y, e),
|
|
2696
|
+
dragEnd: (x, y, e) => this.touchDragEnd(x, y, e),
|
|
2697
|
+
dragCancel: () => this.cancelDrag()
|
|
2698
|
+
},
|
|
2699
|
+
false,
|
|
2700
|
+
// Where the page scrolls, the browser pans it from a touch the
|
|
2701
|
+
// surface leaves alone.
|
|
2702
|
+
this.host.pageScrolls ? (e) => !this.axes && this.inScroller(e.target) || !this.pansFreely(e.target) : void 0
|
|
2703
|
+
);
|
|
2478
2704
|
win.addEventListener("focus", () => {
|
|
2479
2705
|
if (!this.programFocus && !this.auxPress && !this.keyboard) this.settle();
|
|
2480
2706
|
});
|
|
@@ -2781,6 +3007,10 @@ function isElement(n) {
|
|
|
2781
3007
|
function listens(el, what) {
|
|
2782
3008
|
return (el.getAttribute("data-on") ?? "").split(/\s+/).includes(what);
|
|
2783
3009
|
}
|
|
3010
|
+
function stepsOf(el) {
|
|
3011
|
+
const n = parseSteps(el.getAttribute("data-steps"));
|
|
3012
|
+
return n && { el, x: n[0], y: n[1] };
|
|
3013
|
+
}
|
|
2784
3014
|
function ancestry(el) {
|
|
2785
3015
|
const out = [];
|
|
2786
3016
|
for (let n = el; n; n = n.parentElement) out.push(n);
|
|
@@ -3324,6 +3554,7 @@ var HottyAddon = class {
|
|
|
3324
3554
|
limits: { resources: this.store.quota, surfaces: this.opts.maxSurfaces },
|
|
3325
3555
|
net: this.policy,
|
|
3326
3556
|
scroll: true,
|
|
3557
|
+
steps: true,
|
|
3327
3558
|
host: HOST,
|
|
3328
3559
|
version: VERSION
|
|
3329
3560
|
};
|