diff options
Diffstat (limited to 'ext/js/dom/text-source-range.js')
-rw-r--r-- | ext/js/dom/text-source-range.js | 110 |
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( |