mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
initial commit
This commit is contained in:
262
vendor/yomitan/js/dom/text-source-element.js
vendored
Normal file
262
vendor/yomitan/js/dom/text-source-element.js
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Yomitan Authors
|
||||
* Copyright (C) 2016-2022 Yomichan Authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {readCodePointsBackward, readCodePointsForward} from '../data/string-util.js';
|
||||
import {convertMultipleRectZoomCoordinates} from './document-util.js';
|
||||
|
||||
/**
|
||||
* This class represents a text source that is attached to a HTML element, such as an <img>
|
||||
* with alt text or a <button>.
|
||||
*/
|
||||
export class TextSourceElement {
|
||||
/**
|
||||
* Creates a new instance of the class.
|
||||
* @param {Element} element The source element.
|
||||
* @param {string} fullContent The string representing the element's full text value.
|
||||
* @param {number} startOffset The text start offset position within the full content.
|
||||
* @param {number} endOffset The text end offset position within the full content.
|
||||
*/
|
||||
constructor(element, fullContent, startOffset, endOffset) {
|
||||
/** @type {Element} */
|
||||
this._element = element;
|
||||
/** @type {string} */
|
||||
this._fullContent = fullContent;
|
||||
/** @type {number} */
|
||||
this._startOffset = startOffset;
|
||||
/** @type {number} */
|
||||
this._endOffset = endOffset;
|
||||
/** @type {string} */
|
||||
this._content = this._fullContent.substring(this._startOffset, this._endOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type name of this instance.
|
||||
* @type {'element'}
|
||||
*/
|
||||
get type() {
|
||||
return 'element';
|
||||
}
|
||||
|
||||
/**
|
||||
* The source element.
|
||||
* @type {Element}
|
||||
*/
|
||||
get element() {
|
||||
return this._element;
|
||||
}
|
||||
|
||||
/**
|
||||
* The string representing the element's full text value.
|
||||
* @type {string}
|
||||
*/
|
||||
get fullContent() {
|
||||
return this._fullContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* The string representing the element's constrained text value.
|
||||
* @type {string}
|
||||
*/
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
|
||||
/**
|
||||
* The text start offset position within the full content.
|
||||
* @type {number}
|
||||
*/
|
||||
get startOffset() {
|
||||
return this._startOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* The text end offset position within the full content.
|
||||
* @type {number}
|
||||
*/
|
||||
get endOffset() {
|
||||
return this._endOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of the instance.
|
||||
* @returns {TextSourceElement} The new clone.
|
||||
*/
|
||||
clone() {
|
||||
return new TextSourceElement(this._element, this._fullContent, this._startOffset, this._endOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any cleanup that is necessary after the element has been used.
|
||||
*/
|
||||
cleanup() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selected text of element, which is a substring of the full content
|
||||
* starting at `startOffset` and ending at `endOffset`.
|
||||
* @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`).
|
||||
* @returns {number} The actual number of characters (not codepoints) that were read.
|
||||
*/
|
||||
setEndOffset(length, fromEnd) {
|
||||
const offset = fromEnd ? this._endOffset : this._startOffset;
|
||||
length = Math.min(this._fullContent.length - offset, length);
|
||||
if (length > 0) {
|
||||
length = readCodePointsForward(this._fullContent, offset, length).length;
|
||||
}
|
||||
this._endOffset = offset + length;
|
||||
this._content = this._fullContent.substring(this._startOffset, this._endOffset);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @returns {number} The actual number of characters (not codepoints) that were read.
|
||||
*/
|
||||
setStartOffset(length) {
|
||||
length = Math.min(this._startOffset, length);
|
||||
if (length > 0) {
|
||||
length = readCodePointsBackward(this._fullContent, this._startOffset - 1, length).length;
|
||||
}
|
||||
this._startOffset -= length;
|
||||
this._content = this._fullContent.substring(this._startOffset, this._endOffset);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rects that represent the position and bounds of the text source.
|
||||
* @returns {DOMRect[]} The rects.
|
||||
*/
|
||||
getRects() {
|
||||
return convertMultipleRectZoomCoordinates(this._element.getClientRects(), this._element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets writing mode that is used for this element.
|
||||
* See: https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode.
|
||||
* @returns {import('document-util').NormalizedWritingMode} The writing mode.
|
||||
*/
|
||||
getWritingMode() {
|
||||
return 'horizontal-tb';
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the text source in the document.
|
||||
*/
|
||||
select() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselects the text source in the document.
|
||||
*/
|
||||
deselect() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether another text source has the same starting point.
|
||||
* @param {import('text-source').TextSource} other The other source to test.
|
||||
* @returns {boolean} `true` if the starting points are equivalent, `false` otherwise.
|
||||
*/
|
||||
hasSameStart(other) {
|
||||
return (
|
||||
typeof other === 'object' &&
|
||||
other !== null &&
|
||||
other instanceof TextSourceElement &&
|
||||
this._element === other.element &&
|
||||
this._fullContent === other.fullContent &&
|
||||
this._startOffset === other.startOffset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the nodes in this text source's range.
|
||||
* @returns {Node[]} The nodes in the range.
|
||||
*/
|
||||
getNodesInRange() {
|
||||
return [this._element];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for a given element.
|
||||
* @param {Element} element The source element.
|
||||
* @returns {TextSourceElement} A new instance of the class corresponding to the element.
|
||||
*/
|
||||
static create(element) {
|
||||
return new TextSourceElement(element, this._getElementContent(element), 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full content string for a given element.
|
||||
* @param {Element} element The element to get the full content of.
|
||||
* @returns {string} The content string.
|
||||
*/
|
||||
static _getElementContent(element) {
|
||||
let content = '';
|
||||
switch (element.nodeName.toUpperCase()) {
|
||||
case 'BUTTON':
|
||||
{
|
||||
const {textContent} = /** @type {HTMLButtonElement} */ (element);
|
||||
if (textContent !== null) {
|
||||
content = textContent;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'IMG':
|
||||
{
|
||||
const alt = /** @type {HTMLImageElement} */ (element).getAttribute('alt');
|
||||
if (typeof alt === 'string') {
|
||||
content = alt;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'SELECT':
|
||||
{
|
||||
const {selectedIndex, options} = /** @type {HTMLSelectElement} */ (element);
|
||||
if (selectedIndex >= 0 && selectedIndex < options.length) {
|
||||
const {textContent} = options[selectedIndex];
|
||||
if (textContent !== null) {
|
||||
content = textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'INPUT':
|
||||
{
|
||||
content = /** @type {HTMLInputElement} */ (element).value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove zero-width space, zero-width non-joiner, soft hyphen
|
||||
content = content.replace(/[\u200b\u200c\u00ad]/g, '');
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user