From ccaa37ba362622d83772b7bac69b7e3860c72da8 Mon Sep 17 00:00:00 2001 From: PalmerAL Date: Sat, 23 Jan 2016 14:56:52 -0600 Subject: [PATCH] update build --- dist/build.js | 1764 ++++++++++++++++++++++++------------------- dist/build.min.js | 6 +- dist/webview.js | 143 ++-- dist/webview.min.js | 4 +- 4 files changed, 1105 insertions(+), 812 deletions(-) diff --git a/dist/build.js b/dist/build.js index 5452ea555..694d1290a 100644 --- a/dist/build.js +++ b/dist/build.js @@ -206,6 +206,7 @@ function getWebviewDom(options) { var url = (options || {}).url || "about:blank"; var w = $(""); + var w0 = w[0]; w.attr("preload", "dist/webview.min.js"); w.attr("src", urlParser.parse(url)); @@ -220,24 +221,24 @@ function getWebviewDom(options) { //webview events webviewEvents.forEach(function (i) { - w.on(i.event, i.fn); + w0.addEventListener(i.event, i.fn); }); - w.on("page-favicon-updated", function (e) { - var id = $(this).attr("data-tab"); - updateTabColor(e.originalEvent.favicons, id); + w0.addEventListener("page-favicon-updated", function (e) { + var id = this.getAttribute("data-tab"); + updateTabColor(e.favicons, id); }); - w.on("page-title-set", function (e) { - var tab = $(this).attr("data-tab"); + w0.addEventListener("page-title-set", function (e) { + var tab = this.getAttribute("data-tab"); tabs.update(tab, { - title: e.originalEvent.title + title: e.title }); rerenderTabElement(tab); }); - w.on("did-finish-load", function (e) { - var tab = $(this).attr("data-tab"); + w0.addEventListener("did-finish-load", function (e) { + var tab = this.getAttribute("data-tab"); var url = $(this).attr("src"); //src attribute changes whenever a page is loaded if (url.indexOf("https://") === 0 || url.indexOf("about:") == 0 || url.indexOf("chrome:") == 0 || url.indexOf("file://") == 0) { @@ -270,17 +271,17 @@ function getWebviewDom(options) { //open links in new tabs - w.on("new-window", function (e) { - var tab = $(this).attr("data-tab"); + w0.addEventListener("new-window", function (e) { + var tab = this.getAttribute("data-tab"); var currentIndex = tabs.getIndex(tabs.getSelected()); var newTab = tabs.add({ - url: e.originalEvent.url, + url: e.url, private: tabs.get(tab).private //inherit private status from the current tab }, currentIndex + 1); addTab(newTab, { - focus: false, - openInBackground: e.originalEvent.disposition == "background-tab", //possibly open in background based on disposition + enterEditMode: false, + openInBackground: e.disposition == "background-tab", //possibly open in background based on disposition }); }); @@ -288,7 +289,7 @@ function getWebviewDom(options) { // In embedder page. Send the text content to bookmarks when recieved. w.on('ipc-message', function (e) { var w = this; - var tab = $(this).attr("data-tab"); + var tab = this.getAttribute("data-tab"); webviewIPC.forEach(function (item) { if (item.name == e.originalEvent.channel) { @@ -307,7 +308,7 @@ function getWebviewDom(options) { w.on("contextmenu", webviewMenu.show); w.on("crashed", function (e) { - var tabId = $(this).attr("data-tab"); + var tabId = this.getAttribute("data-tab"); destroyWebview(tabId); tabs.update(tabId, { @@ -322,6 +323,14 @@ function getWebviewDom(options) { if (e.originalEvent.errorCode != -3 && e.originalEvent.validatedURL == e.target.getURL()) { navigate($(this).attr("data-tab"), errorPage + "?ec=" + encodeURIComponent(e.originalEvent.errorCode) + "&url=" + e.target.getURL()); } + }); + + w0.addEventListener("enter-html-full-screen", function (e) { + this.classList.add("fullscreen"); + }); + + w0.addEventListener("leave-html-full-screen", function (e) { + this.classList.remove("fullscreen"); }) return w; @@ -349,15 +358,10 @@ function addWebview(tabId) { webviewBase.append(webview); } -function switchToWebview(id, options) { +function switchToWebview(id) { $("webview").prop("hidden", true); - var webview = getWebview(id); - webview.removeClass("hidden").prop("hidden", false); //in some cases, webviews had the hidden class instead of display:none to make them load in the background. We need to make sure to remove that. - - if (options && options.focus) { - webview[0].focus(); - } + getWebview(id).removeClass("hidden").prop("hidden", false); //in some cases, webviews had the hidden class instead of display:none to make them load in the background. We need to make sure to remove that. } function updateWebview(id, url) { @@ -410,8 +414,10 @@ var webviewMenu = { }, tabs.getIndex(tabs.getSelected()) + 1); addTab(newTab, { - focus: false, + enterEditMode: false, }); + + getWebview(newTab).get(0).focus(); } })); @@ -426,8 +432,10 @@ var webviewMenu = { private: true, }, tabs.getIndex(tabs.getSelected()) + 1) addTab(newTab, { - focus: false, + enterEditMode: false, }); + + getWebview(newTab).get(0).focus(); } })); } @@ -464,8 +472,10 @@ var webviewMenu = { private: tab.private, }) addTab(newTab, { - focus: false, + enterEditMode: false, }); + + getWebview(newTab).get(0).focus(); } })); } @@ -545,7 +555,7 @@ var bookmarks = { }); } - }, 2000); + }, 500); }, currentCallback: function () {}, onDataRecieved: function (data) { @@ -688,13 +698,6 @@ function navigate(tabId, newURL) { }); } -function switchToNextTab(oldIndex) { - var nextTab = tabs.getAtIndex(oldIndex + 1) || tabs.getAtIndex(oldIndex - 1); - if (nextTab) { - switchToTab(nextTab.id); - } -} - function destroyTab(id) { getTabElement(id).remove(); //remove the actual tab element @@ -705,7 +708,9 @@ function destroyTab(id) { /* switches to a tab - update the webview, state, tabstrip, etc. */ -function switchToTab(id) { +function switchToTab(id, options) { + + options = options || {}; /* tab switching disabled in focus mode */ if (isFocusMode) { @@ -715,12 +720,13 @@ function switchToTab(id) { leaveTabEditMode(); + tabs.setSelected(id); setActiveTabElement(id); - switchToWebview(id, { - focus: !isExpandedMode //trying to focus a webview while in expanded mode breaks the page - }); + switchToWebview(id); - tabs.setSelected(id); + if (options.focusWebview != false && !isExpandedMode) { //trying to focus a webview while in expanded mode breaks the page + getWebview(id).get(0).focus(); + } var tabData = tabs.get(id); setColor(tabData.backgroundColor, tabData.foregroundColor); @@ -739,982 +745,1133 @@ function switchToTab(id) { sessionRestore.save(); } -;var DDGSearchURLRegex = /^https:\/\/duckduckgo.com\/\?q=([^&]*).*/g, - trailingSlashRegex = /\/$/g, - plusRegex = /\+/g; +;var searchbarCachedText = ""; +var METADATA_SEPARATOR = "·"; +var didFireKeydownSelChange = false; +var currentsearchbarInput; -var currentACItem = null; -var deleteKeyPressed = false; +//https://remysharp.com/2010/07/21/throttling-function-calls# -var maxHistoryResults = 4; +function throttle(fn, threshhold, scope) { + threshhold || (threshhold = 250); + var last, + deferTimer; + return function () { + var context = scope || this; -function searchbarAutocomplete(text, input, historyResults) { - if (!text) { - currentACItem = null; - return; - } + var now = +new Date, + args = arguments; + if (last && now < last + threshhold) { + // hold on to it + clearTimeout(deferTimer); + deferTimer = setTimeout(function () { + last = now; + fn.apply(context, args); + }, threshhold); + } else { + last = now; + fn.apply(context, args); + } + }; +} - if (text == searchbarCachedText && input[0].selectionStart != input[0].selectionEnd) { //if nothing has actually changed, don't try to autocomplete - return; +function debounce(fn, delay) { + var timer = null; + return function () { + var context = this, + args = arguments; + clearTimeout(timer); + timer = setTimeout(function () { + fn.apply(context, args); + }, delay); + }; +} + +function empty(node) { + var n; + while (n = node.firstElementChild) { + node.removeChild(n); } - //if we moved the selection, we don't want to autocomplete again - if (didFireKeydownSelChange) { - return; +} + +function removeTags(text) { + return text.replace(/<.*?>/g, ""); +} + +/* this is used by navbar-tabs.js. When a url is entered, endings such as ? need to be parsed and removed. */ +function parsesearchbarURL(url) { + //always use a search engine if the query starts with "?" + + if (url.indexOf("?") == 0) { + url = urlParser.searchBaseURL.replace("%s", encodeURIComponent(url.replace("?", ""))); } - var didAutocomplete = false; + if (url.indexOf("^") == 0) { + url = url.replace("^", ""); + } - for (var i = 0; !didAutocomplete && i < historyResults.length; i++) { //we only want to autocomplete the first item that matches - didAutocomplete = autocompleteResultIfNeeded(input, historyResults[i]); //this returns true or false depending on whether the item was autocompleted or not + if (url.indexOf("*") == 0) { + url = url.replace("*", ""); } + + return url; } -function autocompleteResultIfNeeded(input, result) { +function openURLInBackground(url) { //used to open a url in the background, without leaving the searchbar + var newTab = tabs.add({ + url: url, + private: tabs.get(tabs.getSelected()).private + }, tabs.getIndex(tabs.getSelected()) + 1); + addTab(newTab, { + enterEditMode: false, + openInBackground: true, + leaveEditMode: false, + }); + $(".result-item:focus").blur(); //remove the highlight from an awesoembar result item, if there is one +} - //figure out if we should autocomplete based on the title +//when clicking on a result item, this function should be called to open the URL - DDGSearchURLRegex.lastIndex = 0; - shouldAutocompleteTitle = DDGSearchURLRegex.test(result.url); +function openURLFromsearchbar(event, url) { + if (event.metaKey) { + openURLInBackground(url); + return true; + } else { + navigate(tabs.getSelected(), url); - if (shouldAutocompleteTitle) { - result.title = decodeURIComponent(result.url.replace(DDGSearchURLRegex, "$1").replace(plusRegex, " ")); - } + if (!tabs.get(tabs.getSelected()).private) { - var text = getValue(input); //make sure the input hasn't changed between start and end of query - var hostname = new URL(result.url).hostname; + //show the color and title of the new page immediately, to make the page load time seem faster + currentHistoryResults.forEach(function (res) { + if (res.url == url) { + setColor(res.color, getTextColor(getRGBObject(res.color))); + tabs.update(tabs.getSelected(), { + title: res.title, + }); + rerenderTabElement(tabs.getSelected()); + } + }); - var possibleAutocompletions = [ //the different variations of the URL we can autocomplete - hostname, //we start with the domain - (hostname + "/").replace(urlParser.startingWWWRegex, "$1").replace("/", ""), //if that doesn't match, try the hostname without the www instead. The regex requires a slash at the end, so we add one, run the regex, and then remove it - urlParser.prettyURL(result.url), //then try the whole url - urlParser.removeProtocol(result.url), //then try the url with querystring - result.url, //then just try the url with protocol - ] + } - if (shouldAutocompleteTitle) { - possibleAutocompletions.push(result.title); + return false; } +} - for (var i = 0; i < possibleAutocompletions.length; i++) { - if (!deleteKeyPressed && possibleAutocompletions[i].toLowerCase().indexOf(text.toLowerCase()) == 0) { //we can autocomplete the item +//attempts to shorten a page title, removing useless text like the site name - input.val(possibleAutocompletions[i]); - input.get(0).setSelectionRange(text.length, possibleAutocompletions[i].length); +function getRealTitle(text) { - if (i < 2) { //if we autocompleted a domain, the cached item should be the domain, not the full url - var url = new URL(result.url); - currentACItem = url.protocol + "//" + url.hostname + "/"; - } else { - currentACItem = result.url; - } - return true; - } + //don't try to parse URL's + if (urlParser.isURL(text)) { + return text; } - //nothing was autocompleted + var possibleCharacters = ["|", ":", " - ", " — "]; - currentACItem = null; - return false; -} + for (var i = 0; i < possibleCharacters.length; i++) { -var showHistoryResults = throttle(function (text, input, maxItems) { + var char = possibleCharacters[i]; + //match url's of pattern: title | website name + var titleChunks = text.split(char); - if (!text && input[0].value) { //if the entire input is highlighted (such as when we first focus the input), don't show anything - return; - } + if (titleChunks.length >= 2) { + titleChunks[0] = titleChunks[0].trim(); + titleChunks[1] = titleChunks[1].trim(); - if (text) { - text = text.trim(); + if (titleChunks[1].length < 5 || titleChunks[1].length / titleChunks[0].length <= 0.5) { + return titleChunks[0] + } + } } - bookmarks.searchHistory(text, function (results) { + //fallback to the regular title - var showedTopAnswer = false; + return text; - maxItems = maxItems || maxHistoryResults; +} - //if there is no text, only history results will be shown, so we can assume that 4 results should be shown. - if (!text) { - maxItems = 4; - } - historyarea.empty(); +//creates a result item - if (topAnswerarea.get(0).getElementsByClassName("history-item").length > 0) { - topAnswerarea.empty(); - } +/* + +data: + +title: string - the title of the item +secondaryText: string - the item's secondary text +url: string - the item's url (if there is one). +icon: string - the name of a font awesome icon. +image: string - the URL of an image to show +descriptionBlock: string - the text in the description block + +classList: array - a list of classes to add to the item +*/ - searchbarAutocomplete(text, input, results); +function createSearchbarItem(data) { + var item = document.createElement("div"); + item.classList.add("result-item"); - if (results.length < 10) { //if we don't have a lot of history results, show search suggestions - limitSearchSuggestions(results.length); - maxItems = 3; - showSearchSuggestions(text, input); - } else if (text.indexOf("!") == -1) { //if we have a !bang, always show results - serarea.empty(); - } + item.setAttribute("tabindex", "-1"); - var resultsShown = 0; + if (data.classList) { + for (var i = 0; i < data.classList.length; i++) { + item.classList.add(data.classList[i]); + } + } - //we will never have more than 5 results, so we don't need to create more DOM elements than that + if (data.icon) { + var i = document.createElement("i"); + i.className = "fa" + " " + data.icon; - results = results.splice(0, 5); + item.appendChild(i); + } - results.forEach(function (result) { + if (data.title) { + var title = document.createElement("span"); + title.classList.add("title"); - var shouldAutocompleteTitle = false; + title.textContent = data.title; - var title = result.title; - var icon = $(""); + item.appendChild(title); + } - //special formatting for ddg search history results - DDGSearchURLRegex.lastIndex = 0; + if (data.url) { + item.setAttribute("data-url", data.url); + } - if (DDGSearchURLRegex.test(result.url)) { + if (data.secondaryText) { + var secondaryText = document.createElement("span"); + secondaryText.classList.add("secondary-text"); - //the history item is a search, display it like a search suggestion - title = decodeURIComponent(result.url.replace(DDGSearchURLRegex, "$1").replace(plusRegex, " ")); - icon = $(""); - shouldAutocompleteTitle = true; //previous searches can be autocompleted - } + secondaryText.textContent = data.secondaryText; - //if we're doing a bang search, but the item isn't a web search, it probably isn't useful, so we shouldn't show it - if (!shouldAutocompleteTitle && text.indexOf("!") == 0) { - return; - } + item.appendChild(secondaryText); + } + if (data.image) { + var image = document.createElement("img"); + image.className = "result-icon image low-priority-image"; + image.src = data.image; - var item = $("
").append($("").text(getRealTitle(title))).on("click", function (e) { - openURLFromsearchbar(e, result.url); - }); + if (data.imageIsInline) { + image.classList.add("inline"); + } - item.attr("data-url", result.url); + item.insertBefore(image, item.childNodes[0]); + } - icon.prependTo(item); + if (data.descriptionBlock) { + var dBlock = document.createElement("span"); + dBlock.classList.add("description-block"); - if (!shouldAutocompleteTitle && result.title != result.url) { //if we're autocompleting titles, this is a search, and we don't want to show the URL. If the item title and URL are the same (meaning the item has no title), there is no point in showing a URL since we are showing it in the title field. + dBlock.textContent = data.descriptionBlock; + item.appendChild(dBlock); + } - $("").text(urlParser.prettyURL(result.url)).appendTo(item); - } + return item; +} - if (resultsShown >= maxItems) { //only show up to n history items - item.prop("hidden", true).addClass("unfocusable"); - } +var searchbar = document.getElementById("searchbar"); +var $searchbar = $(searchbar); - if (urlParser.areEqual(currentACItem, result.url) && resultsShown < maxItems && !showedTopAnswer) { //the item is being autocompleted, highlight it - item.addClass("fakefocus"); - requestAnimationFrame(function () { - item.appendTo(topAnswerarea); - }); - showedTopAnswer = true; - } else { - requestAnimationFrame(function () { - item.appendTo(historyarea); - }); - } +function clearsearchbar() { + empty(opentabarea); + empty(topAnswerarea); + empty(bookmarkarea); + empty(historyarea); + empty(iaarea); + empty(suggestedsitearea); + empty(serarea); + //prevent memory leak + cachedBangSnippets = {}; +} - resultsShown++; +function showSearchbar(triggerInput) { + searchbarCachedText = triggerInput.val(); + $(document.body).addClass("searchbar-shown"); - }); + clearsearchbar(); - //show a top answer item if we did domain autocompletion + searchbar.hidden = false; - if (currentACItem && !showedTopAnswer) { - var item = $("
").append($("").text(urlParser.prettyURL(currentACItem))).on("click", function (e) { - openURLFromsearchbar(e, currentACItem); - }); + currentsearchbarInput = triggerInput.get(0); - $("").prependTo(item); +} - requestAnimationFrame(function () { - item.appendTo(topAnswerarea); - }); - } - }); -}, 50); +//gets the typed text in an input, ignoring highlighted suggestions -function limitHistoryResults(maxItems) { - maxHistoryResults = Math.min(4, Math.max(maxItems, 2)); +function getValue(input) { + var text = input.value; + return text.replace(text.substring(input.selectionStart, input.selectionEnd), ""); +} - historyarea.find(".result-item:nth-child(n+{items})".replace("{items}", maxHistoryResults + 1)).prop("hidden", true).addClass("unfocusable"); +function hidesearchbar() { + currentsearchbarInput = null; + $(document.body).removeClass("searchbar-shown"); + searchbar.hidden = true; + cachedBangSnippets = {}; } -;function addBookmarkItem(result) { - //create the basic item - //getRealTitle is defined in searchbar.js - var item = $("
").append($("").text(getRealTitle(result.title))).on("click", function (e) { - openURLFromsearchbar(e, result.url); - }); +var showSearchbarResults = function (text, input, event) { - $("").prependTo(item); + if (event && event.metaKey) { + return; + } - var span = $("").text(urlParser.prettyURL(result.url)); + deleteKeyPressed = event && event.keyCode == 8; + //find the real input value, accounting for highlighted suggestions and the key that was just pressed - if (result.extraData && result.extraData.metadata) { - var captionSpans = []; + //delete key doesn't behave like the others, String.fromCharCode returns an unprintable character (which has a length of one) - if (result.extraData.metadata.rating) { - captionSpans.push($("").text(result.extraData.metadata.rating)); - } - if (result.extraData.metadata.price) { - captionSpans.push($("").text(result.extraData.metadata.price)); - } - if (result.extraData.metadata.location) { - captionSpans.push($("").text(result.extraData.metadata.location)); - } + if (event && event.keyCode != 8) { + text = text.substring(0, input.selectionStart) + String.fromCharCode(event.keyCode) + text.substring(input.selectionEnd, text.length); - captionSpans.reverse().forEach(function (s) { - span.prepend(s); - }) } + console.log("searchbar: ", "'" + text + "'", text.length); - span.appendTo(item); + //there is no text, show only topsites + if (text.length < 1) { + showHistoryResults("", input); + clearsearchbar(); + return; + } - item.appendTo(bookmarkarea); + //when you start with ?, always search with duckduckgo - item.attr("data-url", result.url); -} + if (text.indexOf("?") == 0) { + clearsearchbar(); -var showBookmarkResults = throttle(function (text) { - if (text.length < 5 || text.indexOf("!") == 0) { //if there is not enough text, or we're doing a bang search, don't show results - limitHistoryResults(5); - bookmarkarea.empty(); + currentSuggestionLimit = 5; + showSearchSuggestions(text.replace("?", ""), input); return; } - bookmarks.searchBookmarks(text, function (results) { - bookmarkarea.empty(); - var resultsShown = 1; - results.splice(0, 2).forEach(function (result) { - //as more results are added, the threshold for adding another one gets higher - if (result.score > Math.max(0.0004, 0.0016 - (0.00012 * Math.pow(1.25, text.length))) && (resultsShown == 1 || text.length > 6)) { - requestAnimationFrame(function () { - addBookmarkItem(result); + //when you start with ^, always search history (only) + + if (text.indexOf("^") == 0) { + clearsearchbar(); + showHistoryResults(text.replace("^", ""), input); + return; + } + + //when you start with *, always search bookmarks (only) + + if (text.indexOf("*") == 0) { + clearsearchbar(); + showBookmarkResults(text.replace("*", ""), input); + return; + } + + //show searchbar results + + showBookmarkResults(text); + + showHistoryResults(text, input); + showInstantAnswers(text, input); + searchOpenTabs(text, input); + + //update cache + searchbarCachedText = text; +}; + +function focussearchbarItem(options) { + options = options || {}; //fallback if options is null + var previous = options.focusPrevious; + var allItems = $("#searchbar .result-item:not(.unfocusable)"); + var currentItem = $("#searchbar .result-item:focus, .result-item.fakefocus"); + var index = allItems.index(currentItem); + var logicalNextItem = allItems.eq((previous) ? index - 1 : index + 1); + + $searchbar.find(".fakefocus").removeClass("fakefocus"); //clear previously focused items + + if (currentItem[0] && logicalNextItem[0]) { //an item is focused and there is another item after it, move onto the next one + logicalNextItem.get(0).focus(); + } else if (currentItem[0]) { //the last item is focused, focus the searchbar again + getTabElement(tabs.getSelected()).getInput().get(0).focus(); + } else { // no item is focused. + $("#searchbar .result-item").first().get(0).focus(); + } + + var focusedItem = $("#searchbar .result-item:focus"); + + if (focusedItem.hasClass("iadata-onfocus")) { + + setTimeout(function () { + if (document.activeElement == focusedItem[0]) { + var itext = focusedItem.find(".title").text(); + + showInstantAnswers(itext, currentsearchbarInput, { + alwaysShow: true, + destroyPrevious: false, }); - resultsShown++; } + }, 200); + } +} + +//return key on result items should trigger click +//tab key or arrowdown key should focus next item +//arrowup key should focus previous item +$searchbar.on("keydown", ".result-item", function (e) { + if (e.keyCode == 13) { + $(this).trigger("click"); + } else if (e.keyCode == 9 || e.keyCode == 40) { //tab or arrowdown key + e.preventDefault(); + focussearchbarItem(); + } else if (e.keyCode == 38) { + e.preventDefault(); + focussearchbarItem({ + focusPrevious: true }); - limitHistoryResults(5 - resultsShown); //if we have lots of bookmarks, don't show as many regular history items + } +}); - }); -}, 400); +//swipe left on history items to delete them -var showAllBookmarks = function () { - bookmarks.searchBookmarks("", function (results) { +var lastItemDeletion = Date.now(); - results.sort(function (a, b) { - //http://stackoverflow.com/questions/6712034/sort-array-by-firstname-alphabetically-in-javascript - if (a.url < b.url) return -1; - if (a.url > b.url) return 1; - return 0; +$searchbar.on("mousewheel", ".history-results .result-item, .top-answer-results .result-item", function (e) { + var self = $(this) + if (e.originalEvent.deltaX > 50 && e.originalEvent.deltaY < 3 && self.attr("data-url") && Date.now() - lastItemDeletion > 700) { + lastItemDeletion = Date.now(); + self.animate({ + opacity: "0", + "margin-left": "-100%" + }, 200, function () { + self.remove(); + bookmarks.deleteHistory(self.attr("data-url")); + lastItemDeletion = Date.now(); }); - results.forEach(addBookmarkItem); - }); -} -;var BANG_REGEX = /!\w+/g; -var serarea = $("#searchbar .search-engine-results"); -var iaarea = $("#searchbar .instant-answer-results"); -var topAnswerarea = $("#searchbar .top-answer-results"); -var suggestedsitearea = $("#searchbar .ddg-site-results"); + } +}); -const minSearchSuggestions = 2; -const maxSearchSuggestions = 4; -var currentSuggestionLimit = maxSearchSuggestions; +//when we get keywords data from the page, we show those results in the searchbar -/* custom answer layouts */ +bindWebviewIPC("keywordsData", function (webview, tabId, arguements) { -var IAFormats = { - color_code: function (searchText, answer) { - var alternateFormats = [answer.data.rgb, answer.data.hslc, answer.data.cmyb]; + var data = arguements[0]; - if (searchText.indexOf("#") == -1) { //if the search is not a hex code, show the hex code as an alternate format - alternateFormats.unshift(answer.data.hexc); - } + var itemsCt = 0; - var item = $("
"); - $("").text(searchText).appendTo(item); + var itemsShown = []; - $("
").css("background-color", "#" + answer.data.hex_code).prependTo(item); - $("").text(alternateFormats.join(" " + METADATA_SEPARATOR + " ")).appendTo(item); + data.entities.forEach(function (item, index) { - return item; - }, - minecraft: function (searchText, answer) { + //ignore one-word items, they're usually useless + if (!/\s/g.test(item.trim())) { + return; + } - var item = $("
"); + if (itemsCt >= 5 || itemsShown.indexOf(item.trim()) != -1) { + return; + } - $("").text(answer.data.title).appendTo(item); - $("").attr("src", answer.data.image).prependTo(item); - $("").text(answer.data.description + " " + answer.data.subtitle).appendTo(item); + var div = createSearchbarItem({ + icon: "fa-search", + title: item, + classList: ["iadata-onfocus"] + }); - return item; - }, - figlet: function (searchText, answer) { - var formattedAnswer = removeTags(answer).replace("Font: standard", ""); + div.addEventListener("click", function (e) { + if (e.metaKey) { + openURLInBackground(item); + } else { + navigate(tabs.getSelected(), item); + } + }); - var item = $("
"); - var desc = $("").text(formattedAnswer).appendTo(item); + serarea.appendChild(div); - //display the data correctly - desc.css({ - "white-space": "pre-wrap", - "font-family": "monospace", - "max-height": "10em", - "-webkit-user-select": "auto", - }); + itemsCt++; + itemsShown.push(item.trim()); + }); +}); +;var DDGSearchURLRegex = /^https:\/\/duckduckgo.com\/\?q=([^&]*).*/g, + trailingSlashRegex = /\/$/g, + plusRegex = /\+/g; - return item; +var currentACItem = null; +var deleteKeyPressed = false; - }, -} +var historyarea = searchbar.querySelector(".history-results"); +var $historyarea = $(historyarea); -//this is triggered from history.js - we only show search suggestions if we don't have history results -window.showSearchSuggestions = throttle(function (text, input) { +var maxHistoryResults = 4; +var currentHistoryResults = null; + +function searchbarAutocomplete(text, input, historyResults) { if (!text) { + currentACItem = null; return; } - //we don't show search suggestions in private tabs, since this would send typed text to DDG - - if (tabs.get(tabs.getSelected()).private) { + if (text == searchbarCachedText && input.selectionStart != input.selectionEnd) { //if nothing has actually changed, don't try to autocomplete + return; + } + //if we moved the selection, we don't want to autocomplete again + if (didFireKeydownSelChange) { return; } - if (BANG_REGEX.test(text)) { //we're typing a bang - var bang = text.match(BANG_REGEX)[0]; + currentACItem = null; - var bangACSnippet = cachedBangSnippets[bang]; + var didAutocomplete = false; + for (var i = 0; !didAutocomplete && i < historyResults.length; i++) { //we only want to autocomplete the first item that matches + didAutocomplete = autocompleteResultIfNeeded(input, historyResults[i]); //this returns true or false depending on whether the item was autocompleted or not } - $.ajax("https://ac.duckduckgo.com/ac/?q=" + encodeURIComponent(text)) - .done(function (results) { - - serarea.find(".result-item").addClass("old"); - - if (results && results[0] && results[0].snippet) { //!bang search - ddg api doesn't have a good way to detect this +} - results.splice(0, 5).forEach(function (result) { - cachedBangSnippets[result.phrase] = result.snippet; +function autocompleteResultIfNeeded(input, result) { - //autocomplete the bang, but allow the user to keep typing + //figure out if we should autocomplete based on the title - var item = $("
").append($("").text(result.snippet)).on("click", function () { - setTimeout(function () { //if the click was triggered by the keydown, focusing the input and then keyup will cause a navigation. Wait a bit for keyup before focusing the input again. - input.val(result.phrase + " ").get(0).focus(); - }, 100); - }); + DDGSearchURLRegex.lastIndex = 0; + shouldAutocompleteTitle = DDGSearchURLRegex.test(result.url); - $("").text(result.phrase).appendTo(item); + if (shouldAutocompleteTitle) { + result.title = decodeURIComponent(result.url.replace(DDGSearchURLRegex, "$1").replace(plusRegex, " ")); + } - $("").attr("src", result.image).prependTo(item); + var text = getValue(input); //make sure the input hasn't changed between start and end of query + try { + var hostname = new URL(result.url).hostname; + } catch (e) { + console.warn(result.url); + } - item.appendTo(serarea); - }); + var possibleAutocompletions = [ //the different variations of the URL we can autocomplete + hostname, //we start with the domain + (hostname + "/").replace(urlParser.startingWWWRegex, "$1").replace("/", ""), //if that doesn't match, try the hostname without the www instead. The regex requires a slash at the end, so we add one, run the regex, and then remove it + urlParser.prettyURL(result.url), //then try the whole url + urlParser.removeProtocol(result.url), //then try the url with querystring + result.url, //then just try the url with protocol + ] - } else if (results) { - results = results.splice(0, currentSuggestionLimit); + if (shouldAutocompleteTitle) { + possibleAutocompletions.push(result.title); + } - results.forEach(function (result) { - var title = result.phrase; - if (BANG_REGEX.test(result.phrase) && bangACSnippet) { - title = result.phrase.replace(BANG_REGEX, ""); - var secondaryText = "Search on " + bangACSnippet; - } - var item = $("
").append($("").text(title)).on("click", function (e) { - openURLFromsearchbar(e, result.phrase); - }); - item.appendTo(serarea); + for (var i = 0; i < possibleAutocompletions.length; i++) { + if (!deleteKeyPressed && possibleAutocompletions[i].toLowerCase().indexOf(text.toLowerCase()) == 0) { //we can autocomplete the item - if (urlParser.isURL(result.phrase) || urlParser.isURLMissingProtocol(result.phrase)) { //website suggestions - $("").prependTo(item); - } else { //regular search results - $("").prependTo(item); - } + input.value = possibleAutocompletions[i]; + input.setSelectionRange(text.length, possibleAutocompletions[i].length); - if (secondaryText) { - $("").text(secondaryText).appendTo(item); - } - }); + if (i < 2) { //if we autocompleted a domain, the cached item should be the domain, not the full url + var url = new URL(result.url); + currentACItem = url.protocol + "//" + url.hostname + "/"; + } else { + currentACItem = result.url; } + return true; + } + } - serarea.find(".old").remove(); - }); + //nothing was autocompleted -}, 500); + return false; +} -/* this is called from historySuggestions. When we find history results, we want to limit search suggestions to 2 so the searchbar doesn't get too large. */ +var showHistoryResults = throttle(function (text, input, maxItems) { -var limitSearchSuggestions = function (itemsToRemove) { - var itemsLeft = Math.max(minSearchSuggestions, maxSearchSuggestions - itemsToRemove); - currentSuggestionLimit = itemsLeft; - serarea.find(".result-item:nth-child(n+{items})".replace("{items}", itemsLeft + 1)).remove(); -} + if (!text && input.value) { //if the entire input is highlighted (such as when we first focus the input), don't show anything + return; + } -window.showInstantAnswers = debounce(function (text, input, options) { + if (text) { + text = text.trim(); + } - if (!text) { - iaarea.empty(); - suggestedsitearea.empty(); - return; - } + //if we're offline, the only sites that will work are reader articles, so we should show those as top sites - options = options || {}; + if (!text && !navigator.onLine) { + readerView.showReadingList({ + limitResults: true + }); + return; + } - //don't make useless queries - if (urlParser.isURLMissingProtocol(text)) { - return; - } + bookmarks.searchHistory(text, function (results) { - //don't send typed text in private mode - if (tabs.get(tabs.getSelected()).private) { - return; - } + currentHistoryResults = results; - //instant answers + var showedTopAnswer = false; - iaarea.find(".result-item").addClass("old"); - suggestedsitearea.find(".result-item").addClass("old"); + maxItems = maxItems || maxHistoryResults; - if (text.length > 3) { + //if there is no text, only history results will be shown, so we can assume that 4 results should be shown. + if (!text) { + maxItems = 4; + } - $.getJSON("https://api.duckduckgo.com/?skip_disambig=1&format=json&q=" + encodeURIComponent(text), function (res) { + empty(historyarea); - //if value has changed, don't show results - if (text != getValue(input) && !options.alwaysShow) { - return; + if (topAnswerarea.getElementsByClassName("history-item").length > 0) { + empty(topAnswerarea); } - iaarea.find(".result-item").addClass("old"); - suggestedsitearea.find(".result-item").addClass("old"); + searchbarAutocomplete(text, input, results); - //if there is a custom format for the answer, use that - if (IAFormats[res.AnswerType]) { - item = IAFormats[res.AnswerType](text, res.Answer); + if (text.indexOf("!") == 0) { + showSearchSuggestions(text, input, 5); + } else if (results.length < 10) { + maxItems = 3; + showSearchSuggestions(text, input, 5 - results.length); } else { + empty(serarea); + } - if (res.Abstract || res.Answer) { - var item = $("
"); + var resultsShown = 0; - if (res.Answer) { - item.text(removeTags(res.Answer)); - } else { - item.text(res.Heading); - } + //we will never have more than 5 results, so we don't need to create more DOM elements than that - if (res.Image && !res.ImageIsLogo) { - $("").attr("src", res.Image).prependTo(item); + requestAnimationFrame(function () { + + var isBangSearch = text.indexOf("!") == 0; + + results.slice(0, 4).forEach(function (result) { + + DDGSearchURLRegex.lastIndex = 0; + var isDDGSearch = DDGSearchURLRegex.test(result.url); + + //if we're doing a bang search, but the item isn't a web search, it probably isn't useful, so we shouldn't show it + if (!isDDGSearch && isBangSearch) { + return; } - $("").text(removeTags(res.Abstract) || "Answer").appendTo(item); - } - } + if (isDDGSearch) { //show the result like a search suggestion + var processedTitle = decodeURIComponent(result.url.replace(DDGSearchURLRegex, "$1").replace(plusRegex, " ")); - if (item) { - item.on("click", function (e) { - openURLFromsearchbar(e, res.AbstractURL || text); - }); + var data = { + icon: "fa-search", + title: processedTitle, + url: result.url, + classList: ["history-item"], + } + } else { + var data = { + icon: "fa-globe", + title: getRealTitle(result.title) || result.url, + url: result.url, + classList: ["history-item"], + } + + if (result.title !== result.url) { + data.secondaryText = urlParser.prettyURL(result.url); + } + } - //answers are more relevant, they should be displayed at the top - if (res.Answer) { - topAnswerarea.empty(); - item.appendTo(topAnswerarea); - } else { - item.appendTo(iaarea); - } - } + var item = createSearchbarItem(data); - //suggested site links + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, result.url); + }); + if (resultsShown >= maxItems) { //only show up to n history items + item.hidden = true; + item.classList.add("unfocusable"); + } - if (res.Results && res.Results[0] && res.Results[0].FirstURL) { + if (urlParser.areEqual(currentACItem, result.url) && resultsShown < maxItems && !showedTopAnswer) { //the item is being autocompleted, highlight it + item.classList.add("fakefocus"); + topAnswerarea.appendChild(item); + showedTopAnswer = true; + } else { + historyarea.appendChild(item) + } - var itemsWithSameURL = historyarea.find('.result-item[data-url="{url}"]'.replace("{url}", res.Results[0].FirstURL)); - if (itemsWithSameURL.length == 0) { + resultsShown++; - var url = urlParser.removeProtocol(res.Results[0].FirstURL).replace(trailingSlashRegex, ""); + }); - var item = $("
").append($("").text(url)).on("click", function (e) { + //show a top answer item if we did domain autocompletion - openURLFromsearchbar(e, res.Results[0].FirstURL); + if (currentACItem && !showedTopAnswer && !DDGSearchURLRegex.test(currentACItem)) { + var item = createSearchbarItem({ + classList: ["history-item", "fakefocus"], + icon: "fa-globe", + title: urlParser.prettyURL(currentACItem), }); - $("").prependTo(item); - - $("").text("Suggested site").appendTo(item); + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, currentACItem); + }); - item.appendTo(suggestedsitearea); + topAnswerarea.appendChild(item); } - } + }); + + }); + }, + 50); - //if we're showing a location, show a "view on openstreetmap" link +function limitHistoryResults(maxItems) { + maxHistoryResults = Math.min(4, Math.max(maxItems, 2)); - var entitiesWithLocations = ["location", "country", "u.s. state", "protected area"] + $historyarea.find(".result-item:nth-child(n+{items})".replace("{items}", maxHistoryResults + 1)).prop("hidden", true).addClass("unfocusable"); +} +;var bookmarkarea = searchbar.querySelector(".bookmark-results"); - if (entitiesWithLocations.indexOf(res.Entity) != -1) { - var item = $("
"); +function addBookmarkItem(result) { - $("").appendTo(item); - $("").text(res.Heading).appendTo(item); - $("Search on OpenStreetMap").appendTo(item); + //create the basic item + //getRealTitle is defined in searchbar.js - item.on("click", function (e) { - openURLFromsearchbar(e, "https://www.openstreetmap.org/search?query=" + encodeURIComponent(res.Heading)); - }); + var item = createSearchbarItem({ + icon: "fa-star", + title: getRealTitle(result.title), + secondaryText: urlParser.prettyURL(result.url), + url: result.url, + }); - item.prependTo(iaarea); - } + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, result.url); + }); - if (options.destroyPrevious != false || item) { - iaarea.find(".old").remove(); - suggestedsitearea.find(".old").remove(); - } + if (result.extraData && result.extraData.metadata) { + var secondaryText = item.querySelector(".secondary-text"); - }); - } else { - iaarea.find(".old").remove(); //we still want to remove old items, even if we didn't make a new request - suggestedsitearea.find(".old").remove(); - } + for (var md in result.extraData.metadata) { + var span = document.createElement("span"); -}, 450); -;var spacesRegex = /[\s._/-]/g; //copied from historyworker.js + span.className = "md-info"; + span.textContent = result.extraData.metadata[md]; -var stringScore = require("string_score"); + secondaryText.insertBefore(span, secondaryText.firstChild) + } -var searchOpenTabs = function (searchText) { + } - opentabarea.empty(); + bookmarkarea.appendChild(item); +} - if (searchText.length < 3) { +var showBookmarkResults = debounce(function (text) { + if (text.length < 5 || text.indexOf("!") == 0) { //if there is not enough text, or we're doing a bang search, don't show results + limitHistoryResults(5); + empty(bookmarkarea); return; } - var matches = [], - selTab = tabs.getSelected(); + bookmarks.searchBookmarks(text, function (results) { + empty(bookmarkarea); + var resultsShown = 1; + results.splice(0, 2).forEach(function (result) { - tabs.get().forEach(function (item) { - if (item.id == selTab || !item.title || item.url == "about:blank") { - return; - } + //if a history item for the same page already exists, don't show a bookmark + if ($('.result-item[data-url="{url}"]:not([hidden])'.replace("{url}", result.url))[0]) { + return; + } - item.url = urlParser.removeProtocol(item.url); //don't search protocols + //as more results are added, the threshold for adding another one gets higher + if (result.score > Math.max(0.0004, 0.0016 - (0.00012 * Math.pow(1.25, text.length))) && (resultsShown == 1 || text.length > 6)) { + requestAnimationFrame(function () { + addBookmarkItem(result); + }); + resultsShown++; + } - var exactMatch = item.title.indexOf(searchText) != -1 || item.url.indexOf(searchText) != -1 - var fuzzyMatch = item.title.substring(0, 50).score(searchText, 0.5) > 0.4 || item.url.score(searchText, 0.5) > 0.4; + }); + limitHistoryResults(5 - resultsShown); //if we have lots of bookmarks, don't show as many regular history items - if (exactMatch || fuzzyMatch) { - matches.push(item); - } }); +}, 133); - matches.splice(0, 2).sort(function (a, b) { - return b.title.score(searchText, 0.5) - a.title.score(searchText, 0.5); - }).forEach(function (tab) { - var item = $("
").append($("").text(tab.title)) - $("").text(urlParser.removeProtocol(tab.url).replace(trailingSlashRegex, "")).appendTo(item); - - $("").attr("title", "Switch to Tab").prependTo(item); //TODO better icon +var showAllBookmarks = function () { + bookmarks.searchBookmarks("", function (results) { - item.on("click", function () { - //if we created a new tab but are switching away from it, destroy the current (empty) tab - if (tabs.get(tabs.getSelected()).url == "about:blank") { - destroyTab(tabs.getSelected(), { - switchToTab: false - }); - } - switchToTab(tab.id); + results.sort(function (a, b) { + //http://stackoverflow.com/questions/6712034/sort-array-by-firstname-alphabetically-in-javascript + if (a.url < b.url) return -1; + if (a.url > b.url) return 1; + return 0; }); - - item.appendTo(opentabarea); + results.forEach(addBookmarkItem); }); } -;var searchbarCachedText = ""; -var METADATA_SEPARATOR = "·"; -var didFireKeydownSelChange = false; -var currentsearchbarInput; +;var bangRegex = /!\w+/g; +var serarea = searchbar.querySelector(".search-engine-results"); +var iaarea = searchbar.querySelector(".instant-answer-results"); +var topAnswerarea = searchbar.querySelector(".top-answer-results"); +var suggestedsitearea = searchbar.querySelector("#searchbar .ddg-site-results"); //cache duckduckgo bangs so we make fewer network requests var cachedBangSnippets = {}; -//https://remysharp.com/2010/07/21/throttling-function-calls# +/* custom answer layouts */ -function throttle(fn, threshhold, scope) { - threshhold || (threshhold = 250); - var last, - deferTimer; - return function () { - var context = scope || this; +var IAFormats = { + color_code: function (searchText, answer) { + var alternateFormats = [answer.data.rgb, answer.data.hslc, answer.data.cmyb]; - var now = +new Date, - args = arguments; - if (last && now < last + threshhold) { - // hold on to it - clearTimeout(deferTimer); - deferTimer = setTimeout(function () { - last = now; - fn.apply(context, args); - }, threshhold); - } else { - last = now; - fn.apply(context, args); + if (searchText.indexOf("#") == -1) { //if the search is not a hex code, show the hex code as an alternate format + alternateFormats.unshift(answer.data.hexc); } - }; -} -function debounce(fn, delay) { - var timer = null; - return function () { - var context = this, - args = arguments; - clearTimeout(timer); - timer = setTimeout(function () { - fn.apply(context, args); - }, delay); - }; -} - -function removeTags(text) { - return text.replace(/<.*?>/g, ""); -} - -/* this is used by navbar-tabs.js. When a url is entered, endings such as ? need to be parsed and removed. */ -function parsesearchbarURL(url) { - //always use a search engine if the query starts with "?" - - if (url.indexOf("?") == 0) { - url = urlParser.searchBaseURL.replace("%s", encodeURIComponent(url.replace("?", ""))); - } + var item = $("
"); + $("").text(searchText).appendTo(item); - if (url.indexOf("^") == 0) { - url = url.replace("^", ""); - } + $("
").css("background-color", "#" + answer.data.hex_code).prependTo(item); - if (url.indexOf("*") == 0) { - url = url.replace("*", ""); - } + $("").text(alternateFormats.join(" " + METADATA_SEPARATOR + " ")).appendTo(item); - return url; -} + return item; + }, + minecraft: function (searchText, answer) { -function openURLInBackground(url) { //used to open a url in the background, without leaving the searchbar - var newTab = tabs.add({ - url: url, - private: tabs.get(tabs.getSelected()).private - }, tabs.getIndex(tabs.getSelected()) + 1); - addTab(newTab, { - focus: false, - openInBackground: true, - leaveEditMode: false, - }); - $(".result-item:focus").blur(); //remove the highlight from an awesoembar result item, if there is one -} + var item = $("
"); -//when clicking on a result item, this function should be called to open the URL + $("").text(answer.data.title).appendTo(item); + $("").attr("src", answer.data.image).prependTo(item); + $("").text(answer.data.description + " " + answer.data.subtitle).appendTo(item); -function openURLFromsearchbar(event, url) { - if (event.metaKey) { - openURLInBackground(url); - return true; - } else { - navigate(tabs.getSelected(), url); - return false; - } -} + return item; + }, + figlet: function (searchText, answer) { + var formattedAnswer = removeTags(answer).replace("Font: standard", ""); + var item = $("
"); + var desc = $("").text(formattedAnswer).appendTo(item); -//attempts to shorten a page title, removing useless text like the site name + //display the data correctly + desc.css({ + "white-space": "pre-wrap", + "font-family": "monospace", + "max-height": "10em", + "-webkit-user-select": "auto", + }); -function getRealTitle(text) { + return item; - //don't try to parse URL's - if (urlParser.isURL(text)) { - return text; - } + }, + currency_in: function (searchText, answer) { + var item = $("
"); + var title = ""; + if (typeof answer == "string") { //there is only one currency + title = answer; + } else { //multiple currencies + var currencyArr = [] + for (var countryCode in answer.data.record_data) { + currencyArr.push(answer.data.record_data[countryCode] + " (" + countryCode + ")"); + } - var possibleCharacters = ["|", ":", " - ", " — "]; + title = currencyArr.join(", "); + } - for (var i = 0; i < possibleCharacters.length; i++) { + var desc = $("").text(title).appendTo(item); + if (answer.data) { + var subtitle = $("").text(answer.data.title).appendTo(item); + } else { + var subtitle = $("").text("Answer").appendTo(item); + } - var char = possibleCharacters[i]; - //match url's of pattern: title | website name - var titleChunks = text.split(char); + return item; + }, +} - if (titleChunks.length >= 2) { - titleChunks[0] = titleChunks[0].trim(); - titleChunks[1] = titleChunks[1].trim(); +//this is triggered from history.js - we only show search suggestions if we don't have history results +window.showSearchSuggestions = throttle(function (text, input, itemsToShow) { - if (titleChunks[1].length < 5 || titleChunks[1].length / titleChunks[0].length <= 0.5) { - return titleChunks[0] - } - } + if (!text || tabs.get(tabs.getSelected()).private) { //we don't show search suggestions in private tabs, since this would send typed text to DDG + return; } - //fallback to the regular title + itemsToShow = Math.max(2, itemsToShow); - return text; -} + fetch("https://ac.duckduckgo.com/ac/?q=" + encodeURIComponent(text)) + .then(function (response) { + return response.json(); + }) + .then(function (results) { -var searchbar = $("#searchbar"); -var historyarea = searchbar.find(".history-results"); -var bookmarkarea = searchbar.find(".bookmark-results"); -var opentabarea = searchbar.find(".opentab-results"); + empty(serarea); -function clearsearchbar() { - opentabarea.empty(); - topAnswerarea.empty(); - bookmarkarea.empty(); - historyarea.empty(); - iaarea.empty(); - suggestedsitearea.empty(); - serarea.empty(); + if (results && results[0] && results[0].snippet) { //!bang search - ddg api doesn't have a good way to detect this - //prevent memory leak - cachedBangSnippets = []; -} + results.splice(0, 5).forEach(function (result) { + cachedBangSnippets[result.phrase] = result.snippet; -function showSearchbar(triggerInput) { - searchbarCachedText = triggerInput.val(); - $(document.body).addClass("searchbar-shown"); + //autocomplete the bang, but allow the user to keep typing - clearsearchbar(); + var data = { + image: result.image, + imageIsInline: true, + title: result.snippet, + secondaryText: result.phrase + } + var item = createSearchbarItem(data); - searchbar.prop("hidden", false); + item.addEventListener("click", function () { + setTimeout(function () { + input.value = result.phrase + " "; + input.focus(); + }, 66); + }); - currentsearchbarInput = triggerInput; + serarea.appendChild(item); + }); -} + } else if (results) { + results.splice(0, itemsToShow).forEach(function (result) { -//gets the typed text in an input, ignoring highlighted suggestions + var data = { + title: result.phrase, + classList: ["iadata-onfocus"], + } -function getValue(input) { - var text = input.val(); - return text.replace(text.substring(input[0].selectionStart, input[0].selectionEnd), ""); -} + if (bangRegex.test(result.phrase)) { -function hidesearchbar() { - currentsearchbarInput = null; - $(document.body).removeClass("searchbar-shown"); - searchbar.prop("hidden", true); - cachedBangSnippets = {}; -} -var showSearchbarResults = function (text, input, event) { + data.title = result.phrase.replace(bangRegex, ""); - if (event && event.metaKey) { - return; - } + var bang = result.phrase.match(bangRegex)[0]; + data.secondaryText = "Search on " + cachedBangSnippets[bang]; + } + + if (urlParser.isURL(result.phrase) || urlParser.isURLMissingProtocol(result.phrase)) { //website suggestions + data.icon = "fa-globe"; + } else { //regular search results + data.icon = "fa-search"; + } - deleteKeyPressed = event && event.keyCode == 8; + var item = createSearchbarItem(data); - //find the real input value, accounting for highlighted suggestions and the key that was just pressed + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, result.phrase); + }); - var v = input[0].value; + serarea.appendChild(item); + }); + } + }); - //delete key doesn't behave like the others, String.fromCharCode returns an unprintable character (which has a length of one) +}, 500); - if (event && event.keyCode != 8) { +window.showInstantAnswers = debounce(function (text, input, options) { - text = v.substring(0, input[0].selectionStart) + String.fromCharCode(event.keyCode) + v.substring(input[0].selectionEnd + 1, v.length).trim(); + options = options || {}; - } else { - txt = v; + if (!text) { + empty(iaarea); + empty(suggestedsitearea); + return; } - console.log("searchbar: ", "'" + text + "'", text.length); + //don't make useless queries + if (urlParser.isURLMissingProtocol(text)) { + return; + } - //there is no text, show only topsites - if (text.length < 1) { - showHistoryResults("", input); - clearsearchbar(); + //don't send typed text in private mode + if (tabs.get(tabs.getSelected()).private) { return; } - //when you start with ?, always search with duckduckgo + if (text.length > 3) { - if (text.indexOf("?") == 0) { - clearsearchbar(); + fetch("https://api.duckduckgo.com/?skip_disambig=1&no_redirect=1&format=json&q=" + encodeURIComponent(text)) + .then(function (data) { + return data.json(); + }) + .then(function (res) { - maxSearchSuggestions = 5; - showSearchSuggestions(text.replace("?", ""), input); - return; - } + //if value has changed, don't show results + if (text != getValue(input) && !options.alwaysShow) { + return; + } - //when you start with ^, always search history (only) + //if there is a custom format for the answer, use that + if (IAFormats[res.AnswerType]) { + var item = IAFormats[res.AnswerType](text, res.Answer).get(0); - if (text.indexOf("^") == 0) { - clearsearchbar(); - showHistoryResults(text.replace("^", ""), input); - return; - } + } else if (res.Abstract || res.Answer) { - //when you start with *, always search bookmarks (only) + var data = { + title: removeTags(res.Answer || res.Heading), + descriptionBlock: res.Abstract || "Answer", + classList: ["ddg-answer", "indent"] + } - if (text.indexOf("*") == 0) { - clearsearchbar(); - showBookmarkResults(text.replace("*", ""), input); - return; - } + if (res.Image && !res.ImageIsLogo) { + data.image = res.Image; + } - //show searchbar results + var item = createSearchbarItem(data); + } + if (options.destroyPrevious != false || item) { + $searchbar.find(".ddg-answer").remove(); + } - //show results if a !bang search is occuring - if (text.indexOf("!") == 0) { + if (item) { + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, res.AbstractURL || text); + }); - showSearchSuggestions(text, input); - } + //answers are more relevant, they should be displayed at the top + if (res.Answer) { + empty(topAnswerarea); + topAnswerarea.appendChild(item); + } else { + iaarea.appendChild(item); + } - showBookmarkResults(text); + } - showHistoryResults(text, input); - showInstantAnswers(text, input); - searchOpenTabs(text, input); + //suggested site links - //update cache - searchbarCachedText = text; -}; -function focussearchbarItem(options) { - options = options || {}; //fallback if options is null - var previous = options.focusPrevious; - var allItems = $("#searchbar .result-item:not(.unfocusable)"); - var currentItem = $("#searchbar .result-item:focus, .result-item.fakefocus"); - var index = allItems.index(currentItem); - var logicalNextItem = allItems.eq((previous) ? index - 1 : index + 1); + if (res.Results && res.Results[0] && res.Results[0].FirstURL) { - searchbar.find(".fakefocus").removeClass("fakefocus"); //clear previously focused items + var url = res.Results[0].FirstURL; - if (currentItem[0] && logicalNextItem[0]) { //an item is focused and there is another item after it, move onto the next one - logicalNextItem.get(0).focus(); - } else if (currentItem[0]) { //the last item is focused, focus the searchbar again - getTabElement(tabs.getSelected()).getInput().get(0).focus(); - } else { // no item is focused. - $("#searchbar .result-item").first().get(0).focus(); - } + var data = { + icon: "fa-globe", + title: urlParser.removeProtocol(url).replace(trailingSlashRegex, ""), + secondaryText: "Suggested site", + url: url, + classList: ["ddg-answer"], + } - var focusedItem = $("#searchbar .result-item:focus"); + var item = createSearchbarItem(data); - if (focusedItem.hasClass("iadata-onfocus")) { + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, res.Results[0].FirstURL); + }); - setTimeout(function () { - if (focusedItem.is(":focus")) { - var itext = focusedItem.find(".title").text(); + suggestedsitearea.appendChild(item); + } - showInstantAnswers(itext, currentsearchbarInput, { - alwaysShow: true, - destroyPrevious: false, - }); - } - }, 200); - } -} + //if we're showing a location, show a "Search on OpenStreetMap" link -//return key on result items should trigger click -//tab key or arrowdown key should focus next item -//arrowup key should focus previous item + var entitiesWithLocations = ["location", "country", "u.s. state", "protected area"]; -searchbar.on("keydown", ".result-item", function (e) { - if (e.keyCode == 13) { - $(this).trigger("click"); - } else if (e.keyCode == 9 || e.keyCode == 40) { //tab or arrowdown key - e.preventDefault(); - focussearchbarItem(); - } else if (e.keyCode == 38) { - e.preventDefault(); - focussearchbarItem({ - focusPrevious: true - }); - } -}); + if (entitiesWithLocations.indexOf(res.Entity) != -1) { -//swipe left on history items to delete them + var item = createSearchbarItem({ + icon: "fa-search", + title: res.Heading, + secondaryText: "Search on OpenStreetMap", + classList: ["ddg-answer"] + }); -var lastItemDeletion = Date.now(); + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, "https://www.openstreetmap.org/search?query=" + encodeURIComponent(res.Heading)); + }); -searchbar.on("mousewheel", ".history-results .result-item, .top-answer-results .result-item", function (e) { - var self = $(this) - if (e.originalEvent.deltaX > 50 && e.originalEvent.deltaY < 3 && self.attr("data-url") && Date.now() - lastItemDeletion > 700) { - lastItemDeletion = Date.now(); - self.animate({ - opacity: "0", - "margin-left": "-100%" - }, 200, function () { - self.remove(); - bookmarks.deleteHistory(self.attr("data-url")); - lastItemDeletion = Date.now(); - }); - } -}); + iaarea.insertBefore(item, iaarea.firstChild); + } -//when we get keywords data from the page, we show those results in the searchbar -bindWebviewIPC("keywordsData", function (webview, tabId, arguements) { + }) + .catch(function (e) { + console.error(e); + }); + } else { + $searchbar.find(".ddg-answer").remove(); //we still want to remove old items, even if we didn't make a new request + } - var data = arguements[0]; +}, 450); +;var spacesRegex = /[\s._/-]/g; //copied from historyworker.js +var opentabarea = searchbar.querySelector(".opentab-results"); - var itemsCt = 0; +var stringScore = require("string_score"); - var itemsShown = []; +var searchOpenTabs = function (searchText) { + requestAnimationFrame(function () { - data.entities.forEach(function (item, index) { + empty(opentabarea); - //ignore one-word items, they're usually useless - if (!/\s/g.test(item.trim())) { + if (searchText.length < 3) { return; } - if (itemsCt >= 5 || itemsShown.indexOf(item.trim()) != -1) { - return; - } + var matches = [], + selTab = tabs.getSelected(); - var div = $("
").append($("").text(item)).on("click", function (e) { - if (e.metaKey) { - openURLInBackground(item); - } else { - navigate(tabs.getSelected(), item); + tabs.get().forEach(function (item) { + if (item.id == selTab || !item.title || item.url == "about:blank") { + return; + } + + var itemUrl = urlParser.removeProtocol(item.url); //don't search protocols + + var exactMatch = item.title.indexOf(searchText) != -1 || itemUrl.indexOf(searchText) != -1 + var fuzzyMatch = item.title.substring(0, 50).score(searchText, 0.5) > 0.4 || itemUrl.score(searchText, 0.5) > 0.4; + + if (exactMatch || fuzzyMatch) { + matches.push(item); } }); - $("").prependTo(div); + matches.splice(0, 2).sort(function (a, b) { + return b.title.score(searchText, 0.5) - a.title.score(searchText, 0.5); + }).forEach(function (tab) { + var data = { + icon: "fa-external-link-square", + title: tab.title, + secondaryText: urlParser.removeProtocol(tab.url).replace(trailingSlashRegex, "") + } - div.appendTo(serarea); + var item = createSearchbarItem(data); - itemsCt++; - itemsShown.push(item.trim()); + item.addEventListener("click", function () { + //if we created a new tab but are switching away from it, destroy the current (empty) tab + if (tabs.get(tabs.getSelected()).url == "about:blank") { + destroyTab(tabs.getSelected(), { + switchToTab: false + }); + } + switchToTab(tab.id); + }); + + opentabarea.appendChild(item); + }); }); -}); +} ;var readerView = { readerURL: "file://" + __dirname + "/reader/index.html", getButton: function (tabId) { //TODO better icon - return $("").attr("data-tab", tabId).attr("title", "Enter reader view"); + var item = $("").attr("data-tab", tabId).attr("title", "Enter reader view"); + + item.on("click", function (e) { + var tabId = $(this).parents(".tab-item").attr("data-tab"); + var tab = tabs.get(tabId); + + e.stopPropagation(); + + if (tab.isReaderView) { + readerView.exit(tabId); + } else { + readerView.enter(tabId); + } + }); + + return item; }, updateButton: function (tabId) { var button = $('.reader-button[data-tab="{id}"]'.replace("{id}", tabId)); var tab = tabs.get(tabId); - button.off(); - if (tab.isReaderView) { button.addClass("is-reader").attr("title", "Exit reader view"); - button.on("click", function (e) { - e.stopPropagation(); - readerView.exit(tabId); - }); return; } else { button.removeClass("is-reader").attr("title", "Enter reader view"); @@ -1722,10 +1879,6 @@ bindWebviewIPC("keywordsData", function (webview, tabId, arguements) { if (tab.readerable) { button.addClass("can-reader"); - button.on("click", function (e) { - e.stopPropagation(); - readerView.enter(tabId); - }); } else { button.removeClass("can-reader"); } @@ -1740,8 +1893,64 @@ bindWebviewIPC("keywordsData", function (webview, tabId, arguements) { navigate(tabId, tabs.get(tabId).url.split("?url=")[1]); tabs.update(tabId, { isReaderView: false - }) - } + }); + }, + showReadingList: function (options) { + if (performance.now() < 1000) { //history hasn't loaded yet + return; + } + + bookmarks.searchHistory(readerView.readerURL, function (data) { + var cTime = Date.now(); + var oneWeekInMS = 7 * 24 * 60 * 60 * 1000; + + function calculateReadingListScore(article) { + return article.lastVisit + (5000 * article.visitCount); + } + + var articles = data.filter(function (item) { + return item.url.indexOf(readerView.readerURL) == 0 && (cTime - item.lastVisit < oneWeekInMS || (cTime - item.lastVisit < oneWeekInMS * 3 && item.visitCount == 1)) + }).sort(function (a, b) { + return calculateReadingListScore(b) - calculateReadingListScore(a); + }); + + if (options && options.limitResults) { + articles = articles.slice(0, 4); + } + + articles.forEach(function (article) { + var item = createSearchbarItem({ + title: article.title, + secondaryText: urlParser.prettyURL(article.url.replace(readerView.readerURL + "?url=", "")), + url: article.url + }); + + item.addEventListener("click", function (e) { + openURLFromsearchbar(e, article.url); + }); + + historyarea.appendChild(item); + }); + + if (options && options.limitResults) { + + var seeMoreLink = createSearchbarItem({ + title: "More articles", + }); + + seeMoreLink.style.opacity = 0.5; + + seeMoreLink.addEventListener("click", function (e) { + clearsearchbar(); + readerView.showReadingList({ + limitResults: false + }); + }); + + historyarea.appendChild(seeMoreLink); + } + }); + }, } //update the reader button on page load @@ -1752,19 +1961,16 @@ bindWebviewEvent("did-finish-load", function (e) { if (url.indexOf(readerView.readerURL) == 0) { tabs.update(tab, { - isReaderView: true + isReaderView: true, + readerable: false, //assume the new page can't be readered, we'll get another message if it can }) } else { tabs.update(tab, { - isReaderView: false + isReaderView: false, + readerable: false, }) } - //assume the new page can't be readered, we'll get another message if it can - - tabs.update(tab, { - readerable: false, - }); readerView.updateButton(tab); }); @@ -2034,6 +2240,22 @@ function setColor(bg, fg) { $(document.body).removeClass("dark-theme"); } } + +/* converts a color string into an object that can be used with getTextColor */ + +function getRGBObject(cssColor) { + var c = cssColor.split("(")[1].split(")")[0] + var c2 = c.split(","); + + var obj = { + r: parseInt(c2[0]) / 255, + g: parseInt(c2[1]) / 255, + b: parseInt(c2[2]) / 255, + } + + return obj; + +} ;//http://stackoverflow.com/a/5086688/4603285 jQuery.fn.insertAt = function (index, element) { @@ -2067,7 +2289,7 @@ tabGroup.on("mousewheel", ".tab-item", function (e) { return; } - var tab = $(this).attr("data-tab"); + var tab = this.getAttribute("data-tab"); //TODO this should be a css animation getTabElement(tab).animate({ @@ -2076,7 +2298,7 @@ tabGroup.on("mousewheel", ".tab-item", function (e) { if (tab == tabs.getSelected()) { var currentIndex = tabs.getIndex(tabs.getSelected()); - var nextTab = tabs.getAtIndex(currentIndex + 1) || tabs.getAtIndex(currentIndex - 1); + var nextTab = tabs.getAtIndex(currentIndex - 1) || tabs.getAtIndex(currentIndex + 1); destroyTab(tab); @@ -2102,7 +2324,7 @@ tabGroup.on("mousewheel", ".tab-item", function (e) { //click to enter edit mode or switch to tab tabGroup.on("click", ".tab-item", function (e) { - var tabId = $(this).attr("data-tab"); + var tabId = this.getAttribute("data-tab"); //if the tab isn't focused if (tabs.getSelected() != tabId) { @@ -2140,11 +2362,13 @@ function setActiveTabElement(tabId) { if (!isExpandedMode) { requestIdleCallback(function () { - el[0].scrollIntoView({ - behavior: "smooth" + requestAnimationFrame(function () { + el[0].scrollIntoView({ + behavior: "smooth" + }); }); }, { - timeout: 1000 + timeout: 1500 }); } @@ -2154,7 +2378,10 @@ function setActiveTabElement(tabId) { function leaveTabEditMode(options) { $(".tab-item.selected").removeClass("selected"); if (options && options.blur) { - $(".tab-item .tab-input").blur(); + var input = document.querySelector(".tab-item .tab-input:focus") + if (input) { + input.blur(); + } } tabGroup.removeClass("has-selected-tab"); hidesearchbar(); @@ -2180,7 +2407,7 @@ function enterEditMode(tabId) { input.get(0).focus(); input.select(); showSearchbar(input); - showSearchbarResults("", input, null); + showSearchbarResults("", input.get(0), null); tabGroup.addClass("has-selected-tab"); //show keyword suggestions in the searchbar @@ -2197,7 +2424,10 @@ function rerenderTabElement(tabId) { tabData = tabs.get(tabId); var tabTitle = tabData.title || "New Tab"; - tabEl.find(".tab-view-contents .title").text(tabTitle).attr("title", tabTitle); + var title = tabEl.get(0).querySelector(".tab-view-contents .title"); + + title.textContent = tabTitle; + title.title = tabTitle; var secIcon = tabEl[0].getElementsByClassName("icon-tab-not-secure"); @@ -2210,7 +2440,7 @@ function rerenderTabElement(tabId) { } //update the star to reflect whether the page is bookmarked or not - bookmarks.renderStar(tabId, tabEl.find(".bookmarks-button")); + bookmarks.renderStar(tabId); } function createTabElement(tabId) { @@ -2247,8 +2477,6 @@ function createTabElement(tabId) { vc.append(""); vc.appendTo(tab); - - /* events */ input.on("keydown", function (e) { @@ -2261,7 +2489,7 @@ function createTabElement(tabId) { //keypress doesn't fire on delete key - use keyup instead input.on("keyup", function (e) { if (e.keyCode == 8) { - showSearchbarResults($(this).val(), $(this), e); + showSearchbarResults(this.value, this, e); } }); @@ -2269,13 +2497,12 @@ function createTabElement(tabId) { if (e.keyCode == 13) { //return key pressed; update the url var tabId = $(this).parents(".tab-item").attr("data-tab"); - var newURL = parsesearchbarURL($(this).val()); + var newURL = currentACItem || parsesearchbarURL(this.value); - navigate(tabId, newURL); - leaveTabEditMode(tabId); + openURLFromsearchbar(e, newURL); //focus the webview, so that autofocus inputs on the page work - getWebview(tabs.getSelected())[0].focus(); + getWebview(tabs.getSelected()).get(0).focus(); } else if (e.keyCode == 9) { return; @@ -2287,7 +2514,7 @@ function createTabElement(tabId) { return; //delete key is handled in keyUp } else { //show the searchbar - showSearchbarResults($(this).val(), $(this), e); + showSearchbarResults(this.value, this, e); } //on keydown, if the autocomplete result doesn't change, we move the selection instead of regenerating it to avoid race conditions with typing. Adapted from https://github.com/patrickburke/jquery.inlineComplete @@ -2358,9 +2585,11 @@ function addTab(tabId, options) { return; } - switchToTab(tabId); + switchToTab(tabId, { + focusWebview: false + }); - if (options.focus != false) { + if (options.enterEditMode != false) { enterEditMode(tabId) } } @@ -2400,8 +2629,6 @@ tabContainer.on("mousewheel", function (e) { if (e.originalEvent.deltaY < -30 && e.originalEvent.deltaX < 10) { //swipe down to expand tabs enterExpandedMode(); e.stopImmediatePropagation(); - } else if (e.originalEvent.deltaY > 70 && e.originalEvent.deltaX < 10) { - leaveExpandedMode(); } }); @@ -2430,20 +2657,17 @@ function enterExpandedMode() { //get the subtitles tabs.get().forEach(function (tab) { - try { - var prettyURL = urlParser.prettyURL(tab.url); - } catch (e) { - var prettyURL = ""; - } + var prettyURL = urlParser.prettyURL(tab.url); - var tabEl = getTabElement(tab.id); - - tabEl.find(".secondary-text").text(prettyURL); + getTabElement(tab.id).find(".secondary-text").text(prettyURL); }); - $(document.body).addClass("is-expanded-mode"); - getWebview(tabs.getSelected()).blur(); - tabContainer.get(0).focus(); + requestAnimationFrame(function () { + + $(document.body).addClass("is-expanded-mode"); + tabContainer.get(0).focus(); + + }); isExpandedMode = true; } @@ -2490,10 +2714,22 @@ ipc.on("print", function () { getWebview(tabs.getSelected())[0].print(); }) +ipc.on("findInPage", function () { + findinpage.start(); +}) + ipc.on("inspectPage", function () { getWebview(tabs.getSelected())[0].openDevTools(); }); +ipc.on("showReadingList", function () { + showSearchbar(getTabElement(tabs.getSelected()).getInput()); + enterEditMode(tabs.getSelected()); + clearsearchbar(); + + readerView.showReadingList(); +}) + ipc.on("addTab", function (e) { /* new tabs can't be created in focus mode */ @@ -2557,7 +2793,7 @@ require.async("mousetrap", function (Mousetrap) { var currentTab = tabs.getSelected(); var currentIndex = tabs.getIndex(currentTab); - var nextTab = tabs.getAtIndex(currentIndex + 1) || tabs.getAtIndex(currentIndex - 1); + var nextTab = tabs.getAtIndex(currentIndex - 1) || tabs.getAtIndex(currentIndex + 1); destroyTab(currentTab); if (nextTab) { @@ -2579,10 +2815,6 @@ require.async("mousetrap", function (Mousetrap) { getTabElement(tabs.getSelected()).find(".bookmarks-button").click(); }) - Mousetrap.bind("command+f", function (e) { - findinpage.toggle(); - }); - // cmd+x should switch to tab x. Cmd+9 should switch to the last tab for (var i = 1; i < 9; i++) { @@ -2728,8 +2960,10 @@ ipc.on("openPDF", function (event, filedata) { }, tabs.getIndex(tabs.getSelected()) + 1); addTab(newTab, { - focus: false + enterEditMode: false }); + + getWebview(newTab).get(0).focus(); } }); ;var findinpage = { diff --git a/dist/build.min.js b/dist/build.min.js index 24551136b..e929040b1 100644 --- a/dist/build.min.js +++ b/dist/build.min.js @@ -1,3 +1,3 @@ -/*! 01-08-2016 */ -function bindWebviewEvent(a,b){webviewEvents.push({event:a,fn:b})}function bindWebviewIPC(a,b){webviewIPC.push({name:a,fn:b})}function getWebviewDom(a){var b=(a||{}).url||"about:blank",c=$("");return c.attr("preload","dist/webview.min.js"),c.attr("src",urlParser.parse(b)),c.attr("data-tab",a.tabId),1==tabs.get(a.tabId)["private"]&&c.attr("partition",a.tabId),webviewEvents.forEach(function(a){c.on(a.event,a.fn)}),c.on("page-favicon-updated",function(a){var b=$(this).attr("data-tab");updateTabColor(a.originalEvent.favicons,b)}),c.on("page-title-set",function(a){var b=$(this).attr("data-tab");tabs.update(b,{title:a.originalEvent.title}),rerenderTabElement(b)}),c.on("did-finish-load",function(a){var b=$(this).attr("data-tab"),c=$(this).attr("src");0===c.indexOf("https://")||0==c.indexOf("about:")||0==c.indexOf("chrome:")||0==c.indexOf("file://")?tabs.update(b,{secure:!0,url:c}):tabs.update(b,{secure:!1,url:c});var d=-1!=c.indexOf(__dirname)&&-1==c.indexOf(readerView.readerURL);0!=tabs.get(b)["private"]||d||bookmarks.updateHistory(b),rerenderTabElement(b),this.send("loadfinish")}),c.on("new-window",function(a){var b=$(this).attr("data-tab"),c=tabs.getIndex(tabs.getSelected()),d=tabs.add({url:a.originalEvent.url,"private":tabs.get(b)["private"]},c+1);addTab(d,{focus:!1,openInBackground:"background-tab"==a.originalEvent.disposition})}),c.on("ipc-message",function(a){var b=this,c=$(this).attr("data-tab");webviewIPC.forEach(function(d){d.name==a.originalEvent.channel&&d.fn(b,c,a.originalEvent.args)}),"bookmarksData"==a.originalEvent.channel?bookmarks.onDataRecieved(a.originalEvent.args[0]):"phishingDetected"==a.originalEvent.channel&&navigate($(this).attr("data-tab"),phishingWarningPage)}),c.on("contextmenu",webviewMenu.show),c.on("crashed",function(a){var b=$(this).attr("data-tab");destroyWebview(b),tabs.update(b,{url:crashedWebviewPage}),addWebview(b),switchToWebview(b)}),c.on("did-fail-load",function(a){-3!=a.originalEvent.errorCode&&a.originalEvent.validatedURL==a.target.getURL()&&navigate($(this).attr("data-tab"),errorPage+"?ec="+encodeURIComponent(a.originalEvent.errorCode)+"&url="+a.target.getURL())}),c}function addWebview(a){var b=tabs.get(a),c=getWebviewDom({tabId:a,url:b.url});c.addClass("hidden"),webviewBase.append(c)}function switchToWebview(a,b){$("webview").prop("hidden",!0);var c=getWebview(a);c.removeClass("hidden").prop("hidden",!1),b&&b.focus&&c[0].focus()}function updateWebview(a,b){getWebview(a).attr("src",urlParser.parse(b))}function destroyWebview(a){$('webview[data-tab="{id}"]'.replace("{id}",a)).remove()}function getWebview(a){return $('webview[data-tab="{id}"]'.replace("{id}",a))}function navigate(a,b){b=urlParser.parse(b),tabs.update(a,{url:b}),updateWebview(a,b),leaveTabEditMode({blur:!0})}function switchToNextTab(a){var b=tabs.getAtIndex(a+1)||tabs.getAtIndex(a-1);b&&switchToTab(b.id)}function destroyTab(a){getTabElement(a).remove();tabs.destroy(a);destroyWebview(a)}function switchToTab(a){if(isFocusMode)return void showFocusModeError();leaveTabEditMode(),setActiveTabElement(a),switchToWebview(a,{focus:!isExpandedMode}),tabs.setSelected(a);var b=tabs.get(a);setColor(b.backgroundColor,b.foregroundColor),setTimeout(function(){tabs.get(a)&&tabs.getSelected()==a&&(tabs.update(a,{lastActivity:Date.now()}),tabActivity.refresh())},2500),sessionRestore.save()}function searchbarAutocomplete(a,b,c){if(!a)return void(currentACItem=null);if((a!=searchbarCachedText||b[0].selectionStart==b[0].selectionEnd)&&!didFireKeydownSelChange)for(var d=!1,e=0;!d&&ef){var g=new URL(b.url);currentACItem=g.protocol+"//"+g.hostname+"/"}else currentACItem=b.url;return!0}return currentACItem=null,!1}function limitHistoryResults(a){maxHistoryResults=Math.min(4,Math.max(a,2)),historyarea.find(".result-item:nth-child(n+{items})".replace("{items}",maxHistoryResults+1)).prop("hidden",!0).addClass("unfocusable")}function addBookmarkItem(a){var b=$("
").append($("").text(getRealTitle(a.title))).on("click",function(b){openURLFromsearchbar(b,a.url)});$("").prependTo(b);var c=$("").text(urlParser.prettyURL(a.url));if(a.extraData&&a.extraData.metadata){var d=[];a.extraData.metadata.rating&&d.push($("").text(a.extraData.metadata.rating)),a.extraData.metadata.price&&d.push($("").text(a.extraData.metadata.price)),a.extraData.metadata.location&&d.push($("").text(a.extraData.metadata.location)),d.reverse().forEach(function(a){c.prepend(a)})}c.appendTo(b),b.appendTo(bookmarkarea),b.attr("data-url",a.url)}function throttle(a,b,c){b||(b=250);var d,e;return function(){var f=c||this,g=+new Date,h=arguments;d&&d+b>g?(clearTimeout(e),e=setTimeout(function(){d=g,a.apply(f,h)},b)):(d=g,a.apply(f,h))}}function debounce(a,b){var c=null;return function(){var d=this,e=arguments;clearTimeout(c),c=setTimeout(function(){a.apply(d,e)},b)}}function removeTags(a){return a.replace(/<.*?>/g,"")}function parsesearchbarURL(a){return 0==a.indexOf("?")&&(a=urlParser.searchBaseURL.replace("%s",encodeURIComponent(a.replace("?","")))),0==a.indexOf("^")&&(a=a.replace("^","")),0==a.indexOf("*")&&(a=a.replace("*","")),a}function openURLInBackground(a){var b=tabs.add({url:a,"private":tabs.get(tabs.getSelected())["private"]},tabs.getIndex(tabs.getSelected())+1);addTab(b,{focus:!1,openInBackground:!0,leaveEditMode:!1}),$(".result-item:focus").blur()}function openURLFromsearchbar(a,b){return a.metaKey?(openURLInBackground(b),!0):(navigate(tabs.getSelected(),b),!1)}function getRealTitle(a){if(urlParser.isURL(a))return a;for(var b=["|",":"," - "," — "],c=0;c=2&&(e[0]=e[0].trim(),e[1]=e[1].trim(),e[1].length<5||e[1].length/e[0].length<=.5))return e[0]}return a}function clearsearchbar(){opentabarea.empty(),topAnswerarea.empty(),bookmarkarea.empty(),historyarea.empty(),iaarea.empty(),suggestedsitearea.empty(),serarea.empty(),cachedBangSnippets=[]}function showSearchbar(a){searchbarCachedText=a.val(),$(document.body).addClass("searchbar-shown"),clearsearchbar(),searchbar.prop("hidden",!1),currentsearchbarInput=a}function getValue(a){var b=a.val();return b.replace(b.substring(a[0].selectionStart,a[0].selectionEnd),"")}function hidesearchbar(){currentsearchbarInput=null,$(document.body).removeClass("searchbar-shown"),searchbar.prop("hidden",!0),cachedBangSnippets={}}function focussearchbarItem(a){a=a||{};var b=a.focusPrevious,c=$("#searchbar .result-item:not(.unfocusable)"),d=$("#searchbar .result-item:focus, .result-item.fakefocus"),e=c.index(d),f=c.eq(b?e-1:e+1);searchbar.find(".fakefocus").removeClass("fakefocus"),d[0]&&f[0]?f.get(0).focus():d[0]?getTabElement(tabs.getSelected()).getInput().get(0).focus():$("#searchbar .result-item").first().get(0).focus();var g=$("#searchbar .result-item:focus");g.hasClass("iadata-onfocus")&&setTimeout(function(){if(g.is(":focus")){var a=g.find(".title").text();showInstantAnswers(a,currentsearchbarInput,{alwaysShow:!0,destroyPrevious:!1})}},200)}function getColor(a,b){colorExtractorImage.onload=function(a){var c=document.createElement("canvas"),d=c.getContext("2d"),e=colorExtractorImage.width,f=colorExtractorImage.height;c.width=e,c.height=f;var g=Math.max(1,Math.round(32e-5*e*f));d.drawImage(colorExtractorImage,0,0,e,f);for(var h,i,j,k=d.getImageData(0,0,e,f).data,l={},m=0;mj&&(i=.35),50>j&&(i=.01),(k[m]>210||k[m+1]>210||k[m+2]>210)&&(i=.5-1e-4*j),l[h]?l[h]=l[h]+i:l[h]=i;var n=null,o=0;for(var p in l)("255,255,255"==p||"0,0,0"==p)&&(l[p]*=.05),l[p]>o&&(n=p,o=l[p]);for(var q=n.split(","),m=0;m20?c-=.015*Math.pow(2.75,hours-20):6.5>hours&&(c-=-.15*Math.pow(1.36,hours)+1.15),a[0]=Math.round(a[0]*c),a[1]=Math.round(a[1]*c),a[2]=Math.round(a[2]*c);var d="rgb("+a[0]+","+a[1]+","+a[2]+")",e={r:a[0]/255,g:a[1]/255,b:a[2]/255},f=getTextColor(e);tabs.update(b,{backgroundColor:d,foregroundColor:f}),b==tabs.getSelected()&&setColor(d,f)})},{timeout:1e3})}function setColor(a,b){$(".theme-background-color").css("background-color",a),$(".theme-text-color").css("color",b),"white"==b?$(document.body).addClass("dark-theme"):$(document.body).removeClass("dark-theme")}function getTabElement(a){return $('.tab-item[data-tab="{id}"]'.replace("{id}",a))}function setActiveTabElement(a){$(".tab-item.active").removeClass("active");var b=getTabElement(a);b.addClass("active"),tabs.count()>1?b.addClass("has-highlight"):b.removeClass("has-highlight"),isExpandedMode||requestIdleCallback(function(){b[0].scrollIntoView({behavior:"smooth"})},{timeout:1e3})}function leaveTabEditMode(a){$(".tab-item.selected").removeClass("selected"),a&&a.blur&&$(".tab-item .tab-input").blur(),tabGroup.removeClass("has-selected-tab"),hidesearchbar()}function enterEditMode(a){leaveExpandedMode();var b=getTabElement(a),c=getWebview(a)[0],d=c.getAttribute("src");"about:blank"==d&&(d="");var e=b.getInput();b.addClass("selected"),e.val(d),e.get(0).focus(),e.select(),showSearchbar(e),showSearchbarResults("",e,null),tabGroup.addClass("has-selected-tab");try{getWebview(tabs.getSelected())[0].send("getKeywordsData")}catch(f){}}function rerenderTabElement(a){var b=getTabElement(a),c=tabs.get(a),d=c.title||"New Tab";b.find(".tab-view-contents .title").text(d).attr("title",d);var e=b[0].getElementsByClassName("icon-tab-not-secure");c.secure===!1?e[0]||b.find(".tab-view-contents").prepend(""):e[0]&&e[0].parentNode.removeChild(e[0]),bookmarks.renderStar(a,b.find(".bookmarks-button"))}function createTabElement(a){var b=tabs.get(a),c=urlParser.parse(b.url),d=$("
");d.attr("data-tab",a),b["private"]&&d.addClass("private-tab");var e=$("
"),f=$("");f.attr("placeholder","Search or enter address"),f.attr("value",c),f.appendTo(e),bookmarks.getStar(a).appendTo(e),e.appendTo(d);var g=$("
");return readerView.getButton(a).appendTo(g),b["private"]&&g.prepend("").attr("title","Private tab"),g.append($("").text(b.title||"New Tab")),g.append(""),g.appendTo(d),f.on("keydown",function(a){(9==a.keyCode||40==a.keyCode)&&(focussearchbarItem(),a.preventDefault())}),f.on("keyup",function(a){8==a.keyCode&&showSearchbarResults($(this).val(),$(this),a)}),f.on("keypress",function(a){if(13==a.keyCode){var b=$(this).parents(".tab-item").attr("data-tab"),c=parsesearchbarURL($(this).val());navigate(b,c),leaveTabEditMode(b),getWebview(tabs.getSelected())[0].focus()}else{if(9==a.keyCode)return;if(16==a.keyCode)return;if(8==a.keyCode)return;showSearchbarResults($(this).val(),$(this),a)}var d=String.fromCharCode(a.keyCode).toLowerCase(),e=this.value.substring(this.selectionStart,this.selectionEnd).indexOf(d);return d&&0==e?(this.selectionStart+=1,didFireKeydownSelChange=!0,!1):void(didFireKeydownSelChange=!1)}),f.on("click",function(a){a.stopPropagation()}),d}function addTab(a,b){b=b||{},0!=b.leaveEditMode&&leaveTabEditMode(),a=a||tabs.add();var c=tabs.get(a);c["private"]&&!c.backgroundColor?tabs.update(a,{backgroundColor:defaultColors["private"][0],foregroundColor:defaultColors["private"][1]}):c.backgroundColor||tabs.update(a,{backgroundColor:defaultColors.regular[0],foregroundColor:defaultColors.regular[1]});var d=tabs.getIndex(a);tabGroup.insertAt(d,createTabElement(a)),addWebview(a),b.openInBackground||(switchToTab(a),0!=b.focus&&enterEditMode(a))}function enterExpandedMode(){isExpandedMode||(dragRegion.containers=[tabDragArea],leaveTabEditMode(),tabs.get().forEach(function(a){try{var b=urlParser.prettyURL(a.url)}catch(c){var b=""}var d=getTabElement(a.id);d.find(".secondary-text").text(b)}),$(document.body).addClass("is-expanded-mode"),getWebview(tabs.getSelected()).blur(),tabContainer.get(0).focus(),isExpandedMode=!0)}function leaveExpandedMode(){isExpandedMode&&(dragRegion.containers=[],$(document.body).removeClass("is-expanded-mode"),isExpandedMode=!1)}function addPrivateTab(){if(isFocusMode)return void showFocusModeError();1==tabs.count()&&"about:blank"==tabs.getAtIndex(0).url&&destroyTab(tabs.getAtIndex(0).id);var a=tabs.getIndex(tabs.getSelected())+1,b=tabs.add({url:"about:blank","private":!0},a);addTab(b)}function showFocusModeError(){electron.remote.require("dialog").showMessageBox({type:"info",buttons:["OK"],message:"You're in focus mode.",detail:'You can leave focus mode by unchecking "focus mode" in the view menu.'})}var tabs={_state:{tabs:[],selected:null},add:function(a,b){if(!a)var a={};var c=a.id||Math.round(1e17*Math.random()),d={url:a.url||"about:blank",title:a.title||"",id:c,lastActivity:a.lastActivity||Date.now(),secure:a.secure,"private":a["private"]||!1,readerable:a.readerable||!1,backgroundColor:a.backgroundColor,foregroundColor:a.foregroundColor};return b?tabs._state.tabs.splice(b,0,d):tabs._state.tabs.push(d),c},update:function(a,b){if(!tabs.get(a))throw new ReferenceError("Attempted to update a tab that does not exist.");for(var c=-1,d=0;d0},parse:function(a){if(a=a.trim(),!a)return"";if(urlParser.isURL(a))return a;if(0==a.indexOf("view-source:")){var b=a.replace("view-source:","");return"view-source:"+urlParser.parse(b)}return urlParser.isURLMissingProtocol(a)?"http://"+a:urlParser.searchBaseURL.replace("%s",encodeURIComponent(a))},prettyURL:function(a){var b=new URL(a);return(b.hostname+b.pathname).replace(urlParser.startingWWWRegex,"$1").replace(urlParser.trailingSlashRegex,"")},areEqual:function(a,b){try{var c=new URL(a),d=new URL(b);return c.hostname==d.hostname&&c.pathname==d.pathname}catch(e){return a==b}}},phishingWarningPage="file://"+__dirname+"/pages/phishing/index.html",crashedWebviewPage="file:///"+__dirname+"/pages/crash/index.html",errorPage="file:///"+__dirname+"/pages/error/index.html",webviewBase=$("#webviews"),webviewEvents=[],webviewIPC=[],WebviewsWithHiddenClass=!1,remote,Menu,MenuItem,clipboard,webviewMenu={cache:{event:null,webview:null},loadFromContextData:function(a){var b=tabs.get(tabs.getSelected()),c=webviewMenu.cache.event,d=new Menu;if(a.src&&!isFocusMode){if(a.src.length>60)var e=a.src.substring(0,60)+"...";else var e=a.src;d.append(new MenuItem({label:e,enabled:!1})),d.append(new MenuItem({label:"Open in New Tab",click:function(){var c=tabs.add({url:a.src,"private":b["private"]},tabs.getIndex(tabs.getSelected())+1);addTab(c,{focus:!1})}})),b["private"]||d.append(new MenuItem({label:"Open in New Private Tab",click:function(){var b=tabs.add({url:a.src,"private":!0},tabs.getIndex(tabs.getSelected())+1);addTab(b,{focus:!1})}})),d.append(new MenuItem({type:"separator"})),d.append(new MenuItem({label:"Copy link",click:function(){clipboard.writeText(a.src)}}))}a.selection&&(d.append(new MenuItem({label:"Copy",click:function(){clipboard.writeText(a.selection)}})),d.append(new MenuItem({type:"separator"})),d.append(new MenuItem({label:"Search with DuckDuckGo",click:function(){var c=tabs.add({url:"https://duckduckgo.com/?q="+encodeURIComponent(a.selection),"private":b["private"]});addTab(c,{focus:!1})}}))),a.image&&d.append(new MenuItem({label:"View image",click:function(){navigate(webviewMenu.cache.tab,a.image)}})),d.append(new MenuItem({label:"Inspect Element",click:function(){webviewMenu.cache.webview.inspectElement(c.x,c.y)}})),d.popup(remote.getCurrentWindow())},show:function(a){remote||(remote=require("remote"),Menu=remote.require("menu"),MenuItem=remote.require("menu-item"),clipboard=require("clipboard"));var b=a.originalEvent||a;webviewMenu.cache.event=b;var c=tabs.getSelected(),d=getWebview(c)[0];webviewMenu.cache.tab=c,webviewMenu.cache.webview=d,d.send("getContextData",{x:b.offsetX,y:b.offsetY})}};bindWebviewIPC("contextData",function(a,b,c){webviewMenu.loadFromContextData(c[0])});var bookmarks={authBookmarkTab:null,updateHistory:function(a){setTimeout(function(){var b=tabs.get(a);if(b){var c={url:b.url,title:b.title,color:b.backgroundColor};bookmarks.historyWorker.postMessage({action:"updateHistory",data:c})}},2e3)},currentCallback:function(){},onDataRecieved:function(a){if(!bookmarks.authBookmarkTab||getWebview(bookmarks.authBookmarkTab)[0].getURL()!=a.url)throw new Error("Bookmark operation is unauthoritized.");a.title=getWebview(bookmarks.authBookmarkTab)[0].getTitle(),bookmarks.bookmarksWorker.postMessage({action:"addBookmark",data:a}),bookmarks.authBookmarkTab=null},deleteBookmark:function(a){bookmarks.bookmarksWorker.postMessage({action:"deleteBookmark",data:{url:a}})},deleteHistory:function(a){bookmarks.historyWorker.postMessage({action:"deleteHistory",data:{url:a}})},searchBookmarks:function(a,b){bookmarks.currentCallback=b,bookmarks.bookmarksWorker.postMessage({action:"searchBookmarks",text:a})},searchHistory:function(a,b){bookmarks.currentHistoryCallback=b,bookmarks.historyWorker.postMessage({action:"searchHistory",text:a})},onMessage:function(a){"bookmarks"==a.data.scope?bookmarks.currentCallback(a.data.result):"history"==a.data.scope&&bookmarks.currentHistoryCallback(a.data.result)},bookmark:function(a){bookmarks.authBookmarkTab=a,getWebview(a)[0].send("sendData")},toggleBookmarked:function(a){var b=tabs.get(a).url,c=!1;bookmarks.searchBookmarks(b,function(d){d.forEach(function(a){a.url==b&&(c=!0)}),c?(console.log("deleting bookmark "+tabs.get(a).url),bookmarks.deleteBookmark(tabs.get(a).url)):bookmarks.bookmark(a)})},getStar:function(a){var b=$("").attr("data-tab",a);return b.on("click",function(a){$(this).toggleClass("fa-star").toggleClass("fa-star-o"),bookmarks.toggleBookmarked($(this).attr("data-tab"))}),bookmarks.renderStar(a,b)},renderStar:function(a,b){b=b||$('.bookmarks-button[data-tab="{id}"]'.replace("{id}",a));var c=tabs.get(a).url;return c&&"about:blank"!=c?b.prop("hidden",!1):b.prop("hidden",!0),bookmarks.searchBookmarks(c,function(a){a&&a[0]&&a[0].url==c?b.removeClass("fa-star-o").addClass("fa-star"):b.removeClass("fa-star").addClass("fa-star-o")}),b},init:function(){bookmarks.historyWorker=new Worker("js/bookmarkshistory/historyworker.js"),bookmarks.historyWorker.onmessage=bookmarks.onMessage,bookmarks.bookmarksWorker=new Worker("js/bookmarkshistory/bookmarksworker.js"),bookmarks.bookmarksWorker.onmessage=bookmarks.onMessage}};bookmarks.init();var DDGSearchURLRegex=/^https:\/\/duckduckgo.com\/\?q=([^&]*).*/g,trailingSlashRegex=/\/$/g,plusRegex=/\+/g,currentACItem=null,deleteKeyPressed=!1,maxHistoryResults=4,showHistoryResults=throttle(function(a,b,c){(a||!b[0].value)&&(a&&(a=a.trim()),bookmarks.searchHistory(a,function(d){var e=!1;c=c||maxHistoryResults,a||(c=4),historyarea.empty(),topAnswerarea.get(0).getElementsByClassName("history-item").length>0&&topAnswerarea.empty(),searchbarAutocomplete(a,b,d),d.length<10?(limitSearchSuggestions(d.length),c=3,showSearchSuggestions(a,b)):-1==a.indexOf("!")&&serarea.empty();var f=0;if(d=d.splice(0,5),d.forEach(function(b){var d=!1,g=b.title,h=$("");if(DDGSearchURLRegex.lastIndex=0,DDGSearchURLRegex.test(b.url)&&(g=decodeURIComponent(b.url.replace(DDGSearchURLRegex,"$1").replace(plusRegex," ")),h=$(""),d=!0),d||0!=a.indexOf("!")){var i=$("
").append($("").text(getRealTitle(g))).on("click",function(a){openURLFromsearchbar(a,b.url)});i.attr("data-url",b.url),h.prependTo(i),d||b.title==b.url||$("").text(urlParser.prettyURL(b.url)).appendTo(i),f>=c&&i.prop("hidden",!0).addClass("unfocusable"),urlParser.areEqual(currentACItem,b.url)&&c>f&&!e?(i.addClass("fakefocus"),requestAnimationFrame(function(){i.appendTo(topAnswerarea)}),e=!0):requestAnimationFrame(function(){i.appendTo(historyarea)}),f++}}),currentACItem&&!e){var g=$("
").append($("").text(urlParser.prettyURL(currentACItem))).on("click",function(a){openURLFromsearchbar(a,currentACItem)});$("").prependTo(g),requestAnimationFrame(function(){g.appendTo(topAnswerarea)})}}))},50),showBookmarkResults=throttle(function(a){return a.length<5||0==a.indexOf("!")?(limitHistoryResults(5),void bookmarkarea.empty()):void bookmarks.searchBookmarks(a,function(b){bookmarkarea.empty();var c=1;b.splice(0,2).forEach(function(b){b.score>Math.max(4e-4,.0016-12e-5*Math.pow(1.25,a.length))&&(1==c||a.length>6)&&(requestAnimationFrame(function(){addBookmarkItem(b)}),c++)}),limitHistoryResults(5-c)})},400),showAllBookmarks=function(){bookmarks.searchBookmarks("",function(a){a.sort(function(a,b){return a.urlb.url?1:0}),a.forEach(addBookmarkItem)})},BANG_REGEX=/!\w+/g,serarea=$("#searchbar .search-engine-results"),iaarea=$("#searchbar .instant-answer-results"),topAnswerarea=$("#searchbar .top-answer-results"),suggestedsitearea=$("#searchbar .ddg-site-results");const minSearchSuggestions=2,maxSearchSuggestions=4;var currentSuggestionLimit=maxSearchSuggestions,IAFormats={color_code:function(a,b){var c=[b.data.rgb,b.data.hslc,b.data.cmyb];-1==a.indexOf("#")&&c.unshift(b.data.hexc);var d=$("
");return $("").text(a).appendTo(d),$("
").css("background-color","#"+b.data.hex_code).prependTo(d),$("").text(c.join(" "+METADATA_SEPARATOR+" ")).appendTo(d),d},minecraft:function(a,b){var c=$("
");return $("").text(b.data.title).appendTo(c),$("").attr("src",b.data.image).prependTo(c),$("").text(b.data.description+" "+b.data.subtitle).appendTo(c),c},figlet:function(a,b){var c=removeTags(b).replace("Font: standard",""),d=$("
"),e=$("").text(c).appendTo(d);return e.css({"white-space":"pre-wrap","font-family":"monospace","max-height":"10em","-webkit-user-select":"auto"}),d}};window.showSearchSuggestions=throttle(function(a,b){if(a&&!tabs.get(tabs.getSelected())["private"]){if(BANG_REGEX.test(a))var c=a.match(BANG_REGEX)[0],d=cachedBangSnippets[c];$.ajax("https://ac.duckduckgo.com/ac/?q="+encodeURIComponent(a)).done(function(a){serarea.find(".result-item").addClass("old"),a&&a[0]&&a[0].snippet?a.splice(0,5).forEach(function(a){cachedBangSnippets[a.phrase]=a.snippet;var c=$("
").append($("").text(a.snippet)).on("click",function(){setTimeout(function(){b.val(a.phrase+" ").get(0).focus()},100)});$("").text(a.phrase).appendTo(c),$("").attr("src",a.image).prependTo(c),c.appendTo(serarea)}):a&&(a=a.splice(0,currentSuggestionLimit),a.forEach(function(a){var b=a.phrase;if(BANG_REGEX.test(a.phrase)&&d){b=a.phrase.replace(BANG_REGEX,"");var c="Search on "+d}var e=$("
").append($("").text(b)).on("click",function(b){openURLFromsearchbar(b,a.phrase)});e.appendTo(serarea),urlParser.isURL(a.phrase)||urlParser.isURLMissingProtocol(a.phrase)?$("").prependTo(e):$("").prependTo(e),c&&$("").text(c).appendTo(e)})),serarea.find(".old").remove()})}},500);var limitSearchSuggestions=function(a){var b=Math.max(minSearchSuggestions,maxSearchSuggestions-a);currentSuggestionLimit=b,serarea.find(".result-item:nth-child(n+{items})".replace("{items}",b+1)).remove()};window.showInstantAnswers=debounce(function(a,b,c){return a?(c=c||{},void(urlParser.isURLMissingProtocol(a)||tabs.get(tabs.getSelected())["private"]||(iaarea.find(".result-item").addClass("old"),suggestedsitearea.find(".result-item").addClass("old"),a.length>3?$.getJSON("https://api.duckduckgo.com/?skip_disambig=1&format=json&q="+encodeURIComponent(a),function(d){if(a==getValue(b)||c.alwaysShow){if(iaarea.find(".result-item").addClass("old"),suggestedsitearea.find(".result-item").addClass("old"),IAFormats[d.AnswerType])e=IAFormats[d.AnswerType](a,d.Answer);else if(d.Abstract||d.Answer){var e=$("
");d.Answer?e.text(removeTags(d.Answer)):e.text(d.Heading),d.Image&&!d.ImageIsLogo&&$("").attr("src",d.Image).prependTo(e),$("").text(removeTags(d.Abstract)||"Answer").appendTo(e)}if(e&&(e.on("click",function(b){openURLFromsearchbar(b,d.AbstractURL||a)}),d.Answer?(topAnswerarea.empty(),e.appendTo(topAnswerarea)):e.appendTo(iaarea)),d.Results&&d.Results[0]&&d.Results[0].FirstURL){var f=historyarea.find('.result-item[data-url="{url}"]'.replace("{url}",d.Results[0].FirstURL));if(0==f.length){var g=urlParser.removeProtocol(d.Results[0].FirstURL).replace(trailingSlashRegex,""),e=$("
").append($("").text(g)).on("click",function(a){openURLFromsearchbar(a,d.Results[0].FirstURL)});$("").prependTo(e),$("").text("Suggested site").appendTo(e),e.appendTo(suggestedsitearea)}}var h=["location","country","u.s. state","protected area"];if(-1!=h.indexOf(d.Entity)){var e=$("
");$("").appendTo(e),$("").text(d.Heading).appendTo(e),$("Search on OpenStreetMap").appendTo(e),e.on("click",function(a){openURLFromsearchbar(a,"https://www.openstreetmap.org/search?query="+encodeURIComponent(d.Heading))}),e.prependTo(iaarea)}(0!=c.destroyPrevious||e)&&(iaarea.find(".old").remove(),suggestedsitearea.find(".old").remove())}}):(iaarea.find(".old").remove(),suggestedsitearea.find(".old").remove())))):(iaarea.empty(),void suggestedsitearea.empty())},450);var spacesRegex=/[\s._\/-]/g,stringScore=require("string_score"),searchOpenTabs=function(a){if(opentabarea.empty(),!(a.length<3)){var b=[],c=tabs.getSelected();tabs.get().forEach(function(d){if(d.id!=c&&d.title&&"about:blank"!=d.url){d.url=urlParser.removeProtocol(d.url);var e=-1!=d.title.indexOf(a)||-1!=d.url.indexOf(a),f=d.title.substring(0,50).score(a,.5)>.4||d.url.score(a,.5)>.4;(e||f)&&b.push(d)}}),b.splice(0,2).sort(function(b,c){return c.title.score(a,.5)-b.title.score(a,.5)}).forEach(function(a){var b=$("
").append($("").text(a.title));$("").text(urlParser.removeProtocol(a.url).replace(trailingSlashRegex,"")).appendTo(b),$("").attr("title","Switch to Tab").prependTo(b),b.on("click",function(){"about:blank"==tabs.get(tabs.getSelected()).url&&destroyTab(tabs.getSelected(),{switchToTab:!1}),switchToTab(a.id)}),b.appendTo(opentabarea)})}},searchbarCachedText="",METADATA_SEPARATOR="·",didFireKeydownSelChange=!1,currentsearchbarInput,cachedBangSnippets={},searchbar=$("#searchbar"),historyarea=searchbar.find(".history-results"),bookmarkarea=searchbar.find(".bookmark-results"),opentabarea=searchbar.find(".opentab-results"),showSearchbarResults=function(a,b,c){if(!c||!c.metaKey){deleteKeyPressed=c&&8==c.keyCode;var d=b[0].value;if(c&&8!=c.keyCode?a=d.substring(0,b[0].selectionStart)+String.fromCharCode(c.keyCode)+d.substring(b[0].selectionEnd+1,d.length).trim():txt=d,console.log("searchbar: ","'"+a+"'",a.length),a.length<1)return showHistoryResults("",b),void clearsearchbar();if(0==a.indexOf("?"))return clearsearchbar(),maxSearchSuggestions=5,void showSearchSuggestions(a.replace("?",""),b);if(0==a.indexOf("^"))return clearsearchbar(),void showHistoryResults(a.replace("^",""),b);if(0==a.indexOf("*"))return clearsearchbar(),void showBookmarkResults(a.replace("*",""),b);0==a.indexOf("!")&&showSearchSuggestions(a,b),showBookmarkResults(a),showHistoryResults(a,b),showInstantAnswers(a,b),searchOpenTabs(a,b),searchbarCachedText=a}};searchbar.on("keydown",".result-item",function(a){13==a.keyCode?$(this).trigger("click"):9==a.keyCode||40==a.keyCode?(a.preventDefault(),focussearchbarItem()):38==a.keyCode&&(a.preventDefault(),focussearchbarItem({focusPrevious:!0}))});var lastItemDeletion=Date.now();searchbar.on("mousewheel",".history-results .result-item, .top-answer-results .result-item",function(a){var b=$(this);a.originalEvent.deltaX>50&&a.originalEvent.deltaY<3&&b.attr("data-url")&&Date.now()-lastItemDeletion>700&&(lastItemDeletion=Date.now(),b.animate({opacity:"0","margin-left":"-100%"},200,function(){b.remove(),bookmarks.deleteHistory(b.attr("data-url")),lastItemDeletion=Date.now()}))}),bindWebviewIPC("keywordsData",function(a,b,c){var d=c[0],e=0,f=[];d.entities.forEach(function(a,b){if(/\s/g.test(a.trim())&&!(e>=5||-1!=f.indexOf(a.trim()))){var c=$("
").append($("").text(a)).on("click",function(b){b.metaKey?openURLInBackground(a):navigate(tabs.getSelected(),a)});$("").prependTo(c),c.appendTo(serarea),e++,f.push(a.trim())}})});var readerView={ -readerURL:"file://"+__dirname+"/reader/index.html",getButton:function(a){return $("").attr("data-tab",a).attr("title","Enter reader view")},updateButton:function(a){var b=$('.reader-button[data-tab="{id}"]'.replace("{id}",a)),c=tabs.get(a);return b.off(),c.isReaderView?(b.addClass("is-reader").attr("title","Exit reader view"),void b.on("click",function(b){b.stopPropagation(),readerView.exit(a)})):(b.removeClass("is-reader").attr("title","Enter reader view"),void(c.readerable?(b.addClass("can-reader"),b.on("click",function(b){b.stopPropagation(),readerView.enter(a)})):b.removeClass("can-reader")))},enter:function(a){navigate(a,readerView.readerURL+"?url="+tabs.get(a).url),tabs.update(a,{isReaderView:!0})},exit:function(a){navigate(a,tabs.get(a).url.split("?url=")[1]),tabs.update(a,{isReaderView:!1})}};bindWebviewEvent("did-finish-load",function(a){var b=$(this).attr("data-tab"),c=$(this).attr("src");0==c.indexOf(readerView.readerURL)?tabs.update(b,{isReaderView:!0}):tabs.update(b,{isReaderView:!1}),tabs.update(b,{readerable:!1}),readerView.updateButton(b)}),bindWebviewIPC("canReader",function(a,b){tabs.update(b,{readerable:!0}),readerView.updateButton(b)});var tabActivity={minFadeAge:33e4,refresh:function(){requestAnimationFrame(function(){var a=tabs.get(),b=tabs.getSelected(),c=Date.now();a.forEach(function(a){return b==a.id?void getTabElement(a.id).removeClass("fade"):void(c-a.lastActivity>tabActivity.minFadeAge?getTabElement(a.id).addClass("fade"):getTabElement(a.id).removeClass("fade"))})})},init:function(){setInterval(tabActivity.refresh,7500)}};tabActivity.init();var colorExtractorImage=document.createElement("img");const defaultColors={"private":["rgb(58, 44, 99)","white"],regular:["rgb(255, 255, 255)","black"]};var hours=(new Date).getHours()+(new Date).getMinutes()/60;setInterval(function(){var a=new Date;hours=a.getHours()+a.getMinutes()/60},24e4);var getTextColor=function(a){var b=runNetwork(a);return b.black>.5?"black":"white"},runNetwork=function(a){for(var b={layers:[{r:{},g:{},b:{}},{0:{bias:14.176907520571566,weights:{r:-3.2764240497480652,g:-16.90247884718719,b:-2.9976364179397814}},1:{bias:9.086071102351246,weights:{r:-4.327474143397604,g:-15.780660155750773,b:2.879230202567851}},2:{bias:22.274487339773476,weights:{r:-3.5830205067960965,g:-25.498384261673618,b:-6.998329189107962}}},{black:{bias:17.873962570788997,weights:{0:-15.542217788633987,1:-13.377152708685674,2:-24.52215186113144}}}],outputLookup:!0,inputLookup:!0},c=1;ca&&(a=Math.max(0,c+1+a)),this.append(b),c>a&&this.children().eq(a).before(this.children().last()),this};var tabContainer=$(".tab-group"),tabGroup=$(".tab-group #tabs"),lastTabDeletion=0;tabGroup.on("mousewheel",".tab-item",function(a){if(a.originalEvent.deltaY>65&&a.originalEvent.deltaX<10&&Date.now()-lastTabDeletion>650){if(lastTabDeletion=Date.now(),isFocusMode)return void showFocusModeError();var b=$(this).attr("data-tab");getTabElement(b).animate({"margin-top":"-40px"},125,function(){if(b==tabs.getSelected()){var a=tabs.getIndex(tabs.getSelected()),c=tabs.getAtIndex(a+1)||tabs.getAtIndex(a-1);destroyTab(b),c?switchToTab(c.id):addTab()}else destroyTab(b)})}a.originalEvent.deltaY>0&&a.stopPropagation()}),tabGroup.on("click",".tab-item",function(a){var b=$(this).attr("data-tab");tabs.getSelected()!=b?switchToTab(b):isExpandedMode||enterEditMode(b)}),$.fn.getInput=function(){return this.find(".tab-input")},bindWebviewEvent("focus",function(){leaveExpandedMode(),leaveTabEditMode()});var tabDragArea=tabGroup[0];require.async("dragula",function(a){window.dragRegion=a(),dragRegion.on("drop",function(){var a=[];tabContainer.find(".tab-item").each(function(){var b=parseInt($(this).attr("data-tab"));a.push(b)}),tabs.reorder(a)})}),tabContainer.on("mousewheel",function(a){a.originalEvent.deltaY<-30&&a.originalEvent.deltaX<10?(enterExpandedMode(),a.stopImmediatePropagation()):a.originalEvent.deltaY>70&&a.originalEvent.deltaX<10&&leaveExpandedMode()}),tabContainer.on("mouseenter",".tab-item",function(a){if(isExpandedMode){var b=$(this);setTimeout(function(){if(b.is(":hover")){tabs.get(b.attr("data-tab"));switchToTab(b.attr("data-tab"))}},125)}});var isExpandedMode=!1;tabContainer.on("click",".tab-item",function(){isExpandedMode&&(leaveExpandedMode(),getWebview(tabs.getSelected())[0].focus())});var addTabButton=$(".add-tab");addTabButton.on("click",function(a){var b=tabs.add({},tabs.getIndex(tabs.getSelected())+1);addTab(b)}),ipc.on("zoomIn",function(){getWebview(tabs.getSelected())[0].send("zoomIn")}),ipc.on("zoomOut",function(){getWebview(tabs.getSelected())[0].send("zoomOut")}),ipc.on("zoomReset",function(){getWebview(tabs.getSelected())[0].send("zoomReset")}),ipc.on("print",function(){getWebview(tabs.getSelected())[0].print()}),ipc.on("inspectPage",function(){getWebview(tabs.getSelected())[0].openDevTools()}),ipc.on("addTab",function(a){if(isFocusMode)return void showFocusModeError();var b=tabs.getIndex(tabs.getSelected())+1,c=tabs.add({},b);addTab(c)}),ipc.on("addPrivateTab",addPrivateTab),require.async("mousetrap",function(a){window.Mousetrap=a,a.bind("shift+command+p",addPrivateTab),a.bind(["command+l","command+k"],function(a){return enterEditMode(tabs.getSelected()),!1}),a.bind("command+w",function(a){if(a.preventDefault(),a.stopImmediatePropagation(),isFocusMode)return void showFocusModeError();var b=tabs.getSelected(),c=tabs.getIndex(b),d=tabs.getAtIndex(c+1)||tabs.getAtIndex(c-1);return destroyTab(b),d?switchToTab(d.id):addTab(),1==tabs.count()&&leaveExpandedMode(),!1}),a.bind("command+d",function(a){getTabElement(tabs.getSelected()).find(".bookmarks-button").click()}),a.bind("command+f",function(a){findinpage.toggle()});for(var b=1;9>b;b++)!function(b){a.bind("command+"+b,function(a){var c=tabs.getIndex(tabs.getSelected()),d=tabs.getAtIndex(c+b)||tabs.getAtIndex(c-b);d&&switchToTab(d.id)}),a.bind("shift+command+"+b,function(a){var c=tabs.getIndex(tabs.getSelected()),d=tabs.getAtIndex(c-b)||tabs.getAtIndex(c+b);d&&switchToTab(d.id)})}(b);a.bind("command+9",function(a){switchToTab(tabs.getAtIndex(tabs.count()-1).id)}),a.bind("shift+command+9",function(a){switchToTab(tabs.getAtIndex(0).id)}),a.bind("esc",function(a){leaveTabEditMode(),leaveExpandedMode(),getWebview(tabs.getSelected()).get(0).focus()}),a.bind("shift+command+r",function(){getTabElement(tabs.getSelected()).find(".reader-button").trigger("click")}),a.bind("command+left",function(a){getWebview(tabs.getSelected())[0].goBack()}),a.bind("command+right",function(a){getWebview(tabs.getSelected())[0].goForward()}),a.bind(["option+command+left","shift+ctrl+tab"],function(a){enterExpandedMode();var b=tabs.getIndex(tabs.getSelected()),c=tabs.getAtIndex(b-1);switchToTab(c?c.id:tabs.getAtIndex(tabs.count()-1).id)}),a.bind(["option+command+right","ctrl+tab"],function(a){enterExpandedMode();var b=tabs.getIndex(tabs.getSelected()),c=tabs.getAtIndex(b+1);switchToTab(c?c.id:tabs.getAtIndex(0).id)}),a.bind("command+n",function(a){for(var b=tabs.get(),c=0;c"),d=c[0];return c.attr("preload","dist/webview.min.js"),c.attr("src",urlParser.parse(b)),c.attr("data-tab",a.tabId),1==tabs.get(a.tabId)["private"]&&c.attr("partition",a.tabId),webviewEvents.forEach(function(a){d.addEventListener(a.event,a.fn)}),d.addEventListener("page-favicon-updated",function(a){var b=this.getAttribute("data-tab");updateTabColor(a.favicons,b)}),d.addEventListener("page-title-set",function(a){var b=this.getAttribute("data-tab");tabs.update(b,{title:a.title}),rerenderTabElement(b)}),d.addEventListener("did-finish-load",function(a){var b=this.getAttribute("data-tab"),c=$(this).attr("src");0===c.indexOf("https://")||0==c.indexOf("about:")||0==c.indexOf("chrome:")||0==c.indexOf("file://")?tabs.update(b,{secure:!0,url:c}):tabs.update(b,{secure:!1,url:c});var d=-1!=c.indexOf(__dirname)&&-1==c.indexOf(readerView.readerURL);0!=tabs.get(b)["private"]||d||bookmarks.updateHistory(b),rerenderTabElement(b),this.send("loadfinish")}),d.addEventListener("new-window",function(a){var b=this.getAttribute("data-tab"),c=tabs.getIndex(tabs.getSelected()),d=tabs.add({url:a.url,"private":tabs.get(b)["private"]},c+1);addTab(d,{enterEditMode:!1,openInBackground:"background-tab"==a.disposition})}),c.on("ipc-message",function(a){var b=this,c=this.getAttribute("data-tab");webviewIPC.forEach(function(d){d.name==a.originalEvent.channel&&d.fn(b,c,a.originalEvent.args)}),"bookmarksData"==a.originalEvent.channel?bookmarks.onDataRecieved(a.originalEvent.args[0]):"phishingDetected"==a.originalEvent.channel&&navigate($(this).attr("data-tab"),phishingWarningPage)}),c.on("contextmenu",webviewMenu.show),c.on("crashed",function(a){var b=this.getAttribute("data-tab");destroyWebview(b),tabs.update(b,{url:crashedWebviewPage}),addWebview(b),switchToWebview(b)}),c.on("did-fail-load",function(a){-3!=a.originalEvent.errorCode&&a.originalEvent.validatedURL==a.target.getURL()&&navigate($(this).attr("data-tab"),errorPage+"?ec="+encodeURIComponent(a.originalEvent.errorCode)+"&url="+a.target.getURL())}),d.addEventListener("enter-html-full-screen",function(a){this.classList.add("fullscreen")}),d.addEventListener("leave-html-full-screen",function(a){this.classList.remove("fullscreen")}),c}function addWebview(a){var b=tabs.get(a),c=getWebviewDom({tabId:a,url:b.url});c.addClass("hidden"),webviewBase.append(c)}function switchToWebview(a){$("webview").prop("hidden",!0),getWebview(a).removeClass("hidden").prop("hidden",!1)}function updateWebview(a,b){getWebview(a).attr("src",urlParser.parse(b))}function destroyWebview(a){$('webview[data-tab="{id}"]'.replace("{id}",a)).remove()}function getWebview(a){return $('webview[data-tab="{id}"]'.replace("{id}",a))}function navigate(a,b){b=urlParser.parse(b),tabs.update(a,{url:b}),updateWebview(a,b),leaveTabEditMode({blur:!0})}function destroyTab(a){getTabElement(a).remove();tabs.destroy(a);destroyWebview(a)}function switchToTab(a,b){if(b=b||{},isFocusMode)return void showFocusModeError();leaveTabEditMode(),tabs.setSelected(a),setActiveTabElement(a),switchToWebview(a),0==b.focusWebview||isExpandedMode||getWebview(a).get(0).focus();var c=tabs.get(a);setColor(c.backgroundColor,c.foregroundColor),setTimeout(function(){tabs.get(a)&&tabs.getSelected()==a&&(tabs.update(a,{lastActivity:Date.now()}),tabActivity.refresh())},2500),sessionRestore.save()}function throttle(a,b,c){b||(b=250);var d,e;return function(){var f=c||this,g=+new Date,h=arguments;d&&d+b>g?(clearTimeout(e),e=setTimeout(function(){d=g,a.apply(f,h)},b)):(d=g,a.apply(f,h))}}function debounce(a,b){var c=null;return function(){var d=this,e=arguments;clearTimeout(c),c=setTimeout(function(){a.apply(d,e)},b)}}function empty(a){for(var b;b=a.firstElementChild;)a.removeChild(b)}function removeTags(a){return a.replace(/<.*?>/g,"")}function parsesearchbarURL(a){return 0==a.indexOf("?")&&(a=urlParser.searchBaseURL.replace("%s",encodeURIComponent(a.replace("?","")))),0==a.indexOf("^")&&(a=a.replace("^","")),0==a.indexOf("*")&&(a=a.replace("*","")),a}function openURLInBackground(a){var b=tabs.add({url:a,"private":tabs.get(tabs.getSelected())["private"]},tabs.getIndex(tabs.getSelected())+1);addTab(b,{enterEditMode:!1,openInBackground:!0,leaveEditMode:!1}),$(".result-item:focus").blur()}function openURLFromsearchbar(a,b){return a.metaKey?(openURLInBackground(b),!0):(navigate(tabs.getSelected(),b),tabs.get(tabs.getSelected())["private"]||currentHistoryResults.forEach(function(a){a.url==b&&(setColor(a.color,getTextColor(getRGBObject(a.color))),tabs.update(tabs.getSelected(),{title:a.title}),rerenderTabElement(tabs.getSelected()))}),!1)}function getRealTitle(a){if(urlParser.isURL(a))return a;for(var b=["|",":"," - "," — "],c=0;c=2&&(e[0]=e[0].trim(),e[1]=e[1].trim(),e[1].length<5||e[1].length/e[0].length<=.5))return e[0]}return a}function createSearchbarItem(a){var b=document.createElement("div");if(b.classList.add("result-item"),b.setAttribute("tabindex","-1"),a.classList)for(var c=0;cg){var h=new URL(b.url);currentACItem=h.protocol+"//"+h.hostname+"/"}else currentACItem=b.url;return!0}return!1}function limitHistoryResults(a){maxHistoryResults=Math.min(4,Math.max(a,2)),$historyarea.find(".result-item:nth-child(n+{items})".replace("{items}",maxHistoryResults+1)).prop("hidden",!0).addClass("unfocusable")}function addBookmarkItem(a){var b=createSearchbarItem({icon:"fa-star",title:getRealTitle(a.title),secondaryText:urlParser.prettyURL(a.url),url:a.url});if(b.addEventListener("click",function(b){openURLFromsearchbar(b,a.url)}),a.extraData&&a.extraData.metadata){var c=b.querySelector(".secondary-text");for(var d in a.extraData.metadata){var e=document.createElement("span");e.className="md-info",e.textContent=a.extraData.metadata[d],c.insertBefore(e,c.firstChild)}}bookmarkarea.appendChild(b)}function getColor(a,b){colorExtractorImage.onload=function(a){var c=document.createElement("canvas"),d=c.getContext("2d"),e=colorExtractorImage.width,f=colorExtractorImage.height;c.width=e,c.height=f;var g=Math.max(1,Math.round(32e-5*e*f));d.drawImage(colorExtractorImage,0,0,e,f);for(var h,i,j,k=d.getImageData(0,0,e,f).data,l={},m=0;mj&&(i=.35),50>j&&(i=.01),(k[m]>210||k[m+1]>210||k[m+2]>210)&&(i=.5-1e-4*j),l[h]?l[h]=l[h]+i:l[h]=i;var n=null,o=0;for(var p in l)("255,255,255"==p||"0,0,0"==p)&&(l[p]*=.05),l[p]>o&&(n=p,o=l[p]);for(var q=n.split(","),m=0;m20?c-=.015*Math.pow(2.75,hours-20):6.5>hours&&(c-=-.15*Math.pow(1.36,hours)+1.15),a[0]=Math.round(a[0]*c),a[1]=Math.round(a[1]*c),a[2]=Math.round(a[2]*c);var d="rgb("+a[0]+","+a[1]+","+a[2]+")",e={r:a[0]/255,g:a[1]/255,b:a[2]/255},f=getTextColor(e);tabs.update(b,{backgroundColor:d,foregroundColor:f}),b==tabs.getSelected()&&setColor(d,f)})},{timeout:1e3})}function setColor(a,b){$(".theme-background-color").css("background-color",a),$(".theme-text-color").css("color",b),"white"==b?$(document.body).addClass("dark-theme"):$(document.body).removeClass("dark-theme")}function getRGBObject(a){var b=a.split("(")[1].split(")")[0],c=b.split(","),d={r:parseInt(c[0])/255,g:parseInt(c[1])/255,b:parseInt(c[2])/255};return d}function getTabElement(a){return $('.tab-item[data-tab="{id}"]'.replace("{id}",a))}function setActiveTabElement(a){$(".tab-item.active").removeClass("active");var b=getTabElement(a);b.addClass("active"),tabs.count()>1?b.addClass("has-highlight"):b.removeClass("has-highlight"),isExpandedMode||requestIdleCallback(function(){requestAnimationFrame(function(){b[0].scrollIntoView({behavior:"smooth"})})},{timeout:1500})}function leaveTabEditMode(a){if($(".tab-item.selected").removeClass("selected"),a&&a.blur){var b=document.querySelector(".tab-item .tab-input:focus");b&&b.blur()}tabGroup.removeClass("has-selected-tab"),hidesearchbar()}function enterEditMode(a){leaveExpandedMode();var b=getTabElement(a),c=getWebview(a)[0],d=c.getAttribute("src");"about:blank"==d&&(d="");var e=b.getInput();b.addClass("selected"),e.val(d),e.get(0).focus(),e.select(),showSearchbar(e),showSearchbarResults("",e.get(0),null),tabGroup.addClass("has-selected-tab");try{getWebview(tabs.getSelected())[0].send("getKeywordsData")}catch(f){}}function rerenderTabElement(a){var b=getTabElement(a),c=tabs.get(a),d=c.title||"New Tab",e=b.get(0).querySelector(".tab-view-contents .title");e.textContent=d,e.title=d;var f=b[0].getElementsByClassName("icon-tab-not-secure");c.secure===!1?f[0]||b.find(".tab-view-contents").prepend(""):f[0]&&f[0].parentNode.removeChild(f[0]),bookmarks.renderStar(a)}function createTabElement(a){var b=tabs.get(a),c=urlParser.parse(b.url),d=$("
");d.attr("data-tab",a),b["private"]&&d.addClass("private-tab");var e=$("
"),f=$("");f.attr("placeholder","Search or enter address"),f.attr("value",c),f.appendTo(e),bookmarks.getStar(a).appendTo(e),e.appendTo(d);var g=$("
");return readerView.getButton(a).appendTo(g),b["private"]&&g.prepend("").attr("title","Private tab"),g.append($("").text(b.title||"New Tab")),g.append(""),g.appendTo(d),f.on("keydown",function(a){(9==a.keyCode||40==a.keyCode)&&(focussearchbarItem(),a.preventDefault())}),f.on("keyup",function(a){8==a.keyCode&&showSearchbarResults(this.value,this,a)}),f.on("keypress",function(a){if(13==a.keyCode){var b=($(this).parents(".tab-item").attr("data-tab"),currentACItem||parsesearchbarURL(this.value));openURLFromsearchbar(a,b),getWebview(tabs.getSelected()).get(0).focus()}else{if(9==a.keyCode)return;if(16==a.keyCode)return;if(8==a.keyCode)return;showSearchbarResults(this.value,this,a)}var c=String.fromCharCode(a.keyCode).toLowerCase(),d=this.value.substring(this.selectionStart,this.selectionEnd).indexOf(c);return c&&0==d?(this.selectionStart+=1,didFireKeydownSelChange=!0,!1):void(didFireKeydownSelChange=!1)}),f.on("click",function(a){a.stopPropagation()}),d}function addTab(a,b){b=b||{},0!=b.leaveEditMode&&leaveTabEditMode(),a=a||tabs.add();var c=tabs.get(a);c["private"]&&!c.backgroundColor?tabs.update(a,{backgroundColor:defaultColors["private"][0],foregroundColor:defaultColors["private"][1]}):c.backgroundColor||tabs.update(a,{backgroundColor:defaultColors.regular[0],foregroundColor:defaultColors.regular[1]});var d=tabs.getIndex(a);tabGroup.insertAt(d,createTabElement(a)),addWebview(a),b.openInBackground||(switchToTab(a,{focusWebview:!1}),0!=b.enterEditMode&&enterEditMode(a))}function enterExpandedMode(){isExpandedMode||(dragRegion.containers=[tabDragArea],leaveTabEditMode(),tabs.get().forEach(function(a){var b=urlParser.prettyURL(a.url);getTabElement(a.id).find(".secondary-text").text(b)}),requestAnimationFrame(function(){$(document.body).addClass("is-expanded-mode"),tabContainer.get(0).focus()}),isExpandedMode=!0)}function leaveExpandedMode(){isExpandedMode&&(dragRegion.containers=[],$(document.body).removeClass("is-expanded-mode"),isExpandedMode=!1)}function addPrivateTab(){if(isFocusMode)return void showFocusModeError();1==tabs.count()&&"about:blank"==tabs.getAtIndex(0).url&&destroyTab(tabs.getAtIndex(0).id);var a=tabs.getIndex(tabs.getSelected())+1,b=tabs.add({url:"about:blank","private":!0},a);addTab(b)}function showFocusModeError(){electron.remote.require("dialog").showMessageBox({type:"info",buttons:["OK"],message:"You're in focus mode.",detail:'You can leave focus mode by unchecking "focus mode" in the view menu.'})}var tabs={_state:{tabs:[],selected:null},add:function(a,b){if(!a)var a={};var c=a.id||Math.round(1e17*Math.random()),d={url:a.url||"about:blank",title:a.title||"",id:c,lastActivity:a.lastActivity||Date.now(),secure:a.secure,"private":a["private"]||!1,readerable:a.readerable||!1,backgroundColor:a.backgroundColor,foregroundColor:a.foregroundColor};return b?tabs._state.tabs.splice(b,0,d):tabs._state.tabs.push(d),c},update:function(a,b){if(!tabs.get(a))throw new ReferenceError("Attempted to update a tab that does not exist.");for(var c=-1,d=0;d0},parse:function(a){if(a=a.trim(),!a)return"";if(urlParser.isURL(a))return a;if(0==a.indexOf("view-source:")){var b=a.replace("view-source:","");return"view-source:"+urlParser.parse(b)}return urlParser.isURLMissingProtocol(a)?"http://"+a:urlParser.searchBaseURL.replace("%s",encodeURIComponent(a))},prettyURL:function(a){var b=new URL(a);return(b.hostname+b.pathname).replace(urlParser.startingWWWRegex,"$1").replace(urlParser.trailingSlashRegex,"")},areEqual:function(a,b){try{var c=new URL(a),d=new URL(b);return c.hostname==d.hostname&&c.pathname==d.pathname}catch(e){return a==b}}},phishingWarningPage="file://"+__dirname+"/pages/phishing/index.html",crashedWebviewPage="file:///"+__dirname+"/pages/crash/index.html",errorPage="file:///"+__dirname+"/pages/error/index.html",webviewBase=$("#webviews"),webviewEvents=[],webviewIPC=[],WebviewsWithHiddenClass=!1,remote,Menu,MenuItem,clipboard,webviewMenu={cache:{event:null,webview:null},loadFromContextData:function(a){var b=tabs.get(tabs.getSelected()),c=webviewMenu.cache.event,d=new Menu;if(a.src&&!isFocusMode){if(a.src.length>60)var e=a.src.substring(0,60)+"...";else var e=a.src;d.append(new MenuItem({label:e,enabled:!1})),d.append(new MenuItem({label:"Open in New Tab",click:function(){var c=tabs.add({url:a.src,"private":b["private"]},tabs.getIndex(tabs.getSelected())+1);addTab(c,{enterEditMode:!1}),getWebview(c).get(0).focus()}})),b["private"]||d.append(new MenuItem({label:"Open in New Private Tab",click:function(){var b=tabs.add({url:a.src,"private":!0},tabs.getIndex(tabs.getSelected())+1);addTab(b,{enterEditMode:!1}),getWebview(b).get(0).focus()}})),d.append(new MenuItem({type:"separator"})),d.append(new MenuItem({label:"Copy link",click:function(){clipboard.writeText(a.src)}}))}a.selection&&(d.append(new MenuItem({label:"Copy",click:function(){clipboard.writeText(a.selection)}})),d.append(new MenuItem({type:"separator"})),d.append(new MenuItem({label:"Search with DuckDuckGo",click:function(){var c=tabs.add({url:"https://duckduckgo.com/?q="+encodeURIComponent(a.selection),"private":b["private"]});addTab(c,{enterEditMode:!1}),getWebview(c).get(0).focus()}}))),a.image&&d.append(new MenuItem({label:"View image",click:function(){navigate(webviewMenu.cache.tab,a.image)}})),d.append(new MenuItem({label:"Inspect Element",click:function(){webviewMenu.cache.webview.inspectElement(c.x,c.y)}})),d.popup(remote.getCurrentWindow())},show:function(a){remote||(remote=require("remote"),Menu=remote.require("menu"),MenuItem=remote.require("menu-item"),clipboard=require("clipboard"));var b=a.originalEvent||a;webviewMenu.cache.event=b;var c=tabs.getSelected(),d=getWebview(c)[0];webviewMenu.cache.tab=c,webviewMenu.cache.webview=d,d.send("getContextData",{x:b.offsetX,y:b.offsetY})}};bindWebviewIPC("contextData",function(a,b,c){webviewMenu.loadFromContextData(c[0])});var bookmarks={authBookmarkTab:null,updateHistory:function(a){setTimeout(function(){var b=tabs.get(a);if(b){var c={url:b.url,title:b.title,color:b.backgroundColor};bookmarks.historyWorker.postMessage({action:"updateHistory",data:c})}},500)},currentCallback:function(){},onDataRecieved:function(a){if(!bookmarks.authBookmarkTab||getWebview(bookmarks.authBookmarkTab)[0].getURL()!=a.url)throw new Error("Bookmark operation is unauthoritized.");a.title=getWebview(bookmarks.authBookmarkTab)[0].getTitle(),bookmarks.bookmarksWorker.postMessage({action:"addBookmark",data:a}),bookmarks.authBookmarkTab=null},deleteBookmark:function(a){bookmarks.bookmarksWorker.postMessage({action:"deleteBookmark",data:{url:a}})},deleteHistory:function(a){bookmarks.historyWorker.postMessage({action:"deleteHistory",data:{url:a}})},searchBookmarks:function(a,b){bookmarks.currentCallback=b,bookmarks.bookmarksWorker.postMessage({action:"searchBookmarks",text:a})},searchHistory:function(a,b){bookmarks.currentHistoryCallback=b,bookmarks.historyWorker.postMessage({action:"searchHistory",text:a})},onMessage:function(a){"bookmarks"==a.data.scope?bookmarks.currentCallback(a.data.result):"history"==a.data.scope&&bookmarks.currentHistoryCallback(a.data.result)},bookmark:function(a){bookmarks.authBookmarkTab=a,getWebview(a)[0].send("sendData")},toggleBookmarked:function(a){var b=tabs.get(a).url,c=!1;bookmarks.searchBookmarks(b,function(d){d.forEach(function(a){a.url==b&&(c=!0)}),c?(console.log("deleting bookmark "+tabs.get(a).url),bookmarks.deleteBookmark(tabs.get(a).url)):bookmarks.bookmark(a)})},getStar:function(a){var b=$("").attr("data-tab",a);return b.on("click",function(a){$(this).toggleClass("fa-star").toggleClass("fa-star-o"),bookmarks.toggleBookmarked($(this).attr("data-tab"))}),bookmarks.renderStar(a,b)},renderStar:function(a,b){b=b||$('.bookmarks-button[data-tab="{id}"]'.replace("{id}",a));var c=tabs.get(a).url;return c&&"about:blank"!=c?b.prop("hidden",!1):b.prop("hidden",!0),bookmarks.searchBookmarks(c,function(a){a&&a[0]&&a[0].url==c?b.removeClass("fa-star-o").addClass("fa-star"):b.removeClass("fa-star").addClass("fa-star-o")}),b},init:function(){bookmarks.historyWorker=new Worker("js/bookmarkshistory/historyworker.js"),bookmarks.historyWorker.onmessage=bookmarks.onMessage,bookmarks.bookmarksWorker=new Worker("js/bookmarkshistory/bookmarksworker.js"),bookmarks.bookmarksWorker.onmessage=bookmarks.onMessage}};bookmarks.init();var searchbarCachedText="",METADATA_SEPARATOR="·",didFireKeydownSelChange=!1,currentsearchbarInput,searchbar=document.getElementById("searchbar"),$searchbar=$(searchbar),showSearchbarResults=function(a,b,c){if(!c||!c.metaKey){if(deleteKeyPressed=c&&8==c.keyCode,c&&8!=c.keyCode&&(a=a.substring(0,b.selectionStart)+String.fromCharCode(c.keyCode)+a.substring(b.selectionEnd,a.length)),console.log("searchbar: ","'"+a+"'",a.length),a.length<1)return showHistoryResults("",b),void clearsearchbar();if(0==a.indexOf("?"))return clearsearchbar(),currentSuggestionLimit=5,void showSearchSuggestions(a.replace("?",""),b);if(0==a.indexOf("^"))return clearsearchbar(),void showHistoryResults(a.replace("^",""),b);if(0==a.indexOf("*"))return clearsearchbar(),void showBookmarkResults(a.replace("*",""),b);showBookmarkResults(a),showHistoryResults(a,b),showInstantAnswers(a,b),searchOpenTabs(a,b),searchbarCachedText=a}};$searchbar.on("keydown",".result-item",function(a){13==a.keyCode?$(this).trigger("click"):9==a.keyCode||40==a.keyCode?(a.preventDefault(),focussearchbarItem()):38==a.keyCode&&(a.preventDefault(),focussearchbarItem({focusPrevious:!0}))});var lastItemDeletion=Date.now();$searchbar.on("mousewheel",".history-results .result-item, .top-answer-results .result-item",function(a){var b=$(this);a.originalEvent.deltaX>50&&a.originalEvent.deltaY<3&&b.attr("data-url")&&Date.now()-lastItemDeletion>700&&(lastItemDeletion=Date.now(),b.animate({opacity:"0","margin-left":"-100%"},200,function(){b.remove(),bookmarks.deleteHistory(b.attr("data-url")),lastItemDeletion=Date.now()}))}),bindWebviewIPC("keywordsData",function(a,b,c){var d=c[0],e=0,f=[];d.entities.forEach(function(a,b){if(/\s/g.test(a.trim())&&!(e>=5||-1!=f.indexOf(a.trim()))){var c=createSearchbarItem({icon:"fa-search",title:a,classList:["iadata-onfocus"]});c.addEventListener("click",function(b){b.metaKey?openURLInBackground(a):navigate(tabs.getSelected(),a)}),serarea.appendChild(c),e++,f.push(a.trim())}})});var DDGSearchURLRegex=/^https:\/\/duckduckgo.com\/\?q=([^&]*).*/g,trailingSlashRegex=/\/$/g,plusRegex=/\+/g,currentACItem=null,deleteKeyPressed=!1,historyarea=searchbar.querySelector(".history-results"),$historyarea=$(historyarea),maxHistoryResults=4,currentHistoryResults=null,showHistoryResults=throttle(function(a,b,c){return a||!b.value?(a&&(a=a.trim()),a||navigator.onLine?void bookmarks.searchHistory(a,function(d){currentHistoryResults=d;var e=!1;c=c||maxHistoryResults,a||(c=4),empty(historyarea),topAnswerarea.getElementsByClassName("history-item").length>0&&empty(topAnswerarea),searchbarAutocomplete(a,b,d),0==a.indexOf("!")?showSearchSuggestions(a,b,5):d.length<10?(c=3,showSearchSuggestions(a,b,5-d.length)):empty(serarea);var f=0;requestAnimationFrame(function(){var b=0==a.indexOf("!");if(d.slice(0,4).forEach(function(a){DDGSearchURLRegex.lastIndex=0;var d=DDGSearchURLRegex.test(a.url);if(d||!b){if(d)var g=decodeURIComponent(a.url.replace(DDGSearchURLRegex,"$1").replace(plusRegex," ")),h={icon:"fa-search",title:g,url:a.url,classList:["history-item"]};else{var h={icon:"fa-globe",title:getRealTitle(a.title)||a.url,url:a.url,classList:["history-item"]};a.title!==a.url&&(h.secondaryText=urlParser.prettyURL(a.url))}var i=createSearchbarItem(h);i.addEventListener("click",function(b){openURLFromsearchbar(b,a.url)}),f>=c&&(i.hidden=!0,i.classList.add("unfocusable")),urlParser.areEqual(currentACItem,a.url)&&c>f&&!e?(i.classList.add("fakefocus"),topAnswerarea.appendChild(i),e=!0):historyarea.appendChild(i),f++}}),currentACItem&&!e&&!DDGSearchURLRegex.test(currentACItem)){var g=createSearchbarItem({classList:["history-item","fakefocus"],icon:"fa-globe",title:urlParser.prettyURL(currentACItem)});g.addEventListener("click",function(a){openURLFromsearchbar(a,currentACItem)}),topAnswerarea.appendChild(g)}})}):void readerView.showReadingList({limitResults:!0})):void 0},50),bookmarkarea=searchbar.querySelector(".bookmark-results"),showBookmarkResults=debounce(function(a){return a.length<5||0==a.indexOf("!")?(limitHistoryResults(5),void empty(bookmarkarea)):void bookmarks.searchBookmarks(a,function(b){empty(bookmarkarea);var c=1;b.splice(0,2).forEach(function(b){$('.result-item[data-url="{url}"]:not([hidden])'.replace("{url}",b.url))[0]||b.score>Math.max(4e-4,.0016-12e-5*Math.pow(1.25,a.length))&&(1==c||a.length>6)&&(requestAnimationFrame(function(){addBookmarkItem(b)}),c++)}),limitHistoryResults(5-c)})},133),showAllBookmarks=function(){bookmarks.searchBookmarks("",function(a){a.sort(function(a,b){return a.urlb.url?1:0}),a.forEach(addBookmarkItem)})},bangRegex=/!\w+/g,serarea=searchbar.querySelector(".search-engine-results"),iaarea=searchbar.querySelector(".instant-answer-results"),topAnswerarea=searchbar.querySelector(".top-answer-results"),suggestedsitearea=searchbar.querySelector("#searchbar .ddg-site-results"),cachedBangSnippets={},IAFormats={color_code:function(a,b){var c=[b.data.rgb,b.data.hslc,b.data.cmyb];-1==a.indexOf("#")&&c.unshift(b.data.hexc);var d=$("
");return $("").text(a).appendTo(d),$("
").css("background-color","#"+b.data.hex_code).prependTo(d),$("").text(c.join(" "+METADATA_SEPARATOR+" ")).appendTo(d),d},minecraft:function(a,b){var c=$("
");return $("").text(b.data.title).appendTo(c),$("").attr("src",b.data.image).prependTo(c),$("").text(b.data.description+" "+b.data.subtitle).appendTo(c),c},figlet:function(a,b){var c=removeTags(b).replace("Font: standard",""),d=$("
"),e=$("").text(c).appendTo(d);return e.css({"white-space":"pre-wrap","font-family":"monospace","max-height":"10em","-webkit-user-select":"auto"}),d},currency_in:function(a,b){var c=$("
"),d="";if("string"==typeof b)d=b;else{var e=[];for(var f in b.data.record_data)e.push(b.data.record_data[f]+" ("+f+")");d=e.join(", ")}$("").text(d).appendTo(c);if(b.data){$("").text(b.data.title).appendTo(c)}else{$("").text("Answer").appendTo(c)}return c}};window.showSearchSuggestions=throttle(function(a,b,c){a&&!tabs.get(tabs.getSelected())["private"]&&(c=Math.max(2,c),fetch("https://ac.duckduckgo.com/ac/?q="+encodeURIComponent(a)).then(function(a){return a.json()}).then(function(a){empty(serarea),a&&a[0]&&a[0].snippet?a.splice(0,5).forEach(function(a){cachedBangSnippets[a.phrase]=a.snippet;var c={image:a.image,imageIsInline:!0,title:a.snippet,secondaryText:a.phrase},d=createSearchbarItem(c);d.addEventListener("click",function(){setTimeout(function(){b.value=a.phrase+" ",b.focus()},66)}),serarea.appendChild(d)}):a&&a.splice(0,c).forEach(function(a){var b={title:a.phrase,classList:["iadata-onfocus"]};if(bangRegex.test(a.phrase)){b.title=a.phrase.replace(bangRegex,"");var c=a.phrase.match(bangRegex)[0];b.secondaryText="Search on "+cachedBangSnippets[c]}urlParser.isURL(a.phrase)||urlParser.isURLMissingProtocol(a.phrase)?b.icon="fa-globe":b.icon="fa-search";var d=createSearchbarItem(b);d.addEventListener("click",function(b){openURLFromsearchbar(b,a.phrase)}),serarea.appendChild(d)})}))},500),window.showInstantAnswers=debounce(function(a,b,c){return c=c||{},a?void(urlParser.isURLMissingProtocol(a)||tabs.get(tabs.getSelected())["private"]||(a.length>3?fetch("https://api.duckduckgo.com/?skip_disambig=1&no_redirect=1&format=json&q="+encodeURIComponent(a)).then(function(a){return a.json()}).then(function(d){if(a==getValue(b)||c.alwaysShow){if(IAFormats[d.AnswerType])var e=IAFormats[d.AnswerType](a,d.Answer).get(0);else if(d.Abstract||d.Answer){var f={title:removeTags(d.Answer||d.Heading),descriptionBlock:d.Abstract||"Answer",classList:["ddg-answer","indent"]};d.Image&&!d.ImageIsLogo&&(f.image=d.Image);var e=createSearchbarItem(f)}if((0!=c.destroyPrevious||e)&&$searchbar.find(".ddg-answer").remove(),e&&(e.addEventListener("click",function(b){openURLFromsearchbar(b,d.AbstractURL||a)}),d.Answer?(empty(topAnswerarea),topAnswerarea.appendChild(e)):iaarea.appendChild(e)),d.Results&&d.Results[0]&&d.Results[0].FirstURL){var g=d.Results[0].FirstURL,f={icon:"fa-globe",title:urlParser.removeProtocol(g).replace(trailingSlashRegex,""),secondaryText:"Suggested site",url:g,classList:["ddg-answer"]},e=createSearchbarItem(f);e.addEventListener("click",function(a){openURLFromsearchbar(a,d.Results[0].FirstURL)}),suggestedsitearea.appendChild(e)}var h=["location","country","u.s. state","protected area"];if(-1!=h.indexOf(d.Entity)){var e=createSearchbarItem({icon:"fa-search",title:d.Heading,secondaryText:"Search on OpenStreetMap",classList:["ddg-answer"]});e.addEventListener("click",function(a){openURLFromsearchbar(a,"https://www.openstreetmap.org/search?query="+encodeURIComponent(d.Heading))}),iaarea.insertBefore(e,iaarea.firstChild)}}})["catch"](function(a){console.error(a)}):$searchbar.find(".ddg-answer").remove())):(empty(iaarea),void empty(suggestedsitearea))},450);var spacesRegex=/[\s._\/-]/g,opentabarea=searchbar.querySelector(".opentab-results"),stringScore=require("string_score"),searchOpenTabs=function(a){ +requestAnimationFrame(function(){if(empty(opentabarea),!(a.length<3)){var b=[],c=tabs.getSelected();tabs.get().forEach(function(d){if(d.id!=c&&d.title&&"about:blank"!=d.url){var e=urlParser.removeProtocol(d.url),f=-1!=d.title.indexOf(a)||-1!=e.indexOf(a),g=d.title.substring(0,50).score(a,.5)>.4||e.score(a,.5)>.4;(f||g)&&b.push(d)}}),b.splice(0,2).sort(function(b,c){return c.title.score(a,.5)-b.title.score(a,.5)}).forEach(function(a){var b={icon:"fa-external-link-square",title:a.title,secondaryText:urlParser.removeProtocol(a.url).replace(trailingSlashRegex,"")},c=createSearchbarItem(b);c.addEventListener("click",function(){"about:blank"==tabs.get(tabs.getSelected()).url&&destroyTab(tabs.getSelected(),{switchToTab:!1}),switchToTab(a.id)}),opentabarea.appendChild(c)})}})},readerView={readerURL:"file://"+__dirname+"/reader/index.html",getButton:function(a){var b=$("").attr("data-tab",a).attr("title","Enter reader view");return b.on("click",function(a){var b=$(this).parents(".tab-item").attr("data-tab"),c=tabs.get(b);a.stopPropagation(),c.isReaderView?readerView.exit(b):readerView.enter(b)}),b},updateButton:function(a){var b=$('.reader-button[data-tab="{id}"]'.replace("{id}",a)),c=tabs.get(a);return c.isReaderView?void b.addClass("is-reader").attr("title","Exit reader view"):(b.removeClass("is-reader").attr("title","Enter reader view"),void(c.readerable?b.addClass("can-reader"):b.removeClass("can-reader")))},enter:function(a){navigate(a,readerView.readerURL+"?url="+tabs.get(a).url),tabs.update(a,{isReaderView:!0})},exit:function(a){navigate(a,tabs.get(a).url.split("?url=")[1]),tabs.update(a,{isReaderView:!1})},showReadingList:function(a){performance.now()<1e3||bookmarks.searchHistory(readerView.readerURL,function(b){function c(a){return a.lastVisit+5e3*a.visitCount}var d=Date.now(),e=6048e5,f=b.filter(function(a){return 0==a.url.indexOf(readerView.readerURL)&&(d-a.lastVisittabActivity.minFadeAge?getTabElement(a.id).addClass("fade"):getTabElement(a.id).removeClass("fade"))})})},init:function(){setInterval(tabActivity.refresh,7500)}};tabActivity.init();var colorExtractorImage=document.createElement("img");const defaultColors={"private":["rgb(58, 44, 99)","white"],regular:["rgb(255, 255, 255)","black"]};var hours=(new Date).getHours()+(new Date).getMinutes()/60;setInterval(function(){var a=new Date;hours=a.getHours()+a.getMinutes()/60},24e4);var getTextColor=function(a){var b=runNetwork(a);return b.black>.5?"black":"white"},runNetwork=function(a){for(var b={layers:[{r:{},g:{},b:{}},{0:{bias:14.176907520571566,weights:{r:-3.2764240497480652,g:-16.90247884718719,b:-2.9976364179397814}},1:{bias:9.086071102351246,weights:{r:-4.327474143397604,g:-15.780660155750773,b:2.879230202567851}},2:{bias:22.274487339773476,weights:{r:-3.5830205067960965,g:-25.498384261673618,b:-6.998329189107962}}},{black:{bias:17.873962570788997,weights:{0:-15.542217788633987,1:-13.377152708685674,2:-24.52215186113144}}}],outputLookup:!0,inputLookup:!0},c=1;ca&&(a=Math.max(0,c+1+a)),this.append(b),c>a&&this.children().eq(a).before(this.children().last()),this};var tabContainer=$(".tab-group"),tabGroup=$(".tab-group #tabs"),lastTabDeletion=0;tabGroup.on("mousewheel",".tab-item",function(a){if(a.originalEvent.deltaY>65&&a.originalEvent.deltaX<10&&Date.now()-lastTabDeletion>650){if(lastTabDeletion=Date.now(),isFocusMode)return void showFocusModeError();var b=this.getAttribute("data-tab");getTabElement(b).animate({"margin-top":"-40px"},125,function(){if(b==tabs.getSelected()){var a=tabs.getIndex(tabs.getSelected()),c=tabs.getAtIndex(a-1)||tabs.getAtIndex(a+1);destroyTab(b),c?switchToTab(c.id):addTab()}else destroyTab(b)})}a.originalEvent.deltaY>0&&a.stopPropagation()}),tabGroup.on("click",".tab-item",function(a){var b=this.getAttribute("data-tab");tabs.getSelected()!=b?switchToTab(b):isExpandedMode||enterEditMode(b)}),$.fn.getInput=function(){return this.find(".tab-input")},bindWebviewEvent("focus",function(){leaveExpandedMode(),leaveTabEditMode()});var tabDragArea=tabGroup[0];require.async("dragula",function(a){window.dragRegion=a(),dragRegion.on("drop",function(){var a=[];tabContainer.find(".tab-item").each(function(){var b=parseInt($(this).attr("data-tab"));a.push(b)}),tabs.reorder(a)})}),tabContainer.on("mousewheel",function(a){a.originalEvent.deltaY<-30&&a.originalEvent.deltaX<10&&(enterExpandedMode(),a.stopImmediatePropagation())}),tabContainer.on("mouseenter",".tab-item",function(a){if(isExpandedMode){var b=$(this);setTimeout(function(){if(b.is(":hover")){tabs.get(b.attr("data-tab"));switchToTab(b.attr("data-tab"))}},125)}});var isExpandedMode=!1;tabContainer.on("click",".tab-item",function(){isExpandedMode&&(leaveExpandedMode(),getWebview(tabs.getSelected())[0].focus())});var addTabButton=$(".add-tab");addTabButton.on("click",function(a){var b=tabs.add({},tabs.getIndex(tabs.getSelected())+1);addTab(b)}),ipc.on("zoomIn",function(){getWebview(tabs.getSelected())[0].send("zoomIn")}),ipc.on("zoomOut",function(){getWebview(tabs.getSelected())[0].send("zoomOut")}),ipc.on("zoomReset",function(){getWebview(tabs.getSelected())[0].send("zoomReset")}),ipc.on("print",function(){getWebview(tabs.getSelected())[0].print()}),ipc.on("findInPage",function(){findinpage.start()}),ipc.on("inspectPage",function(){getWebview(tabs.getSelected())[0].openDevTools()}),ipc.on("showReadingList",function(){showSearchbar(getTabElement(tabs.getSelected()).getInput()),enterEditMode(tabs.getSelected()),clearsearchbar(),readerView.showReadingList()}),ipc.on("addTab",function(a){if(isFocusMode)return void showFocusModeError();var b=tabs.getIndex(tabs.getSelected())+1,c=tabs.add({},b);addTab(c)}),ipc.on("addPrivateTab",addPrivateTab),require.async("mousetrap",function(a){window.Mousetrap=a,a.bind("shift+command+p",addPrivateTab),a.bind(["command+l","command+k"],function(a){return enterEditMode(tabs.getSelected()),!1}),a.bind("command+w",function(a){if(a.preventDefault(),a.stopImmediatePropagation(),isFocusMode)return void showFocusModeError();var b=tabs.getSelected(),c=tabs.getIndex(b),d=tabs.getAtIndex(c-1)||tabs.getAtIndex(c+1);return destroyTab(b),d?switchToTab(d.id):addTab(),1==tabs.count()&&leaveExpandedMode(),!1}),a.bind("command+d",function(a){getTabElement(tabs.getSelected()).find(".bookmarks-button").click()});for(var b=1;9>b;b++)!function(b){a.bind("command+"+b,function(a){var c=tabs.getIndex(tabs.getSelected()),d=tabs.getAtIndex(c+b)||tabs.getAtIndex(c-b);d&&switchToTab(d.id)}),a.bind("shift+command+"+b,function(a){var c=tabs.getIndex(tabs.getSelected()),d=tabs.getAtIndex(c-b)||tabs.getAtIndex(c+b);d&&switchToTab(d.id)})}(b);a.bind("command+9",function(a){switchToTab(tabs.getAtIndex(tabs.count()-1).id)}),a.bind("shift+command+9",function(a){switchToTab(tabs.getAtIndex(0).id)}),a.bind("esc",function(a){leaveTabEditMode(),leaveExpandedMode(),getWebview(tabs.getSelected()).get(0).focus()}),a.bind("shift+command+r",function(){getTabElement(tabs.getSelected()).find(".reader-button").trigger("click")}),a.bind("command+left",function(a){getWebview(tabs.getSelected())[0].goBack()}),a.bind("command+right",function(a){getWebview(tabs.getSelected())[0].goForward()}),a.bind(["option+command+left","shift+ctrl+tab"],function(a){enterExpandedMode();var b=tabs.getIndex(tabs.getSelected()),c=tabs.getAtIndex(b-1);switchToTab(c?c.id:tabs.getAtIndex(tabs.count()-1).id)}),a.bind(["option+command+right","ctrl+tab"],function(a){enterExpandedMode();var b=tabs.getIndex(tabs.getSelected()),c=tabs.getAtIndex(b+1);switchToTab(c?c.id:tabs.getAtIndex(0).id)}),a.bind("command+n",function(a){for(var b=tabs.get(),c=0;c