Remove unused components

jh-changes
Shin'ya Ueoka 6 years ago
parent e0c4182f14
commit 4be04628e1
  1. 32
      src/content/actions/follow-controller.ts
  2. 81
      src/content/actions/index.ts
  3. 17
      src/content/actions/input.ts
  4. 17
      src/content/actions/mark.ts
  5. 112
      src/content/actions/operation.ts
  6. 104
      src/content/components/common/follow.ts
  7. 59
      src/content/components/common/index.ts
  8. 44
      src/content/components/common/mark.ts
  9. 3
      src/content/components/frame-content.ts
  10. 181
      src/content/components/top-content/follow-controller.ts
  11. 47
      src/content/components/top-content/index.ts
  12. 24
      src/content/console-frames.ts
  13. 15
      src/content/focuses.ts
  14. 9
      src/content/index.ts
  15. 40
      src/content/reducers/follow-controller.ts
  16. 15
      src/content/reducers/index.ts
  17. 26
      src/content/reducers/input.ts
  18. 27
      src/content/reducers/mark.ts
  19. 8
      src/content/store/index.ts
  20. 34
      test/content/actions/follow-controller.test.ts
  21. 19
      test/content/actions/input.test.ts
  22. 25
      test/content/actions/mark.test.ts
  23. 17
      test/content/components/common/follow.html
  24. 25
      test/content/components/common/follow.test.ts
  25. 47
      test/content/reducers/follow-controller.test.ts
  26. 25
      test/content/reducers/input.test.ts
  27. 31
      test/content/reducers/mark.test.ts

@ -1,32 +0,0 @@
import * as actions from './index';
const enable = (
newTab: boolean, background: boolean,
): actions.FollowAction => {
return {
type: actions.FOLLOW_CONTROLLER_ENABLE,
newTab,
background,
};
};
const disable = (): actions.FollowAction => {
return {
type: actions.FOLLOW_CONTROLLER_DISABLE,
};
};
const keyPress = (key: string): actions.FollowAction => {
return {
type: actions.FOLLOW_CONTROLLER_KEY_PRESS,
key: key
};
};
const backspace = (): actions.FollowAction => {
return {
type: actions.FOLLOW_CONTROLLER_BACKSPACE,
};
};
export { enable, disable, keyPress, backspace };

@ -1,81 +0,0 @@
import Redux from 'redux';
import Key from '../domains/Key';
// User input
export const INPUT_KEY_PRESS = 'input.key.press';
export const INPUT_CLEAR_KEYS = 'input.clear.keys';
// Completion
export const COMPLETION_SET_ITEMS = 'completion.set.items';
export const COMPLETION_SELECT_NEXT = 'completions.select.next';
export const COMPLETION_SELECT_PREV = 'completions.select.prev';
// Follow
export const FOLLOW_CONTROLLER_ENABLE = 'follow.controller.enable';
export const FOLLOW_CONTROLLER_DISABLE = 'follow.controller.disable';
export const FOLLOW_CONTROLLER_KEY_PRESS = 'follow.controller.key.press';
export const FOLLOW_CONTROLLER_BACKSPACE = 'follow.controller.backspace';
// Mark
export const MARK_START_SET = 'mark.start.set';
export const MARK_START_JUMP = 'mark.start.jump';
export const MARK_CANCEL = 'mark.cancel';
export const NOOP = 'noop';
export interface InputKeyPressAction extends Redux.Action {
type: typeof INPUT_KEY_PRESS;
key: Key;
}
export interface InputClearKeysAction extends Redux.Action {
type: typeof INPUT_CLEAR_KEYS;
}
export interface FollowControllerEnableAction extends Redux.Action {
type: typeof FOLLOW_CONTROLLER_ENABLE;
newTab: boolean;
background: boolean;
}
export interface FollowControllerDisableAction extends Redux.Action {
type: typeof FOLLOW_CONTROLLER_DISABLE;
}
export interface FollowControllerKeyPressAction extends Redux.Action {
type: typeof FOLLOW_CONTROLLER_KEY_PRESS;
key: string;
}
export interface FollowControllerBackspaceAction extends Redux.Action {
type: typeof FOLLOW_CONTROLLER_BACKSPACE;
}
export interface MarkStartSetAction extends Redux.Action {
type: typeof MARK_START_SET;
}
export interface MarkStartJumpAction extends Redux.Action {
type: typeof MARK_START_JUMP;
}
export interface MarkCancelAction extends Redux.Action {
type: typeof MARK_CANCEL;
}
export interface NoopAction extends Redux.Action {
type: typeof NOOP;
}
export type InputAction = InputKeyPressAction | InputClearKeysAction;
export type FollowAction =
FollowControllerEnableAction | FollowControllerDisableAction |
FollowControllerKeyPressAction | FollowControllerBackspaceAction;
export type MarkAction =
MarkStartSetAction | MarkStartJumpAction | MarkCancelAction | NoopAction;
export type Action =
InputAction |
FollowAction |
MarkAction |
NoopAction;

