commit
						8b72aac09a
					
				
					 24 changed files with 451 additions and 18 deletions
				
			
		
							
								
								
									
										15
									
								
								src/background/controllers/mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/background/controllers/mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| import MarkInteractor from '../usecases/mark'; | ||||
| 
 | ||||
| export default class MarkController { | ||||
|   constructor() { | ||||
|     this.markInteractor = new MarkInteractor(); | ||||
|   } | ||||
| 
 | ||||
|   setGlobal(key, x, y) { | ||||
|     this.markInteractor.setGlobal(key, x, y); | ||||
|   } | ||||
| 
 | ||||
|   jumpGlobal(key) { | ||||
|     this.markInteractor.jumpGlobal(key); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										24
									
								
								src/background/domains/global-mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/background/domains/global-mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| export default class GlobalMark { | ||||
|   constructor(tabId, url, x, y) { | ||||
|     this.tabId0 = tabId; | ||||
|     this.url0 = url; | ||||
|     this.x0 = x; | ||||
|     this.y0 = y; | ||||
|   } | ||||
| 
 | ||||
|   get tabId() { | ||||
|     return this.tabId0; | ||||
|   } | ||||
| 
 | ||||
|   get url() { | ||||
|     return this.url0; | ||||
|   } | ||||
| 
 | ||||
|   get x() { | ||||
|     return this.x0; | ||||
|   } | ||||
| 
 | ||||
|   get y() { | ||||
|     return this.y0; | ||||
|   } | ||||
| } | ||||
|  | @ -22,4 +22,12 @@ export default class ContentMessageClient { | |||
|       type: messages.ADDON_TOGGLE_ENABLED, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   scrollTo(tabId, x, y) { | ||||
|     return browser.tabs.sendMessage(tabId, { | ||||
|       type: messages.TAB_SCROLL_TO, | ||||
|       x, | ||||
|       y, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import FindController from '../controllers/find'; | |||
| import AddonEnabledController from '../controllers/addon-enabled'; | ||||
| import LinkController from '../controllers/link'; | ||||
| import OperationController from '../controllers/operation'; | ||||
| import MarkController from '../controllers/mark'; | ||||
| 
 | ||||
| export default class ContentMessageListener { | ||||
|   constructor() { | ||||
|  | @ -14,6 +15,7 @@ export default class ContentMessageListener { | |||
|     this.addonEnabledController = new AddonEnabledController(); | ||||
|     this.linkController = new LinkController(); | ||||
|     this.backgroundOperationController = new OperationController(); | ||||
|     this.markController = new MarkController(); | ||||
|   } | ||||
| 
 | ||||
|   run() { | ||||
|  | @ -59,6 +61,10 @@ export default class ContentMessageListener { | |||
|         message.newTab, message.url, sender.tab.id, message.background); | ||||
|     case messages.BACKGROUND_OPERATION: | ||||
|       return this.onBackgroundOperation(message.operation); | ||||
|     case messages.MARK_SET_GLOBAL: | ||||
|       return this.onMarkSetGlobal(message.key, message.x, message.y); | ||||
|     case messages.MARK_JUMP_GLOBAL: | ||||
|       return this.onMarkJumpGlobal(message.key); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -102,4 +108,12 @@ export default class ContentMessageListener { | |||
|   onBackgroundOperation(operation) { | ||||
|     return this.backgroundOperationController.exec(operation); | ||||
|   } | ||||
| 
 | ||||
|   onMarkSetGlobal(key, x, y) { | ||||
|     return this.markController.setGlobal(key, x, y); | ||||
|   } | ||||
| 
 | ||||
|   onMarkJumpGlobal(key) { | ||||
|     return this.markController.jumpGlobal(key); | ||||
|   } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										33
									
								
								src/background/repositories/mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/background/repositories/mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| import MemoryStorage from '../infrastructures/memory-storage'; | ||||
| import GlobalMark from 'background/domains/global-mark'; | ||||
| 
 | ||||
| const MARK_KEY = 'mark'; | ||||
| 
 | ||||
| export default class MarkRepository { | ||||
|   constructor() { | ||||
|     this.cache = new MemoryStorage(); | ||||
|   } | ||||
| 
 | ||||
|   getMark(key) { | ||||
|     let marks = this.getOrEmptyMarks(); | ||||
|     let data = marks[key]; | ||||
|     if (!data) { | ||||
|       return Promise.resolve(undefined); | ||||
|     } | ||||
|     let mark = new GlobalMark(data.tabId, data.url, data.x, data.y); | ||||
|     return Promise.resolve(mark); | ||||
|   } | ||||
| 
 | ||||
|   setMark(key, mark) { | ||||
|     let marks = this.getOrEmptyMarks(); | ||||
|     marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y }; | ||||
|     this.cache.set(MARK_KEY, marks); | ||||
| 
 | ||||
|     return Promise.resolve(); | ||||
|   } | ||||
| 
 | ||||
|   getOrEmptyMarks() { | ||||
|     return this.cache.get(MARK_KEY) || {}; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										39
									
								
								src/background/usecases/mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/background/usecases/mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| import GlobalMark from '../domains/global-mark'; | ||||
| import TabPresenter from '../presenters/tab'; | ||||
| import MarkRepository from '../repositories/mark'; | ||||
| import ConsolePresenter from '../presenters/console'; | ||||
| import ContentMessageClient from '../infrastructures/content-message-client'; | ||||
| 
 | ||||
| export default class MarkInteractor { | ||||
|   constructor() { | ||||
|     this.tabPresenter = new TabPresenter(); | ||||
|     this.markRepository = new MarkRepository(); | ||||
|     this.consolePresenter = new ConsolePresenter(); | ||||
|     this.contentMessageClient = new ContentMessageClient(); | ||||
|   } | ||||
| 
 | ||||
|   async setGlobal(key, x, y) { | ||||
|     let tab = await this.tabPresenter.getCurrent(); | ||||
|     let mark = new GlobalMark(tab.id, tab.url, x, y); | ||||
|     return this.markRepository.setMark(key, mark); | ||||
|   } | ||||
| 
 | ||||
|   async jumpGlobal(key) { | ||||
|     let current = await this.tabPresenter.getCurrent(); | ||||
| 
 | ||||
|     let mark = await this.markRepository.getMark(key); | ||||
|     if (!mark) { | ||||
|       return this.consolePresenter.showError(current.id, 'Mark is not set'); | ||||
|     } | ||||
| 
 | ||||
|     return this.contentMessageClient.scrollTo( | ||||
|       mark.tabId, mark.x, mark.y | ||||
|     ).then(() => { | ||||
|       return this.tabPresenter.select(mark.tabId); | ||||
|     }).catch(async() => { | ||||
|       let tab = await this.tabPresenter.create(mark.url); | ||||
|       let mark2 = new GlobalMark(tab.id, mark.url, mark.x, mark.y); | ||||
|       return this.markRepository.setMark(key, mark2); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | @ -22,4 +22,10 @@ export default { | |||
| 
 | ||||
|   // Find
 | ||||
|   FIND_SET_KEYWORD: 'find.set.keyword', | ||||
| 
 | ||||
|   // Mark
 | ||||
|   MARK_START_SET: 'mark.start.set', | ||||
|   MARK_START_JUMP: 'mark.start.jump', | ||||
|   MARK_CANCEL: 'mark.cancel', | ||||
|   MARK_SET_LOCAL: 'mark.set.local', | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										46
									
								
								src/content/actions/mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/content/actions/mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| import actions from 'content/actions'; | ||||
| import messages from 'shared/messages'; | ||||
| 
 | ||||
| const startSet = () => { | ||||
|   return { type: actions.MARK_START_SET }; | ||||
| }; | ||||
| 
 | ||||
| const startJump = () => { | ||||
|   return { type: actions.MARK_START_JUMP }; | ||||
| }; | ||||
| 
 | ||||
| const cancel = () => { | ||||
|   return { type: actions.MARK_CANCEL }; | ||||
| }; | ||||
| 
 | ||||
| const setLocal = (key, x, y) => { | ||||
|   return { | ||||
|     type: actions.MARK_SET_LOCAL, | ||||
|     key, | ||||
|     x, | ||||
|     y, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const setGlobal = (key, x, y) => { | ||||
|   browser.runtime.sendMessage({ | ||||
|     type: messages.MARK_SET_GLOBAL, | ||||
|     key, | ||||
|     x, | ||||
|     y, | ||||
|   }); | ||||
|   return { type: '' }; | ||||
| }; | ||||
| 
 | ||||
| const jumpGlobal = (key) => { | ||||
|   browser.runtime.sendMessage({ | ||||
|     type: messages.MARK_JUMP_GLOBAL, | ||||
|     key, | ||||
|   }); | ||||
|   return { type: '' }; | ||||
| }; | ||||
| 
 | ||||
| export { | ||||
|   startSet, startJump, cancel, setLocal, | ||||
|   setGlobal, jumpGlobal, | ||||
| }; | ||||
|  | @ -6,6 +6,7 @@ import * as focuses from 'content/focuses'; | |||
| import * as urls from 'content/urls'; | ||||
| import * as consoleFrames from 'content/console-frames'; | ||||
| import * as addonActions from './addon'; | ||||
| import * as markActions from './mark'; | ||||
| import * as properties from 'shared/settings/properties'; | ||||
| 
 | ||||
| // eslint-disable-next-line complexity, max-lines-per-function
 | ||||
|  | @ -39,16 +40,16 @@ const exec = (operation, repeat, settings, addonEnabled) => { | |||
|     scrolls.scrollPages(operation.count, smoothscroll, repeat); | ||||
|     break; | ||||
|   case operations.SCROLL_TOP: | ||||
|     scrolls.scrollTop(smoothscroll, repeat); | ||||
|     scrolls.scrollToTop(smoothscroll); | ||||
|     break; | ||||
|   case operations.SCROLL_BOTTOM: | ||||
|     scrolls.scrollBottom(smoothscroll, repeat); | ||||
|     scrolls.scrollToBottom(smoothscroll); | ||||
|     break; | ||||
|   case operations.SCROLL_HOME: | ||||
|     scrolls.scrollHome(smoothscroll, repeat); | ||||
|     scrolls.scrollToHome(smoothscroll); | ||||
|     break; | ||||
|   case operations.SCROLL_END: | ||||
|     scrolls.scrollEnd(smoothscroll, repeat); | ||||
|     scrolls.scrollToEnd(smoothscroll); | ||||
|     break; | ||||
|   case operations.FOLLOW_START: | ||||
|     window.top.postMessage(JSON.stringify({ | ||||
|  | @ -57,6 +58,10 @@ const exec = (operation, repeat, settings, addonEnabled) => { | |||
|       background: 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; | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import InputComponent from './input'; | ||||
| import KeymapperComponent from './keymapper'; | ||||
| import FollowComponent from './follow'; | ||||
| import MarkComponent from './mark'; | ||||
| import KeymapperComponent from './keymapper'; | ||||
| import * as settingActions from 'content/actions/setting'; | ||||
| import messages from 'shared/messages'; | ||||
| import * as addonActions from '../../actions/addon'; | ||||
|  | @ -8,11 +9,13 @@ import * as blacklists from 'shared/blacklists'; | |||
| 
 | ||||
| export default class Common { | ||||
|   constructor(win, store) { | ||||
|     const follow = new FollowComponent(win, store); | ||||
|     const input = new InputComponent(win.document.body, store); | ||||
|     const follow = new FollowComponent(win, store); | ||||
|     const mark = new MarkComponent(win.document.body, store); | ||||
|     const keymapper = new KeymapperComponent(store); | ||||
| 
 | ||||
|     input.onKey(key => follow.key(key)); | ||||
|     input.onKey(key => mark.key(key)); | ||||
|     input.onKey(key => keymapper.key(key)); | ||||
| 
 | ||||
|     this.win = win; | ||||
|  |  | |||
							
								
								
									
										74
									
								
								src/content/components/common/mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/content/components/common/mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | |||
| import * as markActions from 'content/actions/mark'; | ||||
| import * as scrolls from 'content/scrolls'; | ||||
| import * as consoleFrames from 'content/console-frames'; | ||||
| import * as properties from 'shared/settings/properties'; | ||||
| 
 | ||||
| const cancelKey = (key) => { | ||||
|   return key.key === 'Esc' || key.key === '[' && key.ctrlKey; | ||||
| }; | ||||
| 
 | ||||
| const globalKey = (key) => { | ||||
|   return (/^[A-Z0-9]$/).test(key); | ||||
| }; | ||||
| 
 | ||||
| export default class MarkComponent { | ||||
|   constructor(body, store) { | ||||
|     this.body = body; | ||||
|     this.store = store; | ||||
|   } | ||||
| 
 | ||||
|   // eslint-disable-next-line max-statements
 | ||||
|   key(key) { | ||||
|     let { mark: markStage, setting } = this.store.getState(); | ||||
|     let smoothscroll = setting.properties.smoothscroll || | ||||
|       properties.defaults.smoothscroll; | ||||
| 
 | ||||
|     if (!markStage.setMode && !markStage.jumpMode) { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     if (cancelKey(key)) { | ||||
|       this.store.dispatch(markActions.cancel()); | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     if (key.ctrlKey || key.metaKey || key.altKey) { | ||||
|       consoleFrames.postError(window.document, 'Unknown mark'); | ||||
|     } else if (globalKey(key.key) && markStage.setMode) { | ||||
|       this.doSetGlobal(key); | ||||
|     } else if (globalKey(key.key) && markStage.jumpMode) { | ||||
|       this.doJumpGlobal(key); | ||||
|     } else if (markStage.setMode) { | ||||
|       this.doSet(key); | ||||
|     } else if (markStage.jumpMode) { | ||||
|       this.doJump(markStage.marks, key, smoothscroll); | ||||
|     } | ||||
| 
 | ||||
|     this.store.dispatch(markActions.cancel()); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   doSet(key) { | ||||
|     let { x, y } = scrolls.getScroll(); | ||||
|     this.store.dispatch(markActions.setLocal(key.key, x, y)); | ||||
|   } | ||||
| 
 | ||||
|   doJump(marks, key, smoothscroll) { | ||||
|     if (!marks[key.key]) { | ||||
|       consoleFrames.postError(window.document, 'Mark is not set'); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     let { x, y } = marks[key.key]; | ||||
|     scrolls.scrollTo(x, y, smoothscroll); | ||||
|   } | ||||
| 
 | ||||
|   doSetGlobal(key) { | ||||
|     let { x, y } = scrolls.getScroll(); | ||||
|     this.store.dispatch(markActions.setGlobal(key.key, x, y)); | ||||
|   } | ||||
| 
 | ||||
|   doJumpGlobal(key) { | ||||
|     this.store.dispatch(markActions.jumpGlobal(key.key)); | ||||
|   } | ||||
| } | ||||
|  | @ -3,6 +3,7 @@ import FollowController from './follow-controller'; | |||
| import FindComponent from './find'; | ||||
| import * as consoleFrames from '../../console-frames'; | ||||
| import messages from 'shared/messages'; | ||||
| import * as scrolls from 'content/scrolls'; | ||||
| 
 | ||||
| export default class TopContent { | ||||
| 
 | ||||
|  | @ -33,6 +34,8 @@ export default class TopContent { | |||
|         type: messages.ADDON_ENABLED_RESPONSE, | ||||
|         enabled: addonState.enabled, | ||||
|       }); | ||||
|     case messages.TAB_SCROLL_TO: | ||||
|       return scrolls.scrollTo(message.x, message.y, false); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,8 @@ import find from './find'; | |||
| import setting from './setting'; | ||||
| import input from './input'; | ||||
| import followController from './follow-controller'; | ||||
| import mark from './mark'; | ||||
| 
 | ||||
| export default combineReducers({ | ||||
|   addon, find, setting, input, followController, | ||||
|   addon, find, setting, input, followController, mark, | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										25
									
								
								src/content/reducers/mark.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/content/reducers/mark.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| import actions from 'content/actions'; | ||||
| 
 | ||||
| const defaultState = { | ||||
|   setMode: false, | ||||
|   jumpMode: false, | ||||
|   marks: {}, | ||||
| }; | ||||
| 
 | ||||
| export default function reducer(state = defaultState, action = {}) { | ||||
|   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 }; | ||||
|   case actions.MARK_SET_LOCAL: { | ||||
|     let marks = { ...state.marks }; | ||||
|     marks[action.key] = { x: action.x, y: action.y }; | ||||
|     return { ...state, setMode: false, marks }; | ||||
|   } | ||||
|   default: | ||||
|     return state; | ||||
|   } | ||||
| } | ||||
|  | @ -130,6 +130,11 @@ const scroller = (element, smooth, repeat) => { | |||
|   return new RoughtScroller(element); | ||||
| }; | ||||
| 
 | ||||
| const getScroll = () => { | ||||
|   let target = scrollTarget(); | ||||
|   return { x: target.scrollLeft, y: target.scrollTop }; | ||||
| }; | ||||
| 
 | ||||
| const scrollVertically = (count, smooth, repeat) => { | ||||
|   let target = scrollTarget(); | ||||
|   let x = target.scrollLeft; | ||||
|  | @ -158,35 +163,42 @@ const scrollPages = (count, smooth, repeat) => { | |||
|   scroller(target, smooth, repeat).scroll(x, y); | ||||
| }; | ||||
| 
 | ||||
| const scrollTop = (smooth, repeat) => { | ||||
| const scrollTo = (x, y, smooth) => { | ||||
|   let target = scrollTarget(); | ||||
|   scroller(target, smooth, false).scroll(x, y); | ||||
| }; | ||||
| 
 | ||||
| const scrollToTop = (smooth) => { | ||||
|   let target = scrollTarget(); | ||||
|   let x = target.scrollLeft; | ||||
|   let y = 0; | ||||
|   scroller(target, smooth, repeat).scroll(x, y); | ||||
|   scroller(target, smooth, false).scroll(x, y); | ||||
| }; | ||||
| 
 | ||||
| const scrollBottom = (smooth, repeat) => { | ||||
| const scrollToBottom = (smooth) => { | ||||
|   let target = scrollTarget(); | ||||
|   let x = target.scrollLeft; | ||||
|   let y = target.scrollHeight; | ||||
|   scroller(target, smooth, repeat).scroll(x, y); | ||||
|   scroller(target, smooth, false).scroll(x, y); | ||||
| }; | ||||
| 
 | ||||
| const scrollHome = (smooth, repeat) => { | ||||
| const scrollToHome = (smooth) => { | ||||
|   let target = scrollTarget(); | ||||
|   let x = 0; | ||||
|   let y = target.scrollTop; | ||||
|   scroller(target, smooth, repeat).scroll(x, y); | ||||
|   scroller(target, smooth, false).scroll(x, y); | ||||
| }; | ||||
| 
 | ||||
| const scrollEnd = (smooth, repeat) => { | ||||
| const scrollToEnd = (smooth) => { | ||||
|   let target = scrollTarget(); | ||||
|   let x = target.scrollWidth; | ||||
|   let y = target.scrollTop; | ||||
|   scroller(target, smooth, repeat).scroll(x, y); | ||||
|   scroller(target, smooth, false).scroll(x, y); | ||||
| }; | ||||
| 
 | ||||
| export { | ||||
|   getScroll, | ||||
|   scrollVertically, scrollHorizonally, scrollPages, | ||||
|   scrollTop, scrollBottom, scrollHome, scrollEnd | ||||
|   scrollTo, | ||||
|   scrollToTop, scrollToBottom, scrollToHome, scrollToEnd | ||||
| }; | ||||
|  |  | |||
|  | @ -16,6 +16,9 @@ const KeyMapFields = [ | |||
|     ['scroll.pages?{"count":0.5}', 'Scroll down by half of screen'], | ||||
|     ['scroll.pages?{"count":-1}', 'Scroll up by a screen'], | ||||
|     ['scroll.pages?{"count":1}', 'Scroll down by a screen'], | ||||
|   ], [ | ||||
|     ['mark.set.prefix', 'Set mark at current position'], | ||||
|     ['mark.jump.prefix', 'Jump to the mark'], | ||||
|   ], [ | ||||
|     ['tabs.close', 'Close a tab'], | ||||
|     ['tabs.reopen', 'Reopen closed tab'], | ||||
|  |  | |||
|  | @ -43,6 +43,11 @@ export default { | |||
|   FOLLOW_ACTIVATE: 'follow.activate', | ||||
|   FOLLOW_KEY_PRESS: 'follow.key.press', | ||||
| 
 | ||||
|   MARK_SET_GLOBAL: 'mark.set.global', | ||||
|   MARK_JUMP_GLOBAL: 'mark.jump.global', | ||||
| 
 | ||||
|   TAB_SCROLL_TO: 'tab.scroll.to', | ||||
| 
 | ||||
|   FIND_NEXT: 'find.next', | ||||
|   FIND_PREV: 'find.prev', | ||||
|   FIND_GET_KEYWORD: 'find.get.keyword', | ||||
|  |  | |||
|  | @ -69,4 +69,8 @@ export default { | |||
|   FIND_START: 'find.start', | ||||
|   FIND_NEXT: 'find.next', | ||||
|   FIND_PREV: 'find.prev', | ||||
| 
 | ||||
|   // Mark
 | ||||
|   MARK_SET_PREFIX: 'mark.set.prefix', | ||||
|   MARK_JUMP_PREFIX: 'mark.jump.prefix', | ||||
| }; | ||||
|  |  | |||
|  | @ -42,6 +42,8 @@ export default { | |||
|     "zz": { "type": "zoom.neutral" }, | ||||
|     "f": { "type": "follow.start", "newTab": false }, | ||||
|     "F": { "type": "follow.start", "newTab": true, "background": false }, | ||||
|     "m": { "type": "mark.set.prefix" }, | ||||
|     "'": { "type": "mark.jump.prefix" }, | ||||
|     "H": { "type": "navigate.history.prev" }, | ||||
|     "L": { "type": "navigate.history.next" }, | ||||
|     "[[": { "type": "navigate.link.prev" }, | ||||
|  |  | |||
							
								
								
									
										11
									
								
								test/background/domains/global-mark.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/background/domains/global-mark.test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| import GlobalMark from 'background/domains/global-mark'; | ||||
| 
 | ||||
| describe('background/domains/global-mark', () => { | ||||
|   describe('constructor and getter', () => { | ||||
|     let mark = new GlobalMark(1, 'http://example.com', 10, 30); | ||||
|     expect(mark.tabId).to.equal(1); | ||||
|     expect(mark.url).to.equal('http://example.com'); | ||||
|     expect(mark.x).to.equal(10); | ||||
|     expect(mark.y).to.equal(30); | ||||
|   }); | ||||
| }); | ||||
|  | @ -1,8 +1,6 @@ | |||
| import MemoryStorage from 'background/infrastructures/memory-storage'; | ||||
| 
 | ||||
| describe("background/infrastructures/memory-storage", () => { | ||||
|   let versionRepository; | ||||
| 
 | ||||
|   it('stores values', () => { | ||||
|     let cache = new MemoryStorage(); | ||||
|     cache.set('number', 123); | ||||
|  |  | |||
							
								
								
									
										26
									
								
								test/background/repositories/mark.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								test/background/repositories/mark.test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| import MarkRepository from 'background/repositories/mark'; | ||||
| import GlobalMark from 'background/domains/global-mark'; | ||||
| 
 | ||||
| describe('background/repositories/mark', () => { | ||||
|   let repository; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     repository = new MarkRepository; | ||||
|   }); | ||||
| 
 | ||||
|   it('get and set', async() => { | ||||
|     let mark = new GlobalMark(1, 'http://example.com', 10, 30); | ||||
| 
 | ||||
|     repository.setMark('A', mark); | ||||
| 
 | ||||
|     let got = await repository.getMark('A'); | ||||
|     expect(got).to.be.a('object'); | ||||
|     expect(got.tabId).to.equal(1); | ||||
|     expect(got.url).to.equal('http://example.com'); | ||||
|     expect(got.x).to.equal(10); | ||||
|     expect(got.y).to.equal(30); | ||||
| 
 | ||||
|     got = await repository.getMark('B'); | ||||
|     expect(got).to.be.undefined; | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										35
									
								
								test/content/actions/mark.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								test/content/actions/mark.test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| import 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); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('setLocal', () => { | ||||
|     it('create setLocal action', () => { | ||||
|       let action = markActions.setLocal('a', 20, 30); | ||||
|       expect(action.type).to.equal(actions.MARK_SET_LOCAL); | ||||
|       expect(action.key).to.equal('a'); | ||||
|       expect(action.x).to.equal(20); | ||||
|       expect(action.y).to.equal(30); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										41
									
								
								test/content/reducers/mark.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								test/content/reducers/mark.test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| import 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; | ||||
|     expect(state.marks).to.be.empty; | ||||
|   }); | ||||
| 
 | ||||
|   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; | ||||
|   }); | ||||
| 
 | ||||
|   it('stores local mark', () => { | ||||
|     let action = { type: actions.MARK_SET_LOCAL, key: 'a', x: 20, y: 30}; | ||||
|     let state = reducer({ setMode: true }, action); | ||||
|     expect(state.setMode).to.be.false; | ||||
|     expect(state.marks['a']).to.be.an('object') | ||||
|     expect(state.marks['a'].x).to.equal(20) | ||||
|     expect(state.marks['a'].y).to.equal(30) | ||||
|   }); | ||||
| }); | ||||
		Reference in a new issue