diff options
| author | siikamiika <siikamiika@users.noreply.github.com> | 2019-10-30 03:58:24 +0200 | 
|---|---|---|
| committer | siikamiika <siikamiika@users.noreply.github.com> | 2019-11-23 17:40:52 +0200 | 
| commit | c35a05cd62d43ff435c022a353de55510b020277 (patch) | |
| tree | eed5ca7f70aed856ec2be69b39013202dfcba596 | |
| parent | f63e8e4be0e7bdb1a2e45e349bf667ea7ca4adab (diff) | |
add kana to text
| -rw-r--r-- | ext/bg/js/api.js | 37 | ||||
| -rw-r--r-- | ext/bg/js/backend.js | 1 | ||||
| -rw-r--r-- | ext/bg/js/search-query-parser.js | 97 | ||||
| -rw-r--r-- | ext/fg/js/api.js | 4 | ||||
| -rw-r--r-- | ext/mixed/css/display.css | 4 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 78 | 
6 files changed, 188 insertions, 33 deletions
| diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index df73aa2a..064903ca 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -79,6 +79,43 @@ async function apiTermsFind(text, details, optionsContext) {      return {length, definitions};  } +async function apiTextParse(text, optionsContext) { +    const options = await apiOptionsGet(optionsContext); +    const translator = utilBackend().translator; + +    const results = []; +    while (text) { +        let [definitions, length] = await translator.findTerms(text, {}, options); +        if (definitions.length > 0) { +            definitions = dictTermsSort(definitions); +            const {expression, source, reading} = definitions[0]; + +            let stemLength = source.length; +            while (source[stemLength - 1] !== expression[stemLength - 1]) { +                --stemLength; +            } +            const offset = source.length - stemLength; + +            for (const result of jpDistributeFurigana( +                source.slice(0, offset === 0 ? source.length : source.length - offset), +                reading.slice(0, offset === 0 ? reading.length : source.length + (reading.length - expression.length) - offset) +            )) { +                results.push(result); +            } + +            if (stemLength !== source.length) { +                results.push({text: source.slice(stemLength)}); +            } + +            text = text.slice(source.length); +        } else { +            results.push({text: text[0]}); +            text = text.slice(1); +        } +    } +    return results; +} +  async function apiKanjiFind(text, optionsContext) {      const options = await apiOptionsGet(optionsContext);      const definitions = await utilBackend().translator.findKanji(text, options); diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index efad153a..d0e404f2 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -180,6 +180,7 @@ Backend.messageHandlers = {      optionsSet: ({changedOptions, optionsContext, source}) => apiOptionsSet(changedOptions, optionsContext, source),      kanjiFind: ({text, optionsContext}) => apiKanjiFind(text, optionsContext),      termsFind: ({text, details, optionsContext}) => apiTermsFind(text, details, optionsContext), +    textParse: ({text, optionsContext}) => apiTextParse(text, optionsContext),      definitionAdd: ({definition, mode, context, optionsContext}) => apiDefinitionAdd(definition, mode, context, optionsContext),      definitionsAddable: ({definitions, modes, optionsContext}) => apiDefinitionsAddable(definitions, modes, optionsContext),      noteView: ({noteId}) => apiNoteView(noteId), diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js index debe53b4..c3a3900b 100644 --- a/ext/bg/js/search-query-parser.js +++ b/ext/bg/js/search-query-parser.js @@ -23,22 +23,103 @@ class QueryParser {          this.queryParser = document.querySelector('#query-parser'); -        // TODO also enable for mouseover scanning -        this.queryParser.addEventListener('click', (e) => this.onTermLookup(e)); +        this.queryParser.addEventListener('click', (e) => this.onClick(e)); +        this.queryParser.addEventListener('mousemove', (e) => this.onMouseMove(e));      }      onError(error) {          logError(error, false);      } -    async onTermLookup(e) { -        const {textSource} = await this.search.onTermLookup(e, {isQueryParser: true}); -        if (textSource) { -            textSource.select(); +    onClick(e) { +        this.onTermLookup(e, {disableScroll: true, selectText: true}); +    } + +    async onMouseMove(e) { +        if ( +            (e.buttons & 0x1) !== 0x0 // Left mouse button +        ) { +            return; +        } + +        const scanningOptions = this.search.options.scanning; +        const scanningModifier = scanningOptions.modifier; +        if (!( +            QueryParser.isScanningModifierPressed(scanningModifier, e) || +            (scanningOptions.middleMouse && (e.buttons & 0x4) !== 0x0) // Middle mouse button +        )) { +            return;          } + +        await this.onTermLookup(e, {disableScroll: true, selectText: true, disableHistory: true}) +    } + +    onTermLookup(e, params) { +        this.search.onTermLookup(e, params);      } -    setText(text) { -        this.queryParser.innerText = text; +    async setText(text) { +        this.search.setSpinnerVisible(true); + +        const results = await apiTextParse(text, this.search.getOptionsContext()); + +        const tempContainer = document.createElement('div'); +        for (const {text, furigana} of results) { +            if (furigana) { +                const rubyElement = document.createElement('ruby'); +                const furiganaElement = document.createElement('rt'); +                furiganaElement.innerText = furigana; +                rubyElement.appendChild(document.createTextNode(text)); +                rubyElement.appendChild(furiganaElement); +                tempContainer.appendChild(rubyElement); +            } else { +                tempContainer.appendChild(document.createTextNode(text)); +            } +        } +        this.queryParser.innerHTML = ''; +        this.queryParser.appendChild(tempContainer); + +        this.search.setSpinnerVisible(false); +    } + +    async parseText(text) { +        const results = []; +        while (text) { +            const {definitions, length} =  await apiTermsFind(text, {}, this.search.getOptionsContext()); +            if (length) { +                results.push(definitions); +                text = text.slice(length); +            } else { +                results.push(text[0]); +                text = text.slice(1); +            } +        } +        return results; +    } + +    popupTimerSet(callback) { +        const delay = this.options.scanning.delay; +        if (delay > 0) { +            this.popupTimer = window.setTimeout(callback, delay); +        } else { +            Promise.resolve().then(callback); +        } +    } + +    popupTimerClear() { +        if (this.popupTimer !== null) { +            window.clearTimeout(this.popupTimer); +            this.popupTimer = null; +        } +    } + +    static isScanningModifierPressed(scanningModifier, mouseEvent) { +        switch (scanningModifier) { +            case 'alt': return mouseEvent.altKey; +            case 'ctrl': return mouseEvent.ctrlKey; +            case 'shift': return mouseEvent.shiftKey; +            case 'none': return true; +            default: return false; +        }      }  } diff --git a/ext/fg/js/api.js b/ext/fg/js/api.js index 945ba076..cc1e0e90 100644 --- a/ext/fg/js/api.js +++ b/ext/fg/js/api.js @@ -29,6 +29,10 @@ function apiTermsFind(text, details, optionsContext) {      return utilInvoke('termsFind', {text, details, optionsContext});  } +function apiTextParse(text, optionsContext) { +    return utilInvoke('textParse', {text, optionsContext}); +} +  function apiKanjiFind(text, optionsContext) {      return utilInvoke('kanjiFind', {text, optionsContext});  } diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css index 5e5213ff..81109fc6 100644 --- a/ext/mixed/css/display.css +++ b/ext/mixed/css/display.css @@ -73,6 +73,10 @@ ol, ul {  } +html:root[data-yomichan-page=search] body { +    min-height: 101vh; /* always show scroll bar to avoid scanning problems */ +} +  /*   * Search page   */ diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 07a851f5..82385bf9 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -98,17 +98,62 @@ class Display {          }      } -    async onTermLookup(e) { +    async onTermLookup(e, {disableScroll, selectText, disableHistory}) { +        const termLookupResults = await this.termLookup(e); +        if (!termLookupResults) { +            return false; +        } + +        try { +            const {textSource, definitions} = termLookupResults; + +            const scannedElement = e.target; +            const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + +            if (!disableScroll) { +                this.windowScroll.toY(0); +            } +            let context; +            if (disableHistory) { +                const {url, source} = this.context || {}; +                context = {sentence, url, source, disableScroll}; +            } else { +                context = { +                    disableScroll, +                    source: { +                        definitions: this.definitions, +                        index: this.entryIndexFind(scannedElement), +                        scroll: this.windowScroll.y +                    } +                }; + +                if (this.context) { +                    context.sentence = sentence; +                    context.url = this.context.url; +                    context.source.source = this.context.source; +                } +            } + +            this.setContentTerms(definitions, context); + +            if (selectText) { +                textSource.select(); +            } +        } catch (error) { +            this.onError(error); +        } +    } + +    async termLookup(e) {          try {              e.preventDefault(); -            const clickedElement = e.target;              const textSource = docRangeFromPoint(e.clientX, e.clientY, this.options);              if (textSource === null) {                  return false;              } -            let definitions, length, sentence; +            let definitions, length;              try {                  textSource.setEndOffset(this.options.scanning.length); @@ -118,30 +163,11 @@ class Display {                  }                  textSource.setEndOffset(length); - -                sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt);              } finally {                  textSource.cleanup();              } -            this.windowScroll.toY(0); -            const context = { -                source: { -                    definitions: this.definitions, -                    index: this.entryIndexFind(clickedElement), -                    scroll: this.windowScroll.y -                } -            }; - -            if (this.context) { -                context.sentence = sentence; -                context.url = this.context.url; -                context.source.source = this.context.source; -            } - -            this.setContentTerms(definitions, context); - -            return {textSource}; +            return {textSource, definitions};          } catch (error) {              this.onError(error);          } @@ -338,8 +364,10 @@ class Display {              const content = await apiTemplateRender('terms.html', params);              this.container.innerHTML = content; -            const {index, scroll} = context || {}; -            this.entryScrollIntoView(index || 0, scroll); +            const {index, scroll, disableScroll} = context || {}; +            if (!disableScroll) { +                this.entryScrollIntoView(index || 0, scroll); +            }              if (options.audio.enabled && options.audio.autoPlay) {                  this.autoPlayAudio(); |