@ -1,17 +0,0 @@
import * as actions from './index';
import Key from '../domains/Key';
const keyPress = (key: Key): actions.InputAction => {
return {
type: actions.INPUT_KEY_PRESS,
key,
};
};
const clearKeys = (): actions.InputAction => {
return {
type: actions.INPUT_CLEAR_KEYS
};
};
export { keyPress, clearKeys };

@ -1,17 +0,0 @@
import * as actions from './index';
const startSet = (): actions.MarkAction => {
return { type: actions.MARK_START_SET };
};
const startJump = (): actions.MarkAction => {
return { type: actions.MARK_START_JUMP };
};
const cancel = (): actions.MarkAction => {
return { type: actions.MARK_CANCEL };
};
export {
startSet, startJump, cancel,
};

@ -1,112 +0,0 @@
import * as operations from '../../shared/operations';
import * as actions from './index';
import * as messages from '../../shared/messages';
import * as navigates from '../navigates';
import * as focuses from '../focuses';
import * as markActions from './mark';
import AddonEnabledUseCase from '../usecases/AddonEnabledUseCase';
import ClipboardUseCase from '../usecases/ClipboardUseCase';
import { SettingRepositoryImpl } from '../repositories/SettingRepository';
import { ScrollPresenterImpl } from '../presenters/ScrollPresenter';
import { FollowMasterClientImpl } from '../client/FollowMasterClient';
let addonEnabledUseCase = new AddonEnabledUseCase();
let clipbaordUseCase = new ClipboardUseCase();
let settingRepository = new SettingRepositoryImpl();
let scrollPresenter = new ScrollPresenterImpl();
let followMasterClient = new FollowMasterClientImpl(window.top);
// eslint-disable-next-line complexity, max-lines-per-function
const exec = async(
operation: operations.Operation,
): Promise<actions.Action> => {
let settings = settingRepository.get();
let smoothscroll = settings.properties.smoothscroll;
switch (operation.type) {
case operations.ADDON_ENABLE:
await addonEnabledUseCase.enable();
return { type: actions.NOOP };
case operations.ADDON_DISABLE:
await addonEnabledUseCase.disable();
return { type: actions.NOOP };
case operations.ADDON_TOGGLE_ENABLED:
await addonEnabledUseCase.toggle();
return { type: actions.NOOP };
case operations.FIND_NEXT:
window.top.postMessage(JSON.stringify({
type: messages.FIND_NEXT,
}), '*');
break;
case operations.FIND_PREV:
window.top.postMessage(JSON.stringify({
type: messages.FIND_PREV,
}), '*');
break;
case operations.SCROLL_VERTICALLY:
scrollPresenter.scrollVertically(operation.count, smoothscroll);
break;
case operations.SCROLL_HORIZONALLY:
scrollPresenter.scrollHorizonally(operation.count, smoothscroll);
break;
case operations.SCROLL_PAGES:
scrollPresenter.scrollPages(operation.count, smoothscroll);
break;
case operations.SCROLL_TOP:
scrollPresenter.scrollToTop(smoothscroll);
break;
case operations.SCROLL_BOTTOM:
scrollPresenter.scrollToBottom(smoothscroll);
break;
case operations.SCROLL_HOME:
scrollPresenter.scrollToHome(smoothscroll);
break;
case operations.SCROLL_END:
scrollPresenter.scrollToEnd(smoothscroll);
break;
case operations.FOLLOW_START:
followMasterClient.startFollow(operation.newTab, operation.background);
break;
case operations.MARK_SET_PREFIX:
return markActions.startSet();
case operations.MARK_JUMP_PREFIX:
return markActions.startJump();
case operations.NAVIGATE_HISTORY_PREV:
navigates.historyPrev(window);
break;
case operations.NAVIGATE_HISTORY_NEXT:
navigates.historyNext(window);
break;
case operations.NAVIGATE_LINK_PREV:
navigates.linkPrev(window);
break;
case operations.NAVIGATE_LINK_NEXT:
navigates.linkNext(window);
break;
case operations.NAVIGATE_PARENT:
navigates.parent(window);
break;
case operations.NAVIGATE_ROOT:
navigates.root(window);
break;
case operations.FOCUS_INPUT:
focuses.focusInput();
break;
case operations.URLS_YANK:
await clipbaordUseCase.yankCurrentURL();
break;
case operations.URLS_PASTE:
await clipbaordUseCase.openOrSearch(
operation.newTab ? operation.newTab : false,
);
break;
default:
browser.runtime.sendMessage({
type: messages.BACKGROUND_OPERATION,
operation,
});
}
return { type: actions.NOOP };
};
export { exec };

