Skip to content

Instantly share code, notes, and snippets.

@kara-ryli
Created July 27, 2010 21:53
Show Gist options
  • Save kara-ryli/492947 to your computer and use it in GitHub Desktop.
Save kara-ryli/492947 to your computer and use it in GitHub Desktop.
How to convert text into HTML links in JavaScript, based on http://daringfireball.net/2010/07/improved_regex_for_matching_urls
var GRUBERS_URL_RE = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i;
// Should log a URL
[
"http://foo.com/blah_blah",
"http://foo.com/blah_blah/",
"(Something like http://foo.com/blah_blah)",
"http://foo.com/blah_blah_(wikipedia)",
"http://foo.com/more_(than)_one_(parens)",
"(Something like http://foo.com/blah_blah_(wikipedia))",
"http://foo.com/blah_(wikipedia)#cite-1",
"http://foo.com/blah_(wikipedia)_blah#cite-1",
"http://foo.com/unicode_(✪)_in_parens",
"http://foo.com/(something)?after=parens",
"http://foo.com/blah_blah.",
"http://foo.com/blah_blah/.",
"<http://foo.com/blah_blah>",
"<http://foo.com/blah_blah/>",
"http://foo.com/blah_blah,",
"http://www.extinguishedscholar.com/wpglob/?p=364.",
"http://✪df.ws/1234",
"rdar://1234",
"rdar:/1234",
"x-yojimbo-item://6303E4C1-6A6E-45A6-AB9D-3A908F59AE0E",
"message://%3c330e7f840905021726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e",
"http://➡.ws/䨹",
"www.c.ws/䨹",
"<tag>http://example.com</tag>",
"Just a www.example.com link.",
"http://example.com/something?with,commas,in,url, but not at end",
"What about <mailto:gruber@daringfireball.net?subject=TEST> (including brokets).",
"mailto:name@example.com",
"bit.ly/foo",
"“is.gd/foo/”",
"WWW.EXAMPLE.COM",
"http://www.asianewsphoto.com/(S(neugxif4twuizg551ywh3f55))/Web_ENG/View_DetailPhoto.aspx?PicId=752",
"http://www.asianewsphoto.com/(S(neugxif4twuizg551ywh3f55))",
"http://lcweb2.loc.gov/cgi-bin/query/h?pp/horyd:@field(NUMBER+@band(thc+5a46634))"
].forEach(function (s) {
var m = s.match(GRUBERS_URL_RE);
console.log(m ? m[1] : 'fail');
});
// Should fail
[
"6:00p",
"filename.txt"
].forEach(function (s) {
var m = s.match(GRUBERS_URL_RE);
console.log(m ? 'fail' : 'pass');
});
/**
* Returns the text specified with URLs replaced by an html link.
* does not honor whitespace. Escapes HTML in result. Example:
*
* linkify("Go to twitter.com/rcanine to read my <i>status</i>.");
* -> "Go to <a href="http://twitter.com/rcanine">twitter.com/rcanine</a>
* -> to read my &lt;i&gt;status&lt;/i&gt;."
*/
var linkify = (function () {
var GRUBERS_URL_RE = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i,
HAS_PROTOCOL = /^[a-z][\w-]+:/;
function escapeHTML(text) {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/>/g, '&gt;');
}
function wordToURL(word, index, array) {
var m = word.match(GRUBERS_URL_RE),
result = escapeHTML(word),
url, escapedURL, text;
if (m) {
text = escapeHTML(m[1]);
url = HAS_PROTOCOL.test(text) ? text : 'http://' + text;
result = result.replace(text, '<a href="' + url + '">' + text + '</a>');
}
return result;
}
var map = Array.prototype.map ? function (arr, callback) {
return arr.map(callback);
} : function (arr, callback) {
var arr2 = [], i, l;
for (i = 0, l = arr.length; i < l; i = i + 1) {
arr2[i] = callback(arr[i], i, arr);
}
return arr2;
};
return function (text) {
return map(text.split(/\s+/), wordToURL).join(' ');
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment