From 7c7b64fd6d8254ac4d2d815ab1777f3643c65e90 Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Sun, 19 Aug 2012 10:18:51 +0200 Subject: More idiomatic CoffeeScript for Utils.isUrl --- lib/utils.coffee | 100 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index 598b631a..e52c4160 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -43,60 +43,68 @@ Utils = 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 + # 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 + '([^:]+|\\[[^\\]]+\\])' + + # port number (optional) => \4 + '(?::(\\d+))?$' ) - # 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' ] + # Official ASCII TLDs that are longer than 3 characters + longTlds = [ + 'arpa' + 'asia' + 'coop' + 'info' + 'jobs' + 'local' + 'mobi' + 'museum' + 'name' + 'onion' # Inofficial .onion TLD used by TOR + ] + + specialHostNames = ['localhost'] + + # Starts with a scheme: URL + return true if /^[a-z]{3,}:\/\//.test str + + # Must not contain spaces + return false if ' ' in str + + # 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] - # are there more? - specialHostNames = [ 'localhost' ] + # Allow known special host names + return true if hostName in specialHostNames - # it starts with a scheme, so it's definitely an URL - if (/^[a-z]{3,}:\/\//.test(str)) - return true + # 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 - # spaces => definitely not a valid URL - if (str.indexOf(' ') >= 0) - return false + # 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 '.' - # 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 + 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. -- cgit v1.2.3 From c9a11f75ac986e38a84487bc05ec573bb4ba13fb Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Tue, 4 Sep 2012 22:11:02 +0200 Subject: Tests for Utils.isUrl --- tests/utils_test.coffee | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) 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/") -- cgit v1.2.3 From 164f7919ba625717a41948726c387b31a494aff9 Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Sun, 19 Aug 2012 10:32:20 +0200 Subject: More idiomatic CoffeeScript --- lib/utils.coffee | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index e52c4160..9ddab62f 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, ">") # Generates a unique ID - createUniqueId: (-> + createUniqueId: do -> id = 0 - return -> id += 1 - )() + -> id += 1 hasChromePrefix: (url) -> chromePrefixes = [ 'about', 'view-source' ] @@ -109,21 +109,26 @@ Utils = # 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))) @@ -132,19 +137,20 @@ 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. +# 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) + @init.apply(this, arguments) if @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] -- cgit v1.2.3 From f9af8019e58a0a9cdc53c3cc96a7938376d089f5 Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Wed, 5 Sep 2012 09:18:13 +0200 Subject: Wrap lines at 110 chars --- lib/utils.coffee | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index 9ddab62f..c43a283c 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -45,8 +45,7 @@ Utils = # 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 + # More or less RFC compliant URL host part parsing. This should be sufficient for our needs urlRegex = new RegExp( # user:password (optional) => \1, \2 '^(?:([^:]+)(?::([^:]+))?@)?' + @@ -78,8 +77,8 @@ Utils = # Must not contain spaces return false if ' ' in str - # 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. + # 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] @@ -87,14 +86,12 @@ Utils = # 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 + # 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 + # 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 @@ -112,8 +109,8 @@ Utils = # 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() @@ -125,9 +122,8 @@ Utils = else 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. +# 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 -- cgit v1.2.3 From edb1d3c8a5a5d9af7349cb87dda7a722b5210360 Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Wed, 5 Sep 2012 09:19:34 +0200 Subject: Tidy up if-statement --- lib/utils.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index c43a283c..597e5f6c 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -38,7 +38,7 @@ 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 -- cgit v1.2.3 From 5901cee9b9b7df3ad07e3cf895bf74fb65b276cc Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Thu, 6 Sep 2012 10:33:33 +0200 Subject: Align regex comments on the same line as the expression --- lib/utils.coffee | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index 597e5f6c..c5bfcc26 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -47,12 +47,9 @@ Utils = isUrl: (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 - '([^:]+|\\[[^\\]]+\\])' + - # port number (optional) => \4 - '(?::(\\d+))?$' + '^(?:([^:]+)(?::([^:]+))?@)?' + # user:password (optional) => \1, \2 + '([^:]+|\\[[^\\]]+\\])' + # host name (IPv6 addresses in square brackets allowed) => \3 + '(?::(\\d+))?$' # port number (optional) => \4 ) # Official ASCII TLDs that are longer than 3 characters -- cgit v1.2.3 From 560ceb7828a1911f6227f36180726094f8e7fbfa Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Thu, 6 Sep 2012 10:33:58 +0200 Subject: Lay out long TLDs on one line --- lib/utils.coffee | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index c5bfcc26..00dce5b4 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -52,19 +52,8 @@ Utils = '(?::(\\d+))?$' # port number (optional) => \4 ) - # Official ASCII TLDs that are longer than 3 characters - longTlds = [ - 'arpa' - 'asia' - 'coop' - 'info' - 'jobs' - 'local' - 'mobi' - 'museum' - 'name' - 'onion' # Inofficial .onion TLD used by TOR - ] + # 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'] -- cgit v1.2.3 From cf5d98a8cadff87a54ffbf3fc55dbff4b12831bc Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Thu, 6 Sep 2012 10:35:53 +0200 Subject: Move instant tests to the top of the function: return early if possible --- lib/utils.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index 00dce5b4..49481445 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -45,6 +45,12 @@ Utils = # Tries to detect if :str is a valid URL. isUrl: (str) -> + # 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 @@ -57,12 +63,6 @@ Utils = specialHostNames = ['localhost'] - # Starts with a scheme: URL - return true if /^[a-z]{3,}:\/\//.test str - - # Must not contain spaces - return false if ' ' in str - # 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] -- cgit v1.2.3 From 95d538d0458ae6206a791edead92cd0e4b1e3990 Mon Sep 17 00:00:00 2001 From: Johan Liesén Date: Thu, 6 Sep 2012 10:37:47 +0200 Subject: Remove unused Class --- lib/utils.coffee | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/utils.coffee b/lib/utils.coffee index 49481445..39279760 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -119,19 +119,6 @@ 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 = -> - @init.apply(this, arguments) if @init - null - newClass.prototype = properties - newClass.constructor = newClass - newClass - globalRoot = window ? global globalRoot.extend = (hash1, hash2) -> for key of hash2 @@ -140,4 +127,3 @@ globalRoot.extend = (hash1, hash2) -> root = exports ? window root.Utils = Utils -root.Class = Class -- cgit v1.2.3