@jsenv/dom 0.17.17 → 0.17.18
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 +81 -20
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -8651,43 +8651,64 @@ const createDragGestureController = (options = {}) => {
|
|
|
8651
8651
|
document.removeEventListener("selectstart", preventSelectStart);
|
|
8652
8652
|
});
|
|
8653
8653
|
/*
|
|
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
|
-
*
|
|
8654
|
+
* Refusing every selection also refuses the ones a press is entitled to
|
|
8655
|
+
* make: a double click selects the word under it, a triple click the
|
|
8656
|
+
* paragraph around it, and both are over before the pointer has gone
|
|
8657
|
+
* anywhere. They are made here instead, spelled out (see
|
|
8658
|
+
* selectWordAtPoint, selectParagraphAtPoint) rather than left to a browser
|
|
8658
8659
|
* heuristic that cannot tell a drag from a click.
|
|
8659
8660
|
*
|
|
8661
|
+
* Read from `click` rather than `dblclick`, because a triple click has no
|
|
8662
|
+
* event of its own: what tells the clicks apart is `detail`, the count the
|
|
8663
|
+
* browser keeps of how many presses landed in the same place in a row — 2
|
|
8664
|
+
* for a word, 3 and beyond for a paragraph (a fourth click keeps the
|
|
8665
|
+
* paragraph, the way the browser does).
|
|
8666
|
+
*
|
|
8660
8667
|
* On the document and outliving the gesture, because the gesture is
|
|
8661
|
-
* already over when the second click completes:
|
|
8662
|
-
*
|
|
8663
|
-
*
|
|
8664
|
-
*
|
|
8668
|
+
* already over when the second click completes: click comes after mouseup,
|
|
8669
|
+
* the gesture ends at pointerup. Kept installed after it fires, since the
|
|
8670
|
+
* click that selects a word is also the one the next click turns into a
|
|
8671
|
+
* paragraph; it goes when the next press installs its own — a listener
|
|
8672
|
+
* waiting for a click that never comes costs nothing until then.
|
|
8665
8673
|
*/
|
|
8666
|
-
|
|
8667
|
-
const
|
|
8668
|
-
|
|
8674
|
+
removePendingMultiClickListener();
|
|
8675
|
+
const onClick = clickEvent => {
|
|
8676
|
+
const clickCount = clickEvent.detail;
|
|
8677
|
+
if (clickCount < 2) {
|
|
8678
|
+
return;
|
|
8679
|
+
}
|
|
8669
8680
|
// A drag that happened is a gesture, not a click: the second press of a
|
|
8670
8681
|
// double click can be the one that drags, and what it drags must not end
|
|
8671
8682
|
// up selected too.
|
|
8672
8683
|
if (dragGesture.gestureInfo.started) {
|
|
8673
8684
|
return;
|
|
8674
8685
|
}
|
|
8686
|
+
// Only the clicks that continue THIS press: the listener outlives the
|
|
8687
|
+
// gesture and the page keeps being clicked elsewhere, where the browser
|
|
8688
|
+
// is doing its own selecting — a second opinion there would only fight
|
|
8689
|
+
// it.
|
|
8690
|
+
const clickTarget = clickEvent.target;
|
|
8691
|
+
if (clickTarget !== grabEvent.target && !clickTarget.contains(grabEvent.target)) {
|
|
8692
|
+
return;
|
|
8693
|
+
}
|
|
8675
8694
|
// Text the page says is not selectable stays not selectable: a
|
|
8676
8695
|
// programmatic selection goes through `user-select: none` in every
|
|
8677
8696
|
// engine — it is a rule about what the USER may start, and the browser
|
|
8678
8697
|
// does not read it back when asked directly. Read here so that doing the
|
|
8679
8698
|
// browser's work does not also undo what the page asked of it.
|
|
8680
|
-
if (!isSelectable(
|
|
8699
|
+
if (!isSelectable(clickEvent.target)) {
|
|
8700
|
+
return;
|
|
8701
|
+
}
|
|
8702
|
+
if (clickCount === 2) {
|
|
8703
|
+
selectWordAtPoint(clickEvent.clientX, clickEvent.clientY);
|
|
8681
8704
|
return;
|
|
8682
8705
|
}
|
|
8683
|
-
|
|
8706
|
+
selectParagraphAtPoint(clickEvent.clientX, clickEvent.clientY);
|
|
8684
8707
|
};
|
|
8685
|
-
document.addEventListener("
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
removePendingDoubleClickListener = NOOP;
|
|
8690
|
-
document.removeEventListener("dblclick", onDoubleClick);
|
|
8708
|
+
document.addEventListener("click", onClick);
|
|
8709
|
+
removePendingMultiClickListener = () => {
|
|
8710
|
+
removePendingMultiClickListener = NOOP;
|
|
8711
|
+
document.removeEventListener("click", onClick);
|
|
8691
8712
|
};
|
|
8692
8713
|
const cleanup = initializer({
|
|
8693
8714
|
onMove: dragViaPointer,
|
|
@@ -8882,7 +8903,7 @@ const definePropertyAsReadOnly = (object, propertyName) => {
|
|
|
8882
8903
|
});
|
|
8883
8904
|
};
|
|
8884
8905
|
const NOOP = () => {};
|
|
8885
|
-
let
|
|
8906
|
+
let removePendingMultiClickListener = NOOP;
|
|
8886
8907
|
|
|
8887
8908
|
// What a double click selects when the browser is allowed to do it itself: the
|
|
8888
8909
|
// word around the caret the click lands on.
|
|
@@ -8902,6 +8923,46 @@ const selectWordAtPoint = (x, y) => {
|
|
|
8902
8923
|
selection.modify("extend", "forward", "word");
|
|
8903
8924
|
}
|
|
8904
8925
|
};
|
|
8926
|
+
|
|
8927
|
+
// What a triple click selects when the browser is allowed to do it itself: the
|
|
8928
|
+
// paragraph around the caret the click lands on — in the DOM, the contents of
|
|
8929
|
+
// the closest block the caret sits in.
|
|
8930
|
+
//
|
|
8931
|
+
// Drawn from the box tree rather than from `selection.modify("extend",
|
|
8932
|
+
// "forward", "paragraph")`: "paragraph" is a granularity Gecko never
|
|
8933
|
+
// implemented (it stops at word and line), so the modify route would select a
|
|
8934
|
+
// line in Firefox and a paragraph in Chrome.
|
|
8935
|
+
const selectParagraphAtPoint = (x, y) => {
|
|
8936
|
+
const caretRange = createCaretRange(x, y);
|
|
8937
|
+
if (!caretRange) {
|
|
8938
|
+
return;
|
|
8939
|
+
}
|
|
8940
|
+
const block = getClosestBlock(caretRange.startContainer);
|
|
8941
|
+
if (!block) {
|
|
8942
|
+
return;
|
|
8943
|
+
}
|
|
8944
|
+
const selection = window.getSelection();
|
|
8945
|
+
selection.removeAllRanges();
|
|
8946
|
+
const paragraphRange = document.createRange();
|
|
8947
|
+
paragraphRange.selectNodeContents(block);
|
|
8948
|
+
selection.addRange(paragraphRange);
|
|
8949
|
+
};
|
|
8950
|
+
const getClosestBlock = node => {
|
|
8951
|
+
let element = node.nodeType === 1 ? node : node.parentElement;
|
|
8952
|
+
while (element) {
|
|
8953
|
+
const {
|
|
8954
|
+
display
|
|
8955
|
+
} = window.getComputedStyle(element);
|
|
8956
|
+
// Everything that is not laid out as a line inside another line: the first
|
|
8957
|
+
// ancestor that breaks the flow is the one whose text reads as a paragraph
|
|
8958
|
+
// of its own.
|
|
8959
|
+
if (!display.startsWith("inline") && display !== "contents") {
|
|
8960
|
+
return element;
|
|
8961
|
+
}
|
|
8962
|
+
element = element.parentElement;
|
|
8963
|
+
}
|
|
8964
|
+
return null;
|
|
8965
|
+
};
|
|
8905
8966
|
const createCaretRange = (x, y) => {
|
|
8906
8967
|
if (document.caretPositionFromPoint) {
|
|
8907
8968
|
const caretPosition = document.caretPositionFromPoint(x, y);
|