aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts/completion_engines.coffee
diff options
context:
space:
mode:
authorStephen Blott2015-06-06 06:18:35 +0100
committerStephen Blott2015-06-06 06:46:32 +0100
commitf5db9ea1dc8d23dff732dd3345bf85798d64f1e9 (patch)
treef8aff705dfe0a666f3ba53190a2c35799c689f0c /background_scripts/completion_engines.coffee
parent97e5da6cbdcf1b5cf4f80cf550fc26c1ce194a3e (diff)
downloadvimium-f5db9ea1dc8d23dff732dd3345bf85798d64f1e9.tar.bz2
Re-work completions: initial refactor.
The original completion-engine interface was based on three functions. With some experience, it seems there is a pattern involving explicit regular expressions which is used by all actual engine implementations. This is a refactoring to make those regular expressions explicit (and required), and is a first step towards adding additional fucntionality. This also simplifies the completion cache key (use JSON instead of some weird hash).
Diffstat (limited to 'background_scripts/completion_engines.coffee')
-rw-r--r--background_scripts/completion_engines.coffee210
1 files changed, 114 insertions, 96 deletions
diff --git a/background_scripts/completion_engines.coffee b/background_scripts/completion_engines.coffee
index 18aa00bb..afbb2040 100644
--- a/background_scripts/completion_engines.coffee
+++ b/background_scripts/completion_engines.coffee
@@ -1,142 +1,160 @@
-# 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.
+# A completion engine provides search suggestions for a custom search engine. A custom search engine is
+# identified by a "searchUrl". An "engineUrl" is used for fetching suggestions, whereas a "searchUrl" is used
+# for the actual search itself.
#
-# Each completion engine defines three functions:
+# Each completion engine defines:
#
-# 1. "match" - This takes a searchUrl and returns a boolean indicating whether this completion engine can
-# perform completion for the given search engine.
+# 1. An "engineUrl". This is the URL to use for search completions and is passed as the option "engineUrl"
+# to the "BaseEngine" constructor.
#
-# 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.
+# 2. One or more regular expressions which define the custom search engine URLs for which the completion
+# engine will be used. This is passed as the "regexps" option to the "BaseEngine" constructor.
#
-# 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.
+# 3. A "parse" function. 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.
+#
+# 4. For documentation only, each completion engine *must* and example custom search engine. The example
+# must include an example "keyword" and and example "searchUrl", and may include and example
+# "description".
#
# 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
+# A base class for common regexp-based matching engines. "options" must define:
+# options.engineUrl: the URL to use for the completion engine. This must be a string.
+# options.regexps: one or regular expressions. This may either a single string or a list of strings.
+# options.example: an example object containing at least "keyword" and "searchUrl", and optional "description".
+class BaseEngine
+ constructor: (options) ->
+ extend this, options
+ @regexps = [ @regexps ] if "string" == typeof @regexps
+ @regexps = @regexps.map (regexp) -> new RegExp regexp
+
match: (searchUrl) -> Utils.matchesAnyRegexp @regexps, searchUrl
+ getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, @engineUrl
-# Several Google completion engines package XML responses in this way.
-class GoogleXMLRegexpEngine extends RegexpEngine
+# Several Google completion engines package responses as XML. This parses such XML.
+class GoogleXMLBaseEngine extends BaseEngine
parse: (xhr) ->
for suggestion in xhr.responseXML.getElementsByTagName "suggestion"
continue unless suggestion = suggestion.getAttribute "data"
suggestion
-class Google extends GoogleXMLRegexpEngine
+class Google extends GoogleXMLBaseEngine
constructor: (regexps = null) ->
- super regexps ? "^https?://[a-z]+\\.google\\.(com|ie|co\\.uk|ca|com\\.au)/"
- @exampleSearchUrl = "http://www.google.com/search?q=%s"
- @exampleKeyword = "m"
-
- 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 extends Google
- constructor: (prefix, args...) ->
- super args...
- prefix = prefix.trim()
- @prefix = "#{prefix} "
- @queryTerms = prefix.split /\s+/
- getUrl: (queryTerms) -> super [ @queryTerms..., queryTerms... ]
- parse: (xhr) ->
- super(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
+ super
+ engineUrl: "http://suggestqueries.google.com/complete/search?ss_protocol=legace&client=toolbar&q=%s"
+ regexps: regexps ? "^https?://[a-z]+\\.google\\.(com|ie|co\\.uk|ca|com\\.au)/"
+ example:
+ searchUrl: "http://www.google.com/search?q=%s"
+ keyword: "g"
+
+## # 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 extends Google
+## constructor: (prefix, args...) ->
+## super args...
+## prefix = prefix.trim()
+## @prefix = "#{prefix} "
+## @queryTerms = prefix.split /\s+/
+## getUrl: (queryTerms) -> super [ @queryTerms..., queryTerms... ]
+## parse: (xhr) ->
+## super(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
+## constructor: ->
+## super "map of", "^https?://[a-z]+\\.google\\.(com|ie|co\\.uk|ca|com\\.au)/maps"
+## @exampleSearchUrl = "https://www.google.com/maps?q=%s"
+## @exampleKeyword = "m"
+## @exampleDescription = "Google maps"
+
+class Youtube extends GoogleXMLBaseEngine
constructor: ->
- super "map of", "^https?://[a-z]+\\.google\\.(com|ie|co\\.uk|ca|com\\.au)/maps"
- @exampleSearchUrl = "https://www.google.com/maps?q=%s"
- @exampleKeyword = "m"
- @exampleDescription = "Google maps"
-
-class Youtube extends GoogleXMLRegexpEngine
+ super
+ engineUrl: "http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&xml=t&q=%s"
+ regexps: "^https?://[a-z]+\\.youtube\\.com/results"
+ example:
+ searchUrl: "http://www.youtube.com/results?search_query=%s"
+ keyword: "y"
+
+class Wikipedia extends BaseEngine
constructor: ->
- super "^https?://[a-z]+\\.youtube\\.com/results"
- @exampleSearchUrl = "http://www.youtube.com/results?search_query=%s"
- @exampleKeyword = "y"
+ super
+ engineUrl: "https://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=%s"
+ regexps: "^https?://[a-z]+\\.wikipedia\\.org/"
+ example:
+ searchUrl: "http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s"
+ keyword: "w"
- getUrl: (queryTerms) ->
- Utils.createSearchUrl queryTerms,
- "http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&xml=t&q=%s"
+ parse: (xhr) -> JSON.parse(xhr.responseText)[1]
-class Wikipedia extends RegexpEngine
+class Bing extends BaseEngine
constructor: ->
- super "^https?://[a-z]+\\.wikipedia\\.org/"
- @exampleSearchUrl = "http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s"
- @exampleKeyword = "y"
-
- getUrl: (queryTerms) ->
- Utils.createSearchUrl queryTerms,
- "https://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=%s"
+ super
+ engineUrl: "http://api.bing.com/osjson.aspx?query=%s"
+ regexps: "^https?://www\\.bing\\.com/search"
+ example:
+ searchUrl: "https://www.bing.com/search?q=%s"
+ keyword: "b"
- parse: (xhr) ->
- JSON.parse(xhr.responseText)[1]
-
-class Bing extends RegexpEngine
- constructor: ->
- super "^https?://www\\.bing\\.com/search"
- @exampleSearchUrl = "https://www.bing.com/search?q=%s"
- @exampleKeyword = "b"
- getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, "http://api.bing.com/osjson.aspx?query=%s"
parse: (xhr) -> JSON.parse(xhr.responseText)[1]
-class Amazon extends RegexpEngine
+class Amazon extends BaseEngine
constructor: ->
- super "^https?://www\\.amazon\\.(com|co\\.uk|ca|com\\.au)/s/"
- @exampleSearchUrl = "http://www.amazon.com/s/?field-keywords=%s"
- @exampleKeyword = "a"
- getUrl: (queryTerms) ->
- Utils.createSearchUrl queryTerms,
- "https://completion.amazon.com/search/complete?method=completion&search-alias=aps&client=amazon-search-ui&mkt=1&q=%s"
+ super
+ engineUrl: "https://completion.amazon.com/search/complete?method=completion&search-alias=aps&client=amazon-search-ui&mkt=1&q=%s"
+ regexps: "^https?://www\\.amazon\\.(com|co\\.uk|ca|com\\.au)/s/"
+ example:
+ searchUrl: "http://www.amazon.com/s/?field-keywords=%s"
+ keyword: "a"
+
parse: (xhr) -> JSON.parse(xhr.responseText)[1]
-class DuckDuckGo extends RegexpEngine
+class DuckDuckGo extends BaseEngine
constructor: ->
- super "^https?://([a-z]+\\.)?duckduckgo\\.com/"
- @exampleSearchUrl = "https://duckduckgo.com/?q=%s"
- @exampleKeyword = "d"
- getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, "https://duckduckgo.com/ac/?q=%s"
+ super
+ engineUrl: "https://duckduckgo.com/ac/?q=%s"
+ regexps: "^https?://([a-z]+\\.)?duckduckgo\\.com/"
+ example:
+ searchUrl: "https://duckduckgo.com/?q=%s"
+ keyword: "d"
+
parse: (xhr) ->
suggestion.phrase for suggestion in JSON.parse xhr.responseText
-class Webster extends RegexpEngine
+class Webster extends BaseEngine
constructor: ->
- super "^https?://www.merriam-webster.com/dictionary/"
- @exampleSearchUrl = "http://www.merriam-webster.com/dictionary/%s"
- @exampleKeyword = "dw"
- @exampleDescription = "Dictionary"
- getUrl: (queryTerms) -> Utils.createSearchUrl queryTerms, "http://www.merriam-webster.com/autocomplete?query=%s"
+ super
+ engineUrl: "http://www.merriam-webster.com/autocomplete?query=%s"
+ regexps: "^https?://www.merriam-webster.com/dictionary/"
+ example:
+ searchUrl: "http://www.merriam-webster.com/dictionary/%s"
+ keyword: "dw"
+ description: "Dictionary"
+
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: -> []
+class DummyCompletionEngine extends BaseEngine
+ constructor: ->
+ super
+ regexps: "."
+ dummy: true
# Note: Order matters here.
CompletionEngines = [
Youtube
- GoogleMaps
+ # GoogleMaps
Google
DuckDuckGo
Wikipedia