From b8df9896e643c772253749a335b2abf0daa7a282 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 1 Aug 2019 02:20:09 +0300 Subject: allow looking up terms from within terms resolves #167, resolves #139, resolves #75, resolves #151 --- ext/fg/js/float.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'ext/fg/js') diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 1deb61a9..65ed89a1 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -123,6 +123,41 @@ class DisplayFloat extends Display { parent.appendChild(this.styleNode); } } + + async onTermLookup(e) { + try { + e.preventDefault(); + + const clickedElement = $(e.target); + const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); + textSource.setEndOffset(this.options.scanning.length); + + const {definitions, length} = await apiTermsFind(textSource.text()); + if (definitions.length === 0) { + return false; + } + + textSource.setEndOffset(length); + + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + + const context = { + source: { + definitions: this.definitions, + index: Display.entryIndexFind(clickedElement) + } + }; + + if (this.context) { + context.sentence = sentence; + context.url = this.context.url; + } + + this.termsShow(definitions, this.options, context); + } catch (e) { + this.onError(e); + } + } } window.yomichan_display = new DisplayFloat(); -- cgit v1.2.3 From 04f4607922e84d93ca8a05708802fb6bbd61f359 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Fri, 2 Aug 2019 00:04:39 +0300 Subject: use ES6 import and export --- ext/bg/js/search.js | 1 + ext/bg/search.html | 3 +- ext/fg/float.html | 5 +- ext/fg/js/document.module.js | 177 +++++++++++++++++++++++++++++++ ext/fg/js/float.js | 36 +------ ext/fg/js/source.module.js | 244 +++++++++++++++++++++++++++++++++++++++++++ ext/mixed/js/display.js | 39 +++++++ 7 files changed, 464 insertions(+), 41 deletions(-) create mode 100644 ext/fg/js/document.module.js create mode 100644 ext/fg/js/source.module.js (limited to 'ext/fg/js') diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 40bf2019..911b5566 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import {Display} from '../../mixed/js/display.js'; class DisplaySearch extends Display { constructor() { diff --git a/ext/bg/search.html b/ext/bg/search.html index 0d6c7cad..09b761cc 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -43,9 +43,8 @@ - - + diff --git a/ext/fg/float.html b/ext/fg/float.html index 07f2d58b..4860753a 100644 --- a/ext/fg/float.html +++ b/ext/fg/float.html @@ -36,10 +36,7 @@ - - - - + diff --git a/ext/fg/js/document.module.js b/ext/fg/js/document.module.js new file mode 100644 index 00000000..d86aff33 --- /dev/null +++ b/ext/fg/js/document.module.js @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016-2017 Alex Yatskov + * Author: Alex Yatskov + * + * 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 . + */ + +import {TextSourceRange, TextSourceElement} from './source.module.js'; + +export function docOffsetCalc(element) { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; + const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; + + const clientTop = document.documentElement.clientTop || document.body.clientTop || 0; + const clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; + + const rect = element.getBoundingClientRect(); + const top = Math.round(rect.top + scrollTop - clientTop); + const left = Math.round(rect.left + scrollLeft - clientLeft); + + return {top, left}; +} + +export function docImposterCreate(element) { + const styleProps = window.getComputedStyle(element); + const stylePairs = []; + for (const key of styleProps) { + stylePairs.push(`${key}: ${styleProps[key]};`); + } + + const offset = docOffsetCalc(element); + const imposter = document.createElement('div'); + imposter.className = 'yomichan-imposter'; + imposter.innerText = element.value; + imposter.style.cssText = stylePairs.join('\n'); + imposter.style.position = 'absolute'; + imposter.style.top = `${offset.top}px`; + imposter.style.left = `${offset.left}px`; + imposter.style.opacity = 0; + imposter.style.zIndex = 2147483646; + if (element.nodeName === 'TEXTAREA' && styleProps.overflow === 'visible') { + imposter.style.overflow = 'auto'; + } + + document.body.appendChild(imposter); + imposter.scrollTop = element.scrollTop; + imposter.scrollLeft = element.scrollLeft; + + return imposter; +} + +export function docImposterDestroy() { + for (const element of document.getElementsByClassName('yomichan-imposter')) { + element.parentNode.removeChild(element); + } +} + +export function docRangeFromPoint(point) { + const element = document.elementFromPoint(point.x, point.y); + let imposter = null; + if (element) { + if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') { + return new TextSourceElement(element); + } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { + imposter = docImposterCreate(element); + } + } + + if (!document.caretRangeFromPoint) { + document.caretRangeFromPoint = (x, y) => { + const position = document.caretPositionFromPoint(x,y); + if (position && position.offsetNode && position.offsetNode.nodeType === Node.TEXT_NODE) { + const range = document.createRange(); + range.setStart(position.offsetNode, position.offset); + range.setEnd(position.offsetNode, position.offset); + return range; + } + return null; + }; + } + + const range = document.caretRangeFromPoint(point.x, point.y); + if (range === null) { + return; + } + + if(imposter !== null) imposter.style.zIndex = -2147483646; + + const rects = range.getClientRects(); + for (const rect of rects) { + if (point.y <= rect.bottom + 2) { + return new TextSourceRange(range); + } + } +} + +export function docSentenceExtract(source, extent) { + const quotesFwd = {'「': '」', '『': '』', "'": "'", '"': '"'}; + const quotesBwd = {'」': '「', '』': '『', "'": "'", '"': '"'}; + const terminators = '…。..??!!'; + + const sourceLocal = source.clone(); + const position = sourceLocal.setStartOffset(extent); + sourceLocal.setEndOffset(position + extent); + const content = sourceLocal.text(); + + let quoteStack = []; + + let startPos = 0; + for (let i = position; i >= startPos; --i) { + const c = content[i]; + + if (c === '\n') { + startPos = i + 1; + break; + } + + if (quoteStack.length === 0 && (terminators.includes(c) || c in quotesFwd)) { + startPos = i + 1; + break; + } + + if (quoteStack.length > 0 && c === quoteStack[0]) { + quoteStack.pop(); + } else if (c in quotesBwd) { + quoteStack = [quotesBwd[c]].concat(quoteStack); + } + } + + quoteStack = []; + + let endPos = content.length; + for (let i = position; i <= endPos; ++i) { + const c = content[i]; + + if (c === '\n') { + endPos = i + 1; + break; + } + + if (quoteStack.length === 0) { + if (terminators.includes(c)) { + endPos = i + 1; + break; + } + else if (c in quotesBwd) { + endPos = i; + break; + } + } + + if (quoteStack.length > 0 && c === quoteStack[0]) { + quoteStack.pop(); + } else if (c in quotesFwd) { + quoteStack = [quotesFwd[c]].concat(quoteStack); + } + } + + const text = content.substring(startPos, endPos); + const padding = text.length - text.replace(/^\s+/, '').length; + + return { + text: text.trim(), + offset: position - startPos - padding + }; +} diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 65ed89a1..9301135b 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import {Display} from '../../mixed/js/display.js'; class DisplayFloat extends Display { constructor() { @@ -123,41 +124,6 @@ class DisplayFloat extends Display { parent.appendChild(this.styleNode); } } - - async onTermLookup(e) { - try { - e.preventDefault(); - - const clickedElement = $(e.target); - const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); - textSource.setEndOffset(this.options.scanning.length); - - const {definitions, length} = await apiTermsFind(textSource.text()); - if (definitions.length === 0) { - return false; - } - - textSource.setEndOffset(length); - - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - - const context = { - source: { - definitions: this.definitions, - index: Display.entryIndexFind(clickedElement) - } - }; - - if (this.context) { - context.sentence = sentence; - context.url = this.context.url; - } - - this.termsShow(definitions, this.options, context); - } catch (e) { - this.onError(e); - } - } } window.yomichan_display = new DisplayFloat(); diff --git a/ext/fg/js/source.module.js b/ext/fg/js/source.module.js new file mode 100644 index 00000000..ed9263c5 --- /dev/null +++ b/ext/fg/js/source.module.js @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2016-2017 Alex Yatskov + * Author: Alex Yatskov + * + * 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 . + */ + + +/* + * TextSourceRange + */ + +export class TextSourceRange { + constructor(range, content='') { + this.range = range; + this.content = content; + } + + clone() { + return new TextSourceRange(this.range.cloneRange(), this.content); + } + + text() { + return this.content; + } + + setEndOffset(length) { + const state = TextSourceRange.seekForward(this.range.startContainer, this.range.startOffset, length); + this.range.setEnd(state.node, state.offset); + this.content = state.content; + return length - state.remainder; + } + + setStartOffset(length) { + const state = TextSourceRange.seekBackward(this.range.startContainer, this.range.startOffset, length); + this.range.setStart(state.node, state.offset); + this.content = state.content; + return length - state.remainder; + } + + containsPoint(point) { + const rect = this.getPaddedRect(); + return point.x >= rect.left && point.x <= rect.right; + } + + getRect() { + return this.range.getBoundingClientRect(); + } + + getPaddedRect() { + const range = this.range.cloneRange(); + const startOffset = range.startOffset; + const endOffset = range.endOffset; + const node = range.startContainer; + + range.setStart(node, Math.max(0, startOffset - 1)); + range.setEnd(node, Math.min(node.length, endOffset + 1)); + + return range.getBoundingClientRect(); + } + + select() { + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(this.range); + } + + deselect() { + const selection = window.getSelection(); + selection.removeAllRanges(); + } + + equals(other) { + return other && other.range && other.range.compareBoundaryPoints(Range.START_TO_START, this.range) === 0; + } + + static shouldEnter(node) { + if (node.nodeType !== 1) { + return false; + } + + const skip = ['RT', 'SCRIPT', 'STYLE']; + if (skip.includes(node.nodeName)) { + return false; + } + + const style = window.getComputedStyle(node); + const hidden = + style.visibility === 'hidden' || + style.display === 'none' || + parseFloat(style.fontSize) === 0; + + return !hidden; + } + + static seekForward(node, offset, length) { + const state = {node, offset, remainder: length, content: ''}; + if (!TextSourceRange.seekForwardHelper(node, state)) { + return state; + } + + for (let current = node; current !== null; current = current.parentElement) { + for (let sibling = current.nextSibling; sibling !== null; sibling = sibling.nextSibling) { + if (!TextSourceRange.seekForwardHelper(sibling, state)) { + return state; + } + } + } + + return state; + } + + static seekForwardHelper(node, state) { + if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { + const offset = state.node === node ? state.offset : 0; + const remaining = node.length - offset; + const consumed = Math.min(remaining, state.remainder); + state.content = state.content + node.nodeValue.substring(offset, offset + consumed); + state.node = node; + state.offset = offset + consumed; + state.remainder -= consumed; + } else if (TextSourceRange.shouldEnter(node)) { + for (let i = 0; i < node.childNodes.length; ++i) { + if (!TextSourceRange.seekForwardHelper(node.childNodes[i], state)) { + break; + } + } + } + + return state.remainder > 0; + } + + static seekBackward(node, offset, length) { + const state = {node, offset, remainder: length, content: ''}; + if (!TextSourceRange.seekBackwardHelper(node, state)) { + return state; + } + + for (let current = node; current !== null; current = current.parentElement) { + for (let sibling = current.previousSibling; sibling !== null; sibling = sibling.previousSibling) { + if (!TextSourceRange.seekBackwardHelper(sibling, state)) { + return state; + } + } + } + + return state; + } + + static seekBackwardHelper(node, state) { + if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { + const offset = state.node === node ? state.offset : node.length; + const remaining = offset; + const consumed = Math.min(remaining, state.remainder); + state.content = node.nodeValue.substring(offset - consumed, offset) + state.content; + state.node = node; + state.offset = offset - consumed; + state.remainder -= consumed; + } else if (TextSourceRange.shouldEnter(node)) { + for (let i = node.childNodes.length - 1; i >= 0; --i) { + if (!TextSourceRange.seekBackwardHelper(node.childNodes[i], state)) { + break; + } + } + } + + return state.remainder > 0; + } +} + + +/* + * TextSourceElement + */ + +export class TextSourceElement { + constructor(element, content='') { + this.element = element; + this.content = content; + } + + clone() { + return new TextSourceElement(this.element, this.content); + } + + text() { + return this.content; + } + + setEndOffset(length) { + switch (this.element.nodeName) { + case 'BUTTON': + this.content = this.element.innerHTML; + break; + case 'IMG': + this.content = this.element.getAttribute('alt'); + break; + default: + this.content = this.element.value; + break; + } + + this.content = this.content || ''; + this.content = this.content.substring(0, length); + + return this.content.length; + } + + setStartOffset(length) { + return 0; + } + + containsPoint(point) { + const rect = this.getRect(); + return point.x >= rect.left && point.x <= rect.right; + } + + getRect() { + return this.element.getBoundingClientRect(); + } + + select() { + // NOP + } + + deselect() { + // NOP + } + + equals(other) { + return other && other.element === this.element && other.content === this.content; + } +} diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index dc6f5798..5d259936 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -17,6 +17,8 @@ */ +import {docRangeFromPoint, docSentenceExtract} from '../../fg/js/document.module.js'; + class Display { constructor(spinner, container) { this.spinner = spinner; @@ -69,6 +71,41 @@ class Display { } } + async onTermLookup(e) { + try { + e.preventDefault(); + + const clickedElement = $(e.target); + const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); + textSource.setEndOffset(this.options.scanning.length); + + const {definitions, length} = await apiTermsFind(textSource.text()); + if (definitions.length === 0) { + return false; + } + + textSource.setEndOffset(length); + + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + + const context = { + source: { + definitions: this.definitions, + index: Display.entryIndexFind(clickedElement) + } + }; + + if (this.context) { + context.sentence = sentence; + context.url = this.context.url; + } + + this.termsShow(definitions, this.options, context); + } catch (e) { + this.onError(e); + } + } + onAudioPlay(e) { e.preventDefault(); const link = $(e.currentTarget); @@ -460,3 +497,5 @@ class Display { return $('.entry').eq(index).find('.action-view-note'); } } + +export {Display}; -- cgit v1.2.3 From 0ee6d05cb15a8bc94f120c2896e7537fd969aaed Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 3 Aug 2019 14:57:17 +0300 Subject: Revert "use ES6 import and export" This reverts commit 04f4607922e84d93ca8a05708802fb6bbd61f359. --- ext/bg/js/search.js | 1 - ext/bg/search.html | 3 +- ext/fg/float.html | 5 +- ext/fg/js/document.module.js | 177 ------------------------------- ext/fg/js/float.js | 36 ++++++- ext/fg/js/source.module.js | 244 ------------------------------------------- ext/mixed/js/display.js | 39 ------- 7 files changed, 41 insertions(+), 464 deletions(-) delete mode 100644 ext/fg/js/document.module.js delete mode 100644 ext/fg/js/source.module.js (limited to 'ext/fg/js') diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 911b5566..40bf2019 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import {Display} from '../../mixed/js/display.js'; class DisplaySearch extends Display { constructor() { diff --git a/ext/bg/search.html b/ext/bg/search.html index 09b761cc..0d6c7cad 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -43,8 +43,9 @@ + - + diff --git a/ext/fg/float.html b/ext/fg/float.html index 4860753a..07f2d58b 100644 --- a/ext/fg/float.html +++ b/ext/fg/float.html @@ -36,7 +36,10 @@ + + + - + diff --git a/ext/fg/js/document.module.js b/ext/fg/js/document.module.js deleted file mode 100644 index d86aff33..00000000 --- a/ext/fg/js/document.module.js +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2016-2017 Alex Yatskov - * Author: Alex Yatskov - * - * 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 . - */ - -import {TextSourceRange, TextSourceElement} from './source.module.js'; - -export function docOffsetCalc(element) { - const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; - const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; - - const clientTop = document.documentElement.clientTop || document.body.clientTop || 0; - const clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; - - const rect = element.getBoundingClientRect(); - const top = Math.round(rect.top + scrollTop - clientTop); - const left = Math.round(rect.left + scrollLeft - clientLeft); - - return {top, left}; -} - -export function docImposterCreate(element) { - const styleProps = window.getComputedStyle(element); - const stylePairs = []; - for (const key of styleProps) { - stylePairs.push(`${key}: ${styleProps[key]};`); - } - - const offset = docOffsetCalc(element); - const imposter = document.createElement('div'); - imposter.className = 'yomichan-imposter'; - imposter.innerText = element.value; - imposter.style.cssText = stylePairs.join('\n'); - imposter.style.position = 'absolute'; - imposter.style.top = `${offset.top}px`; - imposter.style.left = `${offset.left}px`; - imposter.style.opacity = 0; - imposter.style.zIndex = 2147483646; - if (element.nodeName === 'TEXTAREA' && styleProps.overflow === 'visible') { - imposter.style.overflow = 'auto'; - } - - document.body.appendChild(imposter); - imposter.scrollTop = element.scrollTop; - imposter.scrollLeft = element.scrollLeft; - - return imposter; -} - -export function docImposterDestroy() { - for (const element of document.getElementsByClassName('yomichan-imposter')) { - element.parentNode.removeChild(element); - } -} - -export function docRangeFromPoint(point) { - const element = document.elementFromPoint(point.x, point.y); - let imposter = null; - if (element) { - if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') { - return new TextSourceElement(element); - } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { - imposter = docImposterCreate(element); - } - } - - if (!document.caretRangeFromPoint) { - document.caretRangeFromPoint = (x, y) => { - const position = document.caretPositionFromPoint(x,y); - if (position && position.offsetNode && position.offsetNode.nodeType === Node.TEXT_NODE) { - const range = document.createRange(); - range.setStart(position.offsetNode, position.offset); - range.setEnd(position.offsetNode, position.offset); - return range; - } - return null; - }; - } - - const range = document.caretRangeFromPoint(point.x, point.y); - if (range === null) { - return; - } - - if(imposter !== null) imposter.style.zIndex = -2147483646; - - const rects = range.getClientRects(); - for (const rect of rects) { - if (point.y <= rect.bottom + 2) { - return new TextSourceRange(range); - } - } -} - -export function docSentenceExtract(source, extent) { - const quotesFwd = {'「': '」', '『': '』', "'": "'", '"': '"'}; - const quotesBwd = {'」': '「', '』': '『', "'": "'", '"': '"'}; - const terminators = '…。..??!!'; - - const sourceLocal = source.clone(); - const position = sourceLocal.setStartOffset(extent); - sourceLocal.setEndOffset(position + extent); - const content = sourceLocal.text(); - - let quoteStack = []; - - let startPos = 0; - for (let i = position; i >= startPos; --i) { - const c = content[i]; - - if (c === '\n') { - startPos = i + 1; - break; - } - - if (quoteStack.length === 0 && (terminators.includes(c) || c in quotesFwd)) { - startPos = i + 1; - break; - } - - if (quoteStack.length > 0 && c === quoteStack[0]) { - quoteStack.pop(); - } else if (c in quotesBwd) { - quoteStack = [quotesBwd[c]].concat(quoteStack); - } - } - - quoteStack = []; - - let endPos = content.length; - for (let i = position; i <= endPos; ++i) { - const c = content[i]; - - if (c === '\n') { - endPos = i + 1; - break; - } - - if (quoteStack.length === 0) { - if (terminators.includes(c)) { - endPos = i + 1; - break; - } - else if (c in quotesBwd) { - endPos = i; - break; - } - } - - if (quoteStack.length > 0 && c === quoteStack[0]) { - quoteStack.pop(); - } else if (c in quotesFwd) { - quoteStack = [quotesFwd[c]].concat(quoteStack); - } - } - - const text = content.substring(startPos, endPos); - const padding = text.length - text.replace(/^\s+/, '').length; - - return { - text: text.trim(), - offset: position - startPos - padding - }; -} diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 9301135b..65ed89a1 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import {Display} from '../../mixed/js/display.js'; class DisplayFloat extends Display { constructor() { @@ -124,6 +123,41 @@ class DisplayFloat extends Display { parent.appendChild(this.styleNode); } } + + async onTermLookup(e) { + try { + e.preventDefault(); + + const clickedElement = $(e.target); + const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); + textSource.setEndOffset(this.options.scanning.length); + + const {definitions, length} = await apiTermsFind(textSource.text()); + if (definitions.length === 0) { + return false; + } + + textSource.setEndOffset(length); + + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + + const context = { + source: { + definitions: this.definitions, + index: Display.entryIndexFind(clickedElement) + } + }; + + if (this.context) { + context.sentence = sentence; + context.url = this.context.url; + } + + this.termsShow(definitions, this.options, context); + } catch (e) { + this.onError(e); + } + } } window.yomichan_display = new DisplayFloat(); diff --git a/ext/fg/js/source.module.js b/ext/fg/js/source.module.js deleted file mode 100644 index ed9263c5..00000000 --- a/ext/fg/js/source.module.js +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2016-2017 Alex Yatskov - * Author: Alex Yatskov - * - * 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 . - */ - - -/* - * TextSourceRange - */ - -export class TextSourceRange { - constructor(range, content='') { - this.range = range; - this.content = content; - } - - clone() { - return new TextSourceRange(this.range.cloneRange(), this.content); - } - - text() { - return this.content; - } - - setEndOffset(length) { - const state = TextSourceRange.seekForward(this.range.startContainer, this.range.startOffset, length); - this.range.setEnd(state.node, state.offset); - this.content = state.content; - return length - state.remainder; - } - - setStartOffset(length) { - const state = TextSourceRange.seekBackward(this.range.startContainer, this.range.startOffset, length); - this.range.setStart(state.node, state.offset); - this.content = state.content; - return length - state.remainder; - } - - containsPoint(point) { - const rect = this.getPaddedRect(); - return point.x >= rect.left && point.x <= rect.right; - } - - getRect() { - return this.range.getBoundingClientRect(); - } - - getPaddedRect() { - const range = this.range.cloneRange(); - const startOffset = range.startOffset; - const endOffset = range.endOffset; - const node = range.startContainer; - - range.setStart(node, Math.max(0, startOffset - 1)); - range.setEnd(node, Math.min(node.length, endOffset + 1)); - - return range.getBoundingClientRect(); - } - - select() { - const selection = window.getSelection(); - selection.removeAllRanges(); - selection.addRange(this.range); - } - - deselect() { - const selection = window.getSelection(); - selection.removeAllRanges(); - } - - equals(other) { - return other && other.range && other.range.compareBoundaryPoints(Range.START_TO_START, this.range) === 0; - } - - static shouldEnter(node) { - if (node.nodeType !== 1) { - return false; - } - - const skip = ['RT', 'SCRIPT', 'STYLE']; - if (skip.includes(node.nodeName)) { - return false; - } - - const style = window.getComputedStyle(node); - const hidden = - style.visibility === 'hidden' || - style.display === 'none' || - parseFloat(style.fontSize) === 0; - - return !hidden; - } - - static seekForward(node, offset, length) { - const state = {node, offset, remainder: length, content: ''}; - if (!TextSourceRange.seekForwardHelper(node, state)) { - return state; - } - - for (let current = node; current !== null; current = current.parentElement) { - for (let sibling = current.nextSibling; sibling !== null; sibling = sibling.nextSibling) { - if (!TextSourceRange.seekForwardHelper(sibling, state)) { - return state; - } - } - } - - return state; - } - - static seekForwardHelper(node, state) { - if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { - const offset = state.node === node ? state.offset : 0; - const remaining = node.length - offset; - const consumed = Math.min(remaining, state.remainder); - state.content = state.content + node.nodeValue.substring(offset, offset + consumed); - state.node = node; - state.offset = offset + consumed; - state.remainder -= consumed; - } else if (TextSourceRange.shouldEnter(node)) { - for (let i = 0; i < node.childNodes.length; ++i) { - if (!TextSourceRange.seekForwardHelper(node.childNodes[i], state)) { - break; - } - } - } - - return state.remainder > 0; - } - - static seekBackward(node, offset, length) { - const state = {node, offset, remainder: length, content: ''}; - if (!TextSourceRange.seekBackwardHelper(node, state)) { - return state; - } - - for (let current = node; current !== null; current = current.parentElement) { - for (let sibling = current.previousSibling; sibling !== null; sibling = sibling.previousSibling) { - if (!TextSourceRange.seekBackwardHelper(sibling, state)) { - return state; - } - } - } - - return state; - } - - static seekBackwardHelper(node, state) { - if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { - const offset = state.node === node ? state.offset : node.length; - const remaining = offset; - const consumed = Math.min(remaining, state.remainder); - state.content = node.nodeValue.substring(offset - consumed, offset) + state.content; - state.node = node; - state.offset = offset - consumed; - state.remainder -= consumed; - } else if (TextSourceRange.shouldEnter(node)) { - for (let i = node.childNodes.length - 1; i >= 0; --i) { - if (!TextSourceRange.seekBackwardHelper(node.childNodes[i], state)) { - break; - } - } - } - - return state.remainder > 0; - } -} - - -/* - * TextSourceElement - */ - -export class TextSourceElement { - constructor(element, content='') { - this.element = element; - this.content = content; - } - - clone() { - return new TextSourceElement(this.element, this.content); - } - - text() { - return this.content; - } - - setEndOffset(length) { - switch (this.element.nodeName) { - case 'BUTTON': - this.content = this.element.innerHTML; - break; - case 'IMG': - this.content = this.element.getAttribute('alt'); - break; - default: - this.content = this.element.value; - break; - } - - this.content = this.content || ''; - this.content = this.content.substring(0, length); - - return this.content.length; - } - - setStartOffset(length) { - return 0; - } - - containsPoint(point) { - const rect = this.getRect(); - return point.x >= rect.left && point.x <= rect.right; - } - - getRect() { - return this.element.getBoundingClientRect(); - } - - select() { - // NOP - } - - deselect() { - // NOP - } - - equals(other) { - return other && other.element === this.element && other.content === this.content; - } -} diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 5d259936..dc6f5798 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -17,8 +17,6 @@ */ -import {docRangeFromPoint, docSentenceExtract} from '../../fg/js/document.module.js'; - class Display { constructor(spinner, container) { this.spinner = spinner; @@ -71,41 +69,6 @@ class Display { } } - async onTermLookup(e) { - try { - e.preventDefault(); - - const clickedElement = $(e.target); - const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); - textSource.setEndOffset(this.options.scanning.length); - - const {definitions, length} = await apiTermsFind(textSource.text()); - if (definitions.length === 0) { - return false; - } - - textSource.setEndOffset(length); - - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - - const context = { - source: { - definitions: this.definitions, - index: Display.entryIndexFind(clickedElement) - } - }; - - if (this.context) { - context.sentence = sentence; - context.url = this.context.url; - } - - this.termsShow(definitions, this.options, context); - } catch (e) { - this.onError(e); - } - } - onAudioPlay(e) { e.preventDefault(); const link = $(e.currentTarget); @@ -497,5 +460,3 @@ class Display { return $('.entry').eq(index).find('.action-view-note'); } } - -export {Display}; -- cgit v1.2.3 From afc771de9fd34a7b16a6bbf8bcdc85dd758d7836 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 3 Aug 2019 15:06:28 +0300 Subject: inject doc functions as dependencies --- ext/bg/js/search.js | 2 ++ ext/fg/js/float.js | 37 ++----------------------------------- ext/mixed/js/display.js | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 35 deletions(-) (limited to 'ext/fg/js') diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 40bf2019..f08f22da 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -25,6 +25,8 @@ class DisplaySearch extends Display { this.query = $('#query').on('input', this.onSearchInput.bind(this)); this.intro = $('#intro'); + this.dependencies = {...this.dependencies, ...{docRangeFromPoint, docSentenceExtract}}; + window.wanakana.bind(this.query.get(0)); } diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 65ed89a1..090839a1 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -23,6 +23,8 @@ class DisplayFloat extends Display { this.autoPlayAudioTimer = null; this.styleNode = null; + this.dependencies = {...this.dependencies, ...{docRangeFromPoint, docSentenceExtract}}; + $(window).on('message', utilAsync(this.onMessage.bind(this))); } @@ -123,41 +125,6 @@ class DisplayFloat extends Display { parent.appendChild(this.styleNode); } } - - async onTermLookup(e) { - try { - e.preventDefault(); - - const clickedElement = $(e.target); - const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); - textSource.setEndOffset(this.options.scanning.length); - - const {definitions, length} = await apiTermsFind(textSource.text()); - if (definitions.length === 0) { - return false; - } - - textSource.setEndOffset(length); - - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - - const context = { - source: { - definitions: this.definitions, - index: Display.entryIndexFind(clickedElement) - } - }; - - if (this.context) { - context.sentence = sentence; - context.url = this.context.url; - } - - this.termsShow(definitions, this.options, context); - } catch (e) { - this.onError(e); - } - } } window.yomichan_display = new DisplayFloat(); diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index dc6f5798..0067d457 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -28,6 +28,8 @@ class Display { this.index = 0; this.audioCache = {}; + this.dependencies = {}; + $(document).keydown(this.onKeyDown.bind(this)); $(document).on('wheel', this.onWheel.bind(this)); } @@ -69,6 +71,43 @@ class Display { } } + async onTermLookup(e) { + try { + e.preventDefault(); + + const {docRangeFromPoint, docSentenceExtract} = this.dependencies; + + const clickedElement = $(e.target); + const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); + textSource.setEndOffset(this.options.scanning.length); + + const {definitions, length} = await apiTermsFind(textSource.text()); + if (definitions.length === 0) { + return false; + } + + textSource.setEndOffset(length); + + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + + const context = { + source: { + definitions: this.definitions, + index: Display.entryIndexFind(clickedElement) + } + }; + + if (this.context) { + context.sentence = sentence; + context.url = this.context.url; + } + + this.termsShow(definitions, this.options, context); + } catch (e) { + this.onError(e); + } + } + onAudioPlay(e) { e.preventDefault(); const link = $(e.currentTarget); -- cgit v1.2.3