diff --git a/auto-style.js b/auto-style.js index 3b6da76..c65a5bc 100644 --- a/auto-style.js +++ b/auto-style.js @@ -3,6 +3,7 @@ function setStyle(theme) { let documentBody = document.body; let inputFieldStyle = document.getElementById("find-input").style; let generalBodyStyle = document.body.style; + let modeIndicatorStyle = document.getElementById("mode-indicator").style; let sSheets = document.styleSheets[0]; // General body style @@ -15,6 +16,7 @@ function setStyle(theme) { // default text color generalBodyStyle.color = theme.colors.toolbar_field_text; inputFieldStyle.color = theme.colors.toolbar_field_text; + modeIndicatorStyle.color = theme.colors.toolbar_field_text; } // Input field style diff --git a/background.js b/background.js index e3171e2..7466a6d 100644 --- a/background.js +++ b/background.js @@ -1,49 +1,20 @@ var opened = false; -var pluginPanelId = null - -async function find(query) { - browser.runtime.sendMessage({msg: "clear-results"}); - - if (!query) - return; - - let this_tab_url = browser.runtime.getURL("find-tab.html"); - let tabs = await browser.tabs.query({}).then(allTabs => findMatchingTab(query, allTabs, this_tab_url)); - -} - -async function findMatchingTab(query, allTabs, thisTabUrl) { - const regex = RegExp(query); - for (let tab of allTabs) { // just search through tabs titles for now, maybe add in depth tab searching later? - if (tab.url == thisTabUrl) - { - continue; - } - if (regex.test(tab.title) || tab.title.toLowerCase().includes(query)) { - browser.runtime.sendMessage({ - msg: "found-result", - title: tab.title, - id: tab.id, - url: tab.url, - query: query - }); - } - } - browser.runtime.sendMessage({ - msg: "results-complete" - }); - //console.log("Handled find " + query); -} +var pluginPanelId = null; +var tabsFromLastWindow = null; +var tabsFromAllWindows = null; function createNewWindow() { let createData = { type: "detached_panel", url: "find-tab.html", width: window.screen.width / 2, // find reasonable size - height: window.screen.height / 2 + height: window.screen.height / 2, + // current bug 1271047 prevents from pos being set + left: window.screen.width / 2, // position in the centre + top: window.screen.height / 2 }; let pluginPanel = browser.windows.create(createData); - waitForPanelId(pluginPanel) + waitForPanelId(pluginPanel); opened = true; }; @@ -51,19 +22,48 @@ async function waitForPanelId(pluginPanel) { pluginPanelId = await pluginPanel.then(pluginPanel => pluginPanel.id); } +// Requests tabs, from all windows and the current one. +async function getTabs() { + await browser.tabs.query({currentWindow: true}).then((allT) => {tabsFromLastWindow = allT}); + await browser.tabs.query({currentWindow: false}).then((allT) => {tabsFromAllWindows = allT}); +} + +function sendTabs() { + browser.runtime.sendMessage({ + msg: "all-tabs", + tabsLastW: tabsFromLastWindow, + tabsAllW: tabsFromAllWindows + }); +} + // Listeners browser.browserAction.onClicked.addListener(() => { // check if already open if (opened) return; createNewWindow(); + getTabs(); }); browser.commands.onCommand.addListener(function (command) { - if (opened) - return; - if (command == "toggle-plugin") { - createNewWindow(); + // Toggle the plugin on and off + if (opened) + { + browser.windows.remove(pluginPanelId); + // opened is set to false and handled by find-tab.js by handlePanelClose + } + else { + createNewWindow(); + getTabs(); + } } -}); \ No newline at end of file + else if (command == "close-Tab") { + if (opened) + browser.runtime.sendMessage({msg: "close-tab"}); + } + else if (command == "toggle-search-mode") { + if (opened) + browser.runtime.sendMessage({msg: "toggle-search-mode"}); + } +}); diff --git a/find-tab.html b/find-tab.html index f40ceb8..0402b88 100644 --- a/find-tab.html +++ b/find-tab.html @@ -10,10 +10,11 @@
- + +
A
-

+
diff --git a/find-tab.js b/find-tab.js index ea0747a..9d7fc84 100644 --- a/find-tab.js +++ b/find-tab.js @@ -1,15 +1,16 @@ let backgroundPage = browser.extension.getBackgroundPage(); let find_input = document.getElementById("find-input"); let result_list = document.getElementById("result-list"); // to avoid FF errors, they have to be obtained each time +let modeIndicator = document.getElementById("mode-indicator"); +let tabsListLastWindow = null; +let tabsListAllWindows = null; -let timeout = null; +let searchThroughLastWindow = false; let selected = null; -// hack to ignore reload on 'Enter' Keypress in form field -document.getElementById("find-form").addEventListener("keypress", function(e) { - if (e.key === "Enter") - e.preventDefault(); -}); +// request all tabs from the bg script +backgroundPage.sendTabs(); + document.getElementById("find-form").addEventListener("keyup", function(e) { if (e.key !== "Enter" && e.key !== "Escape" && e.key !== "ArrowUp" && e.key !== "ArrowDown") { @@ -26,11 +27,16 @@ document.getElementById("find-form").addEventListener("keyup", function(e) { if (tab.classList.contains("Selected")) { result_list.removeChild(tab); i -= 1; - selected = { - "idx" : 0, - "val" : result_list.firstChild - }; - selected.val.classList.add("Selected"); + if (result_list.childElementCount) + { + selected = { + "idx" : 0, + "val" : result_list.firstChild + }; + selected.val.classList.add("Selected"); + } else { + selected = null; + } } else { result_list.removeChild(tab); i -= 1; @@ -40,27 +46,16 @@ document.getElementById("find-form").addEventListener("keyup", function(e) { } } else { selected = null; - clearTimeout(timeout); - - timeout = setTimeout(() => { - //console.log("Pre find " + find_input.value); - backgroundPage.find(find_input.value); - }, 300); + find(find_input.value); } - e.preventDefault(); - } -}) - -document.addEventListener("keydown", function(e) { - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault(); } -}) +}); // prevent popping up the textinput -document.addEventListener("keyup", function(e) { +document.addEventListener("keydown", function(e) { switch (e.key) { case "Enter": + e.preventDefault(); if (selected) { //navigate to selected @@ -85,18 +80,20 @@ document.addEventListener("keyup", function(e) { break; case "Escape": + e.preventDefault(); closeWidget(); break; case "ArrowUp": + e.preventDefault(); selectPreceding(); break; case "ArrowDown": + e.preventDefault(); selectSuceeding(); break; } - e.preventDefault(); }); window.addEventListener("keyup", function(e) { @@ -106,60 +103,137 @@ window.addEventListener("keyup", function(e) { e.preventDefault(); }); -function handleMessage(request, sender, sendResponse) { - if (request.msg === "clear-results") { - result_list.innerHTML = ""; - } - if (request.msg === "found-result") { - let tr = document.createElement("tr"); - let title = document.createElement("td"); - let url = document.createElement("td"); - let id = document.createElement("td"); - title.innerText = request.title; - url.innerText = request.url; - id.innerText = request.id; - tr.appendChild(title); - tr.appendChild(url); - tr.appendChild(id); - result_list.appendChild(tr); +function find(query) { + result_list.innerHTML = ''; + + if (!query) + return; + + let this_tab_url = browser.runtime.getURL("find-tab.html"); + let tabsList = searchThroughLastWindow ? tabsListLastWindow : tabsListAllWindows; + const regex = RegExp(query); + + for (let tab of tabsList) + { + if (tab.url == this_tab_url) + continue; + + if (regex.test(tab.title) || tab.title.toLowerCase().includes(query)) { + let tr = document.createElement("tr"); + let title = document.createElement("td"); + let url = document.createElement("td"); + let id = document.createElement("td"); + title.innerText = tab.title; + url.innerText = tab.url; + id.innerText = tab.id; + tr.appendChild(title); + tr.appendChild(url); + tr.appendChild(id); + result_list.appendChild(tr); + } } - if (request.msg === "results-complete") { - //console.log("Obtained results for: " + request.query) - //console.log(result_list); - //console.log(result_list.hasChildNodes()); - //console.log(selected); - if (result_list.hasChildNodes()) { + if (result_list.hasChildNodes()) { selected = { "idx" : 0, "val" : result_list.firstChild - }; + } //console.log("SELECTED: " + selected.val.innerText); // add Class Selected for CSS highlight selected.val.classList.add("Selected"); + selected.val.scrollIntoView(true); + } +} + +function handleMessage(request, sender, sendResponse) { + if(request.msg == "all-tabs") + { + tabsListLastWindow = request.tabsLastW; + tabsListAllWindows = request.tabsAllW; + } + else if (request.msg == "close-tab") { + let currentSelected = selected.val; + index = 0; + for (index; index el.id === idCurrentSelected), 1); + let indexInCurrWindow = tabsListLastWindow.findIndex(el => el.id === idCurrentSelected); + + if (indexInCurrWindow !== -1) { + tabsListLastWindow.splice(indexInCurrWindow, 1); + } + + if (index > 0) { + selectPreceding(); + } + else if (result_list.hasChildNodes()) { + selected = { + "idx" : 0, + "val" : result_list.firstChild + } + //console.log("SELECTED: " + selected.val.innerText); + // add Class Selected for CSS highlight + selected.val.classList.add("Selected"); } -} + } + else if (request.msg == "toggle-search-mode") { + updateSearchMode(); + } +} + +function updateSearchMode() { + searchThroughLastWindow = !searchThroughLastWindow; + find_input.placeholder = 'Search through: ' + (searchThroughLastWindow ? 'Last Window' : 'All Windows'); + modeIndicator.innerText = searchThroughLastWindow ? 'C' : 'A'; + find(find_input.value); +} function selectPreceding() { - if (selected.idx !== 0) { - selected.val.classList.remove("Selected"); //remove Selected class from prev selected + if (selected.idx > 0) { + selected.val.classList.remove("Selected"); //remove Selected class from prev selected selected = { "idx" : selected.idx -= 1, "val" : result_list.children[selected.idx] }; selected.val.classList.add("Selected"); // add Selected class to new selected + selected.val.scrollIntoView(true); + } + else if (result_list.childElementCount > 1) { + selected.val.classList.remove("Selected"); + selected = { + "idx" : result_list.childElementCount - 1, + "val" : result_list.lastChild + }; + selected.val.classList.add("Selected"); // add Selected class to new selected + selected.val.scrollIntoView(true); } } function selectSuceeding() { let size = result_list.childElementCount; - if (selected.idx !== size -1) { + if (selected.idx < size - 1) { selected.val.classList.remove("Selected"); // remove Selected class from prev selected selected = { "idx" : selected.idx += 1, "val" : result_list.children[selected.idx] }; selected.val.classList.add("Selected"); // add Selected class to new selected + selected.val.scrollIntoView(true); + } + else if (result_list.childElementCount > 1) { + selected.val.classList.remove("Selected"); + selected = { + "idx" : 0, + "val" : result_list.firstChild + }; + selected.val.classList.add("Selected"); // add Selected class to new selected + selected.val.scrollIntoView(true); } } @@ -171,12 +245,10 @@ function closeWidget() { function handlePanelClose(windowId) { if (backgroundPage.pluginPanelId == windowId) backgroundPage.opened = false; - }; // sort the list on the fly, most occurences matching listed first, how about near matches? browser.runtime.onMessage.addListener(handleMessage); - browser.windows.onRemoved.addListener(handlePanelClose); // On lost focus, close -//window.addEventListener("blur", closeWidget); \ No newline at end of file +window.addEventListener("blur", closeWidget); \ No newline at end of file diff --git a/manifest.json b/manifest.json index 1a29fdb..eb17a15 100644 --- a/manifest.json +++ b/manifest.json @@ -20,7 +20,7 @@ "32": "search.svg" } }, - "version": "1.0", + "version": "1.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "65.0" @@ -32,6 +32,18 @@ "default": "Ctrl+Space" }, "description": "Send a 'toggle-plugin' event to turn it on and off" + }, + "close-Tab": { + "suggested_key": { + "default": "Ctrl+E" + }, + "description": "Send a 'close-Tab' event to close the currently selected tab" + }, + "toggle-search-mode": { + "suggested_key": { + "default": "Ctrl+Shift+Space" + }, + "description": "Send a 'toggle-search-mode' event to alter between searching through all windows and the last window" } } - } \ No newline at end of file + } diff --git a/stylesheet.css b/stylesheet.css index 26b9b80..230ac9b 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -13,6 +13,12 @@ hr { margin-right: 0.5%; } +div#mode-indicator { + display: inline-block; + font-weight: bold; + width: 1%; +} + table#result-list { padding-left: 0.5%; padding-right: 0.5%; @@ -23,6 +29,8 @@ table#result-list tr { display: block; border-radius: 0.15em; width: 100%; + /*offset when scrolling at the top*/ + scroll-margin-top:2.2em; } table#result-list td:nth-child(1) { @@ -34,15 +42,25 @@ table#result-list td:nth-child(1) ~ td { display: none; } +form#find-form { + /* keeps the input field at the top + higher z-index => drawn on top of the rest*/ + position: sticky; + top:0; + z-index:2; +} + div#form { padding-top: 0.5%; padding-bottom: 0.5%; + padding-left: 0.5%; + padding-right: 0.5%; } input#find-input { border: none; - width: 99%; - display: block; + width: 98%; + display: inline-block; margin-left: auto; margin-right: auto; }