1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# A completion engine provides search suggestions for a search engine. A search engine is identified by a
# "searchUrl", e.g. Settings.get("searchUrl"), or a custom search engine URL.
#
# Each completion engine defines three functions:
#
# 1. "match" - This takes a searchUrl and returns a boolean indicating whether this completion engine can
# perform completion for the given search engine.
#
# 2. "getUrl" - This takes a list of query terms (queryTerms) and generates a completion URL, that is, a URL
# which will provide completions for this completion engine.
#
# 3. "parse" - This takes a successful XMLHttpRequest object (the request has completed successfully), and
# returns a list of suggestions (a list of strings). This method is always executed within the context
# of a try/catch block, so errors do not propagate.
#
# Each new completion engine must be added to the list "CompletionEngines" at the bottom of this file.
#
# The lookup logic which uses these completion engines is in "./completion_search.coffee".
#
# A base class for common regexp-based matching engines.
class RegexpEngine
constructor: (args...) -> @regexps = args.map (regexp) -> new RegExp regexp
match: (searchUrl) -> Utils.matchesAnyRegexp @regexps, searchUrl
# Several Google completion engines package XML responses in this way.
class GoogleXMLRegexpEngine extends RegexpEngine
parse: (xhr) ->
for suggestion in xhr.responseXML.getElementsByTagName "suggestion"
continue unless suggestion = suggestion.getAttribute "data"
suggestion
class Google extends GoogleXMLRegexpEngine
# Example search URL: http://www.google.com/search?q=%s
constructor: (regexps = null) ->
super regexps ? "^https?://[a-z]+\.google\.(com|ie|co\.uk|ca|com\.au)/"
getUrl: (queryTerms) ->
Utils.createSearchUrl queryTerms,
"http://suggestqueries.google.com/complete/search?ss_protocol=legace&client=toolbar&q=%s"
# A wrapper class for Google completions. This adds prefix terms to the query, and strips those terms from
# the resulting suggestions. For example, for Google Maps, we add "map of" as a prefix, then strip "map of"
# from the resulting suggestions.
class GoogleWithPrefix
constructor: (prefix, args...) ->
@engine = new Google args...
@prefix = "#{prefix.trim()} "
@queryTerms = @prefix.split /\s+/
match: (args...) -> @engine.match args...
getUrl: (queryTerms) -> @engine.getUrl [ @queryTerms..., queryTerms... ]
parse: (xhr) ->
@engine.parse(xhr)
.filter (suggestion) => suggestion.startsWith @prefix
.map (suggestion) => suggestion[@prefix.length..].ltrim()
# For Google Maps, we add the prefix "map of" to the query, and send it to Google's general search engine,
# then strip "map of" from the resulting suggestions.
class GoogleMaps extends GoogleWithPrefix
# Example search URL: https://www.google.com/maps?q=%s
constructor: -> super "map of", "https?://[a-z]+\.google\.(com|ie|co\.uk|ca|com\.au)/maps"
class Youtube extends GoogleXMLRegexpEngine
# Example search URL: http://www.youtube.com/results?search_query=%s
constructor: ->
super "^https?://[a-z]+\.youtube\.com/results"
getUrl: (queryTerms) ->
Utils.createSearchUrl queryTerms,
"http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&xml=t&q=%s"
class Wikipedia extends RegexpEngine
# Example search URL: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s
constructor: ->
super "^https?://[a-z]+\.wikipedia\.org/"
getUrl: (queryTerms) ->
Utils.createSearchUrl queryTerms,
"https://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=%s"
parse: (xhr) ->
JSON.parse(xhr.responseText)[1]
class Bing extends RegexpEngine
# Example search URL: https://www.bing.com/search?q=%s
constructor: -> super "^https?://www\.bing\.com/search"
getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, "http://api.bing.com/osjson.aspx?query=%s"
parse: (xhr) -> JSON.parse(xhr.responseText)[1]
class Amazon extends RegexpEngine
# Example search URL: http://www.amazon.com/s/?field-keywords=%s
constructor: -> super "^https?://www\.amazon\.(com|co.uk|ca|com.au)/s/"
getUrl: (queryTerms) ->
Utils.createSearchUrl queryTerms,
"https://completion.amazon.com/search/complete?method=completion&search-alias=aps&client=amazon-search-ui&mkt=1&q=%s"
parse: (xhr) -> JSON.parse(xhr.responseText)[1]
class DuckDuckGo extends RegexpEngine
# Example search URL: https://duckduckgo.com/?q=%s
constructor: -> super "^https?://([a-z]+\.)?duckduckgo\.com/"
getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, "https://duckduckgo.com/ac/?q=%s"
parse: (xhr) ->
suggestion.phrase for suggestion in JSON.parse xhr.responseText
class Webster extends RegexpEngine
# Example search URL: http://www.merriam-webster.com/dictionary/%s
constructor: -> super "^https?://www.merriam-webster.com/dictionary/"
getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, "http://www.merriam-webster.com/autocomplete?query=%s"
parse: (xhr) -> JSON.parse(xhr.responseText).suggestions
# A dummy search engine which is guaranteed to match any search URL, but never produces completions. This
# allows the rest of the logic to be written knowing that there will always be a completion engine match.
class DummyCompletionEngine
dummy: true
match: -> true
# We return a useless URL which we know will succeed, but which won't generate any network traffic.
getUrl: -> chrome.runtime.getURL "content_scripts/vimium.css"
parse: -> []
# Note: Order matters here.
CompletionEngines = [
Youtube
GoogleMaps
Google
DuckDuckGo
Wikipedia
Bing
Amazon
Webster
DummyCompletionEngine
]
root = exports ? window
root.CompletionEngines = CompletionEngines
|