@ -1,104 +0,0 @@
import MessageListener from '../../MessageListener';
import { LinkHint, InputHint } from '../../presenters/Hint';
import * as messages from '../../../shared/messages';
import Key from '../../domains/Key';
import TabsClient, { TabsClientImpl } from '../../client/TabsClient';
import FollowMasterClient, { FollowMasterClientImpl }
from '../../client/FollowMasterClient';
import FollowPresenter, { FollowPresenterImpl }
from '../../presenters/FollowPresenter';
let tabsClient: TabsClient = new TabsClientImpl();
let followMasterClient: FollowMasterClient =
new FollowMasterClientImpl(window.top);
let followPresenter: FollowPresenter =
new FollowPresenterImpl();
interface Size {
width: number;
height: number;
}
interface Point {
x: number;
y: number;
}
export default class Follow {
private enabled: boolean;
constructor() {
this.enabled = false;
new MessageListener().onWebMessage(this.onMessage.bind(this));
}
key(key: Key): boolean {
if (!this.enabled) {
return false;
}
followMasterClient.sendKey(key);
return true;
}
countHints(viewSize: Size, framePosition: Point) {
let count = followPresenter.getTargetCount(viewSize, framePosition);
followMasterClient.responseHintCount(count);
}
createHints(viewSize: Size, framePosition: Point, tags: string[]) {
this.enabled = true;
followPresenter.createHints(viewSize, framePosition, tags);
}
showHints(prefix: string) {
followPresenter.filterHints(prefix);
}
removeHints() {
followPresenter.clearHints();
this.enabled = false;
}
async activateHints(
tag: string, newTab: boolean, background: boolean,
): Promise<void> {
let hint = followPresenter.getHint(tag);
if (!hint) {
return;
}
if (hint instanceof LinkHint) {
let url = hint.getLink();
// ignore taget='_blank'
if (!newTab && hint.getLinkTarget() !== '_blank') {
hint.click();
return;
}
// eslint-disable-next-line no-script-url
if (!url || url === '#' || url.toLowerCase().startsWith('javascript:')) {
return;
}
await tabsClient.openUrl(url, newTab, background);
} else if (hint instanceof InputHint) {
hint.activate();
}
}
onMessage(message: messages.Message, _sender: Window) {
switch (message.type) {
case messages.FOLLOW_REQUEST_COUNT_TARGETS:
return this.countHints(message.viewSize, message.framePosition);
case messages.FOLLOW_CREATE_HINTS:
return this.createHints(
message.viewSize, message.framePosition, message.tags);
case messages.FOLLOW_SHOW_HINTS:
return this.showHints(message.prefix);
case messages.FOLLOW_ACTIVATE:
return this.activateHints(
message.tag, message.newTab, message.background);
case messages.FOLLOW_REMOVE_HINTS:
return this.removeHints();
}
}
}

