diff options
Diffstat (limited to 'ext/js/client.js')
-rw-r--r-- | ext/js/client.js | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/ext/js/client.js b/ext/js/client.js new file mode 100644 index 00000000..2d9a470f --- /dev/null +++ b/ext/js/client.js @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 Alex Yatskov <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +class Client { + constructor() { + this.popupOffset = 10; + this.lastMosePos = null; + this.enabled = false; + + this.popup = document.createElement('div'); + this.popup.classList.add('yomichan-popup'); + this.popup.addEventListener('mousedown', (e) => e.stopPropagation()); + this.popup.addEventListener('scroll', (e) => e.stopPropagation()); + document.body.appendChild(this.popup); + + chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); + window.addEventListener('mousedown', this.onMouseDown.bind(this)); + window.addEventListener('mousemove', this.onMouseMove.bind(this)); + window.addEventListener('keydown', this.onKeyDown.bind(this)); + window.addEventListener('scroll', (e) => this.hidePopup()); + window.addEventListener('resize', (e) => this.hidePopup()); + + getState((state) => this.setEnabled(state === 'enabled')); + } + + onKeyDown(e) { + if (this.enabled && this.lastMousePos !== null && (e.keyCode === 16 || e.charCode === 16)) { + this.searchAtPoint(this.lastMousePos); + } + } + + onMouseMove(e) { + this.lastMousePos = {x: e.clientX, y: e.clientY}; + if (this.enabled && (e.shiftKey || e.which === 2)) { + this.searchAtPoint(this.lastMousePos); + } + } + + onMouseDown(e) { + this.lastMousePos = {x: e.clientX, y: e.clientY}; + if (this.enabled && (e.shiftKey || e.which === 2)) { + this.searchAtPoint(this.lastMousePos); + } else { + this.hidePopup(); + } + } + + onMessage(request, sender, callback) { + this.setEnabled(request === 'enabled'); + callback(); + } + + searchAtPoint(point) { + const range = getRangeAtPoint(point, 10); + if (range === null) { + this.hidePopup(); + return; + } + + const rect = getRangePaddedRect(range); + if (point.x < rect.left || point.x > rect.right) { + this.hidePopup(); + return; + } + + findTerm(range.toString(), ({results, length}) => { + if (length === 0) { + this.hidePopup(); + } else { + range.setEnd(range.endContainer, range.startOffset + length); + renderTemplate({defs: results}, 'defs.html', (html) => { + this.popup.innerHTML = html; + this.showPopup(range); + }); + } + }); + } + + showPopup(range) { + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + + const pos = getPopupPositionForRange(this.popup, range, this.popupOffset); + + this.popup.style.left = pos.x + 'px'; + this.popup.style.top = pos.y + 'px'; + this.popup.style.visibility = 'visible'; + } + + hidePopup() { + if (this.popup.style.visibility !== 'hidden') { + const selection = window.getSelection(); + selection.removeAllRanges(); + this.popup.style.visibility = 'hidden'; + } + } + + setEnabled(enabled) { + if (!(this.enabled = enabled)) { + this.hidePopup(); + } + } +} + +window.yomiClient = new Client(); |