summaryrefslogtreecommitdiff
path: root/ext/js/dom/text-source-range.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/dom/text-source-range.js')
-rw-r--r--ext/js/dom/text-source-range.js110
1 files changed, 110 insertions, 0 deletions
diff --git a/ext/js/dom/text-source-range.js b/ext/js/dom/text-source-range.js
index e03783a5..a0225748 100644
--- a/ext/js/dom/text-source-range.js
+++ b/ext/js/dom/text-source-range.js
@@ -20,7 +20,29 @@
* DocumentUtil
*/
+/**
+ * This class represents a text source that comes from text nodes in the document.
+ * Sometimes a temporary "imposter" element is created and used to store the text.
+ * This element is typically hidden from the page and removed after scanning has completed.
+ */
class TextSourceRange {
+ /**
+ * Creates a new instance of the class.
+ * @param {Range} range The selection range.
+ * @param {number} rangeStartOffset The `startOffset` of the range. This is somewhat redundant
+ * with the `range` parameter, but it is used when for when imposter elements are removed.
+ * @param {string} content The `toString()` value of the range. This is somewhat redundant
+ * with the `range` parameter, but it is used when for when imposter elements are removed.
+ * @param {?Element} imposterElement The temporary imposter element.
+ * @param {?Element} imposterSourceElement The source element which the imposter is imitating.
+ * Must not be `null` if imposterElement is specified.
+ * @param {?Rect[]} cachedRects A set of cached `DOMRect`s representing the rects of the text source,
+ * which can be used after the imposter element is removed from the page.
+ * Must not be `null` if imposterElement is specified.
+ * @param {?Rect} cachedSourceRect A cached `DOMRect` representing the rect of the `imposterSourceElement`,
+ * which can be used after the imposter element is removed from the page.
+ * Must not be `null` if imposterElement is specified.
+ */
constructor(range, rangeStartOffset, content, imposterElement, imposterSourceElement, cachedRects, cachedSourceRect) {
this._range = range;
this._rangeStartOffset = rangeStartOffset;
@@ -31,22 +53,42 @@ class TextSourceRange {
this._cachedSourceRect = cachedSourceRect;
}
+ /**
+ * Gets the type name of this instance.
+ * @type {string}
+ */
get type() {
return 'range';
}
+ /**
+ * The internal range object.
+ * @type {Range}
+ */
get range() {
return this._range;
}
+ /**
+ * The starting offset for the range.
+ * @type {Range}
+ */
get rangeStartOffset() {
return this._rangeStartOffset;
}
+ /**
+ * The source element that the imposter element is imitating, if present.
+ * @type {?Element}
+ */
get imposterSourceElement() {
return this._imposterSourceElement;
}
+ /**
+ * Creates a clone of the instance.
+ * @returns {TextSourceRange} The new clone.
+ */
clone() {
return new TextSourceRange(
this._range.cloneRange(),
@@ -59,16 +101,31 @@ class TextSourceRange {
);
}
+ /**
+ * Performs any cleanup that is necessary after the element has been used.
+ */
cleanup() {
if (this._imposterElement !== null && this._imposterElement.parentNode !== null) {
this._imposterElement.parentNode.removeChild(this._imposterElement);
}
}
+ /**
+ * Gets the selected text of element, which is the `toString()` version of the range.
+ * @returns {string} The text content.
+ */
text() {
return this._content;
}
+ /**
+ * Moves the end offset of the text by a set amount of unicode codepoints.
+ * @param {number} length The maximum number of codepoints to move by.
+ * @param {boolean} fromEnd Whether to move the offset from the current end position (if `true`) or the start position (if `false`).
+ * @param {boolean} layoutAwareScan Whether or not HTML layout information should be used to generate
+ * the string content when scanning.
+ * @returns {number} The actual number of characters (not codepoints) that were read.
+ */
setEndOffset(length, fromEnd, layoutAwareScan) {
let node;
let offset;
@@ -85,6 +142,13 @@ class TextSourceRange {
return length - state.remainder;
}
+ /**
+ * Moves the start offset of the text by a set amount of unicode codepoints.
+ * @param {number} length The maximum number of codepoints to move by.
+ * @param {boolean} layoutAwareScan Whether or not HTML layout information should be used to generate
+ * the string content when scanning.
+ * @returns {number} The actual number of characters (not codepoints) that were read.
+ */
setStartOffset(length, layoutAwareScan) {
const state = new DOMTextScanner(this._range.startContainer, this._range.startOffset, !layoutAwareScan, layoutAwareScan).seek(-length);
this._range.setStart(state.node, state.offset);
@@ -93,16 +157,28 @@ class TextSourceRange {
return length - state.remainder;
}
+ /**
+ * Gets the rects that represent the position and bounds of the text source.
+ * @returns {DOMRect[]} The rects.
+ */
getRects() {
if (this._isImposterDisconnected()) { return this._getCachedRects(); }
return DocumentUtil.convertMultipleRectZoomCoordinates(this._range.getClientRects(), this._range.startContainer);
}
+ /**
+ * Gets writing mode that is used for this element.
+ * See: https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode.
+ * @returns {string} The rects.
+ */
getWritingMode() {
const node = this._isImposterDisconnected() ? this._imposterSourceElement : this._range.startContainer;
return DocumentUtil.getElementWritingMode(node !== null ? node.parentElement : null);
}
+ /**
+ * Selects the text source in the document.
+ */
select() {
if (this._imposterElement !== null) { return; }
const selection = window.getSelection();
@@ -110,12 +186,22 @@ class TextSourceRange {
selection.addRange(this._range);
}
+ /**
+ * Deselects the text source in the document.
+ */
deselect() {
if (this._imposterElement !== null) { return; }
const selection = window.getSelection();
selection.removeAllRanges();
}
+ /**
+ * Checks whether another text source has the same starting point.
+ * @param {TextSourceElement|TextSourceRange} other The other source to test.
+ * @returns {boolean} `true` if the starting points are equivalent, `false` otherwise.
+ * @throws {Error} An exception can be thrown if `Range.compareBoundaryPoints` fails,
+ * which shouldn't happen, but the handler is kept in case of unexpected errors.
+ */
hasSameStart(other) {
if (!(
typeof other === 'object' &&
@@ -142,24 +228,48 @@ class TextSourceRange {
}
}
+ /**
+ * Gets a list of the nodes in this text source's range.
+ * @returns {Node[]} The nodes in the range.
+ */
getNodesInRange() {
return DocumentUtil.getNodesInRange(this._range);
}
+ /**
+ * Creates a new instance for a given range.
+ * @param {Range} range The source range.
+ * @returns {TextSourceRange} A new instance of the class corresponding to the range.
+ */
static create(range) {
return new TextSourceRange(range, range.startOffset, range.toString(), null, null, null, null);
}
+ /**
+ * Creates a new instance for a given range using an imposter element.
+ * @param {Range} range The source range.
+ * @param {Element} imposterElement The temporary imposter element.
+ * @param {Element} imposterSourceElement The source element which the imposter is imitating.
+ * @returns {TextSourceRange} A new instance of the class corresponding to the range.
+ */
static createFromImposter(range, imposterElement, imposterSourceElement) {
const cachedRects = DocumentUtil.convertMultipleRectZoomCoordinates(range.getClientRects(), range.startContainer);
const cachedSourceRect = DocumentUtil.convertRectZoomCoordinates(imposterSourceElement.getBoundingClientRect(), imposterSourceElement);
return new TextSourceRange(range, range.startOffset, range.toString(), imposterElement, imposterSourceElement, cachedRects, cachedSourceRect);
}
+ /**
+ * Checks whether the imposter element has been removed, if the instance is using one.
+ * @returns {boolean} `true` if the instance has an imposter and it's no longer connected to the document, `false` otherwise.
+ */
_isImposterDisconnected() {
return this._imposterElement !== null && !this._imposterElement.isConnected;
}
+ /**
+ * Gets the cached rects for a disconnected imposter element.
+ * @returns {Rect[]} The rects for the element.
+ */
_getCachedRects() {
const sourceRect = DocumentUtil.convertRectZoomCoordinates(this._imposterSourceElement.getBoundingClientRect(), this._imposterSourceElement);
return DocumentUtil.offsetDOMRects(