@ -1,59 +0,0 @@
import InputDriver from './../../InputDriver';
import FollowComponent from './follow';
import MarkComponent from './mark';
// import KeymapperComponent from './keymapper';
import * as messages from '../../../shared/messages';
import MessageListener from '../../MessageListener';
import * as blacklists from '../../../shared/blacklists';
import Key from '../../domains/Key';
import AddonEnabledUseCase from '../../usecases/AddonEnabledUseCase';
import SettingUseCase from '../../usecases/SettingUseCase';
let addonEnabledUseCase = new AddonEnabledUseCase();
let settingUseCase = new SettingUseCase();
export default class Common {
constructor(win: Window, store: any) {
const input = new InputDriver(win.document.body);
const follow = new FollowComponent();
const mark = new MarkComponent(store);
// const keymapper = new KeymapperComponent(store);
input.onKey((key: Key) => follow.key(key));
input.onKey((key: Key) => mark.key(key));
// input.onKey((key: Key) => keymapper.key(key));
this.reloadSettings();
new MessageListener().onBackgroundMessage(this.onMessage.bind(this));
}
onMessage(message: messages.Message) {
switch (message.type) {
case messages.SETTINGS_CHANGED:
return this.reloadSettings();
case messages.ADDON_TOGGLE_ENABLED:
return addonEnabledUseCase.toggle();
}
return undefined;
}
async reloadSettings() {
try {
let current = await settingUseCase.reload();
let disabled = blacklists.includes(
current.blacklist, window.location.href,
);
if (disabled) {
addonEnabledUseCase.disable();
} else {
addonEnabledUseCase.enable();
}
} catch (e) {
// Sometime sendMessage fails when background script is not ready.
console.warn(e);
setTimeout(() => this.reloadSettings(), 500);
}
}
}

@ -1,44 +0,0 @@
import * as markActions from '../../actions/mark';
import * as consoleFrames from '../..//console-frames';
import Key from '../../domains/Key';
import MarkUseCase from '../../usecases/MarkUseCase';
let markUseCase = new MarkUseCase();
const cancelKey = (key: Key): boolean => {
return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey);
};
export default class MarkComponent {
private store: any;
constructor(store: any) {
this.store = store;
}
// eslint-disable-next-line max-statements
key(key: Key) {
let { mark: markState } = this.store.getState();
if (!markState.setMode && !markState.jumpMode) {
return false;
}
if (cancelKey(key)) {
this.store.dispatch(markActions.cancel());
return true;
}
if (key.ctrlKey || key.metaKey || key.altKey) {
consoleFrames.postError('Unknown mark');
} else if (markState.setMode) {
markUseCase.set(key.key);
} else if (markState.jumpMode) {
markUseCase.jump(key.key);
}
this.store.dispatch(markActions.cancel());
return true;
}
}

@ -1,3 +0,0 @@
import CommonComponent from './common';
export default CommonComponent;

