diff options
Diffstat (limited to 'src/content/components/top-content')
| -rw-r--r-- | src/content/components/top-content/follow-controller.js | 115 | ||||
| -rw-r--r-- | src/content/components/top-content/index.js | 32 | 
2 files changed, 147 insertions, 0 deletions
diff --git a/src/content/components/top-content/follow-controller.js b/src/content/components/top-content/follow-controller.js new file mode 100644 index 0000000..0474690 --- /dev/null +++ b/src/content/components/top-content/follow-controller.js @@ -0,0 +1,115 @@ +import * as followActions from 'content/actions/follow'; +import messages from 'shared/messages'; +import HintKeyProducer from 'content/hint-key-producer'; + +const DEFAULT_HINT_CHARSET = 'abcdefghijklmnopqrstuvwxyz'; + +const broadcastMessage = (win, message) => { +  let json = JSON.stringify(message); +  let frames = [window.self].concat(Array.from(window.frames)); +  frames.forEach(frame => frame.postMessage(json, '*')); +}; + +export default class FollowController { +  constructor(win, store) { +    this.win = win; +    this.store = store; +    this.state = {}; +    this.keys = []; +    this.producer = null; +  } + +  onMessage(message, sender) { +    switch (message.type) { +    case messages.FOLLOW_START: +      return this.store.dispatch(followActions.enable(message.newTab)); +    case messages.FOLLOW_RESPONSE_COUNT_TARGETS: +      return this.create(message.count, sender); +    case messages.FOLLOW_KEY_PRESS: +      return this.keyPress(message.key); +    } +  } + +  update() { +    let prevState = this.state; +    this.state = this.store.getState().follow; + +    if (!prevState.enabled && this.state.enabled) { +      this.count(); +    } else if (prevState.enabled && !this.state.enabled) { +      this.remove(); +    } else if (prevState.keys !== this.state.keys) { +      this.updateHints(); +    } +  } + +  updateHints() { +    let shown = this.keys.filter(key => key.startsWith(this.state.keys)); +    if (shown.length === 1) { +      this.activate(); +      this.store.dispatch(followActions.disable()); +    } + +    broadcastMessage(this.win, { +      type: messages.FOLLOW_SHOW_HINTS, +      keys: this.state.keys, +    }); +  } + +  activate() { +    broadcastMessage(this.win, { +      type: messages.FOLLOW_ACTIVATE, +      keys: this.state.keys, +    }); +  } + +  keyPress(key) { +    switch (key) { +    case 'Enter': +      this.activate(); +      this.store.dispatch(followActions.disable()); +      break; +    case 'Escape': +      this.store.dispatch(followActions.disable()); +      break; +    case 'Backspace': +    case 'Delete': +      this.store.dispatch(followActions.backspace()); +      break; +    default: +      if (DEFAULT_HINT_CHARSET.includes(key)) { +        this.store.dispatch(followActions.keyPress(key)); +      } +      break; +    } +    return true; +  } + +  count() { +    this.producer = new HintKeyProducer(DEFAULT_HINT_CHARSET); +    broadcastMessage(this.win, { +      type: messages.FOLLOW_REQUEST_COUNT_TARGETS, +    }); +  } + +  create(count, sender) { +    let produced = []; +    for (let i = 0; i < count; ++i) { +      produced.push(this.producer.produce()); +    } +    this.keys = this.keys.concat(produced); + +    sender.postMessage(JSON.stringify({ +      type: messages.FOLLOW_CREATE_HINTS, +      keysArray: produced, +      newTab: this.state.newTab, +    }), '*'); +  } + +  remove() { +    this.keys = []; +    broadcastMessage(this.win, { +      type: messages.FOLLOW_REMOVE_HINTS, +    }); +  } +} diff --git a/src/content/components/top-content/index.js b/src/content/components/top-content/index.js new file mode 100644 index 0000000..a2179da --- /dev/null +++ b/src/content/components/top-content/index.js @@ -0,0 +1,32 @@ +import CommonComponent from '../common'; +import FollowController from './follow-controller'; +import * as consoleFrames from '../../console-frames'; +import messages from 'shared/messages'; + +export default class TopContent { + +  constructor(win, store) { +    this.win = win; +    this.children = [ +      new CommonComponent(win, store), +      new FollowController(win, store), +    ]; + +    // TODO make component +    consoleFrames.initialize(window.document); +  } + +  update() { +    this.children.forEach(c => c.update()); +  } + +  onMessage(message, sender) { +    switch (message.type) { +    case messages.CONSOLE_HIDE_COMMAND: +      this.win.focus(); +      consoleFrames.blur(window.document); +      return Promise.resolve(); +    } +    this.children.forEach(c => c.onMessage(message, sender)); +  } +}  | 
