Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
at lambda-fork/main 130 lines 4.3 kB view raw
1/* 2 * Copyright (C) 2023-2025 Yomitan Authors 3 * Copyright (C) 2020-2022 Yomichan Authors 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19import {EventListenerCollection} from '../core/event-listener-collection.js'; 20import {base64ToArrayBuffer} from '../data/array-buffer-util.js'; 21 22/** 23 * The content manager which is used when generating HTML display content. 24 */ 25export class DisplayContentManager { 26 /** 27 * Creates a new instance of the class. 28 * @param {import('./display.js').Display} display The display instance that owns this object. 29 */ 30 constructor(display) { 31 /** @type {import('./display.js').Display} */ 32 this._display = display; 33 /** @type {import('core').TokenObject} */ 34 this._token = {}; 35 /** @type {EventListenerCollection} */ 36 this._eventListeners = new EventListenerCollection(); 37 /** @type {import('display-content-manager').LoadMediaRequest[]} */ 38 this._loadMediaRequests = []; 39 } 40 41 /** @type {import('display-content-manager').LoadMediaRequest[]} */ 42 get loadMediaRequests() { 43 return this._loadMediaRequests; 44 } 45 46 /** 47 * Queues loading media file from a given dictionary. 48 * @param {string} path 49 * @param {string} dictionary 50 * @param {OffscreenCanvas} canvas 51 */ 52 loadMedia(path, dictionary, canvas) { 53 this._loadMediaRequests.push({path, dictionary, canvas}); 54 } 55 56 /** 57 * Unloads all media that has been loaded. 58 */ 59 unloadAll() { 60 this._token = {}; 61 62 this._eventListeners.removeAllEventListeners(); 63 64 this._loadMediaRequests = []; 65 } 66 67 /** 68 * Sets up attributes and events for a link element. 69 * @param {HTMLAnchorElement} element The link element. 70 * @param {string} href The URL. 71 * @param {boolean} internal Whether or not the URL is an internal or external link. 72 */ 73 prepareLink(element, href, internal) { 74 element.href = href; 75 if (!internal) { 76 element.target = '_blank'; 77 element.rel = 'noreferrer noopener'; 78 } 79 this._eventListeners.addEventListener(element, 'click', this._onLinkClick.bind(this)); 80 } 81 82 /** 83 * Execute media requests 84 */ 85 async executeMediaRequests() { 86 this._display.application.api.drawMedia(this._loadMediaRequests, this._loadMediaRequests.map(({canvas}) => canvas)); 87 this._loadMediaRequests = []; 88 } 89 90 /** 91 * @param {string} path 92 * @param {string} dictionary 93 * @param {Window} window 94 */ 95 async openMediaInTab(path, dictionary, window) { 96 const data = await this._display.application.api.getMedia([{path, dictionary}]); 97 const buffer = base64ToArrayBuffer(data[0].content); 98 const blob = new Blob([buffer], {type: data[0].mediaType}); 99 const blobUrl = URL.createObjectURL(blob); 100 window.open(blobUrl, '_blank')?.focus(); 101 } 102 103 /** 104 * @param {MouseEvent} e 105 */ 106 _onLinkClick(e) { 107 const {href} = /** @type {HTMLAnchorElement} */ (e.currentTarget); 108 if (typeof href !== 'string') { return; } 109 110 const baseUrl = new URL(location.href); 111 const url = new URL(href, baseUrl); 112 const internal = (url.protocol === baseUrl.protocol && url.host === baseUrl.host); 113 if (!internal) { return; } 114 115 e.preventDefault(); 116 117 /** @type {import('display').HistoryParams} */ 118 const params = {}; 119 for (const [key, value] of url.searchParams.entries()) { 120 params[key] = value; 121 } 122 this._display.setContent({ 123 historyMode: 'new', 124 focus: false, 125 params, 126 state: null, 127 content: null, 128 }); 129 } 130}