@jsenv/dom 0.17.17 → 0.17.19
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/dist/jsenv_dom.js +182 -42
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -8244,7 +8244,12 @@ const createDragGestureController = (options = {}) => {
|
|
|
8244
8244
|
// metadata about interaction sources
|
|
8245
8245
|
grabEvent: event,
|
|
8246
8246
|
dragEvent: null,
|
|
8247
|
-
releaseEvent: null
|
|
8247
|
+
releaseEvent: null,
|
|
8248
|
+
// The gesture ended without the hand ever letting go: the browser took
|
|
8249
|
+
// the touch to scroll with, another gesture took the pointer. Whoever
|
|
8250
|
+
// reads it must not commit anything — a release that was not asked for
|
|
8251
|
+
// says nothing about where things belong.
|
|
8252
|
+
cancelled: false
|
|
8248
8253
|
};
|
|
8249
8254
|
definePropertyAsReadOnly(gestureInfo, "name");
|
|
8250
8255
|
definePropertyAsReadOnly(gestureInfo, "direction");
|
|
@@ -8556,9 +8561,13 @@ const createDragGestureController = (options = {}) => {
|
|
|
8556
8561
|
};
|
|
8557
8562
|
const release = ({
|
|
8558
8563
|
event = new CustomEvent("programmatic"),
|
|
8564
|
+
cancelled = event.type === "pointercancel",
|
|
8559
8565
|
releaseX = gestureInfo.dragX,
|
|
8560
8566
|
releaseY = gestureInfo.dragY
|
|
8561
8567
|
} = {}) => {
|
|
8568
|
+
// Written before the last drag is reported, so the callbacks reading the
|
|
8569
|
+
// end of the gesture all see the same one.
|
|
8570
|
+
gestureInfo.cancelled = cancelled;
|
|
8562
8571
|
drag(releaseX, releaseY, {
|
|
8563
8572
|
event,
|
|
8564
8573
|
isRelease: true
|
|
@@ -8613,10 +8622,13 @@ const createDragGestureController = (options = {}) => {
|
|
|
8613
8622
|
event: dragEvent
|
|
8614
8623
|
});
|
|
8615
8624
|
};
|
|
8616
|
-
const releaseViaPointer =
|
|
8617
|
-
|
|
8625
|
+
const releaseViaPointer = (pointerEvent, {
|
|
8626
|
+
cancelled
|
|
8627
|
+
} = {}) => {
|
|
8628
|
+
const [mouseReleaseX, mouseReleaseY] = mouseEventCoords(pointerEvent);
|
|
8618
8629
|
dragGesture.release({
|
|
8619
|
-
event:
|
|
8630
|
+
event: pointerEvent,
|
|
8631
|
+
cancelled,
|
|
8620
8632
|
releaseX: mouseReleaseX,
|
|
8621
8633
|
releaseY: mouseReleaseY
|
|
8622
8634
|
});
|
|
@@ -8651,48 +8663,70 @@ const createDragGestureController = (options = {}) => {
|
|
|
8651
8663
|
document.removeEventListener("selectstart", preventSelectStart);
|
|
8652
8664
|
});
|
|
8653
8665
|
/*
|
|
8654
|
-
* Refusing every selection also refuses the
|
|
8655
|
-
* make: a double click selects the word under it,
|
|
8656
|
-
* over before the pointer has gone
|
|
8657
|
-
*
|
|
8666
|
+
* Refusing every selection also refuses the ones a press is entitled to
|
|
8667
|
+
* make: a double click selects the word under it, a triple click the
|
|
8668
|
+
* paragraph around it, and both are over before the pointer has gone
|
|
8669
|
+
* anywhere. They are made here instead, spelled out (see
|
|
8670
|
+
* selectWordAtPoint, selectParagraphAtPoint) rather than left to a browser
|
|
8658
8671
|
* heuristic that cannot tell a drag from a click.
|
|
8659
8672
|
*
|
|
8673
|
+
* Read from `click` rather than `dblclick`, because a triple click has no
|
|
8674
|
+
* event of its own: what tells the clicks apart is `detail`, the count the
|
|
8675
|
+
* browser keeps of how many presses landed in the same place in a row — 2
|
|
8676
|
+
* for a word, 3 and beyond for a paragraph (a fourth click keeps the
|
|
8677
|
+
* paragraph, the way the browser does).
|
|
8678
|
+
*
|
|
8660
8679
|
* On the document and outliving the gesture, because the gesture is
|
|
8661
|
-
* already over when the second click completes:
|
|
8662
|
-
*
|
|
8663
|
-
*
|
|
8664
|
-
*
|
|
8680
|
+
* already over when the second click completes: click comes after mouseup,
|
|
8681
|
+
* the gesture ends at pointerup. Kept installed after it fires, since the
|
|
8682
|
+
* click that selects a word is also the one the next click turns into a
|
|
8683
|
+
* paragraph; it goes when the next press installs its own — a listener
|
|
8684
|
+
* waiting for a click that never comes costs nothing until then.
|
|
8665
8685
|
*/
|
|
8666
|
-
|
|
8667
|
-
const
|
|
8668
|
-
|
|
8686
|
+
removePendingMultiClickListener();
|
|
8687
|
+
const onClick = clickEvent => {
|
|
8688
|
+
const clickCount = clickEvent.detail;
|
|
8689
|
+
if (clickCount < 2) {
|
|
8690
|
+
return;
|
|
8691
|
+
}
|
|
8669
8692
|
// A drag that happened is a gesture, not a click: the second press of a
|
|
8670
8693
|
// double click can be the one that drags, and what it drags must not end
|
|
8671
8694
|
// up selected too.
|
|
8672
8695
|
if (dragGesture.gestureInfo.started) {
|
|
8673
8696
|
return;
|
|
8674
8697
|
}
|
|
8698
|
+
// Only the clicks that continue THIS press: the listener outlives the
|
|
8699
|
+
// gesture and the page keeps being clicked elsewhere, where the browser
|
|
8700
|
+
// is doing its own selecting — a second opinion there would only fight
|
|
8701
|
+
// it.
|
|
8702
|
+
const clickTarget = clickEvent.target;
|
|
8703
|
+
if (clickTarget !== grabEvent.target && !clickTarget.contains(grabEvent.target)) {
|
|
8704
|
+
return;
|
|
8705
|
+
}
|
|
8675
8706
|
// Text the page says is not selectable stays not selectable: a
|
|
8676
8707
|
// programmatic selection goes through `user-select: none` in every
|
|
8677
8708
|
// engine — it is a rule about what the USER may start, and the browser
|
|
8678
8709
|
// does not read it back when asked directly. Read here so that doing the
|
|
8679
8710
|
// browser's work does not also undo what the page asked of it.
|
|
8680
|
-
if (!isSelectable(
|
|
8711
|
+
if (!isSelectable(clickEvent.target)) {
|
|
8712
|
+
return;
|
|
8713
|
+
}
|
|
8714
|
+
if (clickCount === 2) {
|
|
8715
|
+
selectWordAtPoint(clickEvent.clientX, clickEvent.clientY);
|
|
8681
8716
|
return;
|
|
8682
8717
|
}
|
|
8683
|
-
|
|
8718
|
+
selectParagraphAtPoint(clickEvent.clientX, clickEvent.clientY);
|
|
8684
8719
|
};
|
|
8685
|
-
document.addEventListener("
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
removePendingDoubleClickListener = NOOP;
|
|
8690
|
-
document.removeEventListener("dblclick", onDoubleClick);
|
|
8720
|
+
document.addEventListener("click", onClick);
|
|
8721
|
+
removePendingMultiClickListener = () => {
|
|
8722
|
+
removePendingMultiClickListener = NOOP;
|
|
8723
|
+
document.removeEventListener("click", onClick);
|
|
8691
8724
|
};
|
|
8692
8725
|
const cleanup = initializer({
|
|
8693
8726
|
onMove: dragViaPointer,
|
|
8694
8727
|
onRelease: releaseViaPointer,
|
|
8695
|
-
gestureInfo: dragGesture.gestureInfo
|
|
8728
|
+
gestureInfo: dragGesture.gestureInfo,
|
|
8729
|
+
dragGesture
|
|
8696
8730
|
});
|
|
8697
8731
|
dragGesture.addReleaseCallback(() => {
|
|
8698
8732
|
cleanup();
|
|
@@ -8704,7 +8738,8 @@ const createDragGestureController = (options = {}) => {
|
|
|
8704
8738
|
return initDragByPointer(grabEvent, options, ({
|
|
8705
8739
|
onMove,
|
|
8706
8740
|
onRelease,
|
|
8707
|
-
gestureInfo
|
|
8741
|
+
gestureInfo,
|
|
8742
|
+
dragGesture
|
|
8708
8743
|
}) => {
|
|
8709
8744
|
// Captured on something that will still be there at the end of the
|
|
8710
8745
|
// gesture: the browser releases the capture when its element leaves the
|
|
@@ -8712,7 +8747,21 @@ const createDragGestureController = (options = {}) => {
|
|
|
8712
8747
|
// finger would lose the pointer at its first move. Callers whose target
|
|
8713
8748
|
// is stable have nothing to say and keep it.
|
|
8714
8749
|
const target = options?.pointerCaptureElement || grabEvent.target;
|
|
8715
|
-
|
|
8750
|
+
// The capture is ONE per pointer for the whole document: taking it is
|
|
8751
|
+
// taking it away from whoever had it, who is then told the exact same
|
|
8752
|
+
// thing it is told when its own gesture ends. So it is taken when this
|
|
8753
|
+
// gesture is established and not a moment earlier — for most callers
|
|
8754
|
+
// that is the grab itself (the intent was settled before, by a handle
|
|
8755
|
+
// or a long press), and for a caller that is still deciding what the
|
|
8756
|
+
// press means, it is whenever it says so (see capturePointer).
|
|
8757
|
+
let captured = false;
|
|
8758
|
+
dragGesture.capturePointer = () => {
|
|
8759
|
+
captured = true;
|
|
8760
|
+
target.setPointerCapture(grabEvent.pointerId);
|
|
8761
|
+
};
|
|
8762
|
+
if (!options?.pointerCaptureDeferred) {
|
|
8763
|
+
dragGesture.capturePointer();
|
|
8764
|
+
}
|
|
8716
8765
|
/*
|
|
8717
8766
|
* A touchmove left alone is the browser deciding the touch belongs to
|
|
8718
8767
|
* it: it takes it to scroll with, and a touch it has taken is a pointer
|
|
@@ -8746,7 +8795,7 @@ const createDragGestureController = (options = {}) => {
|
|
|
8746
8795
|
grabTarget.addEventListener("touchmove", preventTouchScroll, {
|
|
8747
8796
|
passive: false
|
|
8748
8797
|
});
|
|
8749
|
-
// Only OUR capture ending
|
|
8798
|
+
// Only OUR capture ending is this gesture's business:
|
|
8750
8799
|
// lostpointercapture bubbles, so a descendant giving up its own capture
|
|
8751
8800
|
// walks straight into this listener. That is not a rare shape — it is
|
|
8752
8801
|
// exactly what happens when a gesture hands over to another one (a
|
|
@@ -8754,16 +8803,37 @@ const createDragGestureController = (options = {}) => {
|
|
|
8754
8803
|
// the pressed element, while the real one is being held on a container
|
|
8755
8804
|
// above it), and taken as our own it kills the new gesture one
|
|
8756
8805
|
// millisecond after it started.
|
|
8806
|
+
//
|
|
8807
|
+
// And when it IS ours, it is a loss, never an end: the ends a gesture
|
|
8808
|
+
// has are the pointer going up and the pointer being cancelled, both
|
|
8809
|
+
// listened for below. A capture that goes while the pointer is still
|
|
8810
|
+
// down was taken — by another gesture, or by the element it was held
|
|
8811
|
+
// on leaving the document — and what was being carried must go back
|
|
8812
|
+
// rather than land wherever the hand happened to be.
|
|
8757
8813
|
const onCaptureLost = pointerEvent => {
|
|
8758
|
-
if (pointerEvent.target !== target) {
|
|
8814
|
+
if (!captured || pointerEvent.target !== target) {
|
|
8759
8815
|
return;
|
|
8760
8816
|
}
|
|
8761
|
-
onRelease(pointerEvent
|
|
8817
|
+
onRelease(pointerEvent, {
|
|
8818
|
+
cancelled: true
|
|
8819
|
+
});
|
|
8762
8820
|
};
|
|
8763
8821
|
target.addEventListener("lostpointercapture", onCaptureLost);
|
|
8764
8822
|
target.addEventListener("pointercancel", onRelease);
|
|
8765
|
-
target.addEventListener("pointermove", onMove);
|
|
8766
8823
|
target.addEventListener("pointerup", onRelease);
|
|
8824
|
+
// Read from the window rather than from the element while the pointer
|
|
8825
|
+
// is not this gesture's: without a capture a move is delivered to
|
|
8826
|
+
// whatever is under the pointer, and a hand that has left the element
|
|
8827
|
+
// is exactly the hand this gesture is trying to make sense of. The
|
|
8828
|
+
// capture phase reaches the window before anything else, captured or
|
|
8829
|
+
// not, so there is one place to read whichever way the gesture ends up.
|
|
8830
|
+
const onPointerMove = pointerEvent => {
|
|
8831
|
+
if (pointerEvent.pointerId !== grabEvent.pointerId) {
|
|
8832
|
+
return;
|
|
8833
|
+
}
|
|
8834
|
+
onMove(pointerEvent);
|
|
8835
|
+
};
|
|
8836
|
+
window.addEventListener("pointermove", onPointerMove, true);
|
|
8767
8837
|
// The end of the pointer is also listened for on the window, because
|
|
8768
8838
|
// the end is the one event a gesture cannot afford to miss and the
|
|
8769
8839
|
// element it is captured on is not always on its way: a pointer can
|
|
@@ -8793,15 +8863,17 @@ const createDragGestureController = (options = {}) => {
|
|
|
8793
8863
|
grabTarget.removeEventListener("touchmove", preventTouchScroll);
|
|
8794
8864
|
target.removeEventListener("lostpointercapture", onCaptureLost);
|
|
8795
8865
|
target.removeEventListener("pointercancel", onRelease);
|
|
8796
|
-
|
|
8866
|
+
window.removeEventListener("pointermove", onPointerMove, true);
|
|
8797
8867
|
target.removeEventListener("pointerup", onRelease);
|
|
8798
8868
|
window.removeEventListener("pointerup", onPointerEnd, true);
|
|
8799
8869
|
window.removeEventListener("pointercancel", onPointerEnd, true);
|
|
8800
|
-
//
|
|
8801
|
-
//
|
|
8870
|
+
// Only what this gesture took, and only while there is something to
|
|
8871
|
+
// give back: a capture on that element may be someone else's (two
|
|
8872
|
+
// gestures reading the same press hold the same node), and a pointer
|
|
8873
|
+
// that is up no longer exists — the browser has already dropped the
|
|
8802
8874
|
// capture with it, and asking again throws ("No active pointer with
|
|
8803
|
-
// the given id is found")
|
|
8804
|
-
if (target.hasPointerCapture(grabEvent.pointerId)) {
|
|
8875
|
+
// the given id is found") on the most ordinary release there is.
|
|
8876
|
+
if (captured && target.hasPointerCapture(grabEvent.pointerId)) {
|
|
8805
8877
|
target.releasePointerCapture(grabEvent.pointerId);
|
|
8806
8878
|
}
|
|
8807
8879
|
};
|
|
@@ -8882,7 +8954,7 @@ const definePropertyAsReadOnly = (object, propertyName) => {
|
|
|
8882
8954
|
});
|
|
8883
8955
|
};
|
|
8884
8956
|
const NOOP = () => {};
|
|
8885
|
-
let
|
|
8957
|
+
let removePendingMultiClickListener = NOOP;
|
|
8886
8958
|
|
|
8887
8959
|
// What a double click selects when the browser is allowed to do it itself: the
|
|
8888
8960
|
// word around the caret the click lands on.
|
|
@@ -8902,6 +8974,46 @@ const selectWordAtPoint = (x, y) => {
|
|
|
8902
8974
|
selection.modify("extend", "forward", "word");
|
|
8903
8975
|
}
|
|
8904
8976
|
};
|
|
8977
|
+
|
|
8978
|
+
// What a triple click selects when the browser is allowed to do it itself: the
|
|
8979
|
+
// paragraph around the caret the click lands on — in the DOM, the contents of
|
|
8980
|
+
// the closest block the caret sits in.
|
|
8981
|
+
//
|
|
8982
|
+
// Drawn from the box tree rather than from `selection.modify("extend",
|
|
8983
|
+
// "forward", "paragraph")`: "paragraph" is a granularity Gecko never
|
|
8984
|
+
// implemented (it stops at word and line), so the modify route would select a
|
|
8985
|
+
// line in Firefox and a paragraph in Chrome.
|
|
8986
|
+
const selectParagraphAtPoint = (x, y) => {
|
|
8987
|
+
const caretRange = createCaretRange(x, y);
|
|
8988
|
+
if (!caretRange) {
|
|
8989
|
+
return;
|
|
8990
|
+
}
|
|
8991
|
+
const block = getClosestBlock(caretRange.startContainer);
|
|
8992
|
+
if (!block) {
|
|
8993
|
+
return;
|
|
8994
|
+
}
|
|
8995
|
+
const selection = window.getSelection();
|
|
8996
|
+
selection.removeAllRanges();
|
|
8997
|
+
const paragraphRange = document.createRange();
|
|
8998
|
+
paragraphRange.selectNodeContents(block);
|
|
8999
|
+
selection.addRange(paragraphRange);
|
|
9000
|
+
};
|
|
9001
|
+
const getClosestBlock = node => {
|
|
9002
|
+
let element = node.nodeType === 1 ? node : node.parentElement;
|
|
9003
|
+
while (element) {
|
|
9004
|
+
const {
|
|
9005
|
+
display
|
|
9006
|
+
} = window.getComputedStyle(element);
|
|
9007
|
+
// Everything that is not laid out as a line inside another line: the first
|
|
9008
|
+
// ancestor that breaks the flow is the one whose text reads as a paragraph
|
|
9009
|
+
// of its own.
|
|
9010
|
+
if (!display.startsWith("inline") && display !== "contents") {
|
|
9011
|
+
return element;
|
|
9012
|
+
}
|
|
9013
|
+
element = element.parentElement;
|
|
9014
|
+
}
|
|
9015
|
+
return null;
|
|
9016
|
+
};
|
|
8905
9017
|
const createCaretRange = (x, y) => {
|
|
8906
9018
|
if (document.caretPositionFromPoint) {
|
|
8907
9019
|
const caretPosition = document.caretPositionFromPoint(x, y);
|
|
@@ -9162,7 +9274,13 @@ const dragAfterDistance = (grabEvent, dragGestureInitializer, threshold) => {
|
|
|
9162
9274
|
}
|
|
9163
9275
|
});
|
|
9164
9276
|
const significantDragGesture = significantDragGestureController.grabViaPointer(grabEvent, {
|
|
9165
|
-
element: grabEvent.target
|
|
9277
|
+
element: grabEvent.target,
|
|
9278
|
+
// This one owns nothing: it measures a distance to find out whether there
|
|
9279
|
+
// is a gesture at all, and it is over the moment there is. Taking the
|
|
9280
|
+
// pointer for that would take it from whoever is already holding this same
|
|
9281
|
+
// element for a gesture of their own — and giving it back at the threshold
|
|
9282
|
+
// would tell them theirs is over.
|
|
9283
|
+
pointerCaptureDeferred: true
|
|
9166
9284
|
});
|
|
9167
9285
|
};
|
|
9168
9286
|
const dragAfterLongPress = (grabEvent, dragGestureInitializer, {
|
|
@@ -12091,6 +12209,12 @@ const resolveDropMeaning = ({
|
|
|
12091
12209
|
tossDistance = TOSS_DISTANCE_TO_COMMIT,
|
|
12092
12210
|
tossSpeed = TOSS_SPEED_TO_COMMIT
|
|
12093
12211
|
}) => {
|
|
12212
|
+
if (gestureInfo.cancelled) {
|
|
12213
|
+
// Nobody let go of anything: the gesture was taken away mid-air (the
|
|
12214
|
+
// pointer cancelled, another gesture taking it). Where the thing happened
|
|
12215
|
+
// to be at that moment is not a place it was put.
|
|
12216
|
+
return "cancel";
|
|
12217
|
+
}
|
|
12094
12218
|
if (canToss) {
|
|
12095
12219
|
const {
|
|
12096
12220
|
xDelta,
|
|
@@ -12884,8 +13008,10 @@ const DRAG_RESISTANCE = 0.3;
|
|
|
12884
13008
|
|
|
12885
13009
|
// What a drag must not start on: something that reads the pointer itself. A
|
|
12886
13010
|
// button or a link is not in the list — dragging from one travels, and the
|
|
12887
|
-
// click it would have made is swallowed on the way out.
|
|
12888
|
-
|
|
13011
|
+
// click it would have made is swallowed on the way out. A drag source is: it
|
|
13012
|
+
// answers the same press, and a travel starting there takes the pointer capture
|
|
13013
|
+
// away from a gesture already carrying something.
|
|
13014
|
+
const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable=""]', '[contenteditable="true"]', "[data-drag-source]", "[data-drag-handle]", "[data-no-drag-travel]"].join(",");
|
|
12889
13015
|
|
|
12890
13016
|
// Which axes a box travels on, one attribute per gesture, said in the DOM by
|
|
12891
13017
|
// whoever owns the box: it is what a box ABOVE another reads to know the
|
|
@@ -13278,6 +13404,9 @@ const startDragToTravel = (pointerDownEvent, {
|
|
|
13278
13404
|
pulled: started.slack || 0
|
|
13279
13405
|
};
|
|
13280
13406
|
document.documentElement.setAttribute(WALKING_ATTRIBUTE, axis);
|
|
13407
|
+
// The travel exists: from here the pointer is this box's, and it is
|
|
13408
|
+
// followed wherever it goes.
|
|
13409
|
+
dragGesture.capturePointer();
|
|
13281
13410
|
}
|
|
13282
13411
|
const {
|
|
13283
13412
|
axis
|
|
@@ -13349,9 +13478,12 @@ const startDragToTravel = (pointerDownEvent, {
|
|
|
13349
13478
|
const towardsSomething = pulled > 0 ? travel.travelBack : travel.travelOn;
|
|
13350
13479
|
const velocity = axis === "x" ? gestureInfo.velocityX : gestureInfo.velocityY;
|
|
13351
13480
|
// A gesture taken away rather than let go of (the browser scrolling
|
|
13352
|
-
// something else, a call coming in
|
|
13481
|
+
// something else, a call coming in, another gesture taking the pointer)
|
|
13482
|
+
// said nothing: things go back.
|
|
13353
13483
|
const releaseEvent = gestureInfo.releaseEvent || gestureInfo.dragEvent;
|
|
13354
|
-
const
|
|
13484
|
+
const {
|
|
13485
|
+
cancelled
|
|
13486
|
+
} = gestureInfo;
|
|
13355
13487
|
onEnd({
|
|
13356
13488
|
axis,
|
|
13357
13489
|
pulled,
|
|
@@ -13382,7 +13514,15 @@ const startDragToTravel = (pointerDownEvent, {
|
|
|
13382
13514
|
// gesture may take that away (a page that travels navigates, and the
|
|
13383
13515
|
// router unmounts the page being left), and a capture whose element
|
|
13384
13516
|
// leaves the document is a capture the browser drops.
|
|
13385
|
-
pointerCaptureElement: element
|
|
13517
|
+
pointerCaptureElement: element,
|
|
13518
|
+
// A travel is established in two steps, and the pointer is only owned
|
|
13519
|
+
// after the second: the distance below says the press is not a click, and
|
|
13520
|
+
// the first move says which axis it leans on — which this box may not
|
|
13521
|
+
// walk, or the caller may refuse. Taken at the first step, the capture
|
|
13522
|
+
// would be taken away from whoever else is reading the same press for
|
|
13523
|
+
// gestures that give themselves up one event later. It is claimed once
|
|
13524
|
+
// the travel exists, in onDrag below.
|
|
13525
|
+
pointerCaptureDeferred: true
|
|
13386
13526
|
});
|
|
13387
13527
|
return dragGesture;
|
|
13388
13528
|
};
|