diff options
Diffstat (limited to 'src/background')
21 files changed, 582 insertions, 129 deletions
diff --git a/src/background/actions/command.js b/src/background/actions/command.js index 4c52bca..f1ee5b5 100644 --- a/src/background/actions/command.js +++ b/src/background/actions/command.js @@ -1,5 +1,7 @@ +import messages from 'shared/messages'; import actions from '../actions'; -import * as tabs from 'background/tabs'; +import * as tabs from '../shared/tabs'; +import * as bookmarks from '../shared/bookmarks'; import * as parsers from 'shared/commands/parsers'; import * as properties from 'shared/settings/properties'; @@ -17,6 +19,14 @@ const tabopenCommand = (url) => { return browser.tabs.create({ url: url }); }; +const tabcloseCommand = () => { + return browser.tabs.query({ + active: true, currentWindow: true + }).then((tabList) => { + return browser.tabs.remove(tabList.map(tab => tab.id)); + }); +}; + const winopenCommand = (url) => { return browser.windows.create({ url }); }; @@ -39,6 +49,14 @@ const bufferCommand = (keywords) => { }); }; +const addBookmarkCommand = (tab, args) => { + if (!args[0]) { + return Promise.resolve(); + } + + return bookmarks.create(args.join(' '), tab.url); +}; + const setCommand = (args) => { if (!args[0]) { return Promise.resolve(); @@ -52,7 +70,8 @@ const setCommand = (args) => { }; }; -const exec = (line, settings) => { +// eslint-disable-next-line complexity +const exec = (tab, line, settings) => { let [name, args] = parsers.parseCommandLine(line); switch (name) { @@ -68,8 +87,36 @@ const exec = (line, settings) => { case 'b': case 'buffer': return bufferCommand(args); + case 'bd': + case 'bdel': + case 'bdelete': + return tabs.closeTabByKeywords(args.join(' ')); + case 'bd!': + case 'bdel!': + case 'bdelete!': + return tabs.closeTabByKeywordsForce(args.join(' ')); + case 'bdeletes': + return tabs.closeTabsByKeywords(args.join(' ')); + case 'bdeletes!': + return tabs.closeTabsByKeywordsForce(args.join(' ')); + case 'addbookmark': + return addBookmarkCommand(tab, args).then((item) => { + if (!item) { + return browser.tabs.sendMessage(tab.id, { + type: messages.CONSOLE_SHOW_ERROR, + text: 'Could not create a bookmark', + }); + } + return browser.tabs.sendMessage(tab.id, { + type: messages.CONSOLE_SHOW_INFO, + text: 'Saved current page: ' + item.url, + }); + }); case 'set': return setCommand(args); + case 'q': + case 'quit': + return tabcloseCommand(); case '': return Promise.resolve(); } diff --git a/src/background/actions/find.js b/src/background/actions/find.js new file mode 100644 index 0000000..8da5572 --- /dev/null +++ b/src/background/actions/find.js @@ -0,0 +1,10 @@ +import actions from './index'; + +const setKeyword = (keyword) => { + return { + type: actions.FIND_SET_KEYWORD, + keyword, + }; +}; + +export { setKeyword }; diff --git a/src/background/actions/index.js b/src/background/actions/index.js index efe4074..3833389 100644 --- a/src/background/actions/index.js +++ b/src/background/actions/index.js @@ -2,4 +2,10 @@ export default { // Settings SETTING_SET_SETTINGS: 'setting.set.settings', SETTING_SET_PROPERTY: 'setting.set.property', + + // Find + FIND_SET_KEYWORD: 'find.set.keyword', + + // Tab + TAB_SELECTED: 'tab.selected', }; diff --git a/src/background/actions/operation.js b/src/background/actions/operation.js deleted file mode 100644 index 1188ea2..0000000 --- a/src/background/actions/operation.js +++ /dev/null @@ -1,82 +0,0 @@ -import operations from 'shared/operations'; -import messages from 'shared/messages'; -import * as tabs from 'background/tabs'; -import * as zooms from 'background/zooms'; - -const sendConsoleShowCommand = (tab, command) => { - return browser.tabs.sendMessage(tab.id, { - type: messages.CONSOLE_SHOW_COMMAND, - command, - }); -}; - -// This switch statement is only gonna get longer as more -// features are added, so disable complexity check -/* eslint-disable complexity */ -const exec = (operation, tab) => { - switch (operation.type) { - case operations.TAB_CLOSE: - return tabs.closeTab(tab.id); - case operations.TAB_CLOSE_FORCE: - return tabs.closeTabForce(tab.id); - case operations.TAB_REOPEN: - return tabs.reopenTab(); - case operations.TAB_PREV: - return tabs.selectPrevTab(tab.index, operation.count); - case operations.TAB_NEXT: - return tabs.selectNextTab(tab.index, operation.count); - case operations.TAB_FIRST: - return tabs.selectFirstTab(); - case operations.TAB_LAST: - return tabs.selectLastTab(); - case operations.TAB_PREV_SEL: - return tabs.selectPrevSelTab(); - case operations.TAB_RELOAD: - return tabs.reload(tab, operation.cache); - case operations.TAB_PIN: - return tabs.updateTabPinned(tab, true); - case operations.TAB_UNPIN: - return tabs.updateTabPinned(tab, false); - case operations.TAB_TOGGLE_PINNED: - return tabs.toggleTabPinned(tab); - case operations.TAB_DUPLICATE: - return tabs.duplicate(tab.id); - case operations.ZOOM_IN: - return zooms.zoomIn(); - case operations.ZOOM_OUT: - return zooms.zoomOut(); - case operations.ZOOM_NEUTRAL: - return zooms.neutral(); - case operations.COMMAND_SHOW: - return sendConsoleShowCommand(tab, ''); - case operations.COMMAND_SHOW_OPEN: - if (operation.alter) { - // alter url - return sendConsoleShowCommand(tab, 'open ' + tab.url); - } - return sendConsoleShowCommand(tab, 'open '); - case operations.COMMAND_SHOW_TABOPEN: - if (operation.alter) { - // alter url - return sendConsoleShowCommand(tab, 'tabopen ' + tab.url); - } - return sendConsoleShowCommand(tab, 'tabopen '); - case operations.COMMAND_SHOW_WINOPEN: - if (operation.alter) { - // alter url - return sendConsoleShowCommand(tab, 'winopen ' + tab.url); - } - return sendConsoleShowCommand(tab, 'winopen '); - case operations.COMMAND_SHOW_BUFFER: - return sendConsoleShowCommand(tab, 'buffer '); - case operations.FIND_START: - return browser.tabs.sendMessage(tab.id, { - type: messages.CONSOLE_SHOW_FIND - }); - default: - return Promise.resolve(); - } -}; -/* eslint-enable complexity */ - -export { exec }; diff --git a/src/background/actions/tab.js b/src/background/actions/tab.js index e512b6f..0d439fd 100644 --- a/src/background/actions/tab.js +++ b/src/background/actions/tab.js @@ -1,9 +1,30 @@ -const openNewTab = (url) => { - return browser.tabs.create({ url: url }); +import actions from './index'; + +const openNewTab = (url, openerTabId, background = false, adjacent = false) => { + if (adjacent) { + return browser.tabs.query({ + active: true, currentWindow: true + }).then((tabs) => { + return browser.tabs.create({ + url, + openerTabId, + active: !background, + index: tabs[0].index + 1 + }); + }); + } + return browser.tabs.create({ url, active: !background }); }; const openToTab = (url, tab) => { return browser.tabs.update(tab.id, { url: url }); }; -export { openToTab, openNewTab }; +const selected = (tabId) => { + return { + type: actions.TAB_SELECTED, + tabId, + }; +}; + +export { openNewTab, openToTab, selected }; diff --git a/src/background/components/background.js b/src/background/components/background.js index 9578e78..29124a6 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -1,9 +1,9 @@ import messages from 'shared/messages'; -import * as operationActions from 'background/actions/operation'; import * as commandActions from 'background/actions/command'; import * as settingActions from 'background/actions/setting'; +import * as findActions from 'background/actions/find'; import * as tabActions from 'background/actions/tab'; -import * as commands from 'shared/commands'; +import * as completions from '../shared/completions'; export default class BackgroundComponent { constructor(store) { @@ -23,31 +23,36 @@ export default class BackgroundComponent { onMessage(message, sender) { let settings = this.store.getState().setting; + let find = this.store.getState().find; + switch (message.type) { - case messages.BACKGROUND_OPERATION: - return this.store.dispatch( - operationActions.exec(message.operation, sender.tab), - sender); case messages.OPEN_URL: if (message.newTab) { - return this.store.dispatch( - tabActions.openNewTab(message.url), sender); + let action = tabActions.openNewTab( + message.url, sender.tab.id, message.background, + settings.value.properties.adjacenttab); + return this.store.dispatch(action, sender); } return this.store.dispatch( tabActions.openToTab(message.url, sender.tab), sender); case messages.CONSOLE_ENTER_COMMAND: this.store.dispatch( - commandActions.exec(message.text, settings.value), + commandActions.exec(sender.tab, message.text, settings.value), sender ); return this.broadcastSettingsChanged(); case messages.SETTINGS_QUERY: return Promise.resolve(this.store.getState().setting.value); case messages.CONSOLE_QUERY_COMPLETIONS: - return commands.complete(message.text, settings.value); + return completions.complete(message.text, settings.value); case messages.SETTINGS_RELOAD: this.store.dispatch(settingActions.load()); return this.broadcastSettingsChanged(); + case messages.FIND_GET_KEYWORD: + return Promise.resolve(find.keyword); + case messages.FIND_SET_KEYWORD: + this.store.dispatch(findActions.setKeyword(message.keyword)); + return Promise.resolve({}); } } diff --git a/src/background/components/indicator.js b/src/background/components/indicator.js new file mode 100644 index 0000000..cceb119 --- /dev/null +++ b/src/background/components/indicator.js @@ -0,0 +1,45 @@ +import * as indicators from '../shared/indicators'; +import messages from 'shared/messages'; + +export default class IndicatorComponent { + constructor(store) { + this.store = store; + + messages.onMessage(this.onMessage.bind(this)); + + browser.browserAction.onClicked.addListener(this.onClicked); + browser.tabs.onActivated.addListener((info) => { + return browser.tabs.query({ currentWindow: true }).then(() => { + return this.onTabActivated(info); + }); + }); + } + + onTabActivated(info) { + return browser.tabs.sendMessage(info.tabId, { + type: messages.ADDON_ENABLED_QUERY, + }).then((resp) => { + return this.updateIndicator(resp.enabled); + }); + } + + onClicked(tab) { + browser.tabs.sendMessage(tab.id, { + type: messages.ADDON_TOGGLE_ENABLED, + }); + } + + onMessage(message) { + switch (message.type) { + case messages.ADDON_ENABLED_RESPONSE: + return this.updateIndicator(message.enabled); + } + } + + updateIndicator(enabled) { + if (enabled) { + return indicators.enable(); + } + return indicators.disable(); + } +} diff --git a/src/background/components/operation.js b/src/background/components/operation.js new file mode 100644 index 0000000..58edb8c --- /dev/null +++ b/src/background/components/operation.js @@ -0,0 +1,123 @@ +import messages from 'shared/messages'; +import operations from 'shared/operations'; +import * as tabs from '../shared//tabs'; +import * as zooms from '../shared/zooms'; + +export default class BackgroundComponent { + constructor(store) { + this.store = store; + + browser.runtime.onMessage.addListener((message, sender) => { + try { + return this.onMessage(message, sender); + } catch (e) { + return browser.tabs.sendMessage(sender.tab.id, { + type: messages.CONSOLE_SHOW_ERROR, + text: e.message, + }); + } + }); + } + + onMessage(message, sender) { + switch (message.type) { + case messages.BACKGROUND_OPERATION: + return this.store.dispatch( + this.exec(message.operation, sender.tab), + sender); + } + } + + // eslint-disable-next-line complexity + exec(operation, tab) { + let tabState = this.store.getState().tab; + + switch (operation.type) { + case operations.TAB_CLOSE: + return tabs.closeTab(tab.id); + case operations.TAB_CLOSE_FORCE: + return tabs.closeTabForce(tab.id); + case operations.TAB_REOPEN: + return tabs.reopenTab(); + case operations.TAB_PREV: + return tabs.selectPrevTab(tab.index, operation.count); + case operations.TAB_NEXT: + return tabs.selectNextTab(tab.index, operation.count); + case operations.TAB_FIRST: + return tabs.selectFirstTab(); + case operations.TAB_LAST: + return tabs.selectLastTab(); + case operations.TAB_PREV_SEL: + if (tabState.previousSelected > 0) { + return tabs.selectTab(tabState.previousSelected); + } + break; + case operations.TAB_RELOAD: + return tabs.reload(tab, operation.cache); + case operations.TAB_PIN: + return tabs.updateTabPinned(tab, true); + case operations.TAB_UNPIN: + return tabs.updateTabPinned(tab, false); + case operations.TAB_TOGGLE_PINNED: + return tabs.toggleTabPinned(tab); + case operations.TAB_DUPLICATE: + return tabs.duplicate(tab.id); + case operations.ZOOM_IN: + return zooms.zoomIn(); + case operations.ZOOM_OUT: + return zooms.zoomOut(); + case operations.ZOOM_NEUTRAL: + return zooms.neutral(); + case operations.COMMAND_SHOW: + return this.sendConsoleShowCommand(tab, ''); + case operations.COMMAND_SHOW_OPEN: + if (operation.alter) { + // alter url + return this.sendConsoleShowCommand(tab, 'open ' + tab.url); + } + return this.sendConsoleShowCommand(tab, 'open '); + case operations.COMMAND_SHOW_TABOPEN: + if (operation.alter) { + // alter url + return this.sendConsoleShowCommand(tab, 'tabopen ' + tab.url); + } + return this.sendConsoleShowCommand(tab, 'tabopen '); + case operations.COMMAND_SHOW_WINOPEN: + if (operation.alter) { + // alter url + return this.sendConsoleShowCommand(tab, 'winopen ' + tab.url); + } + return this.sendConsoleShowCommand(tab, 'winopen '); + case operations.COMMAND_SHOW_BUFFER: + return this.sendConsoleShowCommand(tab, 'buffer '); + case operations.COMMAND_SHOW_ADDBOOKMARK: + if (operation.alter) { + return this.sendConsoleShowCommand(tab, 'addbookmark ' + tab.title); + } + return this.sendConsoleShowCommand(tab, 'addbookmark '); + case operations.FIND_START: + return browser.tabs.sendMessage(tab.id, { + type: messages.CONSOLE_SHOW_FIND + }); + case operations.CANCEL: + return browser.tabs.sendMessage(tab.id, { + type: messages.CONSOLE_HIDE, + }); + case operations.PAGE_SOURCE: + return browser.tabs.create({ + url: 'view-source:' + tab.url, + index: tab.index + 1, + openerTabId: tab.id, + }); + default: + return Promise.resolve(); + } + } + + sendConsoleShowCommand(tab, command) { + return browser.tabs.sendMessage(tab.id, { + type: messages.CONSOLE_SHOW_COMMAND, + command, + }); + } +} diff --git a/src/background/components/tab.js b/src/background/components/tab.js new file mode 100644 index 0000000..b273546 --- /dev/null +++ b/src/background/components/tab.js @@ -0,0 +1,17 @@ +import * as tabActions from '../actions/tab'; + +export default class TabComponent { + constructor(store) { + this.store = store; + + browser.tabs.onActivated.addListener((info) => { + return browser.tabs.query({ currentWindow: true }).then(() => { + return this.onTabActivated(info); + }); + }); + } + + onTabActivated(info) { + return this.store.dispatch(tabActions.selected(info.tabId)); + } +} diff --git a/src/background/index.js b/src/background/index.js index 3ef712f..3f1013c 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,8 +1,12 @@ import * as settingActions from 'background/actions/setting'; import messages from 'shared/messages'; import BackgroundComponent from 'background/components/background'; +import OperationComponent from 'background/components/operation'; +import TabComponent from 'background/components/tab'; +import IndicatorComponent from 'background/components/indicator'; import reducers from 'background/reducers'; import { createStore } from 'shared/store'; +import * as versions from 'shared/versions'; const store = createStore(reducers, (e, sender) => { console.error('Vim-Vixen:', e); @@ -13,7 +17,21 @@ const store = createStore(reducers, (e, sender) => { }); } }); -// eslint-disable-next-line no-unused-vars + +/* eslint-disable no-unused-vars */ const backgroundComponent = new BackgroundComponent(store); +const operationComponent = new OperationComponent(store); +const tabComponent = new TabComponent(store); +const indicatorComponent = new IndicatorComponent(store); +/* eslint-enable no-unused-vars */ store.dispatch(settingActions.load()); + +versions.checkUpdated().then((updated) => { + if (!updated) { + return; + } + return versions.notify(); +}).then(() => { + return versions.commit(); +}); diff --git a/src/background/reducers/find.js b/src/background/reducers/find.js new file mode 100644 index 0000000..4ded801 --- /dev/null +++ b/src/background/reducers/find.js @@ -0,0 +1,16 @@ +import actions from 'content/actions'; + +const defaultState = { + keyword: null, +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.FIND_SET_KEYWORD: + return Object.assign({}, state, { + keyword: action.keyword, + }); + default: + return state; + } +} diff --git a/src/background/reducers/index.js b/src/background/reducers/index.js index dab0c62..5729f0a 100644 --- a/src/background/reducers/index.js +++ b/src/background/reducers/index.js @@ -1,12 +1,18 @@ import settingReducer from './setting'; +import findReducer from './find'; +import tabReducer from './tab'; // Make setting reducer instead of re-use const defaultState = { setting: settingReducer(undefined, {}), + find: findReducer(undefined, {}), + tab: tabReducer(undefined, {}), }; export default function reducer(state = defaultState, action = {}) { return Object.assign({}, state, { setting: settingReducer(state.setting, action), + find: findReducer(state.find, action), + tab: tabReducer(state.tab, action), }); } diff --git a/src/background/reducers/tab.js b/src/background/reducers/tab.js new file mode 100644 index 0000000..e0cdf32 --- /dev/null +++ b/src/background/reducers/tab.js @@ -0,0 +1,19 @@ +import actions from 'background/actions'; + +const defaultState = { + previousSelected: -1, + currentSelected: -1, +}; + +export default function reducer(state = defaultState, action = {}) { + switch (action.type) { + case actions.TAB_SELECTED: + return { + previousSelected: state.currentSelected, + currentSelected: action.tabId, + }; + default: + return state; + } +} + diff --git a/src/background/shared/bookmarks.js b/src/background/shared/bookmarks.js new file mode 100644 index 0000000..5e7927b --- /dev/null +++ b/src/background/shared/bookmarks.js @@ -0,0 +1,9 @@ +const create = (title, url) => { + return browser.bookmarks.create({ + type: 'bookmark', + title, + url, + }); +}; + +export { create }; diff --git a/src/background/shared/completions/bookmarks.js b/src/background/shared/completions/bookmarks.js new file mode 100644 index 0000000..1adb350 --- /dev/null +++ b/src/background/shared/completions/bookmarks.js @@ -0,0 +1,15 @@ +const getCompletions = (keywords) => { + return browser.bookmarks.search({ query: keywords }).then((items) => { + return items.filter((item) => { + let url = undefined; + try { + url = new URL(item.url); + } catch (e) { + return false; + } + return item.type === 'bookmark' && url.protocol !== 'place:'; + }).slice(0, 10); + }); +}; + +export { getCompletions }; diff --git a/src/background/histories.js b/src/background/shared/completions/histories.js index a7d3d47..a7d3d47 100644 --- a/src/background/histories.js +++ b/src/background/shared/completions/histories.js diff --git a/src/background/shared/completions/index.js b/src/background/shared/completions/index.js new file mode 100644 index 0000000..728cee7 --- /dev/null +++ b/src/background/shared/completions/index.js @@ -0,0 +1,129 @@ +import * as tabs from './tabs'; +import * as histories from './histories'; +import * as bookmarks from './bookmarks'; + +const getSearchCompletions = (command, keywords, searchConfig) => { + let engineNames = Object.keys(searchConfig.engines); + let engineItems = engineNames.filter(name => name.startsWith(keywords)) + .map(name => ({ + caption: name, + content: command + ' ' + name + })); + return Promise.resolve(engineItems); +}; + +const getHistoryCompletions = (command, keywords) => { + return histories.getCompletions(keywords).then((pages) => { + return pages.map((page) => { + return { + caption: page.title, + content: command + ' ' + page.url, + url: page.url + }; + }); + }); +}; + +const getBookmarksCompletions = (command, keywords) => { + return bookmarks.getCompletions(keywords).then((items) => { + return items.map((item) => { + return { + caption: item.title, + content: command + ' ' + item.url, + url: item.url, + }; + }); + }); +}; + +const getOpenCompletions = (command, keywords, searchConfig) => { + return Promise.all([ + getSearchCompletions(command, keywords, searchConfig), + getHistoryCompletions(command, keywords), + getBookmarksCompletions(command, keywords), + ]).then(([engineItems, historyItems, bookmarkItems]) => { + let completions = []; + if (engineItems.length > 0) { + completions.push({ + name: 'Search Engines', + items: engineItems + }); + } + if (historyItems.length > 0) { + completions.push({ + name: 'History', + items: historyItems + }); + } + if (bookmarkItems.length > 0) { + completions.push({ + name: 'Bookmarks', + items: bookmarkItems + }); + } + return completions; + }); +}; + +const getBufferCompletions = (command, keywords, excludePinned) => { + return tabs.getCompletions(keywords, excludePinned).then((got) => { + let items = got.map((tab) => { + return { + caption: tab.title, + content: command + ' ' + tab.title, + url: tab.url, + icon: tab.favIconUrl + }; + }); + return [ + { + name: 'Buffers', + items: items + } + ]; + }); +}; + +const getCompletions = (line, settings) => { + let typedWords = line.trim().split(/ +/); + let typing = ''; + if (!line.endsWith(' ')) { + typing = typedWords.pop(); + } + + if (typedWords.length === 0) { + return Promise.resolve([]); + } + let name = typedWords.shift(); + let keywords = typedWords.concat(typing).join(' '); + + switch (name) { + case 'o': + case 'open': + case 't': + case 'tabopen': + case 'w': + case 'winopen': + return getOpenCompletions(name, keywords, settings.search); + case 'b': + case 'buffer': + return getBufferCompletions(name, keywords, false); + case 'bd!': + case 'bdel!': + case 'bdelete!': + case 'bdeletes!': + return getBufferCompletions(name, keywords, false); + case 'bd': + case 'bdel': + case 'bdelete': + case 'bdeletes': + return getBufferCompletions(name, keywords, true); + } + return Promise.resolve([]); +}; + +const complete = (line, settings) => { + return getCompletions(line, settings); +}; + +export { complete }; diff --git a/src/background/shared/completions/tabs.js b/src/background/shared/completions/tabs.js new file mode 100644 index 0000000..bdb2741 --- /dev/null +++ b/src/background/shared/completions/tabs.js @@ -0,0 +1,8 @@ +import * as tabs from '../tabs'; + +const getCompletions = (keyword, excludePinned) => { + return tabs.queryByKeyword(keyword, excludePinned); +}; + + +export { getCompletions }; diff --git a/src/background/shared/indicators.js b/src/background/shared/indicators.js new file mode 100644 index 0000000..74002c4 --- /dev/null +++ b/src/background/shared/indicators.js @@ -0,0 +1,13 @@ +const enable = () => { + return browser.browserAction.setIcon({ + path: 'resources/enabled_32x32.png', + }); +}; + +const disable = () => { + return browser.browserAction.setIcon({ + path: 'resources/disabled_32x32.png', + }); +}; + +export { enable, disable }; diff --git a/src/background/tabs.js b/src/background/shared/tabs.js index e939870..62e26ac 100644 --- a/src/background/tabs.js +++ b/src/background/shared/tabs.js @@ -1,12 +1,4 @@ -let prevSelTab = 1; -let currSelTab = 1; - -browser.tabs.onActivated.addListener((activeInfo) => { - return browser.tabs.query({ currentWindow: true }).then(() => { - prevSelTab = currSelTab; - currSelTab = activeInfo.tabId; - }); -}); +import * as tabCompletions from './completions/tabs'; const closeTab = (id) => { return browser.tabs.get(id).then((tab) => { @@ -20,6 +12,52 @@ const closeTabForce = (id) => { return browser.tabs.remove(id); }; +const queryByKeyword = (keyword, excludePinned = false) => { + return browser.tabs.query({ currentWindow: true }).then((tabs) => { + return tabs.filter((t) => { + return t.url.toLowerCase().includes(keyword.toLowerCase()) || + t.title && t.title.toLowerCase().includes(keyword.toLowerCase()); + }).filter((t) => { + return !(excludePinned && t.pinned); + }); + }); +}; + +const closeTabByKeywords = (keyword) => { + return queryByKeyword(keyword, false).then((tabs) => { + if (tabs.length === 0) { + throw new Error('No matching buffer for ' + keyword); + } else if (tabs.length > 1) { + throw new Error('More than one match for ' + keyword); + } + browser.tabs.remove(tabs[0].id); + }); +}; + +const closeTabByKeywordsForce = (keyword) => { + return queryByKeyword(keyword, true).then((tabs) => { + if (tabs.length === 0) { + throw new Error('No matching buffer for ' + keyword); + } else if (tabs.length > 1) { + throw new Error('More than one match for ' + keyword); + } + browser.tabs.remove(tabs[0].id); + }); +}; + +const closeTabsByKeywords = (keyword) => { + tabCompletions.getCompletions(keyword).then((tabs) => { + let tabs2 = tabs.filter(tab => !tab.pinned); + browser.tabs.remove(tabs2.map(tab => tab.id)); + }); +}; + +const closeTabsByKeywordsForce = (keyword) => { + tabCompletions.getCompletions(keyword).then((tabs) => { + browser.tabs.remove(tabs.map(tab => tab.id)); + }); +}; + const reopenTab = () => { return browser.sessions.getRecentlyClosed({ maxResults: 1 @@ -49,29 +87,16 @@ const selectAt = (index) => { }; const selectByKeyword = (current, keyword) => { - return browser.tabs.query({ currentWindow: true }).then((tabs) => { - let matched = tabs.filter((t) => { - return t.url.includes(keyword) || t.title.includes(keyword); - }); - - if (matched.length === 0) { + return queryByKeyword(keyword).then((tabs) => { + if (tabs.length === 0) { throw new RangeError('No matching buffer for ' + keyword); } - for (let tab of matched) { + for (let tab of tabs) { if (tab.index > current.index) { return browser.tabs.update(tab.id, { active: true }); } } - return browser.tabs.update(matched[0].id, { active: true }); - }); -}; - -const getCompletions = (keyword) => { - return browser.tabs.query({ currentWindow: true }).then((tabs) => { - let matched = tabs.filter((t) => { - return t.url.includes(keyword) || t.title && t.title.includes(keyword); - }); - return matched; + return browser.tabs.update(tabs[0].id, { active: true }); }); }; @@ -111,8 +136,8 @@ const selectLastTab = () => { }); }; -const selectPrevSelTab = () => { - return browser.tabs.update(prevSelTab, { active: true }); +const selectTab = (id) => { + return browser.tabs.update(id, { active: true }); }; const reload = (current, cache) => { @@ -138,8 +163,11 @@ const duplicate = (id) => { }; export { - closeTab, closeTabForce, reopenTab, selectAt, selectByKeyword, - getCompletions, selectPrevTab, selectNextTab, selectFirstTab, - selectLastTab, selectPrevSelTab, reload, updateTabPinned, + closeTab, closeTabForce, + queryByKeyword, closeTabByKeywords, closeTabByKeywordsForce, + closeTabsByKeywords, closeTabsByKeywordsForce, + reopenTab, selectAt, selectByKeyword, + selectPrevTab, selectNextTab, selectFirstTab, + selectLastTab, selectTab, reload, updateTabPinned, toggleTabPinned, duplicate }; diff --git a/src/background/zooms.js b/src/background/shared/zooms.js index e3e2aa6..e3e2aa6 100644 --- a/src/background/zooms.js +++ b/src/background/shared/zooms.js |