diff options
Diffstat (limited to 'src/pages')
-rw-r--r-- | src/pages/completion.js | 27 | ||||
-rw-r--r-- | src/pages/console.html | 20 | ||||
-rw-r--r-- | src/pages/console.js | 186 | ||||
-rw-r--r-- | src/pages/console.scss | 92 | ||||
-rw-r--r-- | src/pages/settings.html | 18 | ||||
-rw-r--r-- | src/pages/settings.js | 22 | ||||
-rw-r--r-- | src/pages/settings.scss | 8 |
7 files changed, 373 insertions, 0 deletions
diff --git a/src/pages/completion.js b/src/pages/completion.js new file mode 100644 index 0000000..4c69afb --- /dev/null +++ b/src/pages/completion.js @@ -0,0 +1,27 @@ +export default class Completion { + constructor(completions) { + if (typeof completions.length !== 'number') { + throw new TypeError('completions does not have a length in number'); + } + this.completions = completions; + this.index = 0; + } + + prev() { + let length = this.completions.length; + if (length === 0) { + return null; + } + this.index = (this.index + length - 1) % length; + return this.completions[this.index]; + } + + next() { + if (this.completions.length === 0) { + return null; + } + let item = this.completions[this.index]; + this.index = (this.index + 1) % this.completions.length; + return item; + } +} diff --git a/src/pages/console.html b/src/pages/console.html new file mode 100644 index 0000000..4222f12 --- /dev/null +++ b/src/pages/console.html @@ -0,0 +1,20 @@ +<!doctype html> +<html> + <head> + <meta charset=utf-8 /> + <title>VimVixen console</title> + <script src='console.js'></script> + </head> + <body class='vimvixen-console'> + <p id='vimvixen-console-error' + class='vimvixen-console-error'></p> + <div id='vimvixen-console-command'> + <ul id='vimvixen-console-completion' class='vimvixen-console-completion'></ul> + <div class='vimvixen-console-command'> + <i class='vimvixen-console-command-prompt'></i><input + id='vimvixen-console-command-input' + class='vimvixen-console-command-input'></input> + </div> + </div> + </body> +</html> diff --git a/src/pages/console.js b/src/pages/console.js new file mode 100644 index 0000000..31f2643 --- /dev/null +++ b/src/pages/console.js @@ -0,0 +1,186 @@ +import './console.scss'; +import Completion from './completion'; +import messages from '../content/messages'; + +// TODO consider object-oriented +let prevValue = ''; +let completion = null; +let completionOrigin = ''; +let prevState = {}; + +const handleBlur = () => { + return browser.runtime.sendMessage({ + type: messages.CONSOLE_BLURRED, + }); +}; + +const selectCompletion = (target) => { + let container = window.document.querySelector('#vimvixen-console-completion'); + Array.prototype.forEach.call(container.children, (ele) => { + if (!ele.classList.contains('vimvixen-console-completion-item')) { + return; + } + if (ele === target) { + ele.classList.add('vimvixen-completion-selected'); + } else { + ele.classList.remove('vimvixen-completion-selected'); + } + }); +}; + +const completeNext = () => { + if (!completion) { + return; + } + let item = completion.next(); + if (!item) { + return; + } + + let input = window.document.querySelector('#vimvixen-console-command-input'); + input.value = completionOrigin + ' ' + item[0].content; + + selectCompletion(item[1]); +}; + +const completePrev = () => { + if (!completion) { + return; + } + let item = completion.prev(); + if (!item) { + return; + } + + let input = window.document.querySelector('#vimvixen-console-command-input'); + input.value = completionOrigin + ' ' + item[0].content; + + selectCompletion(item[1]); +}; + +const handleKeydown = (e) => { + let input = window.document.querySelector('#vimvixen-console-command-input'); + + switch (e.keyCode) { + case KeyboardEvent.DOM_VK_ESCAPE: + return input.blur(); + case KeyboardEvent.DOM_VK_RETURN: + return browser.runtime.sendMessage({ + type: messages.CONSOLE_ENTERED, + text: e.target.value + }); + case KeyboardEvent.DOM_VK_TAB: + if (e.shiftKey) { + completePrev(); + } else { + completeNext(); + } + e.stopPropagation(); + e.preventDefault(); + break; + } +}; + +const handleKeyup = (e) => { + if (e.keyCode === KeyboardEvent.DOM_VK_TAB) { + return; + } + if (e.target.value === prevValue) { + return; + } + prevValue = e.target.value; + return browser.runtime.sendMessage({ + type: messages.CONSOLE_CHANGEED, + text: e.target.value + }); +}; + +window.addEventListener('load', () => { + let input = window.document.querySelector('#vimvixen-console-command-input'); + input.addEventListener('blur', handleBlur); + input.addEventListener('keydown', handleKeydown); + input.addEventListener('keyup', handleKeyup); +}); + +const createCompletionTitle = (text) => { + let li = document.createElement('li'); + li.className = 'vimvixen-console-completion-title'; + li.textContent = text; + return li; +}; + +const createCompletionItem = (icon, caption, url) => { + let captionEle = document.createElement('span'); + captionEle.className = 'vimvixen-console-completion-item-caption'; + captionEle.textContent = caption; + + let urlEle = document.createElement('span'); + urlEle.className = 'vimvixen-console-completion-item-url'; + urlEle.textContent = url; + + let li = document.createElement('li'); + li.style.backgroundImage = 'url(' + icon + ')'; + li.className = 'vimvixen-console-completion-item'; + li.append(captionEle); + li.append(urlEle); + return li; +}; + +const updateCompletions = (completions) => { + let completionsContainer = + window.document.querySelector('#vimvixen-console-completion'); + let input = window.document.querySelector('#vimvixen-console-command-input'); + + completionsContainer.innerHTML = ''; + + let pairs = []; + + for (let group of completions) { + let title = createCompletionTitle(group.name); + completionsContainer.append(title); + + for (let item of group.items) { + let li = createCompletionItem(item.icon, item.caption, item.url); + completionsContainer.append(li); + + pairs.push([item, li]); + } + } + + completion = new Completion(pairs); + completionOrigin = input.value.split(' ')[0]; +}; + +const update = (state) => { + let error = window.document.querySelector('#vimvixen-console-error'); + let command = window.document.querySelector('#vimvixen-console-command'); + let input = window.document.querySelector('#vimvixen-console-command-input'); + + error.style.display = state.errorShown ? 'block' : 'none'; + error.textContent = state.errorText; + + command.style.display = state.commandShown ? 'block' : 'none'; + if (state.commandShown && !prevState.commandShown) { + input.value = state.commandText; + input.focus(); + } + if (JSON.stringify(state.completions) !== + JSON.stringify(prevState.completions)) { + updateCompletions(state.completions); + } + + prevState = state; +}; + +browser.runtime.onMessage.addListener((action) => { + if (action.type === messages.STATE_UPDATE) { + return update(action.state.console); + } +}); + +window.addEventListener('load', () => { + let error = window.document.querySelector('#vimvixen-console-error'); + let command = window.document.querySelector('#vimvixen-console-command'); + error.style.display = 'none'; + command.style.display = 'none'; +}); diff --git a/src/pages/console.scss b/src/pages/console.scss new file mode 100644 index 0000000..5823dce --- /dev/null +++ b/src/pages/console.scss @@ -0,0 +1,92 @@ +html, body, * { + margin: 0; + padding: 0; +} + +body { + position: absolute; + bottom: 0; + left: 0; + right: 0; + overflow: hidden; +} + +.vimvixen-console { + border-top: 1px solid gray; + bottom: 0; + margin: 0; + padding: 0; + + @mixin consoole-font { + font-style: normal; + font-family: monospace; + font-size: 12px; + line-height: 16px; + } + + &-completion { + background-color: white; + + @include consoole-font; + + &-title { + background-color: lightgray; + font-weight: bold; + margin: 0; + padding: 0; + } + + &-item { + padding-left: 1.5rem; + background-position: 0 center; + background-size: contain; + background-repeat: no-repeat; + white-space: nowrap; + + &.vimvixen-completion-selected { + background-color: yellow; + } + + &-caption { + display: inline-block; + width: 40%; + text-overflow: ellipsis; + overflow: hidden; + } + + &-url { + display: inline-block; + color: green; + width: 60%; + text-overflow: ellipsis; + overflow: hidden; + } + } + } + + &-error { + background-color: red; + font-weight: bold; + color: white; + + @include consoole-font; + } + + &-command { + background-color: white; + display: flex; + + &-prompt:before { + content: ':'; + + @include consoole-font; + } + + &-input { + border: none; + flex-grow: 1; + + @include consoole-font; + } + } +} diff --git a/src/pages/settings.html b/src/pages/settings.html new file mode 100644 index 0000000..99d6c6b --- /dev/null +++ b/src/pages/settings.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset='utf-8'> + </head> + <body> + <h1>Configure</h1> + + <h2>Home page</h2> + <form id='vimvixen-settings-form' class='vimvixen-settings-form'> + <label for='load-from-json'>Load from JSON:</label> + <textarea name='plain-json' spellcheck='false'></textarea> + + <button type='submit'>Save</button> + </form> + <script src='settings.js'></script> + </body> +</html> diff --git a/src/pages/settings.js b/src/pages/settings.js new file mode 100644 index 0000000..6e00ed3 --- /dev/null +++ b/src/pages/settings.js @@ -0,0 +1,22 @@ +import './settings.scss'; +import messages from '../content/messages'; + +document.addEventListener('DOMContentLoaded', () => { + let form = document.getElementById('vimvixen-settings-form'); + form.addEventListener('submit', (e) => { + e.preventDefault(); + browser.storage.local.set({ + settings: { + json: e.target.elements['plain-json'].value + } + }).then(() => { + return browser.runtime.sendMessage({ + type: messages.SETTINGS_RELOAD + }); + }); + }); + + browser.storage.local.get('settings').then((value) => { + form.elements['plain-json'].value = value.settings.json; + }, console.error); +}); diff --git a/src/pages/settings.scss b/src/pages/settings.scss new file mode 100644 index 0000000..5707c8a --- /dev/null +++ b/src/pages/settings.scss @@ -0,0 +1,8 @@ +.vimvixen-settings-form { + textarea[name=plain-json] { + font-family: monospace; + width: 100%; + min-height: 64ex; + resize: vertical; + } +} |