@ -1,181 +0,0 @@
import * as followControllerActions from '../../actions/follow-controller';
import * as messages from '../../../shared/messages';
import MessageListener from '../../MessageListener';
import HintKeyProducer from '../../hint-key-producer';
import { SettingRepositoryImpl } from '../../repositories/SettingRepository';
import FollowSlaveClient, { FollowSlaveClientImpl }
from '../../client/FollowSlaveClient';
let settingRepository = new SettingRepositoryImpl();
export default class FollowController {
private win: Window;
private store: any;
private state: {
enabled?: boolean;
newTab?: boolean;
background?: boolean;
keys?: string,
};
private keys: string[];
private producer: HintKeyProducer | null;
constructor(win: Window, store: any) {
this.win = win;
this.store = store;
this.state = {};
this.keys = [];
this.producer = null;
new MessageListener().onWebMessage(this.onMessage.bind(this));
store.subscribe(() => {
this.update();
});
}
onMessage(message: messages.Message, sender: Window) {
switch (message.type) {
case messages.FOLLOW_START:
return this.store.dispatch(
followControllerActions.enable(message.newTab, message.background));
case messages.FOLLOW_RESPONSE_COUNT_TARGETS:
return this.create(message.count, sender);
case messages.FOLLOW_KEY_PRESS:
return this.keyPress(message.key, message.ctrlKey);
}
}
update(): void {
let prevState = this.state;
this.state = this.store.getState().followController;
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(): void {
let shown = this.keys.filter((key) => {
return key.startsWith(this.state.keys as string);
});
if (shown.length === 1) {
this.activate();
this.store.dispatch(followControllerActions.disable());
}
this.broadcastMessage((c: FollowSlaveClient) => {
c.filterHints(this.state.keys!!);
});
}
activate(): void {
this.broadcastMessage((c: FollowSlaveClient) => {
c.activateIfExists(
this.state.keys!!,
this.state.newTab!!,
this.state.background!!);
});
}
keyPress(key: string, ctrlKey: boolean): boolean {
if (key === '[' && ctrlKey) {
this.store.dispatch(followControllerActions.disable());
return true;
}
switch (key) {
case 'Enter':
this.activate();
this.store.dispatch(followControllerActions.disable());
break;
case 'Esc':
this.store.dispatch(followControllerActions.disable());
break;
case 'Backspace':
case 'Delete':
this.store.dispatch(followControllerActions.backspace());
break;
default:
if (this.hintchars().includes(key)) {
this.store.dispatch(followControllerActions.keyPress(key));
}
break;
}
return true;
}
count() {
this.producer = new HintKeyProducer(this.hintchars());
let doc = this.win.document;
let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth;
let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight;
let frameElements = this.win.document.querySelectorAll('iframe');
new FollowSlaveClientImpl(this.win).requestHintCount(
{ width: viewWidth, height: viewHeight },
{ x: 0, y: 0 });
for (let ele of Array.from(frameElements)) {
let { left: frameX, top: frameY } = ele.getBoundingClientRect();
new FollowSlaveClientImpl(ele.contentWindow!!).requestHintCount(
{ width: viewWidth, height: viewHeight },
{ x: frameX, y: frameY },
);
}
}
create(count: number, sender: Window) {
let produced = [];
for (let i = 0; i < count; ++i) {
produced.push((this.producer as HintKeyProducer).produce());
}
this.keys = this.keys.concat(produced);
let doc = this.win.document;
let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth;
let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight;
let pos = { x: 0, y: 0 };
if (sender !== window) {
let frameElements = this.win.document.querySelectorAll('iframe');
let ele = Array.from(frameElements).find(e => e.contentWindow === sender);
if (!ele) {
// elements of the sender is gone
return;
}
let { left: frameX, top: frameY } = ele.getBoundingClientRect();
pos = { x: frameX, y: frameY };
}
new FollowSlaveClientImpl(sender).createHints(
{ width: viewWidth, height: viewHeight },
pos,
produced,
);
}
remove() {
this.keys = [];
this.broadcastMessage((c: FollowSlaveClient) => {
c.clearHints();
});
}
private hintchars() {
return settingRepository.get().properties.hintchars;
}
private broadcastMessage(f: (clinet: FollowSlaveClient) => void) {
let windows = [window.self].concat(Array.from(window.frames as any));
windows
.map(w => new FollowSlaveClientImpl(w))
.forEach(c => f(c));
}
}

@ -1,47 +0,0 @@
import CommonComponent from '../common';
import FollowController from './follow-controller';
import * as consoleFrames from '../../console-frames';
import * as messages from '../../../shared/messages';
import MessageListener from '../../MessageListener';
import AddonEnabledUseCase from '../../usecases/AddonEnabledUseCase';
import { ScrollPresenterImpl } from '../../presenters/ScrollPresenter';
let addonEnabledUseCase = new AddonEnabledUseCase();
let scrollPresenter = new ScrollPresenterImpl();
export default class TopContent {
private win: Window;
constructor(win: Window, store: any) {
this.win = win;
new CommonComponent(win, store); // eslint-disable-line no-new
new FollowController(win, store); // eslint-disable-line no-new
// TODO make component
consoleFrames.initialize(this.win.document);
new MessageListener().onWebMessage(this.onWebMessage.bind(this));
new MessageListener().onBackgroundMessage(
this.onBackgroundMessage.bind(this));
}
onWebMessage(message: messages.Message) {
switch (message.type) {
case messages.CONSOLE_UNFOCUS:
this.win.focus();
consoleFrames.blur(window.document);
}
}
onBackgroundMessage(message: messages.Message) {
let addonEnabled = addonEnabledUseCase.getEnabled();
switch (message.type) {
case messages.ADDON_ENABLED_QUERY:
return Promise.resolve(addonEnabled);
case messages.TAB_SCROLL_TO:
return scrollPresenter.scrollTo(message.x, message.y, false);
}
}
}

@ -1,5 +1,3 @@
import * as messages from '../shared/messages';
const initialize = (doc: Document): HTMLIFrameElement => { const initialize = (doc: Document): HTMLIFrameElement => {
let iframe = doc.createElement('iframe'); let iframe = doc.createElement('iframe');
iframe.src = browser.runtime.getURL('build/console.html'); iframe.src = browser.runtime.getURL('build/console.html');
@ -15,24 +13,4 @@ const blur = (doc: Document) => {
ele.blur(); ele.blur();
}; };
const postError = (text: string): Promise<any> => { export { initialize, blur };
return browser.runtime.sendMessage({
type: messages.CONSOLE_FRAME_MESSAGE,
message: {
type: messages.CONSOLE_SHOW_ERROR,
text,
},
});
};
const postInfo = (text: string): Promise<any> => {
return browser.runtime.sendMessage({
type: messages.CONSOLE_FRAME_MESSAGE,
message: {
type: messages.CONSOLE_SHOW_INFO,
text,
},
});
};
export { initialize, blur, postError, postInfo };

@ -1,15 +0,0 @@
import * as doms from '../shared/utils/dom';
const focusInput = (): void => {
let inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url'];
let inputSelector = inputTypes.map(type => `input[type=${type}]`).join(',');
let targets = window.document.querySelectorAll(inputSelector + ',textarea');
let target = Array.from(targets).find(doms.isVisible);
if (target instanceof HTMLInputElement) {
target.focus();
} else if (target instanceof HTMLTextAreaElement) {
target.focus();
}
};
export { focusInput };

@ -1,8 +1,5 @@
// import TopContentComponent from './components/top-content';
// import FrameContentComponent from './components/frame-content';
import * as consoleFrames from './console-frames'; import * as consoleFrames from './console-frames';
import consoleFrameStyle from './site-style'; import consoleFrameStyle from './site-style';
// import { newStore } from './store';
import MessageListener from './MessageListener'; import MessageListener from './MessageListener';
import FindController from './controllers/FindController'; import FindController from './controllers/FindController';
import MarkController from './controllers/MarkController'; import MarkController from './controllers/MarkController';
@ -18,12 +15,8 @@ import * as blacklists from '../shared/blacklists';
import MarkKeyController from './controllers/MarkKeyController'; import MarkKeyController from './controllers/MarkKeyController';
import AddonEnabledController from './controllers/AddonEnabledController'; import AddonEnabledController from './controllers/AddonEnabledController';
// const store = newStore();
let listener = new MessageListener(); let listener = new MessageListener();
if (window.self === window.top) { if (window.self === window.top) {
// new TopContentComponent(window, store); // eslint-disable-line no-new
let findController = new FindController(); let findController = new FindController();
let followMasterController = new FollowMasterController(); let followMasterController = new FollowMasterController();
@ -63,8 +56,6 @@ if (window.self === window.top) {
}); });
consoleFrames.initialize(window.document); consoleFrames.initialize(window.document);
} else {
// new FrameContentComponent(window, store); // eslint-disable-line no-new
} }
let followSlaveController = new FollowSlaveController(); let followSlaveController = new FollowSlaveController();

