diff --git a/js/navbar/tabEditor.js b/js/navbar/tabEditor.js index 14cf3db20..54b198d90 100644 --- a/js/navbar/tabEditor.js +++ b/js/navbar/tabEditor.js @@ -102,10 +102,9 @@ const tabEditor = { 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) + if (this.getAttribute('data-autocomplete-text') && this.getAttribute('data-autocomplete-text').toLowerCase() === this.value.toLowerCase()) { + // The original autocompletion can contain additional information, such as a protocol or different capitalization than what was typed + searchbar.openURL(this.getAttribute('data-autocomplete-ref') || this.getAttribute('data-autocomplete-text'), e) } else { searchbar.openURL(this.value, e) } diff --git a/js/searchbar/bangsPlugin.js b/js/searchbar/bangsPlugin.js index a092e7a3c..d4bd9608f 100644 --- a/js/searchbar/bangsPlugin.js +++ b/js/searchbar/bangsPlugin.js @@ -177,7 +177,7 @@ function getBangSearchResults (text, input, inputFlags) { showBangSearchResults(text, results, input, inputFlags) if (results[0] && !inputFlags.isDeletion) { - searchbarAutocomplete.autocomplete(input, [results[0].phrase]) + searchbarAutocomplete.autocomplete(input, [[results[0].phrase]]) } } }) diff --git a/js/searchbar/bookmarkEditor.js b/js/searchbar/bookmarkEditor.js index 93cfe282a..de3ca6ed3 100644 --- a/js/searchbar/bookmarkEditor.js +++ b/js/searchbar/bookmarkEditor.js @@ -183,7 +183,7 @@ const bookmarkEditor = { newTagInput.addEventListener('keypress', function (e) { if (e.keyCode !== 8 && e.keyCode !== 13) { places.getAllTagsRanked(bookmarkEditor.currentInstance.bookmark.url).then(function (results) { - autocomplete.autocomplete(newTagInput, results.map(r => r.tag)) + autocomplete.autocomplete(newTagInput, results.map(r => [r.tag])) }) } }) diff --git a/js/searchbar/placesPlugin.js b/js/searchbar/placesPlugin.js index adca7210c..ea6a3202a 100644 --- a/js/searchbar/placesPlugin.js +++ b/js/searchbar/placesPlugin.js @@ -47,7 +47,7 @@ async function showSearchbarPlaceResults (text, input, inputFlags, pluginName = if (canAutocomplete) { // if the query is autocompleted, pressing enter will search for the result using the current search engine, so only pages from the current engine should be autocompleted if (searchQuery && searchQuery.engine === searchEngine.getCurrent().name && index === 0) { - var acResult = searchbarAutocomplete.autocomplete(input, [searchQuery.search]) + var acResult = searchbarAutocomplete.autocomplete(input, [[searchQuery.search]]) if (acResult.valid) { canAutocomplete = false didAutocompleteResult = true diff --git a/js/util/autocomplete.js b/js/util/autocomplete.js index 93ba0500a..dbd722554 100644 --- a/js/util/autocomplete.js +++ b/js/util/autocomplete.js @@ -1,6 +1,6 @@ var urlParser = require('util/urlParser.js') -function autocomplete (input, strings) { +function autocomplete(input, completions) { // if there is text after the selection, we can never autocomplete if (input.selectionEnd !== input.value.length) { return { @@ -10,12 +10,17 @@ function autocomplete (input, strings) { var text = input.value.substring(0, input.selectionStart) - for (var i = 0; i < strings.length; i++) { + for (var i = 0; i < completions.length; i++) { // check if the item can be autocompleted - if (strings[i].toLowerCase().indexOf(text.toLowerCase()) === 0) { - input.value = text + strings[i].substring(input.selectionStart) - input.setSelectionRange(text.length, strings[i].length) - input.setAttribute('data-autocomplete', strings[i]) + if (completions[i][0].toLowerCase().indexOf(text.toLowerCase()) === 0) { + input.value = text + completions[i][0].substring(input.selectionStart) + input.setSelectionRange(text.length, completions[i][0].length) + input.setAttribute('data-autocomplete-text', completions[i][0]) + if (completions[i][1]) { + input.setAttribute('data-autocomplete-ref', completions[i][1]) + } else { + input.removeAttribute('data-autocomplete-ref') + } return { valid: true, @@ -23,7 +28,8 @@ function autocomplete (input, strings) { } } } - input.removeAttribute('data-autocomplete') + input.removeAttribute('data-autocomplete-text') + input.removeAttribute('data-autocomplete-ref') return { valid: false } @@ -31,23 +37,51 @@ function autocomplete (input, strings) { // autocompletes based on a result item // returns: 1 - the exact URL was autocompleted, 0 - the domain was autocompleted, -1: nothing was autocompleted -function autocompleteURL (input, url) { +function autocompleteURL(input, url) { var urlObj = new URL(url) var hostname = urlObj.hostname // the different variations of the URL we can autocomplete var possibleAutocompletions = [ // we start with the domain, including any non-standard ports (such as localhost:8080) - hostname + (urlObj.port ? ':' + urlObj.port : ''), + [ + hostname + (urlObj.port ? ':' + urlObj.port : ''), + new URL(urlObj.protocol + '//' + hostname + (urlObj.port ? ':' + urlObj.port : '')) + ], // 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 - (hostname + '/').replace(urlParser.startingWWWRegex, '$1').replace('/', ''), + [ + (hostname + '/').replace(urlParser.startingWWWRegex, '$1').replace('/', ''), + new URL(urlObj.protocol + '//' + (hostname + '/').replace(urlParser.startingWWWRegex, '$1').replace('/', '')) + ], // then try the whole URL - urlParser.prettyURL(url), + [ + urlParser.prettyURL(url), + (() => { + try { + return new URL(urlParser.prettyURL(url)) + } catch (e) { return null } + })() + ], // then try the URL with querystring - urlParser.basicURL(url), + [ + urlParser.basicURL(url), + (() => { + try { + return new URL(urlParser.basicURL(url)) + } catch (e) { return null } + })() + ], // then just try the URL with protocol - url + [ + url, + (() => { + try { + return new URL(url) + } catch (e) { return null } + })() + ] ] + .map(completion => [completion[0], completion[1] ? completion[1].toString() : null]) var autocompleteResult = autocomplete(input, possibleAutocompletions)