diff options
author | Alex Yatskov <alex@foosoft.net> | 2017-01-07 12:21:47 -0800 |
---|---|---|
committer | Alex Yatskov <alex@foosoft.net> | 2017-01-07 12:21:47 -0800 |
commit | 15ebc06fb3e0d0d3ceeb57281767a99e0f3389a2 (patch) | |
tree | a42b3ef9038bfeccb6ceb9b3a5ec8329d6802116 /ext/fg | |
parent | 86e39efe281f8b09e199d7e2ddc05e77d5add140 (diff) |
WIP
Diffstat (limited to 'ext/fg')
-rw-r--r-- | ext/fg/css/frame.css | 9 | ||||
-rw-r--r-- | ext/fg/frame.html | 18 | ||||
-rw-r--r-- | ext/fg/js/driver.js | 198 | ||||
-rw-r--r-- | ext/fg/js/frame.js | 195 | ||||
-rw-r--r-- | ext/fg/js/popup.js | 57 |
5 files changed, 219 insertions, 258 deletions
diff --git a/ext/fg/css/frame.css b/ext/fg/css/frame.css index 01b8776c..88581f2a 100644 --- a/ext/fg/css/frame.css +++ b/ext/fg/css/frame.css @@ -18,6 +18,15 @@ /* common styles */ +@font-face { + font-family: kanji-stroke-orders; + src: url('../ttf/kanji-stroke-orders.ttf'); +} +@font-face { + font-family: vl-gothic-regular; + src: url('../ttf/vl-gothic-regular.ttf'); +} + body { background-color: #fff; color: #333; diff --git a/ext/fg/frame.html b/ext/fg/frame.html new file mode 100644 index 00000000..32758786 --- /dev/null +++ b/ext/fg/frame.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <title></title> + <link rel="stylesheet" href="css/frame.css"> + </head> + <body> + <div class="spinner"> + <img src="img/spinner.gif"> + </div> + + <div class="content"></div> + + <script src="../lib/jquery-2.2.2.min.js"></script> + <script src="js/frame.js"></script> + </body> +</html> diff --git a/ext/fg/js/driver.js b/ext/fg/js/driver.js index 0c785d31..1c797201 100644 --- a/ext/fg/js/driver.js +++ b/ext/fg/js/driver.js @@ -21,18 +21,13 @@ class Driver { constructor() { this.popup = new Popup(); this.popupTimer = null; - this.audio = {}; this.lastMousePos = null; this.lastTextSource = null; this.pendingLookup = false; this.enabled = false; this.options = null; - this.definitions = null; - this.sequence = 0; - this.fgRoot = chrome.extension.getURL('fg'); chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this)); - window.addEventListener('message', this.onFrameMessage.bind(this)); window.addEventListener('mouseover', this.onMouseOver.bind(this)); window.addEventListener('mousedown', this.onMouseDown.bind(this)); window.addEventListener('mousemove', this.onMouseMove.bind(this)); @@ -114,20 +109,45 @@ class Driver { callback(); } - onFrameMessage(e) { - const {action, params} = e.data, method = this['api_' + action]; - if (typeof(method) === 'function') { - method.call(this, params); + searchAt(point, hideNotFound) { + if (this.pendingLookup) { + return; + } + + const textSource = textSourceFromPoint(point); + if (textSource === null || !textSource.containsPoint(point)) { + if (hideNotFound) { + this.hidePopup(); + } + + return; + } + + if (this.lastTextSource !== null && this.lastTextSource.equals(textSource)) { + return true; } + + this.pendingLookup = true; + this.searchTerms(textSource).then(found => { + if (!found) { + this.searchKanji(textSource).then(found => { + if (!found && hideNotFound) { + this.hidePopup(); + } + }); + } + }).catch(error => { + alert('Error: ' + error); + }).then(() => { + this.pendingLookup = false; + }); } searchTerms(textSource) { textSource.setEndOffset(this.options.scanLength); - this.pendingLookup = true; return findTerm(textSource.text()).then(({definitions, length}) => { if (definitions.length === 0) { - this.pendingLookup = false; return false; } else { textSource.setEndOffset(length); @@ -138,30 +158,10 @@ class Driver { definition.sentence = sentence; }); - const sequence = ++this.sequence; - const context = { - definitions, - sequence, - addable: this.options.ankiMethod !== 'disabled', - root: this.fgRoot, - options: this.options - }; - - return renderText(context, 'term-list.html').then(content => { - this.definitions = definitions; - this.pendingLookup = false; - this.showPopup(textSource, content); - return canAddDefinitions(definitions, ['term_kanji', 'term_kana']); - }).then(states => { - if (states !== null) { - states.forEach((state, index) => this.popup.invokeApi( - 'setActionState', - {index, state, sequence} - )); - } + this.popup.showNextTo(textSource.getRect()); + this.popup.invoke('showTermDefs', {definitions, options: this.options}); - return true; - }); + return true; } }).catch(error => { alert('Error: ' + error); @@ -171,85 +171,22 @@ class Driver { searchKanji(textSource) { textSource.setEndOffset(1); - this.pendingLookup = true; return findKanji(textSource.text()).then(definitions => { if (definitions.length === 0) { - this.pendingLookup = false; return false; } else { definitions.forEach(definition => definition.url = window.location.href); - const sequence = ++this.sequence; - const context = { - definitions, - sequence, - addable: this.options.ankiMethod !== 'disabled', - root: this.fgRoot, - options: this.options - }; - - return renderText(context, 'kanji-list.html').then(content => { - this.definitions = definitions; - this.pendingLookup = false; - this.showPopup(textSource, content); - return canAddDefinitions(definitions, ['kanji']); - }).then(states => { - if (states !== null) { - states.forEach((state, index) => this.popup.invokeApi( - 'setActionState', - {index, state, sequence} - )); - } + this.popup.showNextTo(textSource.getRect()); + this.popup.invoke('showKanjiDefs', {definitions, options: this.options}); - return true; - }); + return true; } }).catch(error => { alert('Error: ' + error); }); } - searchAt(point, hideNotFound) { - if (this.pendingLookup) { - return; - } - - const textSource = textSourceFromPoint(point); - if (textSource === null || !textSource.containsPoint(point)) { - if (hideNotFound) { - this.hidePopup(); - } - - return; - } - - if (this.lastTextSource !== null && this.lastTextSource.equals(textSource)) { - return true; - } - - this.searchTerms(textSource).then(found => { - if (!found) { - this.searchKanji(textSource).then(found => { - if (!found && hideNotFound) { - this.hidePopup(); - } - }); - } - }).catch(error => { - alert('Error: ' + error); - }); - } - - showPopup(textSource, content) { - this.popup.showNextTo(textSource.getRect(), content); - - if (this.options.selectMatchedText) { - textSource.select(); - } - - this.lastTextSource = textSource; - } - hidePopup() { this.popup.hide(); @@ -270,67 +207,6 @@ class Driver { this.hidePopup(); } } - - api_addNote({index, mode}) { - const state = {[mode]: false}; - addDefinition(this.definitions[index], mode).then(success => { - if (success) { - this.popup.invokeApi('setActionState', {index, state, sequence: this.sequence}); - } else { - alert('Note could not be added'); - } - - this.popup.invokeApi('addNoteComplete'); - }).catch(error => { - alert('Error: ' + error); - }); - } - - api_playAudio(index) { - const definition = this.definitions[index]; - - let url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?kanji=${encodeURIComponent(definition.expression)}`; - if (definition.reading) { - url += `&kana=${encodeURIComponent(definition.reading)}`; - } - - for (const key in this.audio) { - this.audio[key].pause(); - } - - const audio = this.audio[url] || new Audio(url); - audio.currentTime = 0; - audio.play(); - - this.audio[url] = audio; - } - - api_displayKanji(kanji) { - findKanji(kanji).then(definitions => { - definitions.forEach(definition => definition.url = window.location.href); - - const sequence = ++this.sequence; - const context = { - definitions, - sequence, - addable: this.options.ankiMethod !== 'disabled', - root: this.fgRoot, - options: this.options - }; - - return renderText(context, 'kanji-list.html').then(content => { - this.definitions = definitions; - this.popup.setContent(content, definitions); - return canAddDefinitions(definitions, ['kanji']); - }).then(states => { - if (states !== null) { - states.forEach((state, index) => this.popup.invokeApi('setActionState', {index, state, sequence})); - } - }); - }).catch(error => { - alert('Error: ' + error); - }); - } } window.driver = new Driver(); diff --git a/ext/fg/js/frame.js b/ext/fg/js/frame.js index 5878bb43..e3d152c1 100644 --- a/ext/fg/js/frame.js +++ b/ext/fg/js/frame.js @@ -17,76 +17,157 @@ */ -function invokeApi(action, params, target) { - target.postMessage({action, params}, '*'); -} +// function renderText(data, template) { +// return invokeApiBg('renderText', {data, template}); +// } -function showSpinner(show) { - const spinner = document.querySelector('.spinner'); - spinner.style.visibility = show ? 'visible' : 'hidden'; -} +// function canAddDefinitions(definitions, modes) { +// return invokeApiBg('canAddDefinitions', {definitions, modes}).catch(() => null); +// } + +// function addDefinition(definition, mode) { +// return invokeApiBg('addDefinition', {definition, mode}); +// } + +// function invokeApi(action, params, target) { +// target.postMessage({action, params}, '*'); +// } + +// function showSpinner(show) { +// const spinner = document.querySelector('.spinner'); +// spinner.style.visibility = show ? 'visible' : 'hidden'; +// } + +// function registerKanjiLinks() { +// for (const link of Array.from(document.getElementsByClassName('kanji-link'))) { +// link.addEventListener('click', e => { +// e.preventDefault(); +// invokeApi('displayKanji', e.target.innerHTML, window.parent); +// }); +// } +// } + +// function registerAddNoteLinks() { +// for (const link of Array.from(document.getElementsByClassName('action-add-note'))) { +// link.addEventListener('click', e => { +// e.preventDefault(); +// const ds = e.currentTarget.dataset; +// invokeApi('addNote', {index: ds.index, mode: ds.mode}, window.parent); +// showSpinner(true); +// }); +// } +// } -function registerKanjiLinks() { - for (const link of Array.from(document.getElementsByClassName('kanji-link'))) { - link.addEventListener('click', e => { - e.preventDefault(); - invokeApi('displayKanji', e.target.innerHTML, window.parent); +// function registerAudioLinks() { +// for (const link of Array.from(document.getElementsByClassName('action-play-audio'))) { +// link.addEventListener('click', e => { +// e.preventDefault(); +// const ds = e.currentTarget.dataset; +// invokeApi('playAudio', ds.index, window.parent); +// }); +// } +// } + +// function api_setActionState({index, state, sequence}) { +// for (const mode in state) { +// const matches = document.querySelectorAll(`.action-bar[data-sequence="${sequence}"] .action-add-note[data-index="${index}"][data-mode="${mode}"]`); +// if (matches.length === 0) { +// return; +// } + +// const classes = matches[0].classList; +// if (state[mode]) { +// classes.remove('disabled'); +// } else { +// classes.add('disabled'); +// } + +// classes.remove('pending'); +// } +// } + +class FrameContext { + constructor() { + $(window).on('message', e => { + const {action, params} = e.originalEvent.data, method = this['api_' + action]; + if (typeof(method) === 'function') { + method.call(this, params); + } }); } -} -function registerAddNoteLinks() { - for (const link of Array.from(document.getElementsByClassName('action-add-note'))) { - link.addEventListener('click', e => { - e.preventDefault(); - const ds = e.currentTarget.dataset; - invokeApi('addNote', {index: ds.index, mode: ds.mode}, window.parent); - showSpinner(true); + api_showTermDefs({definitions, options}) { + const context = { + definitions, + addable: options.ankiMethod !== 'disabled', + playback: options.enableAudioPlayback + }; + + this.renderText('term-list.html', context).then(content => { + $('.content').html(content); }); } -} -function registerAudioLinks() { - for (const link of Array.from(document.getElementsByClassName('action-play-audio'))) { - link.addEventListener('click', e => { - e.preventDefault(); - const ds = e.currentTarget.dataset; - invokeApi('playAudio', ds.index, window.parent); + api_showKanjiDefs({definitions, options}) { + const context = { + definitions, + addable: options.ankiMethod !== 'disabled' + }; + + this.renderText('kanji-list.html', context).then(content => { + $('.content').html(content); }); } -} -function api_addNoteComplete() { - showSpinner(false); -} + renderText(template, data) { + return this.invokeBgApi('renderText', {data, template}); + } -function api_setActionState({index, state, sequence}) { - for (const mode in state) { - const matches = document.querySelectorAll(`.action-bar[data-sequence="${sequence}"] .action-add-note[data-index="${index}"][data-mode="${mode}"]`); - if (matches.length === 0) { - return; - } - - const classes = matches[0].classList; - if (state[mode]) { - classes.remove('disabled'); - } else { - classes.add('disabled'); - } - - classes.remove('pending'); + invokeBgApi(action, params) { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({action, params}, ({result, error}) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + }); } } -document.addEventListener('DOMContentLoaded', () => { - registerKanjiLinks(); - registerAddNoteLinks(); - registerAudioLinks(); -}); +window.frameContext = new FrameContext(); -window.addEventListener('message', e => { - const {action, params} = e.data, method = window['api_' + action]; - if (typeof(method) === 'function') { - method(params); - } -}); + // api_addNote({index, mode}) { + // const state = {[mode]: false}; + // addDefinition(this.definitions[index], mode).then(success => { + // if (success) { + // this.popup.invokeApi('setActionState', {index, state, sequence: this.sequence}); + // } else { + // alert('Note could not be added'); + // } + + // this.popup.invokeApi('addNoteComplete'); + // }).catch(error => { + // alert('Error: ' + error); + // }); + // } + + // api_playAudio(index) { + // const definition = this.definitions[index]; + + // let url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?kanji=${encodeURIComponent(definition.expression)}`; + // if (definition.reading) { + // url += `&kana=${encodeURIComponent(definition.reading)}`; + // } + + // for (const key in this.audio) { + // this.audio[key].pause(); + // } + + // const audio = this.audio[url] || new Audio(url); + // audio.currentTime = 0; + // audio.play(); + + // this.audio[url] = audio; + // } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 398e975e..35d7a00f 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -19,29 +19,32 @@ class Popup { constructor() { - this.container = null; this.offset = 10; - } - show(rect, content) { - this.inject(); + this.container = document.createElement('iframe'); + this.container.id = 'yomichan-popup'; + this.container.addEventListener('mousedown', e => e.stopPropagation()); + this.container.addEventListener('scroll', e => e.stopPropagation()); + this.container.setAttribute('src', chrome.extension.getURL('fg/frame.html')); + document.body.appendChild(this.container); + } + + show(rect) { this.container.style.left = rect.x + 'px'; this.container.style.top = rect.y + 'px'; this.container.style.height = rect.height + 'px'; this.container.style.width = rect.width + 'px'; this.container.style.visibility = 'visible'; - - this.setContent(content); } - showNextTo(elementRect, content) { - this.inject(); - - const containerRect = this.container.getBoundingClientRect(); + showNextTo(elementRect) { + const containerStyle = window.getComputedStyle(this.container); + const containerHeight = parseInt(containerStyle.height); + const containerWidth = parseInt(containerStyle.width); let x = elementRect.left; - let width = containerRect.width; + let width = containerWidth; if (x + width >= window.innerWidth) { const widthMax = window.innerWidth - x; width = Math.min(width, widthMax); @@ -49,14 +52,14 @@ class Popup { } let y = elementRect.bottom + this.offset; - let height = containerRect.height; + let height = containerHeight; if (y + height >= window.innerHeight) { const heightMax = window.innerHeight - y - this.offset; height = Math.min(height, heightMax); y = elementRect.top - height - this.offset; } - this.show({x, y, width, height}, content); + this.show({x, y, width, height}); } visible() { @@ -69,35 +72,9 @@ class Popup { } } - setContent(content) { - if (this.container === null) { - return; - } - - this.container.contentWindow.scrollTo(0, 0); - - const doc = this.container.contentDocument; - doc.open(); - doc.write(content); - doc.close(); - } - - invokeApi(action, params) { + invoke(action, params) { if (this.container !== null) { this.container.contentWindow.postMessage({action, params}, '*'); } } - - inject() { - if (this.container !== null) { - return; - } - - this.container = document.createElement('iframe'); - this.container.id = 'yomichan-popup'; - this.container.addEventListener('mousedown', e => e.stopPropagation()); - this.container.addEventListener('scroll', e => e.stopPropagation()); - - document.body.appendChild(this.container); - } } |