@ -1,40 +0,0 @@
import * as actions from '../actions';
export interface State {
enabled: boolean;
newTab: boolean;
background: boolean;
keys: string,
}
const defaultState: State = {
enabled: false,
newTab: false,
background: false,
keys: '',
};
export default function reducer(
state: State = defaultState,
action: actions.FollowAction,
): State {
switch (action.type) {
case actions.FOLLOW_CONTROLLER_ENABLE:
return { ...state,
enabled: true,
newTab: action.newTab,
background: action.background,
keys: '', };
case actions.FOLLOW_CONTROLLER_DISABLE:
return { ...state,
enabled: false, };
case actions.FOLLOW_CONTROLLER_KEY_PRESS:
return { ...state,
keys: state.keys + action.key, };
case actions.FOLLOW_CONTROLLER_BACKSPACE:
return { ...state,
keys: state.keys.slice(0, -1), };
default:
return state;
}
}

@ -1,15 +0,0 @@
import { combineReducers } from 'redux';
import input, { State as InputState } from './input';
import followController, { State as FollowControllerState }
from './follow-controller';
import mark, { State as MarkState } from './mark';
export interface State {
input: InputState;
followController: FollowControllerState;
mark: MarkState;
}
export default combineReducers({
input, followController, mark,
});

@ -1,26 +0,0 @@
import * as actions from '../actions';
import Key from '../domains/Key';
export interface State {
keys: Key[],
}
const defaultState: State = {
keys: []
};
export default function reducer(
state: State = defaultState,
action: actions.InputAction,
): State {
switch (action.type) {
case actions.INPUT_KEY_PRESS:
return { ...state,
keys: state.keys.concat([action.key]), };
case actions.INPUT_CLEAR_KEYS:
return { ...state,
keys: [], };
default:
return state;
}
}

