diff --git a/ext/js/accessibility/google-docs-util.js b/ext/js/accessibility/google-docs-util.js new file mode 100644 index 00000000..e50ba652 --- /dev/null +++ b/ext/js/accessibility/google-docs-util.js @@ -0,0 +1,122 @@ +/* + * Copyright (C) 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 . + */ + +/* global + * DocumentUtil + * TextSourceRange + */ + +/** + * This class is a helper for handling Google Docs content in content scripts. + */ +class GoogleDocsUtil { + /** + * Scans the document for text or elements with text information at the given coordinate. + * Coordinates are provided in [client space](https://developer.mozilla.org/en-US/docs/Web/CSS/CSSOM_View/Coordinate_systems). + * @param {number} x The x coordinate to search at. + * @param {number} y The y coordinate to search at. + * @param {GetRangeFromPointOptions} options Options to configure how element detection is performed. + * @returns {?TextSourceRange|TextSourceElement} A range for the hovered text or element, or `null` if no applicable content was found. + */ + static getRangeFromPoint(x, y, {normalizeCssZoom}) { + const selector = '.kix-canvas-tile-content svg>g>rect'; + const styleNode = this._getStyleNode(selector); + styleNode.disabled = false; + const elements = document.elementsFromPoint(x, y); + styleNode.disabled = true; + for (const element of elements) { + if (!element.matches(selector)) { continue; } + const ariaLabel = element.getAttribute('aria-label'); + if (typeof ariaLabel !== 'string' || ariaLabel.length === 0) { continue; } + return this._createRange(element, ariaLabel, x, y, normalizeCssZoom); + } + return null; + } + + static _getStyleNode(selector) { + // This