aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJez Ng2012-09-08 16:37:05 -0400
committerJez Ng2012-09-08 16:37:05 -0400
commitadf8b7cc617279e50594477560ec7031e2ab68ea (patch)
treef43660994fb29770bd38e4b11cc11d828ecc0615
parent3e5bba24ede4f9392ff666634a0a8f05e21a02d1 (diff)
parente740ea52b646f1b7cd0ef13e15571b20adaa3bd8 (diff)
downloadvimium-adf8b7cc617279e50594477560ec7031e2ab68ea.tar.bz2
Merge branch 'next-release'
-rw-r--r--background_scripts/commands.coffee6
-rw-r--r--background_scripts/completion.coffee4
-rw-r--r--background_scripts/main.coffee26
-rw-r--r--content_scripts/link_hints.coffee22
-rw-r--r--content_scripts/vimium_frontend.coffee14
-rw-r--r--content_scripts/vomnibar.coffee1
-rw-r--r--lib/dom_utils.coffee3
-rw-r--r--lib/utils.coffee137
-rw-r--r--tests/dom_tests/dom_tests.coffee19
-rw-r--r--tests/unit_tests/utils_test.coffee22
10 files changed, 131 insertions, 123 deletions
diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee
index e3001f4f..69c37ac4 100644
--- a/background_scripts/commands.coffee
+++ b/background_scripts/commands.coffee
@@ -43,9 +43,9 @@ Commands =
# On the other hand, <c-a> and <c-A> are different named keys - for one of
# them you have to press "shift" as well.
normalizeKey: (key) ->
- key.replace(/<[acm]-/ig, (match) -> match.toLowerCase())
- .replace(/<([acm]-)?([a-zA-Z0-9]{2,5})>/g, (match, optionalPrefix, keyName) ->
- "<" + (if optionalPrefix then optionalPrefix else "") + keyName.toLowerCase() + ">")
+ key.replace(/<[acm]-/ig, (match) -> match.toLowerCase())
+ .replace(/<([acm]-)?([a-zA-Z0-9]{2,5})>/g, (match, optionalPrefix, keyName) ->
+ "<" + (if optionalPrefix then optionalPrefix else "") + keyName.toLowerCase() + ">")
parseCustomKeyMappings: (customKeyMappings) ->
lines = customKeyMappings.split("\n")
diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee
index 5920db0b..5539f7c7 100644
--- a/background_scripts/completion.coffee
+++ b/background_scripts/completion.coffee
@@ -26,7 +26,7 @@ class Suggestion
generateHtml: ->
return @html if @html
- relevancyHtml = if @showRelevancy then "<span class='relevancy'>#{@computeRelevancy() + ''}</span>" else ""
+ relevancyHtml = if @showRelevancy then "<span class='relevancy'>#{@computeRelevancy()}</span>" else ""
# NOTE(philc): We're using these vimium-specific class names so we don't collide with the page's CSS.
@html =
"<div class='vimiumReset vomnibarTopHalf'>
@@ -90,7 +90,7 @@ class BookmarkCompleter
performSearch: ->
results = @bookmarks.filter (bookmark) =>
- RankingUtils.matches(@currentSearch.queryTerms, bookmark.url, bookmark.title)
+ RankingUtils.matches(@currentSearch.queryTerms, bookmark.url, bookmark.title)
suggestions = results.map (bookmark) =>
new Suggestion(@currentSearch.queryTerms, "bookmark", bookmark.url, bookmark.title, @computeRelevancy)
onComplete = @currentSearch.onComplete
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index bc3fde0b..a3857d61 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -265,17 +265,17 @@ selectTab = (callback, direction) ->
chrome.tabs.getAllInWindow(null, (tabs) ->
return unless tabs.length > 1
chrome.tabs.getSelected(null, (currentTab) ->
- switch direction
- when "next"
- toSelect = tabs[(currentTab.index + 1 + tabs.length) % tabs.length]
- when "previous"
- toSelect = tabs[(currentTab.index - 1 + tabs.length) % tabs.length]
- when "first"
- toSelect = tabs[0]
- when "last"
- toSelect = tabs[tabs.length - 1]
- selectionChangedHandlers.push(callback)
- chrome.tabs.update(toSelect.id, { selected: true })))
+ switch direction
+ when "next"
+ toSelect = tabs[(currentTab.index + 1 + tabs.length) % tabs.length]
+ when "previous"
+ toSelect = tabs[(currentTab.index - 1 + tabs.length) % tabs.length]
+ when "first"
+ toSelect = tabs[0]
+ when "last"
+ toSelect = tabs[tabs.length - 1]
+ selectionChangedHandlers.push(callback)
+ chrome.tabs.update(toSelect.id, { selected: true })))
updateOpenTabs = (tab) ->
openTabs[tab.id] = { url: tab.url, positionIndex: tab.index, windowId: tab.windowId }
@@ -367,7 +367,7 @@ updatePositionsAndWindowsForAllTabsInWindow = (windowId) ->
splitKeyIntoFirstAndSecond = (key) ->
if (key.search(namedKeyRegex) == 0)
- { first: RegExp.$1, second: RegExp.$2 }
+ { first: RegExp.$1, second: RegExp.$2 }
else
{ first: key[0], second: key.slice(1) }
@@ -409,7 +409,7 @@ generateCompletionKeys = (keysToCheck) ->
for key of Commands.keyToCommandRegistry
splitKey = splitKeyIntoFirstAndSecond(key)
if (splitKey.first == command)
- completionKeys.push(splitKey.second)
+ completionKeys.push(splitKey.second)
completionKeys
diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee
index 1d5d668a..831408e2 100644
--- a/content_scripts/link_hints.coffee
+++ b/content_scripts/link_hints.coffee
@@ -34,10 +34,10 @@ LinkHints =
# We use translate() instead of lower-case() because Chrome only supports XPath 1.0.
#
clickableElementsXPath: DomUtils.makeXPath(
- ["a", "area[@href]", "textarea", "button", "select",
- "input[not(@type='hidden' or @disabled or @readonly)]",
- "*[@onclick or @tabindex or @role='link' or @role='button' or contains(@class, 'button') or " +
- "@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']"])
+ ["a", "area[@href]", "textarea", "button", "select",
+ "input[not(@type='hidden' or @disabled or @readonly)]",
+ "*[@onclick or @tabindex or @role='link' or @role='button' or contains(@class, 'button') or " +
+ "@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']"])
# We need this as a top-level function because our command system doesn't yet support arguments.
activateModeToOpenInNewTab: -> @activateMode(true, false, false)
@@ -80,8 +80,8 @@ LinkHints =
else if (withQueue)
HUD.show("Open multiple links in a new tab")
@linkActivator = (link) ->
- # When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
- # to open it in a new tab if necessary.
+ # When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on
+ # windows) to open it in a new tab if necessary.
DomUtils.simulateClick(link, {
metaKey: KeyboardUtils.platform == "Mac",
ctrlKey: KeyboardUtils.platform != "Mac" })
@@ -211,7 +211,7 @@ LinkHints =
#
showMarker: (linkMarker, matchingCharCount) ->
linkMarker.style.display = ""
- # TODO(philc):
+ # TODO(philc):
for j in [0...linkMarker.childNodes.length]
if (j < matchingCharCount)
linkMarker.childNodes[j].classList.add("matchingCharacter")
@@ -269,8 +269,8 @@ alphabetHints =
digitsNeeded = Math.ceil(@logXOfBase(linkCount, linkHintCharacters.length))
# Short hints are the number of hints we can possibly show which are (digitsNeeded - 1) digits in length.
shortHintCount = Math.floor(
- (Math.pow(linkHintCharacters.length, digitsNeeded) - linkCount) /
- linkHintCharacters.length)
+ (Math.pow(linkHintCharacters.length, digitsNeeded) - linkCount) /
+ linkHintCharacters.length)
longHintCount = linkCount - shortHintCount
hintStrings = []
@@ -299,8 +299,8 @@ alphabetHints =
result
#
- # Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
- # the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
+ # Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of the
+ # hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
#
numberToHintString: (number, numHintDigits, characterSet) ->
base = characterSet.length
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index ab3b1fbf..adec11c5 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -295,8 +295,8 @@ extend window,
chrome.extension.sendRequest({ handler: "openUrlInNewTab", url: url, selected: true })
copyCurrentUrl: ->
- # TODO(ilya): When the following bug is fixed, revisit this approach of sending back to the background page
- # to copy.
+ # TODO(ilya): When the following bug is fixed, revisit this approach of sending back to the background
+ # page to copy.
# http://code.google.com/p/chromium/issues/detail?id=55188
chrome.extension.sendRequest { handler: "getCurrentTabUrl" }, (url) ->
chrome.extension.sendRequest { handler: "copyToClipboard", data: url }
@@ -895,9 +895,9 @@ window.showHelpDialog = (html, fid) ->
this.dialogElement.style.maxHeight = window.innerHeight - 80
this.showAdvancedCommands(this.getShowAdvancedCommands())
- #
+ #
# Advanced commands are hidden by default so they don't overwhelm new and casual users.
- #
+ #
toggleAdvancedCommands: (event) ->
event.preventDefault()
showAdvanced = VimiumHelpDialog.getShowAdvancedCommands()
@@ -958,9 +958,9 @@ HUD =
HUD.displayElement().style.display = ""
showUpgradeNotification: (version) ->
- HUD.upgradeNotificationElement().innerHTML = "Vimium has been updated to " +
- "<a class='vimiumReset' href='https://chrome.google.com/extensions/detail/dbepggeogbaibhgnhhndojpepiihcmeb'>" +
- version + "</a>.<a class='vimiumReset close-button' href='#'>x</a>"
+ HUD.upgradeNotificationElement().innerHTML = "Vimium has been updated to
+ <a class='vimiumReset' href='https://chrome.google.com/extensions/detail/dbepggeogbaibhgnhhndojpepiihcmeb'>
+ #{version}</a>.<a class='vimiumReset close-button' href='#'>x</a>"
links = HUD.upgradeNotificationElement().getElementsByTagName("a")
links[0].addEventListener("click", HUD.onUpdateLinkClicked, false)
links[1].addEventListener "click", (event) ->
diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee
index 9c1e7a31..1b2af1c4 100644
--- a/content_scripts/vomnibar.coffee
+++ b/content_scripts/vomnibar.coffee
@@ -172,7 +172,6 @@ class VomnibarUI
@input = document.querySelector("#vomnibar input")
@input.addEventListener "input", => @update()
- console.log("@input:", @input);
@completionList = document.querySelector("#vomnibar ul")
@completionList.style.display = "none"
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index d4a4d379..e7fa9c2f 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -85,7 +85,8 @@ DomUtils =
if (clientRect.width == 0 || clientRect.height == 0)
for child in element.children
computedStyle = window.getComputedStyle(child, null)
- # Ignore child elements which are not floated and not absolutely positioned for parent elements with zero width/height
+ # Ignore child elements which are not floated and not absolutely positioned for parent elements with
+ # zero width/height
if (computedStyle.getPropertyValue('float') == 'none' && computedStyle.getPropertyValue('position') != 'absolute')
continue
childClientRect = @getVisibleClientRect(child)
diff --git a/lib/utils.coffee b/lib/utils.coffee
index 598b631a..a2221ba4 100644
--- a/lib/utils.coffee
+++ b/lib/utils.coffee
@@ -1,6 +1,6 @@
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 +25,9 @@ Utils =
escapeHtml: (string) -> string.replace(/</g, "&lt;").replace(/>/g, "&gt;")
# Generates a unique ID
- createUniqueId: (->
+ createUniqueId: do ->
id = 0
- return -> id += 1
- )()
+ -> id += 1
hasChromePrefix: (url) ->
chromePrefixes = [ 'about', 'view-source' ]
@@ -38,84 +37,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 explicitly 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 +118,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 +126,3 @@ globalRoot.extend = (hash1, hash2) ->
root = exports ? window
root.Utils = Utils
-root.Class = Class
diff --git a/tests/dom_tests/dom_tests.coffee b/tests/dom_tests/dom_tests.coffee
index f4e63270..3d981ee5 100644
--- a/tests/dom_tests/dom_tests.coffee
+++ b/tests/dom_tests/dom_tests.coffee
@@ -2,7 +2,7 @@
# Dispatching keyboard events via the DOM would require async tests,
# which tend to be more complicated. Here we create mock events and
# invoke the handlers directly.
-#
+#
mockKeyboardEvent = (keyChar) ->
event = {}
event.charCode = (if keyCodes[keyChar] isnt undefined then keyCodes[keyChar] else keyChar.charCodeAt(0))
@@ -14,14 +14,14 @@ mockKeyboardEvent = (keyChar) ->
#
# Retrieve the hint markers as an array object.
-#
+#
getHintMarkers = ->
Array::slice.call document.getElementsByClassName("vimiumHintMarker"), 0
#
# Generate tests that are common to both default and filtered
# link hinting modes.
-#
+#
createGeneralHintTests = (isFilteredMode) ->
context "Link hints",
@@ -125,7 +125,8 @@ context "Filtered link hints",
context "Image hints",
setup ->
- testContent = "<a><img alt='alt text'/></a>" + "<a><img alt='alt text' title='some title'/></a>" + "<a><img title='some title'/></a>" + "<a><img src='' width='320px' height='100px'/></a>"
+ testContent = "<a><img alt='alt text'/></a><a><img alt='alt text' title='some title'/></a>
+ <a><img title='some title'/></a>" + "<a><img src='' width='320px' height='100px'/></a>"
document.getElementById("test-div").innerHTML = testContent
LinkHints.activateMode()
@@ -143,7 +144,10 @@ context "Filtered link hints",
context "Input hints",
setup ->
- testContent = "<input type='text' value='some value'/>" + "<input type='password' value='some value'/>" + "<textarea>some text</textarea>" + "<label for='test-input'/>a label</label><input type='text' id='test-input' value='some value'/>" + "<label for='test-input-2'/>a label: </label><input type='text' id='test-input-2' value='some value'/>"
+ testContent = "<input type='text' value='some value'/><input type='password' value='some value'/>
+ <textarea>some text</textarea><label for='test-input'/>a label</label>
+ <input type='text' id='test-input' value='some value'/>
+ <label for='test-input-2'/>a label: </label><input type='text' id='test-input-2' value='some value'/>"
document.getElementById("test-div").innerHTML = testContent
LinkHints.activateMode()
@@ -162,7 +166,8 @@ context "Filtered link hints",
context "Input focus",
setup ->
- testContent = "<input type='text' id='first'/>" + "<input style='display:none;' id='second'/>" + "<input type='password' id='third' value='some value'/>"
+ testContent = "<input type='text' id='first'/><input style='display:none;' id='second'/>
+ <input type='password' id='third' value='some value'/>"
document.getElementById("test-div").innerHTML = testContent
tearDown ->
@@ -183,7 +188,7 @@ Tests.outputMethod = (args...) ->
# escape html
newOutput = newOutput.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
# highlight the source of the error
- newOutput = newOutput.replace(/\/([^:/]+):([0-9]+):([0-9]+)/, "/<span class='errorPosition'>$1:$2</span>:$3")
+ newOutput = newOutput.replace /\/([^:/]+):([0-9]+):([0-9]+)/, "/<span class='errorPosition'>$1:$2</span>:$3"
document.getElementById("output-div").innerHTML += "<div class='output-section'>" + newOutput + "</div>"
console.log.apply console, args
diff --git a/tests/unit_tests/utils_test.coffee b/tests/unit_tests/utils_test.coffee
index 6a44b460..9da5ff65 100644
--- a/tests/unit_tests/utils_test.coffee
+++ b/tests/unit_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/")