@ -1,27 +0,0 @@
import * as actions from '../actions';
export interface State {
setMode: boolean;
jumpMode: boolean;
}
const defaultState: State = {
setMode: false,
jumpMode: false,
};
export default function reducer(
state: State = defaultState,
action: actions.MarkAction,
): State {
switch (action.type) {
case actions.MARK_START_SET:
return { ...state, setMode: true };
case actions.MARK_START_JUMP:
return { ...state, jumpMode: true };
case actions.MARK_CANCEL:
return { ...state, setMode: false, jumpMode: false };
default:
return state;
}
}

@ -1,8 +0,0 @@
import promise from 'redux-promise';
import reducers from '../reducers';
import { createStore, applyMiddleware } from 'redux';
export const newStore = () => createStore(
reducers,
applyMiddleware(promise),
);

@ -1,34 +0,0 @@
import * as actions from 'content/actions';
import * as followControllerActions from 'content/actions/follow-controller';
describe('follow-controller actions', () => {
describe('enable', () => {
it('creates FOLLOW_CONTROLLER_ENABLE action', () => {
let action = followControllerActions.enable(true);
expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_ENABLE);
expect(action.newTab).to.equal(true);
});
});
describe('disable', () => {
it('creates FOLLOW_CONTROLLER_DISABLE action', () => {
let action = followControllerActions.disable(true);
expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_DISABLE);
});
});
describe('keyPress', () => {
it('creates FOLLOW_CONTROLLER_KEY_PRESS action', () => {
let action = followControllerActions.keyPress(100);
expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_KEY_PRESS);
expect(action.key).to.equal(100);
});
});
describe('backspace', () => {
it('creates FOLLOW_CONTROLLER_BACKSPACE action', () => {
let action = followControllerActions.backspace(100);
expect(action.type).to.equal(actions.FOLLOW_CONTROLLER_BACKSPACE);
});
});
});

@ -1,19 +0,0 @@
import * as actions from 'content/actions';
import * as inputActions from 'content/actions/input';
describe("input actions", () => {
describe("keyPress", () => {
it('create INPUT_KEY_PRESS action', () => {
let action = inputActions.keyPress('a');
expect(action.type).to.equal(actions.INPUT_KEY_PRESS);
expect(action.key).to.equal('a');
});
});
describe("clearKeys", () => {
it('create INPUT_CLEAR_KEYSaction', () => {
let action = inputActions.clearKeys();
expect(action.type).to.equal(actions.INPUT_CLEAR_KEYS);
});
});
});

@ -1,25 +0,0 @@
import * as actions from 'content/actions';
import * as markActions from 'content/actions/mark';
describe('mark actions', () => {
describe('startSet', () => {
it('create MARK_START_SET action', () => {
let action = markActions.startSet();
expect(action.type).to.equal(actions.MARK_START_SET);
});
});
describe('startJump', () => {
it('create MARK_START_JUMP action', () => {
let action = markActions.startJump();
expect(action.type).to.equal(actions.MARK_START_JUMP);
});
});
describe('cancel', () => {
it('create MARK_CANCEL action', () => {
let action = markActions.cancel();
expect(action.type).to.equal(actions.MARK_CANCEL);
});
});
});

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<a id='visible_a' href='#' >link</a>
<a href='#' style='display:none'>invisible 1</a>
<a href='#' style='visibility:hidden'>invisible 2</a>
<i>not link<i>
<div id='editable_div_1' contenteditable>link</div>
<div id='editable_div_2' contenteditable='true'>link</div>
<div id='x' contenteditable='false'>link</div>
<details>
<summary id='summary_1'>summary link</summary>
Some details
<a href='#'>not visible</a>
</details>
</body>
</html>

