commit
b2cddcd69b
29 changed files with 735 additions and 652 deletions
@ -0,0 +1,11 @@ |
||||
import actions from '../actions'; |
||||
|
||||
export function requestCompletions(line) { |
||||
let command = line.split(' ', 1)[0]; |
||||
let keywords = line.replace(command + ' ', ''); |
||||
return { |
||||
type: actions.BACKGROUND_REQUEST_COMPLETIONS, |
||||
command, |
||||
keywords |
||||
}; |
||||
} |
@ -0,0 +1,35 @@ |
||||
import actions from '../actions'; |
||||
|
||||
const normalizeUrl = (string) => { |
||||
try { |
||||
return new URL(string).href |
||||
} catch (e) { |
||||
return 'http://' + string; |
||||
} |
||||
} |
||||
|
||||
export function exec(line) { |
||||
let name = line.split(' ')[0]; |
||||
let remaining = line.replace(name + ' ', ''); |
||||
|
||||
switch (name) { |
||||
case 'open': |
||||
// TODO use search engined and pass keywords to them
|
||||
return { |
||||
type: actions.COMMAND_OPEN_URL, |
||||
url: normalizeUrl(remaining) |
||||
}; |
||||
case 'tabopen': |
||||
return { |
||||
type: actions.COMMAND_TABOPEN_URL, |
||||
url: normalizeUrl(remaining) |
||||
}; |
||||
case 'b': |
||||
case 'buffer': |
||||
return { |
||||
type: actions.COMMAND_BUFFER, |
||||
keywords: remaining |
||||
}; |
||||
} |
||||
throw new Error(name + ' command is not defined'); |
||||
} |
@ -0,0 +1,28 @@ |
||||
import actions from '../actions'; |
||||
|
||||
export function showCommand(text) { |
||||
return { |
||||
type: actions.CONSOLE_SHOW_COMMAND, |
||||
text: text |
||||
}; |
||||
} |
||||
|
||||
export function setCompletions(completions) { |
||||
return {
|
||||
type: actions.CONSOLE_SET_COMPLETIONS, |
||||
completions: completions |
||||
}; |
||||
} |
||||
|
||||
export function showError(text) { |
||||
return { |
||||
type: actions.CONSOLE_SHOW_ERROR, |
||||
text: text |
||||
}; |
||||
} |
||||
|
||||
export function hide() { |
||||
return {
|
||||
type: actions.CONSOLE_HIDE |
||||
}; |
||||
} |
@ -0,0 +1,40 @@ |
||||
export default { |
||||
// console commands
|
||||
CONSOLE_SHOW_COMMAND: 'vimvixen.console.show.command', |
||||
CONSOLE_SET_COMPLETIONS: 'vimvixen.console.set.completions', |
||||
CONSOLE_SHOW_ERROR: 'vimvixen.console.show.error', |
||||
CONSOLE_HIDE: 'vimvixen.console.hide', |
||||
|
||||
// Background commands
|
||||
BACKGROUND_REQUEST_COMPLETIONS: 'vimvixen.background.request.completions', |
||||
TABS_CLOSE: 'tabs.close', |
||||
TABS_REOPEN: 'tabs.reopen', |
||||
TABS_PREV: 'tabs.prev', |
||||
TABS_NEXT: 'tabs.next', |
||||
TABS_RELOAD: 'tabs.reload', |
||||
ZOOM_IN: 'zoom.in', |
||||
ZOOM_OUT: 'zoom.out', |
||||
ZOOM_NEUTRAL: 'zoom.neutral', |
||||
|
||||
// content commands
|
||||
CMD_OPEN: 'cmd.open', |
||||
CMD_TABS_OPEN: 'cmd.tabs.open', |
||||
CMD_BUFFER: 'cmd.buffer', |
||||
SCROLL_LINES: 'scroll.lines', |
||||
SCROLL_PAGES: 'scroll.pages', |
||||
SCROLL_TOP: 'scroll.top', |
||||
SCROLL_BOTTOM: 'scroll.bottom', |
||||
SCROLL_LEFT: 'scroll.left', |
||||
SCROLL_RIGHT: 'scroll.right', |
||||
FOLLOW_START: 'follow.start', |
||||
HISTORY_PREV: 'history.prev', |
||||
HISTORY_NEXT: 'history.next', |
||||
|
||||
// User input
|
||||
INPUT_KEY_PRESS: 'input.key,press', |
||||
INPUT_CLEAR_KEYS: 'input.clear.keys', |
||||
|
||||
COMMAND_OPEN_URL: 'command.open.url', |
||||
COMMAND_TABOPEN_URL: 'command.tabopen.url', |
||||
COMMAND_BUFFER: 'command.buffer', |
||||
}; |
@ -0,0 +1,15 @@ |
||||
import actions from '../actions'; |
||||
|
||||
export function keyPress(code, ctrl) { |
||||
return { |
||||
type: actions.INPUT_KEY_PRESS, |
||||
code, |
||||
ctrl |
||||
}; |
||||
} |
||||
|
||||
export function clearKeys() { |
||||
return { |
||||
type: actions.INPUT_CLEAR_KEYS |
||||
} |
||||
} |
@ -1,103 +1,45 @@ |
||||
import * as actions from '../shared/actions'; |
||||
import * as tabs from './tabs'; |
||||
import * as zooms from './zooms'; |
||||
import KeyQueue from './key-queue'; |
||||
import * as keys from './keys'; |
||||
import * as inputActions from '../actions/input'; |
||||
import backgroundReducers from '../reducers/background'; |
||||
import commandReducer from '../reducers/command'; |
||||
import inputReducers from '../reducers/input'; |
||||
|
||||
const queue = new KeyQueue(); |
||||
let inputState = inputReducers(undefined, {}); |
||||
|
||||
const keyPressHandle = (request, sender) => { |
||||
let action = queue.push({ |
||||
code: request.code, |
||||
ctrl: request.ctrl |
||||
}); |
||||
if (!action) { |
||||
const keyQueueChanged = (sender, prevState, state) => { |
||||
if (state.keys.length === 0) { |
||||
return Promise.resolve(); |
||||
} |
||||
|
||||
if (actions.isBackgroundAction(action[0])) { |
||||
return doBackgroundAction(sender, action); |
||||
} else if (actions.isContentAction(action[0])) { |
||||
return Promise.resolve({ |
||||
type: 'response.action', |
||||
action: action |
||||
let prefix = keys.asKeymapChars(state.keys); |
||||
let matched = Object.keys(keys.defaultKeymap).filter((keys) => { |
||||
return keys.startsWith(prefix); |
||||
}); |
||||
} |
||||
return Promise.resolve(); |
||||
}; |
||||
|
||||
const doBackgroundAction = (sender, action) => { |
||||
switch(action[0]) { |
||||
case actions.TABS_CLOSE: |
||||
return tabs.closeTab(sender.tab.id); |
||||
case actions.TABS_REOPEN: |
||||
return tabs.reopenTab(); |
||||
case actions.TABS_PREV: |
||||
return tabs.selectPrevTab(sender.tab.index, actions[1] || 1); |
||||
case actions.TABS_NEXT: |
||||
return tabs.selectNextTab(sender.tab.index, actions[1] || 1); |
||||
case actions.TABS_RELOAD: |
||||
return tabs.reload(sender.tab, actions[1] || false); |
||||
case actions.ZOOM_IN: |
||||
return zooms.zoomIn(); |
||||
case actions.ZOOM_OUT: |
||||
return zooms.zoomOut(); |
||||
case actions.ZOOM_NEUTRAL: |
||||
return zooms.neutral(); |
||||
} |
||||
if (matched.length == 0) { |
||||
return handleMessage(inputActions.clearKeys(), sender); |
||||
} else if (matched.length > 1 || matched.length === 1 && prefix !== matched[0]) { |
||||
return Promise.resolve(); |
||||
} |
||||
|
||||
const normalizeUrl = (string) => { |
||||
try { |
||||
return new URL(string).href |
||||
} catch (e) { |
||||
return 'http://' + string; |
||||
} |
||||
} |
||||
|
||||
const cmdBuffer = (sender, arg) => { |
||||
if (isNaN(arg)) { |
||||
return tabs.selectByKeyword(sender.tab, arg); |
||||
} else { |
||||
let index = parseInt(arg, 10) - 1; |
||||
return tabs.selectAt(index); |
||||
} |
||||
} |
||||
|
||||
const cmdEnterHandle = (request, sender) => { |
||||
let words = request.text.split(' ').filter((s) => s.length > 0); |
||||
switch (words[0]) { |
||||
case 'open': |
||||
return browser.tabs.update(sender.tab.id, { url: normalizeUrl(words[1]) }); |
||||
case 'tabopen': |
||||
return browser.tabs.create({ url: normalizeUrl(words[1]) }); |
||||
case 'b': |
||||
case 'buffer': |
||||
return cmdBuffer(sender, words[1]); |
||||
} |
||||
throw new Error(words[0] + ' command is not defined'); |
||||
let action = keys.defaultKeymap[matched]; |
||||
return handleMessage(inputActions.clearKeys(), sender).then(() => { |
||||
return backgroundReducers(undefined, action, sender).then(() => { |
||||
return browser.tabs.sendMessage(sender.tab.id, action); |
||||
}); |
||||
}); |
||||
}; |
||||
|
||||
browser.runtime.onMessage.addListener((request, sender) => { |
||||
switch (request.type) { |
||||
case 'event.keypress': |
||||
return keyPressHandle(request, sender); |
||||
case 'event.cmd.enter': |
||||
return cmdEnterHandle(request, sender); |
||||
case 'event.cmd.tabs.completion': |
||||
return tabs.getCompletions(request.text).then((tabs) => { |
||||
let items = tabs.map((tab) => { |
||||
return { |
||||
caption: tab.title, |
||||
content: tab.title, |
||||
url: tab.url, |
||||
icon: tab.favIconUrl |
||||
const handleMessage = (action, sender) => { |
||||
let nextInputState = inputReducers(inputState, action); |
||||
if (JSON.stringify(nextInputState) !== JSON.stringify(inputState)) { |
||||
let prevState = inputState; |
||||
inputState = nextInputState; |
||||
return keyQueueChanged(sender, prevState, inputState); |
||||
} |
||||
return backgroundReducers(undefined, action, sender).then(() => { |
||||
return commandReducer(undefined, action, sender).then(() => { |
||||
return browser.tabs.sendMessage(sender.tab.id, action); |
||||
}); |
||||
return { |
||||
name: "Buffers", |
||||
items: items |
||||
}; |
||||
}); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
browser.runtime.onMessage.addListener(handleMessage); |
||||
|
@ -1,82 +0,0 @@ |
||||
import * as actions from '../shared/actions'; |
||||
|
||||
const DEFAULT_KEYMAP = { |
||||
':': [ actions.CMD_OPEN ], |
||||
'o': [ actions.CMD_TABS_OPEN, false ], |
||||
'O': [ actions.CMD_TABS_OPEN, true ], |
||||
'b': [ actions.CMD_BUFFER ], |
||||
'k': [ actions.SCROLL_LINES, -1 ], |
||||
'j': [ actions.SCROLL_LINES, 1 ], |
||||
'<C-E>': [ actions.SCROLL_LINES, -1 ], |
||||
'<C-Y>': [ actions.SCROLL_LINES, 1 ], |
||||
'<C-U>': [ actions.SCROLL_PAGES, -0.5 ], |
||||
'<C-D>': [ actions.SCROLL_PAGES, 0.5 ], |
||||
'<C-B>': [ actions.SCROLL_PAGES, -1 ], |
||||
'<C-F>': [ actions.SCROLL_PAGES, 1 ], |
||||
'gg': [ actions.SCROLL_TOP ], |
||||
'G': [ actions.SCROLL_BOTTOM ], |
||||
'0': [ actions.SCROLL_LEFT ], |
||||
'$': [ actions.SCROLL_RIGHT ], |
||||
'd': [ actions.TABS_CLOSE ], |
||||
'u': [ actions.TABS_REOPEN], |
||||
'h': [ actions.TABS_PREV, 1 ], |
||||
'l': [ actions.TABS_NEXT, 1 ], |
||||
'r': [ actions.TABS_RELOAD, false ], |
||||
'R': [ actions.TABS_RELOAD, true ], |
||||
'zi': [ actions.ZOOM_IN ], |
||||
'zo': [ actions.ZOOM_OUT ], |
||||
'zz': [ actions.ZOOM_NEUTRAL], |
||||
'f': [ actions.FOLLOW_START, false ], |
||||
'F': [ actions.FOLLOW_START, true ], |
||||
'H': [ actions.HISTORY_PREV ], |
||||
'L': [ actions.HISTORY_NEXT ], |
||||
} |
||||
|
||||
export default class KeyQueue { |
||||
|
||||
constructor(keymap = DEFAULT_KEYMAP) { |
||||
this.data = []; |
||||
this.keymap = keymap; |
||||
} |
||||
|
||||
push(key) { |
||||
this.data.push(key); |
||||
|
||||
let current = this.asKeymapChars(); |
||||
let filtered = Object.keys(this.keymap).filter((keys) => { |
||||
return keys.startsWith(current); |
||||
}); |
||||
|
||||
if (filtered.length == 0) { |
||||
this.data = []; |
||||
return null; |
||||
} else if (filtered.length === 1 && current === filtered[0]) { |
||||
let action = this.keymap[filtered[0]]; |
||||
this.data = []; |
||||
return action; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
asKeymapChars() { |
||||
return this.data.map((k) => { |
||||
let c = String.fromCharCode(k.code); |
||||
if (k.ctrl) { |
||||
return '<C-' + c.toUpperCase() + '>'; |
||||
} else { |
||||
return c |
||||
} |
||||
}).join(''); |
||||
} |
||||
|
||||
asCaretChars() { |
||||
return this.data.map((k) => { |
||||
let c = String.fromCharCode(k.code); |
||||
if (k.ctrl) { |
||||
return '^' + c.toUpperCase(); |
||||
} else { |
||||
return c; |
||||
} |
||||
}).join(''); |
||||
} |
||||
} |
@ -1,28 +1,57 @@ |
||||
const identifyKey = (key1, key2) => { |
||||
return (key1.code === key2.code) && |
||||
((key1.shift || false) === (key2.shift || false)) && |
||||
((key1.ctrl || false) === (key2.ctrl || false)) && |
||||
((key1.alt || false) === (key2.alt || false)) && |
||||
((key1.meta || false) === (key2.meta || false)); |
||||
}; |
||||
import actions from '../actions'; |
||||
|
||||
const hasPrefix = (keys, prefix) => { |
||||
if (keys.length < prefix.length) { |
||||
return false; |
||||
} |
||||
for (let i = 0; i < prefix.length; ++i) { |
||||
if (!identifyKey(keys[i], prefix[i])) { |
||||
return false; |
||||
const defaultKeymap = { |
||||
':': { type: actions.CMD_OPEN }, |
||||
'o': { type: actions.CMD_TABS_OPEN, alter: false }, |
||||
'O': { type: actions.CMD_TABS_OPEN, alter: true }, |
||||
'b': { type: actions.CMD_BUFFER }, |
||||
'k': { type: actions.SCROLL_LINES, count: -1 }, |
||||
'j': { type: actions.SCROLL_LINES, count: 1 }, |
||||
'<C-E>': { type: actions.SCROLL_LINES, count: -1 }, |
||||
'<C-Y>': { type: actions.SCROLL_LINES, count: 1 }, |
||||
'<C-U>': { type: actions.SCROLL_PAGES, count: -0.5 }, |
||||
'<C-D>': { type: actions.SCROLL_PAGES, count: 0.5 }, |
||||
'<C-B>': { type: actions.SCROLL_PAGES, count: -1 }, |
||||
'<C-F>': { type: actions.SCROLL_PAGES, count: 1 }, |
||||
'gg': { type: actions.SCROLL_TOP }, |
||||
'G': { type: actions.SCROLL_BOTTOM }, |
||||
'0': { type: actions.SCROLL_LEFT }, |
||||
'$': { type: actions.SCROLL_RIGHT }, |
||||
'd': { type: actions.TABS_CLOSE }, |
||||
'u': { type: actions.TABS_REOPEN }, |
||||
'h': { type: actions.TABS_PREV, count: 1 }, |
||||
'l': { type: actions.TABS_NEXT, count: 1 }, |
||||
'r': { type: actions.TABS_RELOAD, cache: false }, |
||||
'R': { type: actions.TABS_RELOAD, cache: true }, |
||||
'zi': { type: actions.ZOOM_IN }, |
||||
'zo': { type: actions.ZOOM_OUT }, |
||||
'zz': { type: actions.ZOOM_NEUTRAL }, |
||||
'f': { type: actions.FOLLOW_START, newTab: false }, |
||||
'F': { type: actions.FOLLOW_START, newTab: true }, |
||||
'H': { type: actions.HISTORY_PREV }, |
||||
'L': { type: actions.HISTORY_NEXT }, |
||||
} |
||||
|
||||
const asKeymapChars = (keys) => { |
||||
return keys.map((k) => { |
||||
let c = String.fromCharCode(k.code); |
||||
if (k.ctrl) { |
||||
return '<C-' + c.toUpperCase() + '>'; |
||||
} else { |
||||
return c |
||||
} |
||||
return true; |
||||
}).join(''); |
||||
} |
||||
|
||||
const identifyKeys = (keys1, keys2) => { |
||||
if (keys1.length !== keys2.length) { |
||||
return false; |
||||
const asCaretChars = (keys) => { |
||||
return keys.map((k) => { |
||||
let c = String.fromCharCode(k.code); |
||||
if (k.ctrl) { |
||||
return '^' + c.toUpperCase(); |
||||
} else { |
||||
return c; |
||||
} |
||||
return hasPrefix(keys1, keys2); |
||||
}).join(''); |
||||
} |
||||
|
||||
export { identifyKey, identifyKeys, hasPrefix }; |
||||
export { defaultKeymap, asKeymapChars, asCaretChars }; |
||||
|
@ -1,61 +0,0 @@ |
||||
import './console-frame.scss'; |
||||
import * as messages from '../shared/messages'; |
||||
|
||||
export default class ConsoleFrame { |
||||
constructor(win) { |
||||
let element = window.document.createElement('iframe'); |
||||
element.src = browser.runtime.getURL('build/console.html'); |
||||
element.className = 'vimvixen-console-frame'; |
||||
win.document.body.append(element); |
||||
|
||||
this.element = element; |
||||
|
||||
this.errorShown = true; |
||||
|
||||
this.hide(); |
||||
} |
||||
|
||||
showCommand(text) { |
||||
this.showFrame(); |
||||
|
||||
let message = { |
||||
type: 'vimvixen.console.show.command', |
||||
text: text |
||||
}; |
||||
messages.send(this.element.contentWindow, message); |
||||
this.errorShown = false; |
||||
} |
||||
|
||||
showError(text) { |
||||
this.showFrame(); |
||||
|
||||
let message = { |
||||
type: 'vimvixen.console.show.error', |
||||
text: text |
||||
}; |
||||
messages.send(this.element.contentWindow, message); |
||||
this.errorShown = true; |
||||
this.element.blur(); |
||||
} |
||||
|
||||
showFrame() { |
||||
this.element.style.display = 'block'; |
||||
} |
||||
|
||||
hide() { |
||||
this.element.style.display = 'none'; |
||||
this.element.blur(); |
||||
this.errorShown = false; |
||||
} |
||||
|
||||
isErrorShown() { |
||||
return this.element.style.display === 'block' && this.errorShown; |
||||
} |
||||
|
||||
setCompletions(completions) { |
||||
messages.send(this.element.contentWindow, { |
||||
type: 'vimvixen.console.set.completions', |
||||
completions: completions |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
import './console-frame.scss'; |
||||
import * as consoleActions from '../actions/console'; |
||||
|
||||
const initialize = (doc) => { |
||||
let iframe = doc.createElement('iframe'); |
||||
iframe.src = browser.runtime.getURL('build/console.html'); |
||||
iframe.id = 'vimvixen-console-frame'; |
||||
iframe.className = 'vimvixen-console-frame'; |
||||
doc.body.append(iframe); |
||||
|
||||
return iframe; |
||||
} |
||||
|
||||
const showCommand = (text) => { |
||||
return browser.runtime.sendMessage(consoleActions.showCommand(text)); |
||||
}; |
||||
|
||||
const showError = (text) => { |
||||
return browser.runtime.sendMessage(consoleActions.showError(text)); |
||||
} |
||||
|
||||
const blur = (doc) => { |
||||
let iframe = doc.getElementById('vimvixen-console-frame'); |
||||
iframe.blur(); |
||||
} |
||||
|
||||
export { initialize, showCommand, showError, blur }; |
@ -1,129 +1,41 @@ |
||||
import * as scrolls from './scrolls'; |
||||
import * as histories from './histories'; |
||||
import * as actions from '../shared/actions'; |
||||
import * as messages from '../shared/messages'; |
||||
import ConsoleFrame from '../console/console-frame'; |
||||
import Follow from './follow'; |
||||
import '../console/console-frame.scss'; |
||||
import * as inputActions from '../actions/input'; |
||||
import * as consoleFrames from '../console/frames'; |
||||
import actions from '../actions'; |
||||
import contentReducer from '../reducers/content'; |
||||
|
||||
let vvConsole = new ConsoleFrame(window); |
||||
consoleFrames.initialize(window.document); |
||||
|
||||
const doAction = (action) => { |
||||
if (typeof action === 'undefined' || action === null) { |
||||
return; |
||||
} |
||||
|
||||
switch (action[0]) { |
||||
case actions.CMD_OPEN: |
||||
vvConsole.showCommand(''); |
||||
break; |
||||
case actions.CMD_TABS_OPEN: |
||||
if (action[1] || false) { |
||||
// alter url
|
||||
vvConsole.showCommand('open ' + window.location.href); |
||||
} else { |
||||
vvConsole.showCommand('open '); |
||||
} |
||||
break; |
||||
case actions.CMD_BUFFER: |
||||
vvConsole.showCommand('buffer '); |
||||
break; |
||||
case actions.SCROLL_LINES: |
||||
scrolls.scrollLines(window, action[1]); |
||||
break; |
||||
case actions.SCROLL_PAGES: |
||||
scrolls.scrollPages(window, action[1]); |
||||
break; |
||||
case actions.SCROLL_TOP: |
||||
scrolls.scrollTop(window); |
||||
break; |
||||
case actions.SCROLL_BOTTOM: |
||||
scrolls.scrollBottom(window); |
||||
break; |
||||
case actions.SCROLL_LEFT: |
||||
scrolls.scrollLeft(window); |
||||
break; |
||||
case actions.SCROLL_RIGHT: |
||||
scrolls.scrollRight(window); |
||||
break; |
||||
case actions.FOLLOW_START: |
||||
new Follow(window.document, action[1] || false); |
||||
break; |
||||
case actions.HISTORY_PREV: |
||||
histories.prev(window); |
||||
break; |
||||
case actions.HISTORY_NEXT: |
||||
histories.next(window); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
const handleResponse = (response) => { |
||||
if (!response) { |
||||
return; |
||||
} |
||||
|
||||
switch(response.type) { |
||||
case 'response.action': |
||||
doAction(response.action); |
||||
break; |
||||
} |
||||
}; |
||||
browser.runtime.onMessage.addListener((action) => { |
||||
contentReducer(undefined, action); |
||||
return Promise.resolve(); |
||||
}); |
||||
|
||||
window.addEventListener("keypress", (e) => { |
||||
if (e.target instanceof HTMLInputElement) { |
||||
return; |
||||
} |
||||
|
||||
let request = { |
||||
type: 'event.keypress', |
||||
code: e.which, |
||||
ctrl: e.ctrlKey, |
||||
} |
||||
|
||||
browser.runtime.sendMessage(request) |
||||
.then(handleResponse) |
||||
browser.runtime.sendMessage(inputActions.keyPress(e.which, e.ctrlKey)) |
||||
.catch((err) => { |
||||
console.error("Vim Vixen:", err); |
||||
vvConsole.showError(err.message); |
||||
return consoleFrames.showError(err.message); |
||||
}); |
||||
}); |
||||
|
||||
const doCompletion = (line) => { |
||||
if (line.startsWith('buffer ')) { |
||||
let keyword = line.replace('buffer ', ''); |
||||
|
||||
browser.runtime.sendMessage({ |
||||
type: 'event.cmd.tabs.completion', |
||||
text: keyword |
||||
}).then((completions) => { |
||||
vvConsole.setCompletions([completions]); |
||||
}).catch((err) => { |
||||
console.error("Vim Vixen:", err); |
||||
vvConsole.showError(err.message); |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
messages.receive(window, (message) => { |
||||
switch (message.type) { |
||||
case 'vimvixen.command.blur': |
||||
if (!vvConsole.isErrorShown()) { |
||||
vvConsole.hide(); |
||||
} |
||||
break; |
||||
browser.runtime.onMessage.addListener((action) => { |
||||
switch (action.type) { |
||||
case actions.CONSOLE_HIDE: |
||||
window.focus(); |
||||
return consoleFrames.blur(window.document); |
||||
case 'vimvixen.command.enter': |
||||
browser.runtime.sendMessage({ |
||||
return browser.runtime.sendMessage({ |
||||
type: 'event.cmd.enter', |
||||
text: message.value |
||||
text: action.value |
||||
}).catch((err) => { |
||||
console.error("Vim Vixen:", err); |
||||
vvConsole.showError(err.message); |
||||
return consoleFrames.showError(err.message); |
||||
}); |
||||
break; |
||||
case 'vimvixen.command.change': |
||||
doCompletion(message.value); |
||||
break; |
||||
default: |
||||
return; |
||||
return Promise.resolve(); |
||||
} |
||||
}); |
||||
|
@ -0,0 +1,53 @@ |
||||
import * as tabs from '../background/tabs'; |
||||
import * as zooms from '../background/zooms'; |
||||
import * as consoleActions from '../actions/console'; |
||||
import actions from '../actions'; |
||||
|
||||
const doCompletion = (command, keywords, sender) => { |
||||
if (command === 'buffer') { |
||||
return tabs.getCompletions(keywords).then((tabs) => { |
||||
let items = tabs.map((tab) => { |
||||
return { |
||||
caption: tab.title, |
||||
content: tab.title, |
||||
url: tab.url, |
||||
icon: tab.favIconUrl |
||||
} |
||||
}); |
||||
let completions = { |
||||
name: "Buffers", |
||||
items: items |
||||
}; |
||||
return browser.tabs.sendMessage( |
||||
sender, |
||||
consoleActions.setCompletions([completions])); |
||||
}); |
||||
} |
||||
return Promise.resolve(); |
||||
}; |
||||
|
||||
export default function reducer(state, action = {}, sender) { |
||||
// TODO hide sender object
|
||||
switch (action.type) { |
||||
case actions.BACKGROUND_REQUEST_COMPLETIONS: |
||||
return doCompletion(action.command, action.keywords, sender.tab.id); |
||||
case actions.TABS_CLOSE: |
||||
return tabs.closeTab(sender.tab.id); |
||||
case actions.TABS_REOPEN: |
||||
return tabs.reopenTab(); |
||||
case actions.TABS_PREV: |
||||
return tabs.selectPrevTab(sender.tab.index, action.count); |
||||
case actions.TABS_NEXT: |
||||
return tabs.selectNextTab(sender.tab.index, action.count); |
||||
case actions.TABS_RELOAD: |
||||
return tabs.reload(sender.tab, action.cache); |
||||
case actions.ZOOM_IN: |
||||
return zooms.zoomIn(); |
||||
case actions.ZOOM_OUT: |
||||
return zooms.zoomOut(); |
||||
case actions.ZOOM_NEUTRAL: |
||||
return zooms.neutral(); |
||||
default: |
||||
return Promise.resolve(); |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
import * as tabs from '../background/tabs'; |
||||
import actions from '../actions'; |
||||
|
||||
const cmdBuffer = (sender, arg) => { |
||||
if (isNaN(arg)) { |
||||
return tabs.selectByKeyword(sender.tab, arg); |
||||
} else { |
||||
let index = parseInt(arg, 10) - 1; |
||||
return tabs.selectAt(index); |
||||
} |
||||
} |
||||
|
||||
export default function reducer(state, action, sender) { |
||||
switch (action.type) { |
||||
case actions.COMMAND_OPEN_URL: |
||||
return browser.tabs.update(sender.tab.id, { url: action.url }); |
||||
case actions.COMMAND_TABOPEN_URL: |
||||
return browser.tabs.create({ url: action.url }); |
||||
case actions.COMMAND_BUFFER: |
||||
return cmdBuffer(sender, action.keywords); |
||||
default: |
||||
return Promise.resolve(); |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
import actions from '../actions'; |
||||
|
||||
const defaultState = { |
||||
errorShown: false, |
||||
errorText: '', |
||||
commandShown: false, |
||||
commandText: '', |
||||
completions: [], |
||||
}; |
||||
|
||||
export default function reducer(state = defaultState, action = {}) { |
||||
switch (action.type) { |
||||
case actions.CONSOLE_SHOW_COMMAND: |
||||
return Object.assign({}, state, { |
||||
commandShown: true,
|
||||
commandText: action.text, |
||||
errorShown: false, |
||||
completions: [] |
||||
}); |
||||
case actions.CONSOLE_SET_COMPLETIONS: |
||||
return Object.assign({}, state, { |
||||
completions: action.completions |
||||
}); |
||||
case actions.CONSOLE_SHOW_ERROR: |
||||
return Object.assign({}, state, { |
||||
errorText: action.text, |
||||
errorShown: true, |
||||
commandShown: false, |
||||
}); |
||||
case actions.CONSOLE_HIDE: |
||||
return Object.assign({}, state, { |
||||
errorShown: false, |
||||
commandShown: false |
||||
|
||||
}); |
||||
default: |
||||
return state; |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
import * as consoleFrames from '../console/frames'; |
||||
import * as histories from '../content/histories'; |
||||
import * as scrolls from '../content/scrolls'; |
||||
import Follow from '../content/follow'; |
||||
import actions from '../actions'; |
||||
|
||||
export default function reducer(state, action = {}) { |
||||
switch (action.type) { |
||||
case actions.CMD_OPEN: |
||||
return consoleFrames.showCommand(''); |
||||
case actions.CMD_TABS_OPEN: |
||||
if (action.alter) { |
||||
// alter url
|
||||
return consoleFrames.showCommand('open ' + window.location.href); |
||||
} else { |
||||
return consoleFrames.showCommand('open '); |
||||
} |
||||
case actions.CMD_BUFFER: |
||||
return consoleFrames.showCommand('buffer '); |
||||
case actions.SCROLL_LINES: |
||||
scrolls.scrollLines(window, action.count); |
||||
break; |
||||
case actions.SCROLL_PAGES: |
||||
scrolls.scrollPages(window, action.count); |
||||
break; |
||||
case actions.SCROLL_TOP: |
||||
scrolls.scrollTop(window); |
||||
break; |
||||
case actions.SCROLL_BOTTOM: |
||||
scrolls.scrollBottom(window); |
||||
break; |
||||
case actions.SCROLL_LEFT: |
||||
scrolls.scrollLeft(window); |
||||
break; |
||||
case actions.SCROLL_RIGHT: |
||||
scrolls.scrollRight(window); |
||||
break; |
||||
case actions.FOLLOW_START: |
||||
new Follow(window.document, action.newTab); |
||||
break; |
||||
case actions.HISTORY_PREV: |
||||
histories.prev(window); |
||||
break; |
||||
case actions.HISTORY_NEXT: |
||||
histories.next(window); |
||||
break; |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
import actions from '../actions'; |
||||
|
||||
const defaultState = { |
||||
keys: [], |
||||
}; |
||||
|
||||
export default function reducer(state = defaultState, action = {}) { |
||||
switch (action.type) { |
||||
case actions.INPUT_KEY_PRESS: |
||||
return Object.assign({}, state, { |
||||
keys: state.keys.concat([{ |
||||
code: action.code, |
||||
ctrl: action.ctrl |
||||
}]) |
||||
}); |
||||
case actions.INPUT_CLEAR_KEYS: |
||||
return Object.assign({}, state, { |
||||
keys: [], |
||||
}); |
||||
default: |
||||
return state; |
||||
} |
||||
} |
@ -1,54 +0,0 @@ |
||||
export const CMD_OPEN = 'cmd.open'; |
||||
export const CMD_TABS_OPEN = 'cmd.tabs.open'; |
||||
export const CMD_BUFFER = 'cmd.buffer'; |
||||
export const TABS_CLOSE = 'tabs.close'; |
||||
export const TABS_REOPEN = 'tabs.reopen'; |
||||
export const TABS_PREV = 'tabs.prev'; |
||||
export const TABS_NEXT = 'tabs.next'; |
||||
export const TABS_RELOAD = 'tabs.reload'; |
||||
export const SCROLL_LINES = 'scroll.lines'; |
||||
export const SCROLL_PAGES = 'scroll.pages'; |
||||
export const SCROLL_TOP = 'scroll.top'; |
||||
export const SCROLL_BOTTOM = 'scroll.bottom'; |
||||
export const SCROLL_LEFT= 'scroll.left'; |
||||
export const SCROLL_RIGHT= 'scroll.right'; |
||||
export const FOLLOW_START = 'follow.start'; |
||||
export const HISTORY_PREV = 'history.prev'; |
||||
export const HISTORY_NEXT = 'history.next'; |
||||
export const ZOOM_IN = 'zoom.in'; |
||||
export const ZOOM_OUT = 'zoom.out'; |
||||
export const ZOOM_NEUTRAL = 'zoom.neutral'; |
||||
|
||||
const BACKGROUND_ACTION_SET = new Set([ |
||||
TABS_CLOSE, |
||||
TABS_REOPEN, |
||||
TABS_PREV, |
||||
TABS_NEXT, |
||||
TABS_RELOAD, |
||||
ZOOM_IN, |
||||
ZOOM_OUT, |
||||
ZOOM_NEUTRAL |
||||
]); |
||||
|
||||
const CONTENT_ACTION_SET = new Set([ |
||||
CMD_OPEN, |
||||
CMD_TABS_OPEN, |
||||
CMD_BUFFER, |
||||
SCROLL_LINES, |
||||
SCROLL_PAGES, |
||||
SCROLL_TOP, |
||||
SCROLL_BOTTOM, |
||||
SCROLL_LEFT, |
||||
SCROLL_RIGHT, |
||||
FOLLOW_START, |
||||
HISTORY_PREV, |
||||
HISTORY_NEXT |
||||
]); |
||||
|
||||
export const isBackgroundAction = (action) => { |
||||
return BACKGROUND_ACTION_SET.has(action); |
||||
}; |
||||
|
||||
export const isContentAction = (action) => { |
||||
return CONTENT_ACTION_SET.has(action); |
||||
}; |
@ -1,19 +0,0 @@ |
||||
const receive = (win, callback) => { |
||||
win.addEventListener('message', (e) => { |
||||
let message; |
||||
try { |
||||
message = JSON.parse(e.data); |
||||
} catch (e) { |
||||
// ignore message posted by author of web page
|
||||
return; |
||||
} |
||||
|
||||
callback(message); |
||||
}) |
||||
} |
||||
|
||||
const send = (win, message) => { |
||||
win.postMessage(JSON.stringify(message), '*'); |
||||
} |
||||
|
||||
export { receive, send }; |
@ -0,0 +1,14 @@ |
||||
import { expect } from "chai"; |
||||
import actions from '../../src/actions'; |
||||
import * as backgroundActions from '../../src/actions/background'; |
||||
|
||||
describe("background actions", () => { |
||||
describe("requestCompletions", () => { |
||||
it('create BACKGROUND_REQUEST_COMPLETIONS action', () => { |
||||
let action = backgroundActions.requestCompletions('buffer hoge fuga'); |
||||
expect(action.type).to.equal(actions.BACKGROUND_REQUEST_COMPLETIONS); |
||||
expect(action.command).to.equal('buffer'); |
||||
expect(action.keywords).to.equal('hoge fuga'); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,51 @@ |
||||
import { expect } from "chai"; |
||||
import actions from '../../src/actions'; |
||||
import * as commandActions from '../../src/actions/command'; |
||||
|
||||
describe("command actions", () => { |
||||
describe("exec", () => { |
||||
context("open command", () => { |
||||
it('create COMMAND_OPEN_URL acion with a full url', () => { |
||||
let action = commandActions.exec("open https://github.com/") |
||||
expect(action.type).to.equal(actions.COMMAND_OPEN_URL); |
||||
expect(action.url).to.equal('https://github.com/'); |
||||
}); |
||||
|
||||
it('create COMMAND_OPEN_URL acion with a domain name', () => { |
||||
let action = commandActions.exec("open github.com") |
||||
expect(action.type).to.equal(actions.COMMAND_OPEN_URL); |
||||
expect(action.url).to.equal('http://github.com'); |
||||
}); |
||||
}); |
||||
|
||||
context("tabopen command", () => { |
||||
it('create COMMAND_TABOPEN_URL acion with a full url', () => { |
||||
let action = commandActions.exec("tabopen https://github.com/") |
||||
expect(action.type).to.equal(actions.COMMAND_TABOPEN_URL); |
||||
expect(action.url).to.equal('https://github.com/'); |
||||
}); |
||||
|
||||
it('create COMMAND_TABOPEN_URL acion with a domain name', () => { |
||||
let action = commandActions.exec("tabopen github.com") |
||||
expect(action.type).to.equal(actions.COMMAND_TABOPEN_URL); |
||||
expect(action.url).to.equal('http://github.com'); |
||||
}); |
||||
}); |
||||
|
||||
context("buffer command", () => { |
||||
it('create COMMAND_BUFFER acion with a keywords', () => { |
||||
let action = commandActions.exec("buffer foo bar") |
||||
expect(action.type).to.equal(actions.COMMAND_BUFFER); |
||||
expect(action.keywords).to.equal('foo bar'); |
||||
}); |
||||
}); |
||||
|
||||
context("b command", () => { |
||||
it('create COMMAND_BUFFER acion with a keywords', () => { |
||||
let action = commandActions.exec("b foo bar") |
||||
expect(action.type).to.equal(actions.COMMAND_BUFFER); |
||||
expect(action.keywords).to.equal('foo bar'); |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,37 @@ |
||||
import { expect } from "chai"; |
||||
import actions from '../../src/actions'; |
||||
import * as consoleActions from '../../src/actions/console'; |
||||
|
||||
describe("console actions", () => { |
||||
describe("showCommand", () => { |
||||
it('create CONSOLE_SHOW_COMMAND action', () => { |
||||
let action = consoleActions.showCommand('hello'); |
||||
expect(action.type).to.equal(actions.CONSOLE_SHOW_COMMAND); |
||||
expect(action.text).to.equal('hello'); |
||||
}); |
||||
}); |
||||
|
||||
describe("setCompletions", () => { |
||||
it('create CONSOLE_SET_COMPLETIONS action', () => { |
||||
let action = consoleActions.setCompletions([1,2,3]); |
||||
expect(action.type).to.equal(actions.CONSOLE_SET_COMPLETIONS); |
||||
expect(action.completions).to.deep.equal([1, 2, 3]); |
||||
}); |
||||
}); |
||||
|
||||
describe("showError", () => { |
||||
it('create CONSOLE_SHOW_ERROR action', () => { |
||||
let action = consoleActions.showError('an error'); |
||||
expect(action.type).to.equal(actions.CONSOLE_SHOW_ERROR); |
||||
expect(action.text).to.equal('an error'); |
||||
}); |
||||
}); |
||||
|
||||
describe("hide", () => { |
||||
it('create CONSOLE_HIDE action', () => { |
||||
let action = consoleActions.hide(); |
||||
expect(action.type).to.equal(actions.CONSOLE_HIDE); |
||||
}); |
||||
}); |
||||
}); |
||||
|
@ -0,0 +1,21 @@ |
||||
import { expect } from "chai"; |
||||
import actions from '../../src/actions'; |
||||
import * as inputActions from '../../src/actions/input'; |
||||
|
||||
describe("input actions", () => { |
||||
describe("keyPress", () => { |
||||
it('create INPUT_KEY_PRESS action', () => { |
||||
let action = inputActions.keyPress(123, true); |
||||
expect(action.type).to.equal(actions.INPUT_KEY_PRESS); |
||||
expect(action.code).to.equal(123); |
||||
expect(action.ctrl).to.be.true; |
||||
}); |
||||
}); |
||||
|
||||
describe("clearKeys", () => { |
||||
it('create INPUT_CLEAR_KEYSaction', () => { |
||||
let action = inputActions.clearKeys(); |
||||
expect(action.type).to.equal(actions.INPUT_CLEAR_KEYS); |
||||
}); |
||||
}); |
||||
}); |
@ -1,50 +0,0 @@ |
||||
import { expect } from "chai"; |
||||
import KeyQueue from '../../src/background/key-queue'; |
||||
|
||||
describe("keyQueue class", () => { |
||||
const KEYMAP = { |
||||
'g<C-X>GG': [], |
||||
'gg': [ 'scroll.top' ], |
||||
}; |
||||
|
||||
const g = 'g'.charCodeAt(0); |
||||
const G = 'G'.charCodeAt(0); |
||||
const x = 'x'.charCodeAt(0); |
||||
|
||||
describe("#push", () => { |
||||
it("returns matched action", () => { |
||||
let queue = new KeyQueue(KEYMAP); |
||||
queue.push({ code: g }); |
||||
let action = queue.push({ code: g }); |
||||
|
||||
expect(action).to.deep.equal([ 'scroll.top' ]); |
||||
}); |
||||
|
||||
it("returns null on no actions matched", () => { |
||||
let queue = new KeyQueue(KEYMAP); |
||||
queue.push({ code: g }); |
||||
let action = queue.push({ code: G }); |
||||
|
||||
expect(action).to.be.null; |
||||
expect(queue.asKeymapChars()).to.be.empty; |
||||
}); |
||||
}); |
||||
|
||||
describe('#asKeymapChars', () => { |
||||
let queue = new KeyQueue(KEYMAP); |
||||
queue.push({ code: g }); |
||||
queue.push({ code: x, ctrl: true }); |
||||
queue.push({ code: G }); |
||||
|
||||
expect(queue.asKeymapChars()).to.equal('g<C-X>G'); |
||||
}); |
||||
|
||||
describe('#asCaretChars', () => { |
||||
let queue = new KeyQueue(KEYMAP); |
||||
queue.push({ code: g }); |
||||
queue.push({ code: x, ctrl: true }); |
||||
queue.push({ code: G }); |
||||
|
||||
expect(queue.asCaretChars()).to.equal('g^XG'); |
||||
}); |
||||
}); |
@ -1,55 +1,31 @@ |
||||
import { expect } from "chai"; |
||||
import { identifyKey, identifyKeys, hasPrefix } from '../../src/background/keys'; |
||||
import * as keys from '../../src/background/keys'; |
||||
|
||||
describe('keys', () => { |
||||
describe('#identifyKey', () => { |
||||
it('return true if key matched', () => { |
||||
expect(identifyKey( |
||||
{ code: 100 }, |
||||
{ code: 100 })).to.be.true; |
||||
expect(identifyKey( |
||||
{ code: 100, shift: true, ctrl: true }, |
||||
{ code: 100, shift: true, ctrl: true })).to.be.true; |
||||
expect(identifyKey( |
||||
{ code: 100, shift: false, ctrl: false }, |
||||
{ code: 100 })).to.be.true; |
||||
}); |
||||
describe("keys", () => { |
||||
const KEYMAP = { |
||||
'g<C-X>GG': [], |
||||
'gg': { type: 'scroll.top' }, |
||||
}; |
||||
|
||||
it('return false if key not matched', () => { |
||||
expect(identifyKey( |
||||
{ code: 100 }, |
||||
{ code: 101 })).to.be.false; |
||||
expect(identifyKey( |
||||
{ code: 100, shift: true, ctrl: true }, |
||||
{ code: 100, shift: true })).to.be.false; |
||||
}); |
||||
}); |
||||
const g = 'g'.charCodeAt(0); |
||||
const G = 'G'.charCodeAt(0); |
||||
const x = 'x'.charCodeAt(0); |
||||
|
||||
describe('#identifyKeys', () => { |
||||
it ('return true if keys matched', () => { |
||||
let keys = [{ code: 100 }, { code: 101, ctrl: false}]; |
||||
let prefix = [{ code: 100, ctrl: false }, { code: 101 }]; |
||||
expect(hasPrefix(keys, prefix)).to.be.true; |
||||
describe('#asKeymapChars', () => { |
||||
let keySequence = [ |
||||
{ code: g }, |
||||
{ code: x, ctrl: true }, |
||||
{ code: G } |
||||
]; |
||||
expect(keys.asKeymapChars(keySequence)).to.equal('g<C-X>G'); |
||||
}); |
||||
|
||||
it ('return false if keys matched', () => { |
||||
let keys = [{ code: 100 }, { code: 101, ctrl: true }]; |
||||
let prefix = [{ code: 100 }, { code: 101 }]; |
||||
expect(hasPrefix(keys, prefix)).to.be.false; |
||||
}); |
||||
}); |
||||
|
||||
describe('#hasPrefix', () => { |
||||
it ('return true if prefix matched', () => { |
||||
let keys = [{ code: 100 }, { code: 101 }, { code: 102 }]; |
||||
let prefix = [{ code: 100 }, { code: 101 }]; |
||||
expect(hasPrefix(keys, prefix)).to.be.true; |
||||
}); |
||||
|
||||
it ('return false if prefix not matched', () => { |
||||
let keys = [{ code: 100 }, { code: 101 }, { code: 102 }]; |
||||
let prefix = [{ code: 102 }]; |
||||
expect(hasPrefix(keys, prefix)).to.be.false; |
||||
}); |
||||
describe('#asCaretChars', () => { |
||||
let keySequence = [ |
||||
{ code: g }, |
||||
{ code: x, ctrl: true }, |
||||
{ code: G } |
||||
]; |
||||
expect(keys.asCaretChars(keySequence)).to.equal('g^XG'); |
||||
}); |
||||
}); |
||||
|
@ -0,0 +1,43 @@ |
||||
import { expect } from "chai"; |
||||
import actions from '../../src/actions'; |
||||
import consoleReducer from '../../src/reducers/console'; |
||||
|
||||
describe("console reducer", () => { |
||||
it('return the initial state', () => { |
||||
let state = consoleReducer(undefined, {}); |
||||
expect(state).to.have.property('errorShown', false); |
||||
expect(state).to.have.property('errorText', ''); |
||||
expect(state).to.have.property('commandShown', false); |
||||
expect(state).to.have.property('commandText', ''); |
||||
expect(state).to.have.deep.property('completions', []); |
||||
}); |
||||
|
||||
it('return next state for CONSOLE_SHOW_COMMAND', () => { |
||||
let action = { type: actions.CONSOLE_SHOW_COMMAND, text: 'open ' }; |
||||
let state = consoleReducer({}, action); |
||||
expect(state).to.have.property('commandShown', true); |
||||
expect(state).to.have.property('commandText', 'open '); |
||||
expect(state).to.have.property('errorShown', false); |
||||
}); |
||||
|
||||
it('return next state for CONSOLE_SET_COMPLETIONS', () => { |
||||
let action = { type: actions.CONSOLE_SET_COMPLETIONS, completions: [1, 2, 3] }; |
||||
let state = consoleReducer({}, action); |
||||
expect(state).to.have.deep.property('completions', [1, 2, 3]); |
||||
}); |
||||
|
||||
it('return next state for CONSOLE_SHOW_ERROR', () => { |
||||
let action = { type: actions.CONSOLE_SHOW_ERROR, text: 'an error' }; |
||||
let state = consoleReducer({}, action); |
||||
expect(state).to.have.property('errorShown', true); |
||||
expect(state).to.have.property('errorText', 'an error'); |
||||
expect(state).to.have.property('commandShown', false); |
||||
}); |
||||
|
||||
it('return next state for CONSOLE_HIDE', () => { |
||||
let action = { type: actions.CONSOLE_HIDE }; |
||||
let state = consoleReducer({}, action); |
||||
expect(state).to.have.property('errorShown', false); |
||||
expect(state).to.have.property('commandShown', false); |
||||
}); |
||||
}); |
@ -0,0 +1,34 @@ |
||||
import { expect } from "chai"; |
||||
import actions from '../../src/actions'; |
||||
import inputReducer from '../../src/reducers/input'; |
||||
|
||||
describe("input reducer", () => { |
||||
it('return the initial state', () => { |
||||
let state = inputReducer(undefined, {}); |
||||
expect(state).to.have.deep.property('keys', []); |
||||
}); |
||||
|
||||
it('return next state for INPUT_KEY_PRESS', () => { |
||||
let action = { type: actions.INPUT_KEY_PRESS, code: 123, ctrl: true }; |
||||
let state = inputReducer(undefined, action); |
||||
expect(state).to.have.deep.property('keys', [{ code: 123, ctrl: true }]); |
||||
|
||||
action = { type: actions.INPUT_KEY_PRESS, code: 456, ctrl: false }; |
||||
state = inputReducer(state, action); |
||||
expect(state).to.have.deep.property('keys', [ |
||||
{ code: 123, ctrl: true }, |
||||
{ code: 456, ctrl: false } |
||||
]); |
||||
}); |
||||
|
||||
it('return next state for INPUT_CLEAR_KEYS', () => { |
||||
let action = { type: actions.INPUT_CLEAR_KEYS }; |
||||
let state = inputReducer({ |
||||
keys: [ |
||||
{ code: 123, ctrl: true }, |
||||
{ code: 456, ctrl: false } |
||||
] |
||||
}, action); |
||||
expect(state).to.have.deep.property('keys', []); |
||||
}); |
||||
}); |
@ -1,25 +0,0 @@ |
||||
import { expect } from "chai"; |
||||
import * as messages from '../../src/shared/messages'; |
||||
|
||||
describe('messages', () => { |
||||
describe('#receive', () => { |
||||
it('received a message', (done) => { |
||||
messages.receive(window, (message) => { |
||||
expect(message).to.deep.equal({ type: 'vimvixen.test' }); |
||||
done(); |
||||
}); |
||||
window.postMessage(JSON.stringify({ type: 'vimvixen.test' }), '*'); |
||||
}); |
||||
}); |
||||
|
||||
describe('#send', () => { |
||||
it('sends a message', (done) => { |
||||
window.addEventListener('message', (e) => { |
||||
let json = JSON.parse(e.data); |
||||
expect(json).to.deep.equal({ type: 'vimvixen.test' }); |
||||
done(); |
||||
}); |
||||
messages.send(window, { type: 'vimvixen.test' }); |
||||
}); |
||||
}); |
||||
}); |
Reference in new issue