diff --git a/css/tabBar.css b/css/tabBar.css index e223934bf..5a96b166a 100644 --- a/css/tabBar.css +++ b/css/tabBar.css @@ -102,23 +102,28 @@ body.linux.menu-bar-hidden #menu-button { /* tab layout */ #navbar #tabs { + display: flex; + flex: 1; + position: relative; + overflow: hidden; +} +#tabs #tabs-inner { display: flex; flex: 1; overflow-x: auto; overflow-y: hidden; } -body.is-edit-mode #navbar #tabs { - overflow: hidden; -} + .tab-item { flex: 1; min-width: 125px; - transition: .15s min-width, 0.2s transform; + transition: 0.2s transform; line-height: 36px; height: 36px; overflow: hidden; word-break: break-all; position: relative; + padding: 0 0.8em; } .tab-item:not(.active):hover { background-color: rgba(0, 0, 0, 0.03); @@ -130,10 +135,6 @@ body.is-edit-mode #navbar #tabs { background-color: rgba(255, 255, 255, 0.15); } -.tab-view-contents { - padding: 0 0.8em; -} - /* icons */ .tab-item .tab-icon-area { @@ -155,12 +156,6 @@ body.is-edit-mode #navbar #tabs { .dark-theme.theme-background-low-contrast .tab-item.active:not(:only-child) { background: rgba(255, 255, 255, 0.25); } -.tab-item.selected { - min-width: 100%; - /* remove the white transparent-ness */ - background: none; - padding-left: 9px; -} .tab-item.fade { opacity: 0.55; } @@ -170,73 +165,30 @@ body.is-edit-mode #navbar #tabs { /* style tab scrollbar */ -#navbar #tabs::-webkit-scrollbar { +#navbar #tabs-inner::-webkit-scrollbar { height: 0.25em; } -#navbar #tabs::-webkit-scrollbar-track { +.is-edit-mode #navbar #tabs-inner::-webkit-scrollbar { + height: 0 +} +#navbar #tabs-inner::-webkit-scrollbar-track { background-color: rgba(0, 0, 0, 0.05); } -#navbar #tabs::-webkit-scrollbar-thumb { +#navbar #tabs-inner::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.175); border-radius: 0.25em; } -.dark-theme #navbar #tabs::-webkit-scrollbar-track { +.dark-theme #navbar #tabs-inner::-webkit-scrollbar-track { background-color: rgba(255, 255, 255, 0.25); } -.dark-theme #navbar #tabs::-webkit-scrollbar-thumb { +.dark-theme #navbar #tabs-inner::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.6); } -/* when an input is selected, hide other tabs */ - -.is-edit-mode .tab-item:not(.selected) { - min-width: 0; - max-width: 0; - width: 0; - height: 0; - padding: 0; - overflow: hidden; -} - -/* the background isn't necessary when there is only one tab */ +/* hide tabs when tab editor is open */ .is-edit-mode .tab-item { - background: none !important; -} - -/* show either the view or edit contents */ - -.tab-item .tab-edit-contents { - display: none; -} -.tab-item.selected .tab-edit-contents { - display: flex; - height: 100%; - align-items: center; -} -.tab-item.selected .tab-view-contents { - display: none; -} - -/* tab inputs */ - -.tab-item .tab-input { - line-height: 1.15em; - font-size: 1.15em; - -webkit-appearance: none; - background: none; - border: none; - color: inherit; - flex: 1; - height: 1.5em; - outline: none; - -webkit-user-select: auto; - -webkit-app-region: no-drag; -} -.tab-item .tab-input::-webkit-input-placeholder { - color: inherit; - opacity: 0.5; - line-height: 1.25em; + opacity: 0; } /* show the overlay in edit mode */ @@ -270,7 +222,7 @@ body.dark-mode #overlay { .tab-item:not(.active) .icon-tab-not-secure { display: none; } -.tab-item .tab-view-contents .title { +.tab-item .title { font-size: 15px; } diff --git a/css/tabEditor.css b/css/tabEditor.css new file mode 100644 index 000000000..21f149cf9 --- /dev/null +++ b/css/tabEditor.css @@ -0,0 +1,31 @@ +#tab-editor { + padding: 0 0.55em; + position: absolute; + top: 0; + left: 0; + width: 100%; + display: flex; + height: 100%; + align-items: center; + transform-origin: top left; + z-index: 1; +} + +#tab-editor-input { + background: none; + line-height: 1.15em; + font-size: 1.15em; + -webkit-appearance: none; + border: none; + color: inherit; + flex: 1; + height: 1.5em; + outline: none; + -webkit-user-select: auto; + -webkit-app-region: no-drag; +} +#tab-editor-input::-webkit-input-placeholder { + color: inherit; + opacity: 0.5; + line-height: 1.25em; +} \ No newline at end of file diff --git a/index.html b/index.html index 8621adaec..0ad74dba0 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + @@ -53,7 +54,12 @@ diff --git a/js/browserUI.js b/js/browserUI.js index e4f32dab2..3709197c7 100644 --- a/js/browserUI.js +++ b/js/browserUI.js @@ -3,6 +3,8 @@ var webviews = require('webviews.js') var urlParser = require('util/urlParser.js') var focusMode = require('focusMode.js') +var tabEditor = require('navbar/tabEditor.js') +var searchbar = require('searchbar/searchbar.js') /* loads a page in a webview */ @@ -15,7 +17,7 @@ function navigate (tabId, newURL) { webviews.update(tabId, newURL) - tabBar.leaveEditMode() + tabEditor.hide() } /* creates a new task */ @@ -54,7 +56,7 @@ function addTab (tabId = tabs.add(), options = {}) { focusWebview: options.enterEditMode === false }) if (options.enterEditMode !== false) { - tabBar.enterEditMode(tabId) + tabEditor.show(tabId) } } else { tabBar.getTab(tabId).scrollIntoView() @@ -172,7 +174,7 @@ function switchToTab (id, options) { return } - tabBar.leaveEditMode() + tabEditor.hide() tabs.setSelected(id) tabBar.setActiveTab(id) @@ -224,6 +226,21 @@ webviews.bindIPC('close-window', function (webview, tabId, args) { closeTab(tabId) }) +searchbar.events.on('url-selected', function (data) { + if (data.background) { + var newTab = tabs.add({ + url: data.url, + private: tabs.get(tabs.getSelected()).private + }) + addTab(newTab, { + enterEditMode: false, + openInBackground: true + }) + } else { + navigate(tabs.getSelected(), data.url) + } +}) + module.exports = { navigate, addTask, diff --git a/js/default.js b/js/default.js index 37c1c99cf..7d07d0111 100644 --- a/js/default.js +++ b/js/default.js @@ -2,6 +2,7 @@ window.userDataPath = process.argv.filter(a => a.startsWith('--user-data-path=') window.electron = require('electron') window.fs = require('fs') +window.EventEmitter = require('events') window.ipc = electron.ipcRenderer window.remote = electron.remote window.Dexie = require('dexie') diff --git a/js/defaultKeybindings.js b/js/defaultKeybindings.js index 2a1048626..6bd6ec3d5 100644 --- a/js/defaultKeybindings.js +++ b/js/defaultKeybindings.js @@ -3,6 +3,7 @@ var webviews = require('webviews.js') var browserUI = require('browserUI.js') var focusMode = require('focusMode.js') var modalMode = require('modalMode.js') +var tabEditor = require('navbar/tabEditor.js') const defaultKeybindings = { initialize: function () { @@ -52,12 +53,12 @@ const defaultKeybindings = { }) keybindings.defineShortcut('enterEditMode', function (e) { - tabBar.enterEditMode(tabs.getSelected()) + tabEditor.show(tabs.getSelected()) return false }) keybindings.defineShortcut('runShortcut', function (e) { - tabBar.enterEditMode(tabs.getSelected(), '!') + tabEditor.show(tabs.getSelected(), '!') }) keybindings.defineShortcut('closeTab', function (e) { @@ -83,8 +84,8 @@ const defaultKeybindings = { }) keybindings.defineShortcut('addToFavorites', function (e) { - tabBar.getTab(tabs.getSelected()).querySelector('.bookmarks-button').click() - tabBar.enterEditMode(tabs.getSelected(), null, false) // we need to show the bookmarks button, which is only visible in edit mode + tabEditor.show(tabs.getSelected(), null, false) // we need to show the bookmarks button, which is only visible in edit mode + tabEditor.container.querySelector('.bookmarks-button').click() }) // cmd+x should switch to tab x. Cmd+9 should switch to the last tab @@ -118,7 +119,7 @@ const defaultKeybindings = { }) keybindings.defineShortcut({ keys: 'esc' }, function (e) { - tabBar.leaveEditMode() + tabEditor.hide() // exit full screen mode webviews.callAsync(tabs.getSelected(), 'executeJavaScript', 'if(document.webkitIsFullScreen){document.webkitExitFullscreen()}') diff --git a/js/menuRenderer.js b/js/menuRenderer.js index 835798316..b92fb61d9 100644 --- a/js/menuRenderer.js +++ b/js/menuRenderer.js @@ -7,6 +7,7 @@ var focusMode = require('focusMode.js') var modalMode = require('modalMode.js') var findinpage = require('findinpage.js') var PDFViewer = require('pdfViewer.js') +var tabEditor = require('navbar/tabEditor.js') module.exports = { initialize: function () { @@ -48,15 +49,15 @@ module.exports = { ipc.on('showReadingList', function () { // open the searchbar with "!readinglist " as the input - tabBar.enterEditMode(tabs.getSelected(), '!readinglist ') + tabEditor.show(tabs.getSelected(), '!readinglist ') }) ipc.on('showBookmarks', function () { - tabBar.enterEditMode(tabs.getSelected(), '!bookmarks ') + tabEditor.show(tabs.getSelected(), '!bookmarks ') }) ipc.on('showHistory', function () { - tabBar.enterEditMode(tabs.getSelected(), '!history ') + tabEditor.show(tabs.getSelected(), '!history ') }) ipc.on('duplicateTab', function (e) { diff --git a/js/navbar/bookmarkStar.js b/js/navbar/bookmarkStar.js index b764b3867..36c77bdce 100644 --- a/js/navbar/bookmarkStar.js +++ b/js/navbar/bookmarkStar.js @@ -5,21 +5,21 @@ const searchbar = require('searchbar/searchbar.js') const searchbarPlugins = require('searchbar/searchbarPlugins.js') const bookmarkStar = { - create: function (tabId) { + create: function () { const star = document.createElement('button') star.className = 'fa fa-star-o tab-icon bookmarks-button' // alternative icon is fa-bookmark star.setAttribute('title', l('addBookmark')) star.setAttribute('aria-label', l('addBookmark')) star.addEventListener('click', function (e) { - bookmarkStar.onClick(tabId, star) + bookmarkStar.onClick(star) }) - bookmarkStar.update(tabId, star) - return star }, - onClick: function (tabId, star) { + onClick: function (star) { + var tabId = star.getAttribute('data-tab') + searchbarPlugins.clearAll() star.classList.toggle('fa-star') @@ -27,15 +27,21 @@ const bookmarkStar = { places.toggleBookmarked(tabId, function (isBookmarked) { if (isBookmarked) { + // since the update happens asynchronously, and star.update() could be called after onClick but before the update, it's possible for the classes to get out of sync with the actual bookmark state. Updating them here fixes tis. + star.classList.add('fa-star') + star.classList.remove('fa-star-o') var editorInsertionPoint = document.createElement('div') searchbarPlugins.getContainer('simpleBookmarkTagInput').appendChild(editorInsertionPoint) bookmarkEditor.show(tabs.get(tabs.getSelected()).url, editorInsertionPoint, null, {simplified: true}) } else { + star.classList.remove('fa-star') + star.classList.add('fa-star-o') searchbar.showResults('') } }) }, update: function (tabId, star) { + star.setAttribute('data-tab', tabId) const currentURL = tabs.get(tabId).url if (!currentURL) { // no url, can't be bookmarked diff --git a/js/navbar/tabBar.js b/js/navbar/tabBar.js index d907bf99b..e77bdaac5 100644 --- a/js/navbar/tabBar.js +++ b/js/navbar/tabBar.js @@ -1,20 +1,18 @@ var webviews = require('webviews.js') var browserUI = require('browserUI.js') var focusMode = require('focusMode.js') -var modalMode = require('modalMode.js') -var searchbar = require('searchbar/searchbar.js') var urlParser = require('util/urlParser.js') +var tabEditor = require('navbar/tabEditor.js') var progressBar = require('navbar/progressBar.js') -var bookmarkStar = require('navbar/bookmarkStar.js') var permissionRequests = require('navbar/permissionRequests.js') var lastTabDeletion = 0 // TODO get rid of this window.tabBar = { container: document.getElementById('tabs'), + containerInner: document.getElementById('tabs-inner'), tabElementMap: {}, // tabId: tab element - editingTab: null, // the ID of the tab that is being edited getTab: function (tabId) { return tabBar.tabElementMap[tabId] }, @@ -31,82 +29,17 @@ window.tabBar = { var el = tabBar.getTab(tabId) el.classList.add('active') - requestIdleCallback(function () { - requestAnimationFrame(function () { - el.scrollIntoView() - }) - }, { - timeout: 1500 + requestAnimationFrame(function () { + el.scrollIntoView() }) }, - enterEditMode: function (tabId, editingValue, showSearchbar) { - // editingValue: an optional string to show in the searchbar instead of the current URL - - /* Edit mode is not available in modal mode. */ - if (modalMode.enabled()) { - return - } - - webviews.requestPlaceholder('editMode') - - var tabEl = tabBar.getTab(tabId) - - document.body.classList.add('is-edit-mode') - tabEl.classList.add('selected') - - var currentURL = urlParser.getSourceURL(tabs.get(tabId).url) - if (currentURL === 'min://newtab') { - currentURL = '' - } - - var input = tabBar.getTabInput(tabId) - input.value = editingValue || currentURL - input.focus() - if (!editingValue) { - input.select() - } - - searchbar.show(input) - - if (showSearchbar !== false) { - if (editingValue) { - searchbar.showResults(editingValue, null) - } else { - searchbar.showResults('', null) - } - } - - tabBar.editingTab = tabId - }, - leaveEditMode: function () { - if (!tabBar.editingTab) { - return - } - var selTab = document.querySelector('.tab-item.selected') - if (selTab) { - selTab.classList.remove('selected') - } - - var input = document.querySelector('.tab-item .tab-input:focus') - if (input) { - input.blur() - } - - document.body.classList.remove('is-edit-mode') - searchbar.hide() - - webviews.hidePlaceholder('editMode') - - tabBar.editingTab = null - }, rerenderTab: function (tabId) { var tabData = tabs.get(tabId) var tabEl = tabBar.getTab(tabId) - var viewContents = tabEl.querySelector('.tab-view-contents') var tabTitle = tabData.title || l('newTabLabel') - var titleEl = viewContents.querySelector('.title') + var titleEl = tabEl.querySelector('.title') titleEl.textContent = tabTitle tabEl.title = tabTitle @@ -114,29 +47,26 @@ window.tabBar = { tabEl.title += ' (' + l('privateTab') + ')' } - viewContents.querySelectorAll('.permission-request-icon').forEach(el => el.remove()) + tabEl.querySelectorAll('.permission-request-icon').forEach(el => el.remove()) permissionRequests.getButtons(tabId).reverse().forEach(function (button) { - viewContents.insertBefore(button, viewContents.children[0]) + tabEl.insertBefore(button, tabEl.children[0]) }) - var secIcon = viewContents.getElementsByClassName('icon-tab-not-secure')[0] + var secIcon = tabEl.getElementsByClassName('icon-tab-not-secure')[0] if (tabData.secure === false) { secIcon.hidden = false } else { secIcon.hidden = true } - - // update the star to reflect whether the page is bookmarked or not - bookmarkStar.update(tabId, tabEl.querySelector('.bookmarks-button')) }, rerenderAll: function () { - empty(tabBar.container) + empty(tabBar.containerInner) tabBar.tabElementMap = {} tabs.get().forEach(function (tab) { var el = tabBar.createElement(tab) - tabBar.container.appendChild(el) + tabBar.containerInner.appendChild(el) tabBar.tabElementMap[tab.id] = el }) @@ -157,28 +87,12 @@ window.tabBar = { tabEl.title += ' (' + l('privateTab') + ')' } - var ec = document.createElement('div') - ec.className = 'tab-edit-contents' - - var input = document.createElement('input') - input.className = 'tab-input mousetrap' - input.setAttribute('placeholder', l('searchbarPlaceholder')) - input.value = url - - ec.appendChild(input) - ec.appendChild(bookmarkStar.create(data.id)) - - tabEl.appendChild(ec) - - var viewContents = document.createElement('div') - viewContents.className = 'tab-view-contents' - permissionRequests.getButtons(data.id).forEach(function (button) { - viewContents.appendChild(button) + tabEl.appendChild(button) }) - viewContents.appendChild(readerView.getButton(data.id)) - viewContents.appendChild(progressBar.create()) + tabEl.appendChild(readerView.getButton(data.id)) + tabEl.appendChild(progressBar.create()) // icons @@ -212,7 +126,7 @@ window.tabBar = { secIcon.hidden = data.secure !== false iconArea.appendChild(secIcon) - viewContents.appendChild(iconArea) + tabEl.appendChild(iconArea) // title @@ -220,69 +134,14 @@ window.tabBar = { title.className = 'title' title.textContent = tabTitle - viewContents.appendChild(title) - - tabEl.appendChild(viewContents) - - input.addEventListener('keydown', function (e) { - if (e.keyCode === 9 || e.keyCode === 40) { // if the tab or arrow down key was pressed - searchbar.focusItem() - e.preventDefault() - } - }) - - // keypress doesn't fire on delete key - use keyup instead - input.addEventListener('keyup', function (e) { - if (e.keyCode === 8) { - searchbar.showResults(this.value, e) - } - }) - - input.addEventListener('keypress', function (e) { - if (e.keyCode === 13) { // return key pressed; update the url - if (this.getAttribute('data-autocomplete') && this.getAttribute('data-autocomplete').toLowerCase() === this.value.toLowerCase()) { - // special case: if the typed input is capitalized differently from the actual URL that was autocompleted (but is otherwise the same), then we want to open the actual URL instead of what was typed. - // see https://github.com/minbrowser/min/issues/314#issuecomment-276678613 - searchbar.openURL(this.getAttribute('data-autocomplete'), e) - } else { - searchbar.openURL(this.value, e) - } - } else if (e.keyCode === 9) { - return - // tab key, do nothing - in keydown listener - } else if (e.keyCode === 16) { - return - // shift key, do nothing - } else if (e.keyCode === 8) { - return - // delete key is handled in keyUp - } else { // show the searchbar - searchbar.showResults(this.value, 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 - - var v = e.key - var sel = this.value.substring(this.selectionStart, this.selectionEnd).indexOf(v) - - if (v && sel === 0) { - this.selectionStart += 1 - e.preventDefault() - } - }) - - // prevent clicking in the input from re-entering edit-tab mode - - input.addEventListener('click', function (e) { - e.stopPropagation() - }) + tabEl.appendChild(title) // click to enter edit mode or switch to a tab - viewContents.addEventListener('click', function (e) { + tabEl.addEventListener('click', function (e) { if (tabs.getSelected() !== data.id) { // else switch to tab if it isn't focused browserUI.switchToTab(data.id) } else { // the tab is focused, edit tab instead - tabBar.enterEditMode(data.id) + tabEditor.show(data.id) } }) @@ -322,26 +181,20 @@ window.tabBar = { var index = tabs.getIndex(tabId) var tabEl = tabBar.createElement(tab) - tabBar.container.insertBefore(tabEl, tabBar.container.childNodes[index]) + tabBar.containerInner.insertBefore(tabEl, tabBar.containerInner.childNodes[index]) tabBar.tabElementMap[tabId] = tabEl }, removeTab: function (tabId) { var tabEl = tabBar.getTab(tabId) if (tabEl) { - // The tab does not have a coresponding .tab-item element. + // The tab does not have a corresponding .tab-item element. // This happens when destroying tabs from other task where this .tab-item is not present - tabBar.container.removeChild(tabEl) + tabBar.containerInner.removeChild(tabEl) delete tabBar.tabElementMap[tabId] } } } -// when we click outside the navbar, we leave editing mode - -document.getElementById('webviews').addEventListener('click', function () { - tabBar.leaveEditMode() -}) - /* progress bar events */ webviews.bindEvent('did-start-loading', function (webview, tabId, e) { diff --git a/js/navbar/tabEditor.js b/js/navbar/tabEditor.js new file mode 100644 index 000000000..19deb6c24 --- /dev/null +++ b/js/navbar/tabEditor.js @@ -0,0 +1,139 @@ +var searchbar = require('searchbar/searchbar.js') +var webviews = require('webviews.js') +var modalMode = require('modalMode.js') +var urlParser = require('util/urlParser.js') +var bookmarkStar = require('navbar/bookmarkStar.js') + +const tabEditor = { + container: document.getElementById('tab-editor'), + input: document.getElementById('tab-editor-input'), + star: null, + show: function (tabId, editingValue, showSearchbar) { + /* Edit mode is not available in modal mode. */ + if (modalMode.enabled()) { + return + } + + tabEditor.container.hidden = false + + bookmarkStar.update(tabId, tabEditor.star) + + webviews.requestPlaceholder('editMode') + + document.body.classList.add('is-edit-mode') + + var currentURL = urlParser.getSourceURL(tabs.get(tabId).url) + if (currentURL === 'min://newtab') { + currentURL = '' + } + + tabEditor.input.value = editingValue || currentURL + tabEditor.input.focus() + if (!editingValue) { + tabEditor.input.select() + } + + searchbar.show(tabEditor.input) + + if (showSearchbar !== false) { + if (editingValue) { + searchbar.showResults(editingValue, null) + } else { + searchbar.showResults('', null) + } + } + + /* animation */ + if (tabs.count() > 1) { + requestAnimationFrame(function () { + + var item = document.querySelector(`.tab-item[data-tab="${tabId}"]`) + var originCoordinates = item.getBoundingClientRect() + + var finalCoordinates = document.querySelector('#tabs').getBoundingClientRect() + + var translateX = Math.min(Math.round(originCoordinates.x - finalCoordinates.x) * 0.5, window.innerWidth) + + tabEditor.container.style.opacity = 0 + tabEditor.container.style.transform = `translateX(${translateX}px)` + requestAnimationFrame(function () { + tabEditor.container.style.transition = '0.15s all' + tabEditor.container.style.opacity = 1 + tabEditor.container.style.transform = '' + }) + }) + } + }, + hide: function () { + tabEditor.container.hidden = true + tabEditor.container.removeAttribute('style') + + tabEditor.input.blur() + searchbar.hide() + + document.body.classList.remove('is-edit-mode') + + webviews.hidePlaceholder('editMode') + }, + initialize: function () { + tabEditor.input.setAttribute('placeholder', l('searchbarPlaceholder')) + + tabEditor.star = bookmarkStar.create() + tabEditor.container.appendChild(tabEditor.star) + + tabEditor.input.addEventListener('keydown', function (e) { + if (e.keyCode === 9 || e.keyCode === 40) { // if the tab or arrow down key was pressed + searchbar.focusItem() + e.preventDefault() + } + }) + + // keypress doesn't fire on delete key - use keyup instead + tabEditor.input.addEventListener('keyup', function (e) { + if (e.keyCode === 8) { + searchbar.showResults(this.value, e) + } + }) + + tabEditor.input.addEventListener('keypress', function (e) { + if (e.keyCode === 13) { // return key pressed; update the url + if (this.getAttribute('data-autocomplete') && this.getAttribute('data-autocomplete').toLowerCase() === this.value.toLowerCase()) { + // special case: if the typed input is capitalized differently from the actual URL that was autocompleted (but is otherwise the same), then we want to open the actual URL instead of what was typed. + // see https://github.com/minbrowser/min/issues/314#issuecomment-276678613 + searchbar.openURL(this.getAttribute('data-autocomplete'), e) + } else { + searchbar.openURL(this.value, e) + } + } else if (e.keyCode === 9) { + return + // tab key, do nothing - in keydown listener + } else if (e.keyCode === 16) { + return + // shift key, do nothing + } else if (e.keyCode === 8) { + return + // delete key is handled in keyUp + } else { // show the searchbar + searchbar.showResults(this.value, 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 + + var v = e.key + var sel = this.value.substring(this.selectionStart, this.selectionEnd).indexOf(v) + + if (v && sel === 0) { + this.selectionStart += 1 + e.preventDefault() + } + }) + + document.getElementById('webviews').addEventListener('click', function () { + tabEditor.hide() + }) + } +} + +tabEditor.initialize() + +module.exports = tabEditor diff --git a/js/searchbar/bangsPlugin.js b/js/searchbar/bangsPlugin.js index f64db23f3..deb9c9e7c 100644 --- a/js/searchbar/bangsPlugin.js +++ b/js/searchbar/bangsPlugin.js @@ -1,3 +1,5 @@ +var tabEditor = require('navbar/tabEditor.js') + var searchbar = require('searchbar/searchbar.js') var searchbarPlugins = require('searchbar/searchbarPlugins.js') var searchbarAutocomplete = require('searchbar/searchbarAutocomplete.js') @@ -195,11 +197,11 @@ function initialize () { if ((!bang || !bang.isAction) && url.split(' ').length === 1 && !url.endsWith(' ')) { // the bang is non-custom or a custom bang that requires search text, so add a space after it - tabBar.enterEditMode(tabs.getSelected(), url + ' ') + tabEditor.show(tabs.getSelected(), url + ' ') return true } else if (bang) { // there is a custom bang that is an action or has search text, so it can be run - tabBar.leaveEditMode() + tabEditor.hide() bang.fn(url.replace(bang.phrase, '').trimLeft()) return true // override the default action } diff --git a/js/searchbar/bookmarkManager.js b/js/searchbar/bookmarkManager.js index 6a4e88358..0d503cce1 100644 --- a/js/searchbar/bookmarkManager.js +++ b/js/searchbar/bookmarkManager.js @@ -5,6 +5,7 @@ var places = require('places/places.js') var urlParser = require('util/urlParser.js') var formatRelativeDate = require('util/relativeDate.js') +var tabEditor = require('navbar/tabEditor.js') var bookmarkEditor = require('searchbar/bookmarkEditor.js') const maxTagSuggestions = 12 @@ -85,7 +86,7 @@ function showBookmarks (text, input, event) { parsedText.tags.forEach(function (tag) { tagBar.appendChild(bookmarkEditor.getTagElement(tag, true, function () { - tabBar.enterEditMode(tabs.getSelected(), '!bookmarks ' + text.replace('#' + tag, '').trim()) + tabEditor.show(tabs.getSelected(), '!bookmarks ' + text.replace('#' + tag, '').trim()) }, {autoRemove: false})) }) // it doesn't make sense to display tag suggestions if there's a search, since the suggestions are generated without taking the search into account @@ -93,7 +94,7 @@ function showBookmarks (text, input, event) { suggestedTags.forEach(function (suggestion, index) { var el = bookmarkEditor.getTagElement(suggestion, false, function () { var needsSpace = text.slice(-1) !== ' ' && text.slice(-1) !== '' - tabBar.enterEditMode(tabs.getSelected(), '!bookmarks ' + text + (needsSpace ? ' #' : '#') + suggestion + ' ') + tabEditor.show(tabs.getSelected(), '!bookmarks ' + text + (needsSpace ? ' #' : '#') + suggestion + ' ') }) if (index >= maxTagSuggestions) { el.classList.add('overflowing') diff --git a/js/searchbar/searchbar.js b/js/searchbar/searchbar.js index 4722738a1..6d877973e 100644 --- a/js/searchbar/searchbar.js +++ b/js/searchbar/searchbar.js @@ -1,18 +1,10 @@ var webviews = require('webviews.js') var keybindings = require('keybindings.js') -var browserUI = require('browserUI.js') var urlParser = require('util/urlParser.js') var searchbarPlugins = require('searchbar/searchbarPlugins.js') 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 - }) - browserUI.addTab(newTab, { - enterEditMode: false, - openInBackground: true - }) + searchbar.events.emit('url-selected', {url: url, background: true}) var i = searchbar.el.querySelector('.searchbar-item:focus') if (i) { // remove the highlight from an awesomebar result item, if there is one @@ -23,6 +15,7 @@ function openURLInBackground (url) { // used to open a url in the background, wi var searchbar = { el: document.getElementById('searchbar'), associatedInput: null, + events: new EventEmitter(), show: function (associatedInput) { searchbar.el.hidden = false searchbar.associatedInput = associatedInput @@ -84,7 +77,7 @@ var searchbar = { openURLInBackground(url) return true } else { - browserUI.navigate(tabs.getSelected(), url) + searchbar.events.emit('url-selected', {url: url, background: false}) // focus the webview, so that autofocus inputs on the page work webviews.focus() return false @@ -115,13 +108,11 @@ keybindings.defineShortcut('completeSearchbar', function () { if (searchbar.associatedInput) { // if the searchbar is open var value = searchbar.associatedInput.value - tabBar.leaveEditMode() - // if the text is already a URL, navigate to that page if (urlParser.isURLMissingProtocol(value)) { - browserUI.navigate(tabs.getSelected(), value) + searchbar.events.emit('url-selected', {url: value, background: false}) } else { - browserUI.navigate(tabs.getSelected(), urlParser.parse(value + '.com')) + searchbar.events.emit('url-selected', {url: urlParser.parse(value + '.com'), background: false}) } } }) diff --git a/js/searchbar/searchbarPlugins.js b/js/searchbar/searchbarPlugins.js index 6ba6fff2a..c8635420e 100644 --- a/js/searchbar/searchbarPlugins.js +++ b/js/searchbar/searchbarPlugins.js @@ -118,7 +118,7 @@ const searchbarPlugins = { var container = document.createElement('div') container.classList.add('searchbar-plugin-container') container.setAttribute('data-plugin', name) - searchbar.insertBefore(container, searchbar.childNodes[object.index + 1]) + searchbar.insertBefore(container, searchbar.childNodes[object.index + 2]) plugins.push({ name: name, diff --git a/js/sessionRestore.js b/js/sessionRestore.js index 84ca65730..b2c210e38 100644 --- a/js/sessionRestore.js +++ b/js/sessionRestore.js @@ -1,4 +1,5 @@ var browserUI = require('browserUI.js') +var tabEditor = require('navbar/tabEditor.js') window.sessionRestore = { savePath: userDataPath + (platformType === 'windows' ? '\\sessionRestore.json' : '/sessionRestore.json'), @@ -88,7 +89,7 @@ window.sessionRestore = { if (tasks.getSelected().tabs.isEmpty() || (!data.saveTime || Date.now() - data.saveTime < 30000)) { browserUI.switchToTask(data.state.selectedTask) if (tasks.getSelected().tabs.isEmpty()) { - tabBar.enterEditMode(tasks.getSelected().tabs.getSelected()) + tabEditor.show(tasks.getSelected().tabs.getSelected()) } } else { window.createdNewTaskOnStartup = true @@ -96,7 +97,7 @@ window.sessionRestore = { var lastTask = tasks.byIndex(tasks.getLength() - 1) if (lastTask && lastTask.tabs.isEmpty() && !lastTask.name) { browserUI.switchToTask(lastTask.id) - tabBar.enterEditMode(lastTask.tabs.getSelected()) + tabEditor.show(lastTask.tabs.getSelected()) } else { browserUI.addTask() } diff --git a/js/taskOverlay/taskOverlay.js b/js/taskOverlay/taskOverlay.js index 7a92fc5e1..ebf8c8632 100644 --- a/js/taskOverlay/taskOverlay.js +++ b/js/taskOverlay/taskOverlay.js @@ -1,6 +1,7 @@ var webviews = require('webviews.js') var keybindings = require('keybindings.js') var browserUI = require('browserUI.js') +var tabEditor = require('navbar/tabEditor.js') var focusMode = require('focusMode.js') var modalMode = require('modalMode.js') @@ -76,7 +77,7 @@ window.taskOverlay = { document.body.classList.add('task-overlay-is-shown') - tabBar.leaveEditMode() + tabEditor.hide() this.isShown = true taskSwitcherButton.classList.add('active') @@ -198,7 +199,7 @@ function addTaskFromMenu () { taskOverlay.show() setTimeout(function () { taskOverlay.hide() - tabBar.enterEditMode(tabs.getSelected()) + tabEditor.show(tabs.getSelected()) }, 600) }