@ -1,25 +0,0 @@
import FollowComponent from 'content/components/common/follow';
describe('FollowComponent', () => {
describe('#getTargetElements', () => {
beforeEach(() => {
document.body.innerHTML = __html__['test/content/components/common/follow.html'];
});
it('returns visible links', () => {
let targets = FollowComponent.getTargetElements(
window,
{ width: window.innerWidth, height: window.innerHeight },
{ x: 0, y: 0 });
expect(targets).to.have.lengthOf(4);
let ids = Array.prototype.map.call(targets, (e) => e.id);
expect(ids).to.include.members([
'visible_a',
'editable_div_1',
'editable_div_2',
'summary_1',
]);
});
});
});

@ -1,47 +0,0 @@
import * as actions from 'content/actions';
import followControllerReducer from 'content/reducers/follow-controller';
describe('follow-controller reducer', () => {
it ('returns the initial state', () => {
let state = followControllerReducer(undefined, {});
expect(state).to.have.property('enabled', false);
expect(state).to.have.property('newTab');
expect(state).to.have.deep.property('keys', '');
});
it ('returns next state for FOLLOW_CONTROLLER_ENABLE', () => {
let action = { type: actions.FOLLOW_CONTROLLER_ENABLE, newTab: true };
let state = followControllerReducer({ enabled: false, newTab: false }, action);
expect(state).to.have.property('enabled', true);
expect(state).to.have.property('newTab', true);
expect(state).to.have.property('keys', '');
});
it ('returns next state for FOLLOW_CONTROLLER_DISABLE', () => {
let action = { type: actions.FOLLOW_CONTROLLER_DISABLE };
let state = followControllerReducer({ enabled: true }, action);
expect(state).to.have.property('enabled', false);
});
it ('returns next state for FOLLOW_CONTROLLER_KEY_PRESS', () => {
let action = { type: actions.FOLLOW_CONTROLLER_KEY_PRESS, key: 'a'};
let state = followControllerReducer({ keys: '' }, action);
expect(state).to.have.deep.property('keys', 'a');
action = { type: actions.FOLLOW_CONTROLLER_KEY_PRESS, key: 'b'};
state = followControllerReducer(state, action);
expect(state).to.have.deep.property('keys', 'ab');
});
it ('returns next state for FOLLOW_CONTROLLER_BACKSPACE', () => {
let action = { type: actions.FOLLOW_CONTROLLER_BACKSPACE };
let state = followControllerReducer({ keys: 'ab' }, action);
expect(state).to.have.deep.property('keys', 'a');
state = followControllerReducer(state, action);
expect(state).to.have.deep.property('keys', '');
state = followControllerReducer(state, action);
expect(state).to.have.deep.property('keys', '');
});
});

@ -1,25 +0,0 @@
import * as actions from 'content/actions';
import inputReducer from 'content/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, key: 'a' };
let state = inputReducer(undefined, action);
expect(state).to.have.deep.property('keys', ['a']);
action = { type: actions.INPUT_KEY_PRESS, key: 'b' };
state = inputReducer(state, action);
expect(state).to.have.deep.property('keys', ['a', 'b']);
});
it('return next state for INPUT_CLEAR_KEYS', () => {
let action = { type: actions.INPUT_CLEAR_KEYS };
let state = inputReducer({ keys: [1, 2, 3] }, action);
expect(state).to.have.deep.property('keys', []);
});
});

@ -1,31 +0,0 @@
import * as actions from 'content/actions';
import reducer from 'content/reducers/mark';
describe("mark reducer", () => {
it('return the initial state', () => {
let state = reducer(undefined, {});
expect(state.setMode).to.be.false;
expect(state.jumpMode).to.be.false;
});
it('starts set mode', () => {
let action = { type: actions.MARK_START_SET };
let state = reducer(undefined, action);
expect(state.setMode).to.be.true;
});
it('starts jump mode', () => {
let action = { type: actions.MARK_START_JUMP };
let state = reducer(undefined, action);
expect(state.jumpMode).to.be.true;
});
it('cancels set and jump mode', () => {
let action = { type: actions.MARK_CANCEL };
let state = reducer({ setMode: true }, action);
expect(state.setMode).to.be.false;
state = reducer({ jumpMode: true }, action);
expect(state.jumpMode).to.be.false;
});
});