diff options
Diffstat (limited to 'ext/fg/js')
-rw-r--r-- | ext/fg/js/frontend.js | 18 | ||||
-rw-r--r-- | ext/fg/js/popup-proxy-host.js | 6 | ||||
-rw-r--r-- | ext/fg/js/popup-proxy.js | 4 | ||||
-rw-r--r-- | ext/fg/js/popup.js | 21 | ||||
-rw-r--r-- | ext/fg/js/source.js | 190 |
5 files changed, 141 insertions, 98 deletions
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 167e82c0..3292cac4 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -128,7 +128,7 @@ class Frontend { } this.popupTimerClear(); - this.searchClear(); + this.searchClear(true); } onMouseOut(e) { @@ -138,7 +138,7 @@ class Frontend { onFrameMessage(e) { const handlers = { popupClose: () => { - this.searchClear(); + this.searchClear(true); }, selectionCopy: () => { @@ -153,7 +153,7 @@ class Frontend { } onResize() { - this.searchClear(); + this.searchClear(true); } onClick(e) { @@ -265,7 +265,7 @@ class Frontend { async updateOptions() { this.options = await apiOptionsGet(this.getOptionsContext()); if (!this.options.enable) { - this.searchClear(); + this.searchClear(false); } } @@ -320,7 +320,7 @@ class Frontend { textSource.cleanup(); } if (hideResults && this.options.scanning.autoHideResults) { - this.searchClear(); + this.searchClear(true); } this.pendingLookup = false; @@ -333,7 +333,7 @@ class Frontend { const searchText = textSource.text(); if (searchText.length === 0) { - return; + return false; } const {definitions, length} = await apiTermsFind(searchText, this.getOptionsContext()); @@ -366,7 +366,7 @@ class Frontend { const searchText = textSource.text(); if (searchText.length === 0) { - return; + return false; } const definitions = await apiKanjiFind(searchText, this.getOptionsContext()); @@ -392,8 +392,8 @@ class Frontend { return true; } - searchClear() { - this.popup.hide(); + searchClear(changeFocus) { + this.popup.hide(changeFocus); this.popup.clearAutoPlayTimer(); if (this.options.scanning.selectText && this.textSourceLast) { diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 396f7556..cb9741be 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -40,7 +40,7 @@ class PopupProxyHost { createNestedPopup: ({parentId}) => this.createNestedPopup(parentId), show: ({id, elementRect, options}) => this.show(id, elementRect, options), showOrphaned: ({id, elementRect, options}) => this.show(id, elementRect, options), - hide: ({id}) => this.hide(id), + hide: ({id, changeFocus}) => this.hide(id, changeFocus), setVisible: ({id, visible}) => this.setVisible(id, visible), containsPoint: ({id, x, y}) => this.containsPoint(id, x, y), termsShow: ({id, elementRect, writingMode, definitions, options, context}) => this.termsShow(id, elementRect, writingMode, definitions, options, context), @@ -98,9 +98,9 @@ class PopupProxyHost { return await popup.showOrphaned(elementRect, options); } - async hide(id) { + async hide(id, changeFocus) { const popup = this.getPopup(id); - return popup.hide(); + return popup.hide(changeFocus); } async setVisible(id, visible) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 235e1730..072cebc9 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -58,11 +58,11 @@ class PopupProxy { return await this.invokeHostApi('showOrphaned', {id, elementRect, options}); } - async hide() { + async hide(changeFocus) { if (this.id === null) { return; } - return await this.invokeHostApi('hide', {id: this.id}); + return await this.invokeHostApi('hide', {id: this.id, changeFocus}); } async setVisible(visible) { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 08965084..64da9aef 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -105,7 +105,7 @@ class Popup { container.style.height = `${height}px`; container.style.visibility = 'visible'; - this.hideChildren(); + this.hideChildren(true); } static getPositionForHorizontalText(elementRect, width, height, maxWidth, maxHeight, optionsGeneral) { @@ -206,16 +206,21 @@ class Popup { this.invokeApi('orphaned'); } - hide() { - this.hideChildren(); + hide(changeFocus) { + if (this.isContainerHidden()) { + changeFocus = false; + } + this.hideChildren(changeFocus); this.hideContainer(); - this.focusParent(); + if (changeFocus) { + this.focusParent(); + } } - hideChildren() { - // recursively hides all children - if (this.child && !this.child.isContainerHidden()) { - this.child.hide(); + hideChildren(changeFocus) { + // Recursively hides all children. + if (this.child !== null && !this.child.isContainerHidden()) { + this.child.hide(changeFocus); } } diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index 4642de50..ee4f58e2 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -83,120 +83,142 @@ class TextSourceRange { } static shouldEnter(node) { - if (node.nodeType !== 1) { - return false; - } - - const skip = ['RT', 'SCRIPT', 'STYLE']; - if (skip.includes(node.nodeName.toUpperCase())) { - return false; + switch (node.nodeName.toUpperCase()) { + case 'RT': + case 'SCRIPT': + case 'STYLE': + return false; } const style = window.getComputedStyle(node); - const hidden = + return !( style.visibility === 'hidden' || style.display === 'none' || - parseFloat(style.fontSize) === 0; + parseFloat(style.fontSize) === 0); + } - return !hidden; + static getRubyElement(node) { + node = TextSourceRange.getParentElement(node); + if (node !== null && node.nodeName.toUpperCase() === "RT") { + node = node.parentNode; + return (node !== null && node.nodeName.toUpperCase() === "RUBY") ? node : null; + } + return null; } static seekForward(node, offset, length) { const state = {node, offset, remainder: length, content: ''}; - if (!TextSourceRange.seekForwardHelper(node, state)) { - return state; + const TEXT_NODE = Node.TEXT_NODE; + const ELEMENT_NODE = Node.ELEMENT_NODE; + let resetOffset = false; + + const ruby = TextSourceRange.getRubyElement(node); + if (ruby !== null) { + node = ruby; + resetOffset = true; } - 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; + while (node !== null) { + let visitChildren = true; + const nodeType = node.nodeType; + + if (nodeType === TEXT_NODE) { + state.node = node; + if (TextSourceRange.seekForwardTextNode(state, resetOffset)) { + break; } + resetOffset = true; + } else if (nodeType === ELEMENT_NODE) { + visitChildren = TextSourceRange.shouldEnter(node); } + + node = TextSourceRange.getNextNode(node, visitChildren); } return state; } - static seekForwardHelper(node, state) { - if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { - const offset = state.node === node ? state.offset : 0; - - let consumed = 0; - let stripped = 0; - while (state.remainder - consumed > 0) { - const currentChar = node.nodeValue[offset + consumed + stripped]; - if (!currentChar) { - break; - } else if (currentChar.match(IGNORE_TEXT_PATTERN)) { - stripped++; - } else { - consumed++; - state.content += currentChar; - } - } - - state.node = node; - state.offset = offset + consumed + stripped; - state.remainder -= consumed; - } else if (TextSourceRange.shouldEnter(node)) { - for (let i = 0; i < node.childNodes.length; ++i) { - if (!TextSourceRange.seekForwardHelper(node.childNodes[i], state)) { + static seekForwardTextNode(state, resetOffset) { + const nodeValue = state.node.nodeValue; + const nodeValueLength = nodeValue.length; + let content = state.content; + let offset = resetOffset ? 0 : state.offset; + let remainder = state.remainder; + let result = false; + + for (; offset < nodeValueLength; ++offset) { + const c = nodeValue[offset]; + if (!IGNORE_TEXT_PATTERN.test(c)) { + content += c; + if (--remainder <= 0) { + result = true; + ++offset; break; } } } - return state.remainder > 0; + state.offset = offset; + state.content = content; + state.remainder = remainder; + return result; } static seekBackward(node, offset, length) { const state = {node, offset, remainder: length, content: ''}; - if (!TextSourceRange.seekBackwardHelper(node, state)) { - return state; + const TEXT_NODE = Node.TEXT_NODE; + const ELEMENT_NODE = Node.ELEMENT_NODE; + let resetOffset = false; + + const ruby = TextSourceRange.getRubyElement(node); + if (ruby !== null) { + node = ruby; + resetOffset = true; } - 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; + while (node !== null) { + let visitChildren = true; + const nodeType = node.nodeType; + + if (nodeType === TEXT_NODE) { + state.node = node; + if (TextSourceRange.seekBackwardTextNode(state, resetOffset)) { + break; } + resetOffset = true; + } else if (nodeType === ELEMENT_NODE) { + visitChildren = TextSourceRange.shouldEnter(node); } + + node = TextSourceRange.getPreviousNode(node, visitChildren); } 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; - - let consumed = 0; - let stripped = 0; - while (state.remainder - consumed > 0) { - const currentChar = node.nodeValue[offset - 1 - consumed - stripped]; // negative indices are undefined in JS - if (!currentChar) { - break; - } else if (currentChar.match(IGNORE_TEXT_PATTERN)) { - stripped++; - } else { - consumed++; - state.content = currentChar + state.content; - } - } - - state.node = node; - state.offset = offset - consumed - stripped; - 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)) { + static seekBackwardTextNode(state, resetOffset) { + const nodeValue = state.node.nodeValue; + let content = state.content; + let offset = resetOffset ? nodeValue.length : state.offset; + let remainder = state.remainder; + let result = false; + + for (; offset > 0; --offset) { + const c = nodeValue[offset - 1]; + if (!IGNORE_TEXT_PATTERN.test(c)) { + content = c + content; + if (--remainder <= 0) { + result = true; + --offset; break; } } } - return state.remainder > 0; + state.offset = offset; + state.content = content; + state.remainder = remainder; + return result; } static getParentElement(node) { @@ -219,22 +241,38 @@ class TextSourceRange { static getNodesInRange(range) { const end = range.endContainer; const nodes = []; - for (let node = range.startContainer; node !== null; node = TextSourceRange.getNextNode(node)) { + for (let node = range.startContainer; node !== null; node = TextSourceRange.getNextNode(node, true)) { nodes.push(node); if (node === end) { break; } } return nodes; } - static getNextNode(node) { - let next = node.firstChild; + static getNextNode(node, visitChildren) { + let next = visitChildren ? node.firstChild : null; if (next === null) { while (true) { next = node.nextSibling; if (next !== null) { break; } next = node.parentNode; - if (node === null) { break; } + if (next === null) { break; } + + node = next; + } + } + return next; + } + + static getPreviousNode(node, visitChildren) { + let next = visitChildren ? node.lastChild : null; + if (next === null) { + while (true) { + next = node.previousSibling; + if (next !== null) { break; } + + next = node.parentNode; + if (next === null) { break; } node = next; } |