aboutsummaryrefslogtreecommitdiffstats
path: root/pages
diff options
context:
space:
mode:
Diffstat (limited to 'pages')
-rw-r--r--pages/help_dialog.html4
-rw-r--r--pages/hud.coffee15
-rw-r--r--pages/hud.html11
-rw-r--r--pages/options.coffee25
-rw-r--r--pages/options.css5
-rw-r--r--pages/options.html36
-rw-r--r--pages/popup.html2
-rw-r--r--pages/vomnibar.coffee369
-rw-r--r--pages/vomnibar.css19
-rw-r--r--pages/vomnibar.html2
10 files changed, 341 insertions, 147 deletions
diff --git a/pages/help_dialog.html b/pages/help_dialog.html
index 0884f2cd..5c09c0ab 100644
--- a/pages/help_dialog.html
+++ b/pages/help_dialog.html
@@ -7,6 +7,7 @@
page with the up-to-date key bindings when the dialog is shown. -->
<div id="vimiumHelpDialog" class="vimiumReset">
<a class="vimiumReset optionsPage" href="#">Options</a>
+ <a class="vimiumReset wikiPage" href="https://github.com/philc/vimium/wiki" target="_blank">Wiki</a>
<a class="vimiumReset closeButton" href="#">&times;</a>
<div id="vimiumTitle" class="vimiumReset"><span class="vimiumReset" style="color:#2f508e">Vim</span>ium {{title}}</div>
<div class="vimiumReset vimiumColumn">
@@ -20,6 +21,8 @@
<div class="vimiumReset vimiumColumn">
<table class="vimiumReset" >
<tbody class="vimiumReset">
+ <tr class="vimiumReset" ><td class="vimiumReset" ></td><td class="vimiumReset" ></td><td class="vimiumReset vimiumHelpSectionTitle">Using the vomnibar</td></tr>
+ {{vomnibarCommands}}
<tr class="vimiumReset" ><td class="vimiumReset" ></td><td class="vimiumReset" ></td><td class="vimiumReset vimiumHelpSectionTitle">Using find</td></tr>
{{findCommands}}
<tr class="vimiumReset" ><td class="vimiumReset" ></td><td class="vimiumReset" ></td><td class="vimiumReset vimiumHelpSectionTitle">Navigating history</td></tr>
@@ -46,6 +49,7 @@
</div>
<div class="vimiumReset vimiumColumn" style="text-align:right">
<span class="vimiumReset">Version {{version}}</span><br/>
+ <a href="https://github.com/philc/vimium#release-notes" class="vimiumReset">What's new?</a>
</div>
</div>
</div>
diff --git a/pages/hud.coffee b/pages/hud.coffee
new file mode 100644
index 00000000..68283451
--- /dev/null
+++ b/pages/hud.coffee
@@ -0,0 +1,15 @@
+handlers =
+ show: (data) ->
+ document.getElementById("hud").innerText = data.text
+ document.getElementById("hud").classList.add "vimiumUIComponentVisible"
+ document.getElementById("hud").classList.remove "vimiumUIComponentHidden"
+ hide: ->
+ # We get a flicker when the HUD later becomes visible again (with new text) unless we reset its contents
+ # here.
+ document.getElementById("hud").innerText = ""
+ document.getElementById("hud").classList.add "vimiumUIComponentHidden"
+ document.getElementById("hud").classList.remove "vimiumUIComponentVisible"
+
+UIComponentServer.registerHandler (event) ->
+ {data} = event
+ handlers[data.name]? data
diff --git a/pages/hud.html b/pages/hud.html
new file mode 100644
index 00000000..bcb38e04
--- /dev/null
+++ b/pages/hud.html
@@ -0,0 +1,11 @@
+<html>
+ <head>
+ <title>HUD</title>
+ <link rel="stylesheet" type="text/css" href="../content_scripts/vimium.css" />
+ <script type="text/javascript" src="ui_component_server.js"></script>
+ <script type="text/javascript" src="hud.js"></script>
+ </head>
+ <body>
+ <div class="vimiumReset vimiumHUD" id="hud"></div>
+ </body>
+</html>
diff --git a/pages/options.coffee b/pages/options.coffee
index 5a4a93ab..c8c21850 100644
--- a/pages/options.coffee
+++ b/pages/options.coffee
@@ -1,7 +1,6 @@
$ = (id) -> document.getElementById id
-bgUtils = chrome.extension.getBackgroundPage().Utils
-bgSettings = chrome.extension.getBackgroundPage().Settings
+Settings.init()
bgExclusions = chrome.extension.getBackgroundPage().Exclusions
#
@@ -22,21 +21,20 @@ class Option
# Fetch a setting from localStorage, remember the @previous value and populate the DOM element.
# Return the fetched value.
fetch: ->
- @populateElement @previous = bgSettings.get @field
+ @populateElement @previous = Settings.get @field
@previous
# Write this option's new value back to localStorage, if necessary.
save: ->
value = @readValueFromElement()
if not @areEqual value, @previous
- bgSettings.set @field, @previous = value
- bgSettings.performPostUpdateHook @field, value
+ Settings.set @field, @previous = value
# Compare values; this is overridden by sub-classes.
areEqual: (a,b) -> a == b
restoreToDefault: ->
- bgSettings.clear @field
+ Settings.clear @field
@fetch()
# Static method.
@@ -118,8 +116,8 @@ class ExclusionRulesOption extends Option
readValueFromElement: ->
rules =
for element in @element.getElementsByClassName "exclusionRuleTemplateInstance"
- pattern: @getPattern(element).value.split(/\s+/).join ""
- passKeys: @getPassKeys(element).value.split(/\s+/).join ""
+ pattern: @getPattern(element).value.trim()
+ passKeys: @getPassKeys(element).value.trim()
rules.filter (rule) -> rule.pattern
areEqual: (a,b) ->
@@ -260,6 +258,7 @@ initOptionsPage = ->
searchEngines: TextOption
searchUrl: NonEmptyTextOption
userDefinedLinkHintCss: TextOption
+ omniSearchWeight: NumberOption
# Populate options. The constructor adds each new object to "Option.all".
for name, type of options
@@ -270,8 +269,12 @@ initPopupPage = ->
exclusions = null
document.getElementById("optionsLink").setAttribute "href", chrome.runtime.getURL("pages/options.html")
+ # As the active URL, we choose the most recently registered URL from a frame in the tab, or the tab's own
+ # URL.
+ url = chrome.extension.getBackgroundPage().urlForTab[tab.id] || tab.url
+
updateState = ->
- rule = bgExclusions.getRule tab.url, exclusions.readValueFromElement()
+ rule = bgExclusions.getRule url, exclusions.readValueFromElement()
$("state").innerHTML = "Vimium will " +
if rule and rule.passKeys
"exclude <span class='code'>#{rule.passKeys}</span>"
@@ -290,8 +293,6 @@ initPopupPage = ->
Option.saveOptions()
$("saveOptions").innerHTML = "Saved"
$("saveOptions").disabled = true
- chrome.tabs.query { windowId: chrome.windows.WINDOW_ID_CURRENT, active: true }, (tabs) ->
- chrome.extension.getBackgroundPage().updateActiveState(tabs[0].id)
$("saveOptions").addEventListener "click", saveOptions
@@ -301,7 +302,7 @@ initPopupPage = ->
window.close()
# Populate options. Just one, here.
- exclusions = new ExclusionRulesOnPopupOption(tab.url, "exclusionRules", onUpdated)
+ exclusions = new ExclusionRulesOnPopupOption url, "exclusionRules", onUpdated
updateState()
document.addEventListener "keyup", updateState
diff --git a/pages/options.css b/pages/options.css
index 8d1014dc..ffb348c6 100644
--- a/pages/options.css
+++ b/pages/options.css
@@ -107,9 +107,10 @@ input#linkHintNumbers {
input#linkHintCharacters {
width: 100%;
}
-input#scrollStepSize {
- width: 40px;
+input#scrollStepSize, input#omniSearchWeight {
+ width: 50px;
margin-right: 3px;
+ padding-left: 3px;
}
textarea#userDefinedLinkHintCss, textarea#keyMappings, textarea#searchEngines {
width: 100%;;
diff --git a/pages/options.html b/pages/options.html
index 777d514a..b14c454f 100644
--- a/pages/options.html
+++ b/pages/options.html
@@ -3,6 +3,7 @@
<title>Vimium Options</title>
<link rel="stylesheet" type="text/css" href="options.css">
<script src="content_script_loader.js"></script>
+ <script type="text/javascript" src="../lib/settings.js"></script>
<script type="text/javascript" src="options.js"></script>
</head>
@@ -68,6 +69,9 @@ b: http://b.com/?q=%s description
</tr>
<tbody id='advancedOptions'>
<tr>
+ <td colspan="2"><header>Advanced Options</header></td>
+ </tr>
+ <tr>
<td class="caption">Scroll step size</td>
<td>
<div class="help">
@@ -197,7 +201,7 @@ b: http://b.com/?q=%s description
<div class="help">
<div class="example">
The page to open with the "create new tab" command.
- Set this to "<tt>pages/blank.html</tt>" for a blank page.<br />
+ Set this to "<tt>pages/blank.html</tt>" for a blank page (except incognito mode).<br />
</div>
</div>
<input id="newTabUrl" type="text" />
@@ -230,6 +234,36 @@ b: http://b.com/?q=%s description
<div class="nonEmptyTextOption">
</td>
</tr>
+
+ <!-- Vimium Labs -->
+ <!--
+ Disabled. But we leave this code here as a template for the next time we need to introduce "Vimium Labs".
+ <tr>
+ <td colspan="2"><header>Vimium Labs</header></td>
+ </tr>
+ <tr>
+ <td class="caption"></td>
+ <td>
+ <div class="help">
+ <div class="example">
+ </div>
+ </div>
+ These features are experimental and may be changed or removed in future releases.
+ </td>
+ </tr>
+ <tr>
+ <td class="caption">Search weighting</td>
+ <td>
+ <div class="help">
+ <div class="example">
+ How prominent should suggestions be in the vomnibar?
+ <tt>0</tt> disables suggestions altogether.
+ </div>
+ </div>
+ <input id="omniSearchWeight" type="number" min="0.0" max="1.0" step="0.05" />(0 to 1)
+ </td>
+ </tr>
+ -->
</tbody>
</table>
</div>
diff --git a/pages/popup.html b/pages/popup.html
index c7e2fd6f..fdf116e5 100644
--- a/pages/popup.html
+++ b/pages/popup.html
@@ -48,6 +48,8 @@
}
</style>
+ <script src="../lib/utils.js"></script>
+ <script src="../lib/settings.js"></script>
<script src="options.js"></script>
</head>
<body>
diff --git a/pages/vomnibar.coffee b/pages/vomnibar.coffee
index 18a72a37..d5659fdc 100644
--- a/pages/vomnibar.coffee
+++ b/pages/vomnibar.coffee
@@ -9,79 +9,97 @@ Vomnibar =
completers: {}
getCompleter: (name) ->
- if (!(name of @completers))
- @completers[name] = new BackgroundCompleter(name)
- @completers[name]
+ @completers[name] ?= new BackgroundCompleter name
- #
- # Activate the Vomnibox.
- #
activate: (userOptions) ->
options =
completer: "omni"
query: ""
newTab: false
selectFirst: false
+ keyword: null
extend options, userOptions
+ extend options, refreshInterval: if options.completer == "omni" then 150 else 0
- options.refreshInterval = switch options.completer
- when "omni" then 100
- else 0
-
- completer = @getCompleter(options.completer)
+ completer = @getCompleter options.completer
@vomnibarUI ?= new VomnibarUI()
- completer.refresh()
- @vomnibarUI.setInitialSelectionValue(if options.selectFirst then 0 else -1)
- @vomnibarUI.setCompleter(completer)
- @vomnibarUI.setRefreshInterval(options.refreshInterval)
- @vomnibarUI.setForceNewTab(options.newTab)
- @vomnibarUI.setQuery(options.query)
- @vomnibarUI.update()
+ completer.refresh @vomnibarUI
+ @vomnibarUI.setInitialSelectionValue if options.selectFirst then 0 else -1
+ @vomnibarUI.setCompleter completer
+ @vomnibarUI.setRefreshInterval options.refreshInterval
+ @vomnibarUI.setForceNewTab options.newTab
+ @vomnibarUI.setQuery options.query
+ @vomnibarUI.setKeyword options.keyword
+ @vomnibarUI.update true
+
+ hide: -> @vomnibarUI?.hide()
+ onHidden: -> @vomnibarUI?.onHidden()
class VomnibarUI
constructor: ->
@refreshInterval = 0
+ @postHideCallback = null
@initDom()
setQuery: (query) -> @input.value = query
-
- setInitialSelectionValue: (initialSelectionValue) ->
- @initialSelectionValue = initialSelectionValue
-
- setCompleter: (completer) ->
- @completer = completer
- @reset()
- @update(true)
-
- setRefreshInterval: (refreshInterval) -> @refreshInterval = refreshInterval
-
- setForceNewTab: (forceNewTab) -> @forceNewTab = forceNewTab
-
- hide: ->
+ setKeyword: (keyword) -> @customSearchMode = keyword
+ setInitialSelectionValue: (@initialSelectionValue) ->
+ setRefreshInterval: (@refreshInterval) ->
+ setForceNewTab: (@forceNewTab) ->
+ setCompleter: (@completer) -> @reset()
+ setKeywords: (@keywords) ->
+
+ # The sequence of events when the vomnibar is hidden is as follows:
+ # 1. Post a "hide" message to the host page.
+ # 2. The host page hides the vomnibar.
+ # 3. When that page receives the focus, and it posts back a "hidden" message.
+ # 3. Only once the "hidden" message is received here is any required action invoked (in onHidden).
+ # This ensures that the vomnibar is actually hidden before any new tab is created, and avoids flicker after
+ # opening a link in a new tab then returning to the original tab (see #1485).
+ hide: (@postHideCallback = null) ->
UIComponentServer.postMessage "hide"
@reset()
+ @completer?.reset()
+
+ onHidden: ->
+ @postHideCallback?()
+ @postHideCallback = null
reset: ->
+ @clearUpdateTimer()
@completionList.style.display = ""
@input.value = ""
- @updateTimer = null
@completions = []
+ @previousInputValue = null
+ @customSearchMode = null
@selection = @initialSelectionValue
+ @keywords = []
+ @seenTabToOpenCompletionList = false
updateSelection: ->
- # We retain global state here (previousAutoSelect) to tell if a search item (for which autoSelect is set)
- # has just appeared or disappeared. If that happens, we set @selection to 0 or -1.
- if @completions[0]
- @selection = 0 if @completions[0].autoSelect and not @previousAutoSelect
- @selection = -1 if @previousAutoSelect and not @completions[0].autoSelect
- @previousAutoSelect = @completions[0].autoSelect
+ # For custom search engines, we suppress the leading term (e.g. the "w" of "w query terms") within the
+ # vomnibar input.
+ if @lastReponse.isCustomSearch and not @customSearchMode?
+ queryTerms = @input.value.trim().split /\s+/
+ @customSearchMode = queryTerms[0]
+ @input.value = queryTerms[1..].join " "
+
+ # For suggestions for custom search engines, we copy the suggested text into the input when the item is
+ # selected, and revert when it is not. This allows the user to select a suggestion and then continue
+ # typing.
+ if 0 <= @selection and @completions[@selection].insertText?
+ @previousInputValue ?= @input.value
+ @input.value = @completions[@selection].insertText
+ else if @previousInputValue?
+ @input.value = @previousInputValue
+ @previousInputValue = null
+
+ # Highlight the selected entry, and only the selected entry.
for i in [0...@completionList.children.length]
@completionList.children[i].className = (if i == @selection then "vomnibarSelected" else "")
- #
- # Returns the user's action ("up", "down", "enter", "dismiss" or null) based on their keypress.
- # We support the arrow keys and other shortcuts for moving, so this method hides that complexity.
- #
+ # Returns the user's action ("up", "down", "tab", etc, or null) based on their keypress. We support the
+ # arrow keys and various other shortcuts, and this function hides the event-decoding complexity.
actionFromKeyEvent: (event) ->
key = KeyboardUtils.getKeyChar(event)
if (KeyboardUtils.isEscape(event))
@@ -90,83 +108,139 @@ class VomnibarUI
(event.shiftKey && event.keyCode == keyCodes.tab) ||
(event.ctrlKey && (key == "k" || key == "p")))
return "up"
+ else if (event.keyCode == keyCodes.tab && !event.shiftKey)
+ return "tab"
else if (key == "down" ||
- (event.keyCode == keyCodes.tab && !event.shiftKey) ||
(event.ctrlKey && (key == "j" || key == "n")))
return "down"
else if (event.keyCode == keyCodes.enter)
return "enter"
+ else if event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey
+ return "delete"
+
+ null
onKeydown: (event) =>
- action = @actionFromKeyEvent(event)
+ @lastAction = action = @actionFromKeyEvent event
return true unless action # pass through
openInNewTab = @forceNewTab ||
(event.shiftKey || event.ctrlKey || KeyboardUtils.isPrimaryModifierKey(event))
if (action == "dismiss")
@hide()
+ else if action in [ "tab", "down" ]
+ if action == "tab" and
+ @completer.name == "omni" and
+ not @seenTabToOpenCompletionList and
+ @input.value.trim().length == 0
+ @seenTabToOpenCompletionList = true
+ @update true
+ else
+ @selection += 1
+ @selection = @initialSelectionValue if @selection == @completions.length
+ @updateSelection()
else if (action == "up")
@selection -= 1
@selection = @completions.length - 1 if @selection < @initialSelectionValue
@updateSelection()
- else if (action == "down")
- @selection += 1
- @selection = @initialSelectionValue if @selection == @completions.length
- @updateSelection()
else if (action == "enter")
- # If they type something and hit enter without selecting a completion from our list of suggestions,
- # try to open their query as a URL directly. If it doesn't look like a URL, we will search using
- # google.
- if (@selection == -1)
+ isCustomSearchPrimarySuggestion = @completions[@selection]?.isPrimarySuggestion and @lastReponse.engine?.searchUrl?
+ if @selection == -1 or isCustomSearchPrimarySuggestion
query = @input.value.trim()
- # <Enter> on an empty vomnibar is a no-op.
+ # <Enter> on an empty query is a no-op.
return unless 0 < query.length
- @hide()
- chrome.runtime.sendMessage({
- handler: if openInNewTab then "openUrlInNewTab" else "openUrlInCurrentTab"
- url: query })
+ # First case (@selection == -1).
+ # If the user types something and hits enter without selecting a completion from the list, then:
+ # - If a search URL has been provided, then use it. This is custom search engine request.
+ # - Otherwise, send the query to the background page, which will open it as a URL or create a
+ # default search, as appropriate.
+ #
+ # Second case (isCustomSearchPrimarySuggestion).
+ # Alternatively, the selected completion could be the primary selection for a custom search engine.
+ # Because the the suggestions are updated asynchronously in omni mode, the user may have typed more
+ # text than that which is included in the URL associated with the primary suggestion. Therefore, to
+ # avoid a race condition, we construct the query from the actual contents of the input (query).
+ query = Utils.createSearchUrl query, @lastReponse.engine.searchUrl if isCustomSearchPrimarySuggestion
+ @hide ->
+ chrome.runtime.sendMessage
+ handler: if openInNewTab then "openUrlInNewTab" else "openUrlInCurrentTab"
+ url: query
+ else
+ completion = @completions[@selection]
+ @hide -> completion.performAction openInNewTab
+ else if action == "delete"
+ inputIsEmpty = @input.value.length == 0
+ if inputIsEmpty and @customSearchMode?
+ # Normally, with custom search engines, the keyword (e,g, the "w" of "w query terms") is suppressed.
+ # If the input is empty, then reinstate the keyword (the "w").
+ @input.value = @customSearchMode
+ @customSearchMode = null
+ @update true
+ else if inputIsEmpty and @seenTabToOpenCompletionList
+ @seenTabToOpenCompletionList = false
+ @update true
else
- @update true, =>
- # Shift+Enter will open the result in a new tab instead of the current tab.
- @completions[@selection].performAction(openInNewTab)
- @hide()
+ return true # Do not suppress event.
# It seems like we have to manually suppress the event here and still return true.
event.stopImmediatePropagation()
event.preventDefault()
true
- updateCompletions: (callback) ->
- query = @input.value.trim()
-
- @completer.filter query, (completions) =>
- @completions = completions
- @populateUiWithCompletions(completions)
- callback() if callback
-
- populateUiWithCompletions: (completions) ->
- # update completion list with the new data
- @completionList.innerHTML = completions.map((completion) -> "<li>#{completion.html}</li>").join("")
- @completionList.style.display = if completions.length > 0 then "block" else ""
- @selection = Math.min(Math.max(@initialSelectionValue, @selection), @completions.length - 1)
- @updateSelection()
-
- update: (updateSynchronously, callback) =>
- if (updateSynchronously)
- # cancel scheduled update
- if (@updateTimer != null)
- window.clearTimeout(@updateTimer)
- @updateCompletions(callback)
- else if (@updateTimer != null)
- # an update is already scheduled, don't do anything
- return
- else
- # always update asynchronously for better user experience and to take some load off the CPU
- # (not every keystroke will cause a dedicated update)
- @updateTimer = setTimeout(=>
- @updateCompletions(callback)
+ # Return the background-page query corresponding to the current input state. In other words, reinstate any
+ # search engine keyword which is currently being suppressed, and strip any prompted text.
+ getInputValueAsQuery: ->
+ (if @customSearchMode? then @customSearchMode + " " else "") + @input.value
+
+ updateCompletions: (callback = null) ->
+ @completer.filter
+ query: @getInputValueAsQuery()
+ seenTabToOpenCompletionList: @seenTabToOpenCompletionList
+ callback: (@lastReponse) =>
+ { results } = @lastReponse
+ @completions = results
+ @selection = if @completions[0]?.autoSelect then 0 else @initialSelectionValue
+ # Update completion list with the new suggestions.
+ @completionList.innerHTML = @completions.map((completion) -> "<li>#{completion.html}</li>").join("")
+ @completionList.style.display = if @completions.length > 0 then "block" else ""
+ @selection = Math.min @completions.length - 1, Math.max @initialSelectionValue, @selection
+ @updateSelection()
+ callback?()
+
+ onInput: =>
+ @seenTabToOpenCompletionList = false
+ @completer.cancel()
+ if 0 <= @selection and @completions[@selection].customSearchMode and not @customSearchMode
+ @customSearchMode = @completions[@selection].customSearchMode
+ updateSynchronously = true
+ # If the user types, then don't reset any previous text, and reset the selection.
+ if @previousInputValue?
+ @previousInputValue = null
+ @selection = -1
+ @update updateSynchronously
+
+ clearUpdateTimer: ->
+ if @updateTimer?
+ window.clearTimeout @updateTimer
+ @updateTimer = null
+
+ shouldActivateCustomSearchMode: ->
+ queryTerms = @input.value.ltrim().split /\s+/
+ 1 < queryTerms.length and queryTerms[0] in @keywords and not @customSearchMode
+
+ update: (updateSynchronously = false, callback = null) =>
+ # If the query text becomes a custom search (the user enters a search keyword), then we need to force a
+ # synchronous update (so that the state is updated immediately).
+ updateSynchronously ||= @shouldActivateCustomSearchMode()
+ if updateSynchronously
+ @clearUpdateTimer()
+ @updateCompletions callback
+ else if not @updateTimer?
+ # Update asynchronously for a better user experience, and to take some load off the CPU (not every
+ # keystroke will cause a dedicated update).
+ @updateTimer = Utils.setTimeout @refreshInterval, =>
@updateTimer = null
- @refreshInterval)
+ @updateCompletions callback
@input.focus()
@@ -174,58 +248,93 @@ class VomnibarUI
@box = document.getElementById("vomnibar")
@input = @box.querySelector("input")
- @input.addEventListener "input", @update
+ @input.addEventListener "input", @onInput
@input.addEventListener "keydown", @onKeydown
@completionList = @box.querySelector("ul")
@completionList.style.display = ""
window.addEventListener "focus", => @input.focus()
+ # A click in the vomnibar itself refocuses the input.
+ @box.addEventListener "click", (event) =>
+ @input.focus()
+ event.stopImmediatePropagation()
+ # A click anywhere else hides the vomnibar.
+ document.body.addEventListener "click", => @hide()
#
-# Sends filter and refresh requests to a Vomnibox completer on the background page.
+# Sends requests to a Vomnibox completer on the background page.
#
class BackgroundCompleter
- # - name: The background page completer that you want to interface with. Either "omni", "tabs", or
- # "bookmarks". */
+ # The "name" is the background-page completer to connect to: "omni", "tabs", or "bookmarks".
constructor: (@name) ->
- @filterPort = chrome.runtime.connect({ name: "filterCompleter" })
-
- refresh: -> chrome.runtime.sendMessage({ handler: "refreshCompleter", name: @name })
-
- filter: (query, callback) ->
- id = Utils.createUniqueId()
- @filterPort.onMessage.addListener (msg) =>
- @filterPort.onMessage.removeListener(arguments.callee)
- # The result objects coming from the background page will be of the form:
- # { html: "", type: "", url: "" }
- # type will be one of [tab, bookmark, history, domain].
- results = msg.results.map (result) ->
- functionToCall = if (result.type == "tab")
- BackgroundCompleter.completionActions.switchToTab.curry(result.tabId)
- else
- BackgroundCompleter.completionActions.navigateToUrl.curry(result.url)
- result.performAction = functionToCall
- result
- callback(results)
-
- @filterPort.postMessage({ id: id, name: @name, query: query })
-
-extend BackgroundCompleter,
- #
- # These are the actions we can perform when the user selects a result in the Vomnibox.
- #
+ @port = chrome.runtime.connect name: "completions"
+ @messageId = null
+ @reset()
+
+ @port.onMessage.addListener (msg) =>
+ switch msg.handler
+ when "keywords"
+ @keywords = msg.keywords
+ @lastUI.setKeywords @keywords
+ when "completions"
+ if msg.id == @messageId
+ # The result objects coming from the background page will be of the form:
+ # { html: "", type: "", url: "", ... }
+ # Type will be one of [tab, bookmark, history, domain, search], or a custom search engine description.
+ for result in msg.results
+ extend result,
+ performAction:
+ if result.type == "tab"
+ @completionActions.switchToTab result.tabId
+ else
+ @completionActions.navigateToUrl result.url
+
+ # Handle the message, but only if it hasn't arrived too late.
+ @mostRecentCallback msg
+
+ filter: (request) ->
+ { query, callback } = request
+ @mostRecentCallback = callback
+
+ @port.postMessage extend request,
+ handler: "filter"
+ name: @name
+ id: @messageId = Utils.createUniqueId()
+ queryTerms: query.trim().split(/\s+/).filter (s) -> 0 < s.length
+ # We don't send these keys.
+ callback: null
+
+ reset: ->
+ @keywords = []
+
+ refresh: (@lastUI) ->
+ @reset()
+ @port.postMessage name: @name, handler: "refresh"
+
+ cancel: ->
+ # Inform the background completer that it may (should it choose to do so) abandon any pending query
+ # (because the user is typing, and there will be another query along soon).
+ @port.postMessage name: @name, handler: "cancel"
+
+ # These are the actions we can perform when the user selects a result.
completionActions:
- navigateToUrl: (url, openInNewTab) ->
- # If the URL is a bookmarklet prefixed with javascript:, we shouldn't open that in a new tab.
- openInNewTab = false if url.startsWith("javascript:")
- chrome.runtime.sendMessage(
+ navigateToUrl: (url) -> (openInNewTab) ->
+ # If the URL is a bookmarklet (so, prefixed with "javascript:"), then we always open it in the current
+ # tab.
+ openInNewTab &&= not Utils.hasJavascriptPrefix url
+ chrome.runtime.sendMessage
handler: if openInNewTab then "openUrlInNewTab" else "openUrlInCurrentTab"
- url: url,
- selected: openInNewTab)
+ url: url
+ selected: openInNewTab
- switchToTab: (tabId) -> chrome.runtime.sendMessage({ handler: "selectSpecificTab", id: tabId })
+ switchToTab: (tabId) -> ->
+ chrome.runtime.sendMessage handler: "selectSpecificTab", id: tabId
-UIComponentServer.registerHandler (event) -> Vomnibar.activate event.data
+UIComponentServer.registerHandler (event) ->
+ switch event.data
+ when "hide" then Vomnibar.hide()
+ when "hidden" then Vomnibar.onHidden()
+ else Vomnibar.activate event.data
root = exports ? window
root.Vomnibar = Vomnibar
diff --git a/pages/vomnibar.css b/pages/vomnibar.css
index 2042a6c4..1b19daad 100644
--- a/pages/vomnibar.css
+++ b/pages/vomnibar.css
@@ -126,7 +126,6 @@
#vomnibar li em { font-style: italic; }
#vomnibar li em .vomnibarMatch, #vomnibar li .vomnibarTitle .vomnibarMatch {
color: #333;
- text-decoration: underline;
}
#vomnibar li.vomnibarSelected {
@@ -134,3 +133,21 @@
font-weight: normal;
}
+#vomnibarInput::selection {
+ /* This is the light grey color of the vomnibar border. */
+ /* background-color: #F1F1F1; */
+
+ /* This is the light blue color of the vomnibar selected item. */
+ /* background-color: #BBCEE9; */
+
+ /* This is a considerably lighter blue than Vimium blue, which seems softer
+ * on the eye for this purpose. */
+ background-color: #E6EEFB;
+}
+
+.vomnibarInsertText {
+}
+
+.vomnibarNoInsertText {
+ visibility: hidden;
+}
diff --git a/pages/vomnibar.html b/pages/vomnibar.html
index 2ca463d0..87acc081 100644
--- a/pages/vomnibar.html
+++ b/pages/vomnibar.html
@@ -14,7 +14,7 @@
<body>
<div id="vomnibar" class="vimiumReset">
<div class="vimiumReset vomnibarSearchArea">
- <input type="text" class="vimiumReset">
+ <input id="vomnibarInput" type="text" class="vimiumReset">
</div>
<ul class="vimiumReset"></ul>
</div>