diff options
| author | Jez Ng | 2012-09-06 10:40:30 -0400 |
|---|---|---|
| committer | Jez Ng | 2012-09-06 10:40:30 -0400 |
| commit | e932e28c6ee3376fc3e2c87f07637f123817e0c9 (patch) | |
| tree | 2efa30068e871f0b7d4d6b0a8decaef1340d711c | |
| parent | eb0955875ba6ba77a90b1e411c42be0df6de950d (diff) | |
| parent | 95d538d0458ae6206a791edead92cd0e4b1e3990 (diff) | |
| download | vimium-e932e28c6ee3376fc3e2c87f07637f123817e0c9.tar.bz2 | |
Merge remote-tracking branch 'liesen/idiomatic-coffee-utils' into next-release
| -rw-r--r-- | lib/utils.coffee | 138 | ||||
| -rw-r--r-- | tests/utils_test.coffee | 22 |
2 files changed, 82 insertions, 78 deletions
diff --git a/lib/utils.coffee b/lib/utils.coffee index 598b631a..39279760 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -1,6 +1,7 @@ Utils = getCurrentVersion: -> - # Chromium #15242 will make this XHR request to access the manifest unnecessary. + # Chromium #15242 will make this XHR request to access the manifest + # unnecessary manifestRequest = new XMLHttpRequest() manifestRequest.open("GET", chrome.extension.getURL("manifest.json"), false) manifestRequest.send(null) @@ -25,10 +26,9 @@ Utils = escapeHtml: (string) -> string.replace(/</g, "<").replace(/>/g, ">") # Generates a unique ID - createUniqueId: (-> + createUniqueId: do -> id = 0 - return -> id += 1 - )() + -> id += 1 hasChromePrefix: (url) -> chromePrefixes = [ 'about', 'view-source' ] @@ -38,84 +38,79 @@ Utils = # Completes a partial URL (without scheme) createFullUrl: (partialUrl) -> - if (!/^[a-z]{3,}:\/\//.test(partialUrl)) + unless /^[a-z]{3,}:\/\//.test partialUrl "http://" + partialUrl else partialUrl - # Tries to detect, whether :str is a valid URL. + # Tries to detect if :str is a valid URL. isUrl: (str) -> - # more or less RFC compliant URL host part parsing. This should be sufficient - # for our needs + # Starts with a scheme: URL + return true if /^[a-z]{3,}:\/\//.test str + + # Must not contain spaces + return false if ' ' in str + + # More or less RFC compliant URL host part parsing. This should be sufficient for our needs urlRegex = new RegExp( - '^(?:([^:]+)(?::([^:]+))?@)?' + # user:password (optional) => \1, \2 - '([^:]+|\\[[^\\]]+\\])' + # host name (IPv6 addresses in square brackets allowed) => \3 - '(?::(\\d+))?$' # port number (optional) => \4 + '^(?:([^:]+)(?::([^:]+))?@)?' + # user:password (optional) => \1, \2 + '([^:]+|\\[[^\\]]+\\])' + # host name (IPv6 addresses in square brackets allowed) => \3 + '(?::(\\d+))?$' # port number (optional) => \4 ) - # these are all official ASCII TLDs that are longer than 3 characters - # (including the inofficial .onion TLD used by TOR) - longTlds = [ 'arpa', 'asia', 'coop', 'info', 'jobs', 'local', 'mobi', 'museum', 'name', 'onion' ] - - # are there more? - specialHostNames = [ 'localhost' ] - - # it starts with a scheme, so it's definitely an URL - if (/^[a-z]{3,}:\/\//.test(str)) - return true - - # spaces => definitely not a valid URL - if (str.indexOf(' ') >= 0) - return false - - # assuming that this is an URL, try to parse it into its meaningful parts. If matching fails, we're - # pretty sure that we don't have some kind of URL here. - match = urlRegex.exec(str.split('/')[0]) - if (!match) - return false - hostname = match[3] - - # allow known special host names - if (specialHostNames.indexOf(hostname) >= 0) - return true - - # allow IPv6 addresses (need to be wrapped in brackets, as required by RFC). It is sufficient to check - # for a colon here, as the regex wouldn't match colons in the host name unless it's an v6 address - if (hostname.indexOf(':') >= 0) - return true - - # at this point we have to make a decision. As a heuristic, we check if the input has dots in it. If - # yes, and if the last part could be a TLD, treat it as an URL. - dottedParts = hostname.split('.') - lastPart = dottedParts[dottedParts.length-1] - if (dottedParts.length > 1 && ((lastPart.length >= 2 && lastPart.length <= 3) || - longTlds.indexOf(lastPart) >= 0)) - return true - - # also allow IPv4 addresses - if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname)) - return true - - # fallback: no URL + # Official ASCII TLDs that are longer than 3 characters + inofficial .onion TLD used by TOR + longTlds = ['arpa', 'asia', 'coop', 'info', 'jobs', 'local', 'mobi', 'museum', 'name', 'onion'] + + specialHostNames = ['localhost'] + + # Try to parse the URL into its meaningful parts. If matching fails we're pretty sure that we don't have + # some kind of URL here. + match = urlRegex.exec (str.split '/')[0] + return false unless match + hostName = match[3] + + # Allow known special host names + return true if hostName in specialHostNames + + # Allow IPv6 addresses (need to be wrapped in brackets as required by RFC). It is sufficient to check for + # a colon, as the regex wouldn't match colons in the host name unless it's an v6 address + return true if ':' in hostName + + # At this point we have to make a decision. As a heuristic, we check if the input has dots in it. If yes, + # and if the last part could be a TLD, treat it as an URL + dottedParts = hostName.split '.' + + if dottedParts.length > 1 + lastPart = dottedParts.pop() + return true if 2 <= lastPart.length <= 3 or lastPart in longTlds + + # Allow IPv4 addresses + return true if /^(\d{1,3}\.){3}\d{1,3}$/.test hostName + + # Fallback: no URL return false # Creates a search URL from the given :query. createSearchUrl: (query) -> - # we need to escape explictely to encode characters like "+" correctly + # Escape explicitely to encode characters like "+" correctly "http://www.google.com/search?q=" + encodeURIComponent(query) - # Converts :string into a google search if it's not already a URL. - # We don't bother with escaping characters as Chrome will do that for us. + # Converts :string into a Google search if it's not already a URL. We don't bother with escaping characters + # as Chrome will do that for us. convertToUrl: (string) -> string = string.trim() - # special-case about:[url] and view-source:[url] - if Utils.hasChromePrefix string then string + + # Special-case about:[url] and view-source:[url] + if Utils.hasChromePrefix string + string + else if Utils.isUrl string + Utils.createFullUrl string else - if (Utils.isUrl(string)) then Utils.createFullUrl(string) else Utils.createSearchUrl(string) + Utils.createSearchUrl string -# This creates a new function out of an existing function, where the new function takes fewer arguments. -# This allows us to pass around functions instead of functions + a partial list of arguments. -Function.prototype.curry = -> +# This creates a new function out of an existing function, where the new function takes fewer arguments. This +# allows us to pass around functions instead of functions + a partial list of arguments. +Function::curry = -> fixedArguments = Array.copy(arguments) fn = this -> fn.apply(this, fixedArguments.concat(Array.copy(arguments))) @@ -124,19 +119,7 @@ Array.copy = (array) -> Array.prototype.slice.call(array, 0) String::startsWith = (str) -> @indexOf(str) == 0 -# A very simple method for defining a new class (constructor and methods) using a single hash. -# No support for inheritance is included because we really shouldn't need it. -# TODO(philc): remove this. -Class = - extend: (properties) -> - newClass = -> - this.init.apply(this, arguments) if (this.init) - null - newClass.prototype = properties - newClass.constructor = newClass - newClass - -globalRoot = if window? then window else global +globalRoot = window ? global globalRoot.extend = (hash1, hash2) -> for key of hash2 hash1[key] = hash2[key] @@ -144,4 +127,3 @@ globalRoot.extend = (hash1, hash2) -> root = exports ? window root.Utils = Utils -root.Class = Class diff --git a/tests/utils_test.coffee b/tests/utils_test.coffee index c5e5d002..f3810114 100644 --- a/tests/utils_test.coffee +++ b/tests/utils_test.coffee @@ -1,6 +1,28 @@ require "./test_helper.js" extend(global, require "../lib/utils.js") +context "isUrl", + should "accept valid URLs", -> + assert.isTrue Utils.isUrl "www.google.com" + assert.isTrue Utils.isUrl "www.bbc.co.uk" + assert.isTrue Utils.isUrl "yahoo.com" + assert.isTrue Utils.isUrl "nunames.nu" + assert.isTrue Utils.isUrl "user:pass@ftp.xyz.com/test" + + assert.isTrue Utils.isUrl "localhost/index.html" + assert.isTrue Utils.isUrl "127.0.0.1:8192/test.php" + + # IPv6 + assert.isTrue Utils.isUrl "[::]:9000" + + # Long TLDs + assert.isTrue Utils.isUrl "illinois.state.museum" + assert.isTrue Utils.isUrl "eqt5g4fuenphqinx.onion" + + should "reject invalid URLs", -> + assert.isFalse Utils.isUrl "a.x" + assert.isFalse Utils.isUrl "www-domain-tld" + context "convertToUrl", should "detect and clean up valid URLs", -> assert.equal "http://www.google.com/", Utils.convertToUrl("http://www.google.com/") |
