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; +  } +} | 
