autolinker
Advanced tools
Comparing version 0.7.2 to 0.11.0
{ | ||
"name": "Autolinker.js", | ||
"main": "Autolinker.js", | ||
"main": "dist/Autolinker.js", | ||
"homepage": "https://github.com/gregjacobs/Autolinker.js", | ||
@@ -5,0 +5,0 @@ "authors": [ |
/*! | ||
* Autolinker.js | ||
* 0.7.0 | ||
* 0.11.0 | ||
* | ||
@@ -10,205 +10,121 @@ * Copyright(c) 2014 Gregory Jacobs <greg@greg-jacobs.com> | ||
*/ | ||
/** | ||
* @class Autolinker | ||
* @extends Object | ||
* @singleton | ||
* | ||
* Singleton class which exposes the {@link #link} method, used to process a given string of text, | ||
* and wrap the URLs, email addresses, and Twitter handles in the appropriate anchor (<a>) tags. | ||
*/ | ||
/*global define, module */ | ||
/*jshint undef:true, smarttabs:true */ | ||
// Set up Autolinker appropriately for the environment. | ||
(function(root, factory) { | ||
// Start with AMD. | ||
if (typeof define === 'function' && define.amd) { | ||
define(factory); | ||
// Next for Node.js or CommonJS. | ||
} else if (typeof exports !== 'undefined') { | ||
module.exports = factory(); | ||
// Finally, as a browser global. | ||
( function( root, factory ) { | ||
if( typeof define === 'function' && define.amd ) { | ||
define( factory ); // Define as AMD module if an AMD loader is present (ex: RequireJS). | ||
} else if( typeof exports !== 'undefined' ) { | ||
module.exports = factory(); // Define as CommonJS module for Node.js, if available. | ||
} else { | ||
root.Autolinker = factory(); | ||
root.Autolinker = factory(); // Finally, define as a browser global if no module loader. | ||
} | ||
}(this, function(root) { | ||
var Autolinker = { | ||
}( this, function() { | ||
/** | ||
* @class Autolinker | ||
* @extends Object | ||
* | ||
* Utility class used to process a given string of text, and wrap the URLs, email addresses, and Twitter handles in | ||
* the appropriate anchor (<a>) tags to turn them into links. | ||
* | ||
* Any of the configuration options may be provided in an Object (map) provided to the Autolinker constructor, which | ||
* will configure how the {@link #link link()} method will process the links. | ||
* | ||
* For example: | ||
* | ||
* var autolinker = new Autolinker( { | ||
* newWindow : false, | ||
* truncate : 30 | ||
* } ); | ||
* | ||
* var html = autolinker.link( "Joe went to www.yahoo.com" ); | ||
* // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' | ||
* | ||
* | ||
* The {@link #static-link static link()} method may also be used to inline options into a single call, which may | ||
* be more convenient for one-off uses. For example: | ||
* | ||
* var html = Autolinker.link( "Joe went to www.yahoo.com", { | ||
* newWindow : false, | ||
* truncate : 30 | ||
* } ); | ||
* // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' | ||
* | ||
* @constructor | ||
* @param {Object} [config] The configuration options for the Autolinker instance, specified in an Object (map). | ||
*/ | ||
var Autolinker = function( cfg ) { | ||
cfg = cfg || {}; | ||
// Assign the properties of `cfg` onto the Autolinker instance | ||
for( var prop in cfg ) | ||
if( cfg.hasOwnProperty( prop ) ) this[ prop ] = cfg[ prop ]; | ||
}; | ||
Autolinker.prototype = { | ||
constructor : Autolinker, // fix constructor property | ||
/** | ||
* @private | ||
* @property {RegExp} htmlRegex | ||
* @cfg {Boolean} newWindow | ||
* | ||
* A regular expression used to pull out HTML tags from a string. | ||
* `true` if the links should open in a new window, `false` otherwise. | ||
*/ | ||
newWindow : true, | ||
/** | ||
* @cfg {Boolean} stripPrefix | ||
* | ||
* Capturing groups: | ||
* `true` if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of links, `false` otherwise. | ||
*/ | ||
stripPrefix : true, | ||
/** | ||
* @cfg {Number} truncate | ||
* | ||
* 1. If it is an end tag, this group will have the '/'. | ||
* 2. The tag name. | ||
* A number for how many characters long URLs/emails/twitter handles should be truncated to inside the text of | ||
* a link. If the URL/email/twitter is over this number of characters, it will be truncated to this length by | ||
* adding a two period ellipsis ('..') into the middle of the string. | ||
* | ||
* For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters might look | ||
* something like this: 'http://www...th/to/a/file' | ||
*/ | ||
htmlRegex : /<(\/)?(\w+)(?:(?:\s+\w+(?:\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g, | ||
/** | ||
* @private | ||
* @property {RegExp} prefixRegex | ||
* @cfg {Boolean} twitter | ||
* | ||
* A regular expression used to remove the 'http://' or 'https://' and/or the 'www.' from URLs. | ||
* `true` if Twitter handles ("@example") should be automatically linked, `false` if they should not be. | ||
*/ | ||
prefixRegex: /^(https?:\/\/)?(www\.)?/, | ||
twitter : true, | ||
/** | ||
* @cfg {Boolean} email | ||
* | ||
* `true` if email addresses should be automatically linked, `false` if they should not be. | ||
*/ | ||
email : true, | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* @cfg {Boolean} urls | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* `true` if miscellaneous URLs should be automatically linked, `false` if they should not be. | ||
*/ | ||
urls : true, | ||
/** | ||
* @cfg {String} className | ||
* | ||
* @method link | ||
* @param {String} html The HTML text to link URLs within. | ||
* @param {Object} [options] Any options for the autolinking, specified in an object. It may have the following properties: | ||
* @param {Boolean} [options.newWindow=true] True if the links should open in a new window, false otherwise. | ||
* @param {Boolean} [options.stripPrefix=true] True if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of links, false otherwise. | ||
* @param {Number} [options.truncate] A number for how many characters long URLs/emails/twitter handles should be truncated to | ||
* inside the text of a link. If the URL/email/twitter is over the number of characters, it will be truncated to this length by | ||
* adding a two period ellipsis ('..') into the middle of the string. | ||
* Ex: a url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters might look like this: 'http://www...th/to/a/file' | ||
* @param {Boolean} [options.twitter=true] True if Twitter handles ("@example") should be automatically linked. | ||
* @param {Boolean} [options.email=true] True if email addresses should be automatically linked. | ||
* @param {Boolean} [options.urls=true] True if miscellaneous URLs should be automatically linked. | ||
* @return {String} The HTML text, with URLs automatically linked | ||
* A CSS class name to add to the generated links. This class will be added to all links, as well as this class | ||
* plus url/email/twitter suffixes for styling url/email/twitter links differently. | ||
* | ||
* For example, if this config is provided as "myLink", then: | ||
* | ||
* 1) URL links will have the CSS classes: "myLink myLink-url" | ||
* 2) Email links will have the CSS classes: "myLink myLink-email", and | ||
* 3) Twitter links will have the CSS classes: "myLink myLink-twitter" | ||
*/ | ||
link : function( html, options ) { | ||
options = options || {}; | ||
var htmlRegex = Autolinker.htmlRegex, // full path for friendly | ||
matcherRegex = Autolinker.matcherRegex, // out-of-scope calls | ||
newWindow = ( 'newWindow' in options ) ? options.newWindow : true, // defaults to true | ||
stripPrefix = ( 'stripPrefix' in options ) ? options.stripPrefix : true, // defaults to true | ||
truncate = options.truncate, | ||
enableTwitter = ( 'twitter' in options ) ? options.twitter : true, // defaults to true | ||
enableEmailAddresses = ( 'email' in options ) ? options.email : true, // defaults to true | ||
enableUrls = ( 'urls' in options ) ? options.urls : true, // defaults to true | ||
currentResult, | ||
lastIndex = 0, | ||
inBetweenTagsText, | ||
resultHtml = "", | ||
anchorTagStackCount = 0; | ||
// Function to process the text that lies between HTML tags. This function does the actual wrapping of | ||
// URLs with anchor tags. | ||
function autolinkText( text ) { | ||
text = text.replace( matcherRegex, function( matchStr, $1, $2, $3, $4, $5 ) { | ||
var twitterMatch = $1, | ||
twitterHandlePrefixWhitespaceChar = $2, // The whitespace char before the @ sign in a Twitter handle match. This is needed because of no lookbehinds in JS regexes | ||
twitterHandle = $3, // The actual twitterUser (i.e the word after the @ sign in a Twitter handle match) | ||
emailAddress = $4, // For both determining if it is an email address, and stores the actual email address | ||
urlMatch = $5, // The matched URL string | ||
prefixStr = "", // A string to use to prefix the anchor tag that is created. This is needed for the Twitter handle match | ||
suffixStr = "", // A string to suffix the anchor tag that is created. This is used if there is a trailing parenthesis that should not be auto-linked. | ||
anchorAttributes = []; | ||
// Handle a closing parenthesis at the end of the match, and exclude it if there is not a matching open parenthesis | ||
// in the match. This handles cases like the string "wikipedia.com/something_(disambiguation)" (which should be auto- | ||
// linked, and when it is enclosed in parenthesis itself, such as: "(wikipedia.com/something_(disambiguation))" (in | ||
// which the outer parens should *not* be auto-linked. | ||
var lastChar = matchStr.charAt( matchStr.length - 1 ); | ||
if( lastChar === ')' ) { | ||
var openParensMatch = matchStr.match( /\(/g ), | ||
closeParensMatch = matchStr.match( /\)/g ), | ||
numOpenParens = ( openParensMatch && openParensMatch.length ) || 0, | ||
numCloseParens = ( closeParensMatch && closeParensMatch.length ) || 0; | ||
if( numOpenParens < numCloseParens ) { | ||
matchStr = matchStr.substr( 0, matchStr.length - 1 ); // remove the trailing ")" | ||
suffixStr = ")"; // this will be added after the <a> tag | ||
} | ||
} | ||
var anchorHref = matchStr, // initialize both of these | ||
anchorText = matchStr; // values as the full match | ||
if( ( twitterMatch && !enableTwitter ) || ( emailAddress && !enableEmailAddresses ) || ( urlMatch && !enableUrls ) ) { | ||
// A disabled link type | ||
return prefixStr + anchorText + suffixStr; | ||
} | ||
// Process the urls that are found. We need to change URLs like "www.yahoo.com" to "http://www.yahoo.com" (or the browser | ||
// will try to direct the user to "http://jux.com/www.yahoo.com"), and we need to prefix 'mailto:' to email addresses. | ||
if( twitterMatch ) { | ||
prefixStr = twitterHandlePrefixWhitespaceChar; | ||
anchorHref = 'https://twitter.com/' + twitterHandle; | ||
anchorText = '@' + twitterHandle; | ||
} else if( emailAddress ) { | ||
anchorHref = 'mailto:' + emailAddress; | ||
anchorText = emailAddress; | ||
} else if( !/^[A-Za-z]{3,9}:/i.test( anchorHref ) ) { // string doesn't begin with a protocol, add http:// | ||
anchorHref = 'http://' + anchorHref; | ||
} | ||
if( stripPrefix ) { | ||
anchorText = anchorText.replace( Autolinker.prefixRegex, '' ); | ||
} | ||
// remove trailing slash | ||
if( anchorText.charAt( anchorText.length - 1 ) === '/' ) { | ||
anchorText = anchorText.slice( 0, -1 ); | ||
} | ||
// Set the attributes for the anchor tag | ||
anchorAttributes.push( 'href="' + anchorHref + '"' ); | ||
if( newWindow ) { | ||
anchorAttributes.push( 'target="_blank"' ); | ||
} | ||
// Truncate the anchor text if it is longer than the provided 'truncate' option | ||
if( truncate && anchorText.length > truncate ) { | ||
anchorText = anchorText.substring( 0, truncate - 2 ) + '..'; | ||
} | ||
return prefixStr + '<a ' + anchorAttributes.join( " " ) + '>' + anchorText + '</a>' + suffixStr; // wrap the match in an anchor tag | ||
} ); | ||
return text; | ||
} | ||
// Loop over the HTML string, ignoring HTML tags, and processing the text that lies between them, | ||
// wrapping the URLs in anchor tags | ||
while( ( currentResult = htmlRegex.exec( html ) ) !== null ) { | ||
var tagText = currentResult[ 0 ], | ||
tagName = currentResult[ 2 ], | ||
isClosingTag = !!currentResult[ 1 ]; | ||
inBetweenTagsText = html.substring( lastIndex, currentResult.index ); | ||
lastIndex = currentResult.index + tagText.length; | ||
// Process around anchor tags, and any inner text / html they may have | ||
if( tagName === 'a' ) { | ||
if( !isClosingTag ) { // it's the start <a> tag | ||
anchorTagStackCount++; | ||
resultHtml += autolinkText( inBetweenTagsText ); | ||
} else { // it's the end </a> tag | ||
anchorTagStackCount--; | ||
if( anchorTagStackCount === 0 ) { | ||
resultHtml += inBetweenTagsText; // We hit the matching </a> tag, simply add all of the text from the start <a> tag to the end </a> tag without linking it | ||
} | ||
} | ||
} else if( anchorTagStackCount === 0 ) { // not within an anchor tag, link the "in between" text | ||
resultHtml += autolinkText( inBetweenTagsText ); | ||
} | ||
resultHtml += tagText; // now add the text of the tag itself verbatim | ||
} | ||
// Process any remaining text after the last HTML element. Will process all of the text if there were no HTML elements. | ||
if( lastIndex < html.length ) { | ||
resultHtml += autolinkText( html.substring( lastIndex ) ); | ||
} | ||
return resultHtml; | ||
}, | ||
className : "", | ||
/** | ||
@@ -220,30 +136,34 @@ * @private | ||
* | ||
* Capturing groups: | ||
* This regular expression has the following capturing groups: | ||
* | ||
* 1. Group that is used to determine if there is a Twitter handle match (i.e. @someTwitterUser). Simply check for its existence | ||
* to determine if there is a Twitter handle match. The next couple of capturing groups give information about the Twitter | ||
* handle match. | ||
* 2. The whitespace character before the @sign in a Twitter handle. This is needed because there are no lookbehinds in JS regular | ||
* expressions, and can be used to reconstruct the original string in a replace(). | ||
* 1. Group that is used to determine if there is a Twitter handle match (i.e. @someTwitterUser). Simply check for its | ||
* existence to determine if there is a Twitter handle match. The next couple of capturing groups give information | ||
* about the Twitter handle match. | ||
* 2. The whitespace character before the @sign in a Twitter handle. This is needed because there are no lookbehinds in | ||
* JS regular expressions, and can be used to reconstruct the original string in a replace(). | ||
* 3. The Twitter handle itself in a Twitter match. If the match is '@someTwitterUser', the handle is 'someTwitterUser'. | ||
* 4. Group that matches an email address. Used to determine if the match is an email address, as well as holding the full address. | ||
* Ex: 'me@my.com' | ||
* 4. Group that matches an email address. Used to determine if the match is an email address, as well as holding the full | ||
* address. Ex: 'me@my.com' | ||
* 5. Group that matches a URL in the input text. Ex: 'http://google.com', 'www.google.com', or just 'google.com'. | ||
* This also includes a path, url parameters, or hash anchors. Ex: google.com/path/to/file?q1=1&q2=2#myAnchor | ||
* 6. A protocol-relative ('//') match for the case of a 'www.' prefixed URL. Will be an empty string if it is not a | ||
* protocol-relative match. We need to know the character before the '//' in order to determine if it is a valid match | ||
* or the // was in a string we don't want to auto-link. | ||
* 7. A protocol-relative ('//') match for the case of a known TLD prefixed URL. Will be an empty string if it is not a | ||
* protocol-relative match. See #6 for more info. | ||
*/ | ||
matcherRegex: (function() { | ||
var twitterRegex = /(^|\s)@(\w{1,15})/, // For matching a twitter handle. Ex: @gregory_jacobs | ||
emailRegex = /(?:[\-;:&=\+\$,\w\.]+@)/, // something@ for email addresses (a.k.a. local-part) | ||
protocolRegex = /(?:[A-Za-z]{3,9}:(?:\/\/)?)/, // match protocol, allow in format http:// or mailto: | ||
wwwRegex = /(?:www\.)/, // starting with 'www.' | ||
domainNameRegex = /[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/, // anything looking at all like a domain, non-unicode domains, not ending in a period | ||
tldRegex = /\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/, // match our known top level domains (TLDs) | ||
// Allow optional path, query string, and hash anchor, not ending in the following characters: "!:,.;" | ||
// http://blog.codinghorror.com/the-problem-with-urls/ | ||
urlSuffixRegex = /(?:[-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#\/%=~_()|])?/; // note: optional part of the full regex | ||
matcherRegex : (function() { | ||
var twitterRegex = /(^|[^\w])@(\w{1,15})/, // For matching a twitter handle. Ex: @gregory_jacobs | ||
emailRegex = /(?:[\-;:&=\+\$,\w\.]+@)/, // something@ for email addresses (a.k.a. local-part) | ||
protocolRegex = /(?:[A-Za-z]{3,9}:(?:\/\/)?)/, // match protocol, allow in format http:// or mailto: | ||
wwwRegex = /(?:www\.)/, // starting with 'www.' | ||
domainNameRegex = /[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/, // anything looking at all like a domain, non-unicode domains, not ending in a period | ||
tldRegex = /\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/, // match our known top level domains (TLDs) | ||
// Allow optional path, query string, and hash anchor, not ending in the following characters: "!:,.;" | ||
// http://blog.codinghorror.com/the-problem-with-urls/ | ||
urlSuffixRegex = /(?:[\-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|])?/; // note: optional part of the full regex | ||
return new RegExp( [ | ||
@@ -268,3 +188,3 @@ '(', // *** Capturing group $1, which can be used to check for a twitter handle match. Use group $3 for the actual twitter handle though. $2 may be used to reconstruct the original string in a replace() | ||
'(?:', // parens to cover match for protocol (optional), and domain | ||
'(?:', // non-capturing paren for a protocol-prefixed url (ex: http://google.com) | ||
'(?:', // non-capturing paren for a protocol-prefixed url (ex: http://google.com) | ||
protocolRegex.source, | ||
@@ -277,2 +197,3 @@ domainNameRegex.source, | ||
'(?:', // non-capturing paren for a 'www.' prefixed url (ex: www.google.com) | ||
'(.?//)?', // *** Capturing group $6 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character | ||
wwwRegex.source, | ||
@@ -285,2 +206,3 @@ domainNameRegex.source, | ||
'(?:', // non-capturing paren for known a TLD url (ex: google.com) | ||
'(.?//)?', // *** Capturing group $7 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character | ||
domainNameRegex.source, | ||
@@ -293,7 +215,399 @@ tldRegex.source, | ||
')' | ||
].join( "" ), 'gi' ); | ||
} )(), | ||
/** | ||
* @private | ||
* @property {RegExp} protocolRelativeRegex | ||
* | ||
* The regular expression used to find protocol-relative URLs. A protocol-relative URL is, for example, "//yahoo.com" | ||
* | ||
* This regular expression needs to match the character before the '//', in order to determine if we should actually | ||
* autolink a protocol-relative URL. For instance, we want to autolink something like "//google.com", but we | ||
* don't want to autolink something like "abc//google.com" | ||
*/ | ||
protocolRelativeRegex : /(.)?\/\//, | ||
/** | ||
* @private | ||
* @property {RegExp} htmlRegex | ||
* | ||
* The regular expression used to pull out HTML tags from a string. Handles namespaced HTML tags and | ||
* attribute names, as specified by http://www.w3.org/TR/html-markup/syntax.html. | ||
* | ||
* Capturing groups: | ||
* | ||
* 1. If it is an end tag, this group will have the '/'. | ||
* 2. The tag name. | ||
*/ | ||
htmlRegex : (function() { | ||
var tagNameRegex = /[0-9a-zA-Z:]+/, | ||
attrNameRegex = /[^\s\0"'>\/=\x01-\x1F\x7F]+/, // the unicode range accounts for excluding control chars, and the delete char | ||
attrValueRegex = /(?:".*?"|'.*?'|[^'"=<>`\s]+)/; // double quoted, single quoted, or unquoted attribute values | ||
return new RegExp( [ | ||
'<(/)?', // Beginning of a tag. Either '<' for a start tag, or '</' for an end tag. The slash or an empty string is Capturing Group 1. | ||
// The tag name (Capturing Group 2) | ||
'(' + tagNameRegex.source + ')', | ||
// Zero or more attributes following the tag name | ||
'(?:', | ||
'\\s+', // one or more whitespace chars before an attribute | ||
attrNameRegex.source, | ||
'(?:\\s*=\\s*' + attrValueRegex.source + ')?', // optional '=[value]' | ||
')*', | ||
'\\s*', // any trailing spaces before the closing '>' | ||
'>' | ||
].join( "" ), 'g' ); | ||
})() | ||
} )(), | ||
/** | ||
* @private | ||
* @property {RegExp} urlPrefixRegex | ||
* | ||
* A regular expression used to remove the 'http://' or 'https://' and/or the 'www.' from URLs. | ||
*/ | ||
urlPrefixRegex: /^(https?:\/\/)?(www\.)?/i, | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* | ||
* @method link | ||
* @param {String} textOrHtml The HTML or text to link URLs, email addresses, and Twitter handles within. | ||
* @return {String} The HTML, with URLs/emails/twitter handles automatically linked. | ||
*/ | ||
link : function( textOrHtml ) { | ||
return this.processHtml( textOrHtml ); | ||
}, | ||
/** | ||
* Processes the given HTML to auto-link URLs/emails/Twitter handles. | ||
* | ||
* Finds the text around any HTML elements in the input `html`, which will be the text that is processed. | ||
* Any original HTML elements will be left as-is, as well as the text that is already wrapped in anchor tags. | ||
* | ||
* @private | ||
* @method processHtml | ||
* @param {String} html The input text or HTML to process in order to auto-link. | ||
* @return {String} | ||
*/ | ||
processHtml : function( html ) { | ||
// Loop over the HTML string, ignoring HTML tags, and processing the text that lies between them, | ||
// wrapping the URLs in anchor tags | ||
var htmlRegex = this.htmlRegex, | ||
currentResult, | ||
inBetweenTagsText, | ||
lastIndex = 0, | ||
anchorTagStackCount = 0, | ||
resultHtml = []; | ||
while( ( currentResult = htmlRegex.exec( html ) ) !== null ) { | ||
var tagText = currentResult[ 0 ], | ||
tagName = currentResult[ 2 ], | ||
isClosingTag = !!currentResult[ 1 ]; | ||
inBetweenTagsText = html.substring( lastIndex, currentResult.index ); | ||
lastIndex = currentResult.index + tagText.length; | ||
// Process around anchor tags, and any inner text / html they may have | ||
if( tagName === 'a' ) { | ||
if( !isClosingTag ) { // it's the start <a> tag | ||
anchorTagStackCount++; | ||
resultHtml.push( this.processTextNode( inBetweenTagsText ) ); | ||
} else { // it's the end </a> tag | ||
anchorTagStackCount = Math.max( anchorTagStackCount - 1, 0 ); // attempt to handle extraneous </a> tags by making sure the stack count never goes below 0 | ||
if( anchorTagStackCount === 0 ) { | ||
resultHtml.push( inBetweenTagsText ); // We hit the matching </a> tag, simply add all of the text from the start <a> tag to the end </a> tag without linking it | ||
} | ||
} | ||
} else if( anchorTagStackCount === 0 ) { // not within an anchor tag, link the "in between" text | ||
resultHtml.push( this.processTextNode( inBetweenTagsText ) ); | ||
} else { | ||
// if we have a tag that is in between anchor tags (ex: <a href="..."><b>google.com</b></a>), | ||
// just append the inner text | ||
resultHtml.push( inBetweenTagsText ); | ||
} | ||
resultHtml.push( tagText ); // now add the text of the tag itself verbatim | ||
} | ||
// Process any remaining text after the last HTML element. Will process all of the text if there were no HTML elements. | ||
if( lastIndex < html.length ) { | ||
var processedTextNode = this.processTextNode( html.substring( lastIndex ) ); | ||
resultHtml.push( processedTextNode ); | ||
} | ||
return resultHtml.join( "" ); | ||
}, | ||
/** | ||
* Process the text that lies inbetween HTML tags. This method does the actual wrapping of URLs with | ||
* anchor tags. | ||
* | ||
* @private | ||
* @param {String} text The text to auto-link. | ||
* @return {String} The text with anchor tags auto-filled. | ||
*/ | ||
processTextNode : function( text ) { | ||
var me = this, // for closures | ||
matcherRegex = this.matcherRegex, | ||
enableTwitter = this.twitter, | ||
enableEmailAddresses = this.email, | ||
enableUrls = this.urls; | ||
return text.replace( matcherRegex, function( matchStr, $1, $2, $3, $4, $5, $6, $7 ) { | ||
var twitterMatch = $1, | ||
twitterHandlePrefixWhitespaceChar = $2, // The whitespace char before the @ sign in a Twitter handle match. This is needed because of no lookbehinds in JS regexes | ||
twitterHandle = $3, // The actual twitterUser (i.e the word after the @ sign in a Twitter handle match) | ||
emailAddress = $4, // For both determining if it is an email address, and stores the actual email address | ||
urlMatch = $5, // The matched URL string | ||
protocolRelativeMatch = $6 || $7, // The '//' for a protocol-relative match, with the character that comes before the '//' | ||
prefixStr = "", // A string to use to prefix the anchor tag that is created. This is needed for the Twitter handle match | ||
suffixStr = ""; // A string to suffix the anchor tag that is created. This is used if there is a trailing parenthesis that should not be auto-linked. | ||
// Early exits with no replacements for: | ||
// 1) Disabled link types | ||
// 2) URL matches which do not have at least have one period ('.') in the domain name (effectively skipping over | ||
// matches like "abc:def") | ||
// 3) A protocol-relative url match (a URL beginning with '//') whose previous character is a word character | ||
// (effectively skipping over strings like "abc//google.com") | ||
if( | ||
( twitterMatch && !enableTwitter ) || ( emailAddress && !enableEmailAddresses ) || ( urlMatch && !enableUrls ) || | ||
( urlMatch && urlMatch.indexOf( '.' ) === -1 ) || // At least one period ('.') must exist in the URL match for us to consider it an actual URL | ||
( urlMatch && /^[A-Za-z]{3,9}:/.test( urlMatch ) && !/:.*?[A-Za-z]/.test( urlMatch ) ) || // At least one letter character must exist in the domain name after a protocol match. Ex: skip over something like "git:1.0" | ||
( protocolRelativeMatch && /^[\w]\/\//.test( protocolRelativeMatch ) ) // a protocol-relative match which has a word character in front of it (so we can skip something like "abc//google.com") | ||
) { | ||
return matchStr; | ||
} | ||
// Handle a closing parenthesis at the end of the match, and exclude it if there is not a matching open parenthesis | ||
// in the match. This handles cases like the string "wikipedia.com/something_(disambiguation)" (which should be auto- | ||
// linked, and when it is enclosed in parenthesis itself, such as: "(wikipedia.com/something_(disambiguation))" (in | ||
// which the outer parens should *not* be auto-linked. | ||
var lastChar = matchStr.charAt( matchStr.length - 1 ); | ||
if( lastChar === ')' ) { | ||
var openParensMatch = matchStr.match( /\(/g ), | ||
closeParensMatch = matchStr.match( /\)/g ), | ||
numOpenParens = ( openParensMatch && openParensMatch.length ) || 0, | ||
numCloseParens = ( closeParensMatch && closeParensMatch.length ) || 0; | ||
if( numOpenParens < numCloseParens ) { | ||
matchStr = matchStr.substr( 0, matchStr.length - 1 ); // remove the trailing ")" | ||
suffixStr = ")"; // this will be added after the <a> tag | ||
} | ||
} | ||
var anchorHref = matchStr, // initialize both of these | ||
anchorText = matchStr, // values as the full match | ||
linkType; | ||
// Process the urls that are found. We need to change URLs like "www.yahoo.com" to "http://www.yahoo.com" (or the browser | ||
// will try to direct the user to "http://current-domain.com/www.yahoo.com"), and we need to prefix 'mailto:' to email addresses. | ||
if( twitterMatch ) { | ||
linkType = 'twitter'; | ||
prefixStr = twitterHandlePrefixWhitespaceChar; | ||
anchorHref = 'https://twitter.com/' + twitterHandle; | ||
anchorText = '@' + twitterHandle; | ||
} else if( emailAddress ) { | ||
linkType = 'email'; | ||
anchorHref = 'mailto:' + emailAddress; | ||
anchorText = emailAddress; | ||
} else { // url match | ||
linkType = 'url'; | ||
if( protocolRelativeMatch ) { | ||
// Strip off any protocol-relative '//' from the anchor text (leaving the previous non-word character | ||
// intact, if there is one) | ||
var protocolRelRegex = new RegExp( "^" + me.protocolRelativeRegex.source ), // for this one, we want to only match at the beginning of the string | ||
charBeforeMatch = protocolRelativeMatch.match( protocolRelRegex )[ 1 ] || ""; | ||
prefixStr = charBeforeMatch + prefixStr; // re-add the character before the '//' to what will be placed before the <a> tag | ||
anchorHref = anchorHref.replace( protocolRelRegex, "//" ); // remove the char before the match for the href | ||
anchorText = anchorText.replace( protocolRelRegex, "" ); // remove both the char before the match and the '//' for the anchor text | ||
} else if( !/^[A-Za-z]{3,9}:/i.test( anchorHref ) ) { | ||
// url string doesn't begin with a protocol, assume http:// | ||
anchorHref = 'http://' + anchorHref; | ||
} | ||
} | ||
// wrap the match in an anchor tag | ||
var anchorTag = me.createAnchorTag( linkType, anchorHref, anchorText ); | ||
return prefixStr + anchorTag + suffixStr; | ||
} ); | ||
}, | ||
/** | ||
* Generates the actual anchor (<a>) tag to use in place of a source url/email/twitter link. | ||
* | ||
* @private | ||
* @param {"url"/"email"/"twitter"} linkType The type of link that an anchor tag is being generated for. | ||
* @param {String} anchorHref The href for the anchor tag. | ||
* @param {String} anchorText The anchor tag's text (i.e. what will be displayed). | ||
* @return {String} The full HTML for the anchor tag. | ||
*/ | ||
createAnchorTag : function( linkType, anchorHref, anchorText ) { | ||
var attributesStr = this.createAnchorAttrsStr( linkType, anchorHref ); | ||
anchorText = this.processAnchorText( anchorText ); | ||
return '<a ' + attributesStr + '>' + anchorText + '</a>'; | ||
}, | ||
/** | ||
* Creates the string which will be the HTML attributes for the anchor (<a>) tag being generated. | ||
* | ||
* @private | ||
* @param {"url"/"email"/"twitter"} linkType The type of link that an anchor tag is being generated for. | ||
* @param {String} href The href for the anchor tag. | ||
* @return {String} The anchor tag's attribute. Ex: `href="http://google.com" class="myLink myLink-url" target="_blank"` | ||
*/ | ||
createAnchorAttrsStr : function( linkType, anchorHref ) { | ||
var attrs = [ 'href="' + anchorHref + '"' ]; // we'll always have the `href` attribute | ||
var cssClass = this.createCssClass( linkType ); | ||
if( cssClass ) { | ||
attrs.push( 'class="' + cssClass + '"' ); | ||
} | ||
if( this.newWindow ) { | ||
attrs.push( 'target="_blank"' ); | ||
} | ||
return attrs.join( " " ); | ||
}, | ||
/** | ||
* Creates the CSS class that will be used for a given anchor tag, based on the `linkType` and the {@link #className} | ||
* config. | ||
* | ||
* @private | ||
* @param {"url"/"email"/"twitter"} linkType The type of link that an anchor tag is being generated for. | ||
* @return {String} The CSS class string for the link. Example return: "myLink myLink-url". If no {@link #className} | ||
* was configured, returns an empty string. | ||
*/ | ||
createCssClass : function( linkType ) { | ||
var className = this.className; | ||
if( !className ) | ||
return ""; | ||
else | ||
return className + " " + className + "-" + linkType; // ex: "myLink myLink-url", "myLink myLink-email", or "myLink myLink-twitter" | ||
}, | ||
/** | ||
* Processes the `anchorText` by stripping the URL prefix (if {@link #stripPrefix} is `true`), removing | ||
* any trailing slash, and truncating the text according to the {@link #truncate} config. | ||
* | ||
* @private | ||
* @param {String} anchorText The anchor tag's text (i.e. what will be displayed). | ||
* @return {String} The processed `anchorText`. | ||
*/ | ||
processAnchorText : function( anchorText ) { | ||
if( this.stripPrefix ) { | ||
anchorText = this.stripUrlPrefix( anchorText ); | ||
} | ||
anchorText = this.removeTrailingSlash( anchorText ); // remove trailing slash, if there is one | ||
anchorText = this.doTruncate( anchorText ); | ||
return anchorText; | ||
}, | ||
/** | ||
* Strips the URL prefix (such as "http://" or "https://") from the given text. | ||
* | ||
* @private | ||
* @param {String} text The text of the anchor that is being generated, for which to strip off the | ||
* url prefix (such as stripping off "http://") | ||
* @return {String} The `anchorText`, with the prefix stripped. | ||
*/ | ||
stripUrlPrefix : function( text ) { | ||
return text.replace( this.urlPrefixRegex, '' ); | ||
}, | ||
/** | ||
* Removes any trailing slash from the given `anchorText`, in prepration for the text to be displayed. | ||
* | ||
* @private | ||
* @param {String} anchorText The text of the anchor that is being generated, for which to remove any trailing | ||
* slash ('/') that may exist. | ||
* @return {String} The `anchorText`, with the trailing slash removed. | ||
*/ | ||
removeTrailingSlash : function( anchorText ) { | ||
if( anchorText.charAt( anchorText.length - 1 ) === '/' ) { | ||
anchorText = anchorText.slice( 0, -1 ); | ||
} | ||
return anchorText; | ||
}, | ||
/** | ||
* Performs the truncation of the `anchorText`, if the `anchorText` is longer than the {@link #truncate} option. | ||
* Truncates the text to 2 characters fewer than the {@link #truncate} option, and adds ".." to the end. | ||
* | ||
* @private | ||
* @param {String} text The anchor tag's text (i.e. what will be displayed). | ||
* @return {String} The truncated anchor text. | ||
*/ | ||
doTruncate : function( anchorText ) { | ||
var truncateLen = this.truncate; | ||
// Truncate the anchor text if it is longer than the provided 'truncate' option | ||
if( truncateLen && anchorText.length > truncateLen ) { | ||
anchorText = anchorText.substring( 0, truncateLen - 2 ) + '..'; | ||
} | ||
return anchorText; | ||
} | ||
}; | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* | ||
* Example: | ||
* | ||
* var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } ); | ||
* // Produces: "Go to <a href="http://google.com">google.com</a>" | ||
* | ||
* @static | ||
* @method link | ||
* @param {String} html The HTML text to link URLs within. | ||
* @param {Object} [options] Any of the configuration options for the Autolinker class, specified in an Object (map). | ||
* See the class description for an example call. | ||
* @return {String} The HTML text, with URLs automatically linked | ||
*/ | ||
Autolinker.link = function( text, options ) { | ||
var autolinker = new Autolinker( options ); | ||
return autolinker.link( text ); | ||
}; | ||
return Autolinker; | ||
})); | ||
} ) ); |
/*! | ||
* Autolinker.js | ||
* 0.7.0 | ||
* 0.11.0 | ||
* | ||
@@ -10,2 +10,2 @@ * Copyright(c) 2014 Gregory Jacobs <greg@greg-jacobs.com> | ||
*/ | ||
!function(a,b){"function"==typeof define&&define.amd?define(b):"undefined"!=typeof exports?module.exports=b():a.Autolinker=b()}(this,function(){var a={htmlRegex:/<(\/)?(\w+)(?:(?:\s+\w+(?:\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g,prefixRegex:/^(https?:\/\/)?(www\.)?/,link:function(b,c){function d(b){return b=b.replace(h,function(b,c,d,e,f,g){var h=c,o=d,p=e,q=f,r=g,s="",t="",u=[],v=b.charAt(b.length-1);if(")"===v){var w=b.match(/\(/g),x=b.match(/\)/g),y=w&&w.length||0,z=x&&x.length||0;z>y&&(b=b.substr(0,b.length-1),t=")")}var A=b,B=b;return h&&!l||q&&!m||r&&!n?s+B+t:(h?(s=o,A="https://twitter.com/"+p,B="@"+p):q?(A="mailto:"+q,B=q):/^[A-Za-z]{3,9}:/i.test(A)||(A="http://"+A),j&&(B=B.replace(a.prefixRegex,"")),"/"===B.charAt(B.length-1)&&(B=B.slice(0,-1)),u.push('href="'+A+'"'),i&&u.push('target="_blank"'),k&&B.length>k&&(B=B.substring(0,k-2)+".."),s+"<a "+u.join(" ")+">"+B+"</a>"+t)})}c=c||{};for(var e,f,g=a.htmlRegex,h=a.matcherRegex,i=("newWindow"in c?c.newWindow:!0),j=("stripPrefix"in c?c.stripPrefix:!0),k=c.truncate,l=("twitter"in c?c.twitter:!0),m=("email"in c?c.email:!0),n=("urls"in c?c.urls:!0),o=0,p="",q=0;null!==(e=g.exec(b));){var r=e[0],s=e[2],t=!!e[1];f=b.substring(o,e.index),o=e.index+r.length,"a"===s?t?(q--,0===q&&(p+=f)):(q++,p+=d(f)):0===q&&(p+=d(f)),p+=r}return o<b.length&&(p+=d(b.substring(o))),p},matcherRegex:function(){var a=/(^|\s)@(\w{1,15})/,b=/(?:[\-;:&=\+\$,\w\.]+@)/,c=/(?:[A-Za-z]{3,9}:(?:\/\/)?)/,d=/(?:www\.)/,e=/[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/,f=/\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/,g=/(?:[-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#\/%=~_()|])?/;return new RegExp(["(",a.source,")","|","(",b.source,e.source,f.source,")","|","(","(?:","(?:",c.source,e.source,")","|","(?:",d.source,e.source,")","|","(?:",e.source,f.source,")",")",g.source,")"].join(""),"g")}()};return a}); | ||
!function(a,b){"function"==typeof define&&define.amd?define(b):"undefined"!=typeof exports?module.exports=b():a.Autolinker=b()}(this,function(){var a=function(a){a=a||{};for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b])};return a.prototype={constructor:a,newWindow:!0,stripPrefix:!0,twitter:!0,email:!0,urls:!0,className:"",matcherRegex:function(){var a=/(^|[^\w])@(\w{1,15})/,b=/(?:[\-;:&=\+\$,\w\.]+@)/,c=/(?:[A-Za-z]{3,9}:(?:\/\/)?)/,d=/(?:www\.)/,e=/[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/,f=/\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/,g=/(?:[\-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|])?/;return new RegExp(["(",a.source,")","|","(",b.source,e.source,f.source,")","|","(","(?:","(?:",c.source,e.source,")","|","(?:","(.?//)?",d.source,e.source,")","|","(?:","(.?//)?",e.source,f.source,")",")",g.source,")"].join(""),"gi")}(),protocolRelativeRegex:/(.)?\/\//,htmlRegex:function(){var a=/[0-9a-zA-Z:]+/,b=/[^\s\0"'>\/=\x01-\x1F\x7F]+/,c=/(?:".*?"|'.*?'|[^'"=<>`\s]+)/;return new RegExp(["<(/)?","("+a.source+")","(?:","\\s+",b.source,"(?:\\s*=\\s*"+c.source+")?",")*","\\s*",">"].join(""),"g")}(),urlPrefixRegex:/^(https?:\/\/)?(www\.)?/i,link:function(a){return this.processHtml(a)},processHtml:function(a){for(var b,c,d=this.htmlRegex,e=0,f=0,g=[];null!==(b=d.exec(a));){var h=b[0],i=b[2],j=!!b[1];c=a.substring(e,b.index),e=b.index+h.length,"a"===i?j?(f=Math.max(f-1,0),0===f&&g.push(c)):(f++,g.push(this.processTextNode(c))):g.push(0===f?this.processTextNode(c):c),g.push(h)}if(e<a.length){var k=this.processTextNode(a.substring(e));g.push(k)}return g.join("")},processTextNode:function(a){var b=this,c=this.matcherRegex,d=this.twitter,e=this.email,f=this.urls;return a.replace(c,function(a,c,g,h,i,j,k,l){var m=c,n=g,o=h,p=i,q=j,r=k||l,s="",t="";if(m&&!d||p&&!e||q&&!f||q&&-1===q.indexOf(".")||q&&/^[A-Za-z]{3,9}:/.test(q)&&!/:.*?[A-Za-z]/.test(q)||r&&/^[\w]\/\//.test(r))return a;var u=a.charAt(a.length-1);if(")"===u){var v=a.match(/\(/g),w=a.match(/\)/g),x=v&&v.length||0,y=w&&w.length||0;y>x&&(a=a.substr(0,a.length-1),t=")")}var z,A=a,B=a;if(m)z="twitter",s=n,A="https://twitter.com/"+o,B="@"+o;else if(p)z="email",A="mailto:"+p,B=p;else if(z="url",r){var C=new RegExp("^"+b.protocolRelativeRegex.source),D=r.match(C)[1]||"";s=D+s,A=A.replace(C,"//"),B=B.replace(C,"")}else/^[A-Za-z]{3,9}:/i.test(A)||(A="http://"+A);var E=b.createAnchorTag(z,A,B);return s+E+t})},createAnchorTag:function(a,b,c){var d=this.createAnchorAttrsStr(a,b);return c=this.processAnchorText(c),"<a "+d+">"+c+"</a>"},createAnchorAttrsStr:function(a,b){var c=['href="'+b+'"'],d=this.createCssClass(a);return d&&c.push('class="'+d+'"'),this.newWindow&&c.push('target="_blank"'),c.join(" ")},createCssClass:function(a){var b=this.className;return b?b+" "+b+"-"+a:""},processAnchorText:function(a){return this.stripPrefix&&(a=this.stripUrlPrefix(a)),a=this.removeTrailingSlash(a),a=this.doTruncate(a)},stripUrlPrefix:function(a){return a.replace(this.urlPrefixRegex,"")},removeTrailingSlash:function(a){return"/"===a.charAt(a.length-1)&&(a=a.slice(0,-1)),a},doTruncate:function(a){var b=this.truncate;return b&&a.length>b&&(a=a.substring(0,b-2)+".."),a}},a.link=function(b,c){var d=new a(c);return d.link(b)},a}); |
@@ -6,3 +6,3 @@ module.exports = function(grunt) { | ||
'/*!', | ||
' * <%= pkg.name %>', | ||
' * Autolinker.js', | ||
' * <%= pkg.version %>', | ||
@@ -52,2 +52,7 @@ ' *', | ||
} | ||
}, | ||
jshint: { | ||
files: { | ||
src: ['src/**/*.js', 'tests/**/*.js'] | ||
} | ||
} | ||
@@ -61,5 +66,7 @@ }); | ||
grunt.loadNpmTasks('grunt-contrib-uglify'); | ||
grunt.loadNpmTasks('grunt-contrib-jshint'); | ||
// Tasks | ||
grunt.registerTask('default', ['test', 'build']); | ||
grunt.registerTask('default', ['lint', 'test', 'build']); | ||
grunt.registerTask('lint', ['jshint']); | ||
grunt.registerTask('test', ['jasmine']); | ||
@@ -66,0 +73,0 @@ grunt.registerTask('build', ['concat:development', 'uglify:production']); |
{ | ||
"name": "autolinker", | ||
"version": "0.7.2", | ||
"version": "0.11.0", | ||
"description": "Simple utility to automatically link the URLs, email addresses, and Twitter handles in a given block of text/HTML", | ||
@@ -36,4 +36,5 @@ "main": "dist/Autolinker.js", | ||
"grunt-contrib-uglify": "^0.4.0", | ||
"grunt-contrib-concat": "^0.4.0" | ||
"grunt-contrib-concat": "^0.4.0", | ||
"grunt-contrib-jshint": "^0.10.0" | ||
} | ||
} |
143
README.md
@@ -24,2 +24,4 @@ # Autolinker.js | ||
#### Download | ||
Simply clone or download the zip of the project, and link to either `dist/Autolinker.js` or `dist/Autolinker.min.js` with a script tag: | ||
@@ -31,4 +33,6 @@ | ||
Can also download via [Bower](http://bower.io): | ||
#### Using with the [Bower](http://bower.io) package manager: | ||
Command line: | ||
```shell | ||
@@ -38,29 +42,68 @@ bower install Autolinker.js --save | ||
#### Using with [Node.js](http://nodejs.org) via [npm](https://www.npmjs.org/): | ||
Command Line: | ||
```shell | ||
npm install autolinker --save | ||
``` | ||
JavaScript: | ||
```javascript | ||
var Autolinker = require( 'autolinker' ); | ||
// note: npm wants an all-lowercase package name, but the utility is a class and should be | ||
// aliased with a captial letter | ||
``` | ||
## Usage | ||
Using the static `link()` method: | ||
```javascript | ||
var linkedText = Autolinker.link( textToAutolink[, options] ); | ||
``` | ||
Example: | ||
Using as a class: | ||
```javascript | ||
var linkedText = Autolinker.link( "Check out google.com" ); | ||
// Produces: "Check out <a href="http://google.com" target="_blank">google.com</a>" | ||
var autolinker = new Autolinker( [ options ] ); | ||
var linkedText = autolinker.link( textToAutoLink ); | ||
``` | ||
### Options | ||
There are options which may be specified for the linking. These are specified by providing an Object as the second parameter to `Autolinker.link()`. Options include: | ||
#### Example: | ||
```javascript | ||
var linkedText = Autolinker.link( "Check out google.com", { className: "myLink" } ); | ||
// Produces: "Check out <a class="myLink myLink-url" href="http://google.com" target="_blank">google.com</a>" | ||
``` | ||
## Options | ||
These are the options which may be specified for linking. These are specified by providing an Object as the second parameter to `Autolinker.link()`. These include: | ||
- **newWindow** : Boolean<br /> | ||
`true` to have the links should open in a new window when clicked, `false` otherwise. Defaults to `true`. | ||
`true` to have the links should open in a new window when clicked, `false` otherwise. Defaults to `true`.<br /><br /> | ||
- **stripPrefix** : Boolean<br /> | ||
`true` to have the 'http://' or 'https://' and/or the 'www.' stripped from the beginning of links, `false` otherwise. Defaults to `true`. | ||
`true` to have the 'http://' or 'https://' and/or the 'www.' stripped from the beginning of links, `false` otherwise. Defaults to `true`.<br /><br /> | ||
- **truncate** : Number<br /> | ||
A number for how many characters long URLs/emails/twitter handles should be truncated to inside the text of a link. If the URL/email/twitter is over the number of characters, it will be truncated to this length by replacing the end of the string with a two period ellipsis ('..'). | ||
Ex: a url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters may look like this: 'yahoo.com/some/long/pat..' | ||
A number for how many characters long URLs/emails/twitter handles should be truncated to inside the text of a link. If the URL/email/twitter is over the number of characters, it will be truncated to this length by replacing the end of the string with a two period ellipsis ('..').<br /><br /> | ||
Example: a url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters may look like this: 'yahoo.com/some/long/pat..'<br /> | ||
- **className** : String<br /> | ||
A CSS class name to add to the generated anchor tags. This class will be added to all links, as well as this class | ||
plus "url"/"email"/"twitter" suffixes for styling url/email/twitter links differently. | ||
For example, if this config is provided as "myLink", then: | ||
1) URL links will have the CSS classes: "myLink myLink-url"<br /> | ||
2) Email links will have the CSS classes: "myLink myLink-email", and<br /> | ||
3) Twitter links will have the CSS classes: "myLink myLink-twitter"<br /> | ||
- **urls** : Boolean<br /> | ||
`true` to have URLs auto-linked, `false` to skip auto-linking of URLs. Defaults to `true`. | ||
`true` to have URLs auto-linked, `false` to skip auto-linking of URLs. Defaults to `true`.<br /> | ||
- **email** : Boolean<br /> | ||
`true` to have email addresses auto-linked, `false` to skip auto-linking of email addresses. Defaults to `true`. | ||
`true` to have email addresses auto-linked, `false` to skip auto-linking of email addresses. Defaults to `true`.<br /><br /> | ||
- **twitter** : Boolean<br /> | ||
@@ -83,3 +126,3 @@ `true` to have Twitter handles auto-linked, `false` to skip auto-linking of Twitter handles. Defaults to `true`. | ||
### More Examples | ||
## More Examples | ||
One could update an entire DOM element that has unlinked text to auto-link them as such: | ||
@@ -92,4 +135,74 @@ | ||
Using the same pre-configured Autolinker instance in multiple locations of a codebase (usually by dependency injection): | ||
```javascript | ||
var autolinker = new Autolinker( { newWindow: false, truncate: 25 } ); | ||
//... | ||
autolinker.link( "Check out http://www.yahoo.com/some/long/path/to/a/file" ); | ||
// Produces: "Check out <a href="http://www.yahoo.com/some/long/path/to/a/file">yahoo.com/some/long/pat..</a>" | ||
//... | ||
autolinker.link( "Go to www.google.com" ); | ||
// Produces: "Go to <a href="http://www.google.com">google.com</a>" | ||
``` | ||
## Changelog: | ||
### 0.11.0 | ||
- Allow Autolinker to link fully-capitalized URLs/Emails/Twitter handles. | ||
### 0.10.1 | ||
- Added fix to not autolink strings like "version:1.0", which were accidentally being interpreted as a protocol:domain string. | ||
### 0.10.0 | ||
- Added support for protocol-relative URLs (ex: `//google.com`, which will effectively either have the `http://` or `https://` | ||
protocol depending on the protocol that is hosting the website) | ||
### 0.9.4 | ||
- Fixed an issue where a string in the form of `abc:def` would be autolinked as a protocol and domain name URL. Autolinker now | ||
requires the domain name to have at least one period in it to be considered. | ||
### 0.9.3 | ||
- Fixed an issue where Twitter handles wouldn't be autolinked if they existed as the sole entity within parenthesis or brackets | ||
(thanks [@busticated](https://github.com/busticated) for pointing this out and providing unit tests) | ||
### 0.9.2 | ||
- Fixed an issue with nested tags within an existing <a> tag, where the nested tags' inner text would be accidentally | ||
removed from the output (thanks [@mjsabin01](https://github.com/mjsabin01)) | ||
### 0.9.1 | ||
- Added a patch to attempt to better handle extraneous </a> tags in the input string if any exist. This is for when the | ||
input may have some invalid markup (for instance, on sites which allow user comments, blog posts, etc.). | ||
### 0.9.0 | ||
- Added better support for the processing of existing HTML in the input string. Now handles namespaced tags, and attribute names | ||
with dashes or any other Unicode character (thanks [@aziraphale](https://github.com/aziraphale)) | ||
### 0.8.0 | ||
- Added `className` option for easily styling produced links (thanks [@busticated](https://github.com/busticated)) | ||
- Refactored into a JS class. Autolinker can now be instantiated using: | ||
```javascript | ||
var autolinker = new Autolinker( { newWindow: false, truncate: 25 } ); | ||
autolinker.link( "Check out http://www.yahoo.com/some/long/path/to/a/file" ); | ||
// Produces: "Check out <a href="http://www.yahoo.com/some/long/path/to/a/file">yahoo.com/some/long/pat..</a>" | ||
``` | ||
This allows options to be set on a single instance, and used throughout a codebase by injecting the `autolinker` instance as a dependency to the modules/classes that use it. (Note: Autolinker may still be used with the static `Autolinker.link()` method as was previously available as well.) | ||
### 0.7.0 | ||
@@ -103,3 +216,3 @@ | ||
(Thanks to [busticated](https://github.com/busticated)!) | ||
(Thanks to [@busticated](https://github.com/busticated)!) | ||
@@ -106,0 +219,0 @@ ### 0.6.1 |
@@ -1,204 +0,120 @@ | ||
/** | ||
* @class Autolinker | ||
* @extends Object | ||
* @singleton | ||
* | ||
* Singleton class which exposes the {@link #link} method, used to process a given string of text, | ||
* and wrap the URLs, email addresses, and Twitter handles in the appropriate anchor (<a>) tags. | ||
*/ | ||
/*global define, module */ | ||
/*jshint undef:true, smarttabs:true */ | ||
// Set up Autolinker appropriately for the environment. | ||
(function(root, factory) { | ||
// Start with AMD. | ||
if (typeof define === 'function' && define.amd) { | ||
define(factory); | ||
// Next for Node.js or CommonJS. | ||
} else if (typeof exports !== 'undefined') { | ||
module.exports = factory(); | ||
// Finally, as a browser global. | ||
( function( root, factory ) { | ||
if( typeof define === 'function' && define.amd ) { | ||
define( factory ); // Define as AMD module if an AMD loader is present (ex: RequireJS). | ||
} else if( typeof exports !== 'undefined' ) { | ||
module.exports = factory(); // Define as CommonJS module for Node.js, if available. | ||
} else { | ||
root.Autolinker = factory(); | ||
root.Autolinker = factory(); // Finally, define as a browser global if no module loader. | ||
} | ||
}(this, function(root) { | ||
var Autolinker = { | ||
}( this, function() { | ||
/** | ||
* @class Autolinker | ||
* @extends Object | ||
* | ||
* Utility class used to process a given string of text, and wrap the URLs, email addresses, and Twitter handles in | ||
* the appropriate anchor (<a>) tags to turn them into links. | ||
* | ||
* Any of the configuration options may be provided in an Object (map) provided to the Autolinker constructor, which | ||
* will configure how the {@link #link link()} method will process the links. | ||
* | ||
* For example: | ||
* | ||
* var autolinker = new Autolinker( { | ||
* newWindow : false, | ||
* truncate : 30 | ||
* } ); | ||
* | ||
* var html = autolinker.link( "Joe went to www.yahoo.com" ); | ||
* // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' | ||
* | ||
* | ||
* The {@link #static-link static link()} method may also be used to inline options into a single call, which may | ||
* be more convenient for one-off uses. For example: | ||
* | ||
* var html = Autolinker.link( "Joe went to www.yahoo.com", { | ||
* newWindow : false, | ||
* truncate : 30 | ||
* } ); | ||
* // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' | ||
* | ||
* @constructor | ||
* @param {Object} [config] The configuration options for the Autolinker instance, specified in an Object (map). | ||
*/ | ||
var Autolinker = function( cfg ) { | ||
cfg = cfg || {}; | ||
// Assign the properties of `cfg` onto the Autolinker instance | ||
for( var prop in cfg ) | ||
if( cfg.hasOwnProperty( prop ) ) this[ prop ] = cfg[ prop ]; | ||
}; | ||
Autolinker.prototype = { | ||
constructor : Autolinker, // fix constructor property | ||
/** | ||
* @private | ||
* @property {RegExp} htmlRegex | ||
* @cfg {Boolean} newWindow | ||
* | ||
* A regular expression used to pull out HTML tags from a string. | ||
* `true` if the links should open in a new window, `false` otherwise. | ||
*/ | ||
newWindow : true, | ||
/** | ||
* @cfg {Boolean} stripPrefix | ||
* | ||
* Capturing groups: | ||
* `true` if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of links, `false` otherwise. | ||
*/ | ||
stripPrefix : true, | ||
/** | ||
* @cfg {Number} truncate | ||
* | ||
* 1. If it is an end tag, this group will have the '/'. | ||
* 2. The tag name. | ||
* A number for how many characters long URLs/emails/twitter handles should be truncated to inside the text of | ||
* a link. If the URL/email/twitter is over this number of characters, it will be truncated to this length by | ||
* adding a two period ellipsis ('..') into the middle of the string. | ||
* | ||
* For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters might look | ||
* something like this: 'http://www...th/to/a/file' | ||
*/ | ||
htmlRegex : /<(\/)?(\w+)(?:(?:\s+\w+(?:\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g, | ||
/** | ||
* @private | ||
* @property {RegExp} prefixRegex | ||
* @cfg {Boolean} twitter | ||
* | ||
* A regular expression used to remove the 'http://' or 'https://' and/or the 'www.' from URLs. | ||
* `true` if Twitter handles ("@example") should be automatically linked, `false` if they should not be. | ||
*/ | ||
prefixRegex: /^(https?:\/\/)?(www\.)?/, | ||
twitter : true, | ||
/** | ||
* @cfg {Boolean} email | ||
* | ||
* `true` if email addresses should be automatically linked, `false` if they should not be. | ||
*/ | ||
email : true, | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* @cfg {Boolean} urls | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* `true` if miscellaneous URLs should be automatically linked, `false` if they should not be. | ||
*/ | ||
urls : true, | ||
/** | ||
* @cfg {String} className | ||
* | ||
* @method link | ||
* @param {String} html The HTML text to link URLs within. | ||
* @param {Object} [options] Any options for the autolinking, specified in an object. It may have the following properties: | ||
* @param {Boolean} [options.newWindow=true] True if the links should open in a new window, false otherwise. | ||
* @param {Boolean} [options.stripPrefix=true] True if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of links, false otherwise. | ||
* @param {Number} [options.truncate] A number for how many characters long URLs/emails/twitter handles should be truncated to | ||
* inside the text of a link. If the URL/email/twitter is over the number of characters, it will be truncated to this length by | ||
* adding a two period ellipsis ('..') into the middle of the string. | ||
* Ex: a url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters might look like this: 'http://www...th/to/a/file' | ||
* @param {Boolean} [options.twitter=true] True if Twitter handles ("@example") should be automatically linked. | ||
* @param {Boolean} [options.email=true] True if email addresses should be automatically linked. | ||
* @param {Boolean} [options.urls=true] True if miscellaneous URLs should be automatically linked. | ||
* @return {String} The HTML text, with URLs automatically linked | ||
* A CSS class name to add to the generated links. This class will be added to all links, as well as this class | ||
* plus url/email/twitter suffixes for styling url/email/twitter links differently. | ||
* | ||
* For example, if this config is provided as "myLink", then: | ||
* | ||
* 1) URL links will have the CSS classes: "myLink myLink-url" | ||
* 2) Email links will have the CSS classes: "myLink myLink-email", and | ||
* 3) Twitter links will have the CSS classes: "myLink myLink-twitter" | ||
*/ | ||
link : function( html, options ) { | ||
options = options || {}; | ||
var htmlRegex = Autolinker.htmlRegex, // full path for friendly | ||
matcherRegex = Autolinker.matcherRegex, // out-of-scope calls | ||
newWindow = ( 'newWindow' in options ) ? options.newWindow : true, // defaults to true | ||
stripPrefix = ( 'stripPrefix' in options ) ? options.stripPrefix : true, // defaults to true | ||
truncate = options.truncate, | ||
enableTwitter = ( 'twitter' in options ) ? options.twitter : true, // defaults to true | ||
enableEmailAddresses = ( 'email' in options ) ? options.email : true, // defaults to true | ||
enableUrls = ( 'urls' in options ) ? options.urls : true, // defaults to true | ||
currentResult, | ||
lastIndex = 0, | ||
inBetweenTagsText, | ||
resultHtml = "", | ||
anchorTagStackCount = 0; | ||
// Function to process the text that lies between HTML tags. This function does the actual wrapping of | ||
// URLs with anchor tags. | ||
function autolinkText( text ) { | ||
text = text.replace( matcherRegex, function( matchStr, $1, $2, $3, $4, $5 ) { | ||
var twitterMatch = $1, | ||
twitterHandlePrefixWhitespaceChar = $2, // The whitespace char before the @ sign in a Twitter handle match. This is needed because of no lookbehinds in JS regexes | ||
twitterHandle = $3, // The actual twitterUser (i.e the word after the @ sign in a Twitter handle match) | ||
emailAddress = $4, // For both determining if it is an email address, and stores the actual email address | ||
urlMatch = $5, // The matched URL string | ||
prefixStr = "", // A string to use to prefix the anchor tag that is created. This is needed for the Twitter handle match | ||
suffixStr = "", // A string to suffix the anchor tag that is created. This is used if there is a trailing parenthesis that should not be auto-linked. | ||
anchorAttributes = []; | ||
// Handle a closing parenthesis at the end of the match, and exclude it if there is not a matching open parenthesis | ||
// in the match. This handles cases like the string "wikipedia.com/something_(disambiguation)" (which should be auto- | ||
// linked, and when it is enclosed in parenthesis itself, such as: "(wikipedia.com/something_(disambiguation))" (in | ||
// which the outer parens should *not* be auto-linked. | ||
var lastChar = matchStr.charAt( matchStr.length - 1 ); | ||
if( lastChar === ')' ) { | ||
var openParensMatch = matchStr.match( /\(/g ), | ||
closeParensMatch = matchStr.match( /\)/g ), | ||
numOpenParens = ( openParensMatch && openParensMatch.length ) || 0, | ||
numCloseParens = ( closeParensMatch && closeParensMatch.length ) || 0; | ||
if( numOpenParens < numCloseParens ) { | ||
matchStr = matchStr.substr( 0, matchStr.length - 1 ); // remove the trailing ")" | ||
suffixStr = ")"; // this will be added after the <a> tag | ||
} | ||
} | ||
var anchorHref = matchStr, // initialize both of these | ||
anchorText = matchStr; // values as the full match | ||
if( ( twitterMatch && !enableTwitter ) || ( emailAddress && !enableEmailAddresses ) || ( urlMatch && !enableUrls ) ) { | ||
// A disabled link type | ||
return prefixStr + anchorText + suffixStr; | ||
} | ||
// Process the urls that are found. We need to change URLs like "www.yahoo.com" to "http://www.yahoo.com" (or the browser | ||
// will try to direct the user to "http://jux.com/www.yahoo.com"), and we need to prefix 'mailto:' to email addresses. | ||
if( twitterMatch ) { | ||
prefixStr = twitterHandlePrefixWhitespaceChar; | ||
anchorHref = 'https://twitter.com/' + twitterHandle; | ||
anchorText = '@' + twitterHandle; | ||
} else if( emailAddress ) { | ||
anchorHref = 'mailto:' + emailAddress; | ||
anchorText = emailAddress; | ||
} else if( !/^[A-Za-z]{3,9}:/i.test( anchorHref ) ) { // string doesn't begin with a protocol, add http:// | ||
anchorHref = 'http://' + anchorHref; | ||
} | ||
if( stripPrefix ) { | ||
anchorText = anchorText.replace( Autolinker.prefixRegex, '' ); | ||
} | ||
// remove trailing slash | ||
if( anchorText.charAt( anchorText.length - 1 ) === '/' ) { | ||
anchorText = anchorText.slice( 0, -1 ); | ||
} | ||
// Set the attributes for the anchor tag | ||
anchorAttributes.push( 'href="' + anchorHref + '"' ); | ||
if( newWindow ) { | ||
anchorAttributes.push( 'target="_blank"' ); | ||
} | ||
// Truncate the anchor text if it is longer than the provided 'truncate' option | ||
if( truncate && anchorText.length > truncate ) { | ||
anchorText = anchorText.substring( 0, truncate - 2 ) + '..'; | ||
} | ||
return prefixStr + '<a ' + anchorAttributes.join( " " ) + '>' + anchorText + '</a>' + suffixStr; // wrap the match in an anchor tag | ||
} ); | ||
return text; | ||
} | ||
// Loop over the HTML string, ignoring HTML tags, and processing the text that lies between them, | ||
// wrapping the URLs in anchor tags | ||
while( ( currentResult = htmlRegex.exec( html ) ) !== null ) { | ||
var tagText = currentResult[ 0 ], | ||
tagName = currentResult[ 2 ], | ||
isClosingTag = !!currentResult[ 1 ]; | ||
inBetweenTagsText = html.substring( lastIndex, currentResult.index ); | ||
lastIndex = currentResult.index + tagText.length; | ||
// Process around anchor tags, and any inner text / html they may have | ||
if( tagName === 'a' ) { | ||
if( !isClosingTag ) { // it's the start <a> tag | ||
anchorTagStackCount++; | ||
resultHtml += autolinkText( inBetweenTagsText ); | ||
} else { // it's the end </a> tag | ||
anchorTagStackCount--; | ||
if( anchorTagStackCount === 0 ) { | ||
resultHtml += inBetweenTagsText; // We hit the matching </a> tag, simply add all of the text from the start <a> tag to the end </a> tag without linking it | ||
} | ||
} | ||
} else if( anchorTagStackCount === 0 ) { // not within an anchor tag, link the "in between" text | ||
resultHtml += autolinkText( inBetweenTagsText ); | ||
} | ||
resultHtml += tagText; // now add the text of the tag itself verbatim | ||
} | ||
// Process any remaining text after the last HTML element. Will process all of the text if there were no HTML elements. | ||
if( lastIndex < html.length ) { | ||
resultHtml += autolinkText( html.substring( lastIndex ) ); | ||
} | ||
return resultHtml; | ||
}, | ||
className : "", | ||
/** | ||
@@ -210,30 +126,34 @@ * @private | ||
* | ||
* Capturing groups: | ||
* This regular expression has the following capturing groups: | ||
* | ||
* 1. Group that is used to determine if there is a Twitter handle match (i.e. @someTwitterUser). Simply check for its existence | ||
* to determine if there is a Twitter handle match. The next couple of capturing groups give information about the Twitter | ||
* handle match. | ||
* 2. The whitespace character before the @sign in a Twitter handle. This is needed because there are no lookbehinds in JS regular | ||
* expressions, and can be used to reconstruct the original string in a replace(). | ||
* 1. Group that is used to determine if there is a Twitter handle match (i.e. @someTwitterUser). Simply check for its | ||
* existence to determine if there is a Twitter handle match. The next couple of capturing groups give information | ||
* about the Twitter handle match. | ||
* 2. The whitespace character before the @sign in a Twitter handle. This is needed because there are no lookbehinds in | ||
* JS regular expressions, and can be used to reconstruct the original string in a replace(). | ||
* 3. The Twitter handle itself in a Twitter match. If the match is '@someTwitterUser', the handle is 'someTwitterUser'. | ||
* 4. Group that matches an email address. Used to determine if the match is an email address, as well as holding the full address. | ||
* Ex: 'me@my.com' | ||
* 4. Group that matches an email address. Used to determine if the match is an email address, as well as holding the full | ||
* address. Ex: 'me@my.com' | ||
* 5. Group that matches a URL in the input text. Ex: 'http://google.com', 'www.google.com', or just 'google.com'. | ||
* This also includes a path, url parameters, or hash anchors. Ex: google.com/path/to/file?q1=1&q2=2#myAnchor | ||
* 6. A protocol-relative ('//') match for the case of a 'www.' prefixed URL. Will be an empty string if it is not a | ||
* protocol-relative match. We need to know the character before the '//' in order to determine if it is a valid match | ||
* or the // was in a string we don't want to auto-link. | ||
* 7. A protocol-relative ('//') match for the case of a known TLD prefixed URL. Will be an empty string if it is not a | ||
* protocol-relative match. See #6 for more info. | ||
*/ | ||
matcherRegex: (function() { | ||
var twitterRegex = /(^|\s)@(\w{1,15})/, // For matching a twitter handle. Ex: @gregory_jacobs | ||
emailRegex = /(?:[\-;:&=\+\$,\w\.]+@)/, // something@ for email addresses (a.k.a. local-part) | ||
protocolRegex = /(?:[A-Za-z]{3,9}:(?:\/\/)?)/, // match protocol, allow in format http:// or mailto: | ||
wwwRegex = /(?:www\.)/, // starting with 'www.' | ||
domainNameRegex = /[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/, // anything looking at all like a domain, non-unicode domains, not ending in a period | ||
tldRegex = /\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/, // match our known top level domains (TLDs) | ||
// Allow optional path, query string, and hash anchor, not ending in the following characters: "!:,.;" | ||
// http://blog.codinghorror.com/the-problem-with-urls/ | ||
urlSuffixRegex = /(?:[-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#\/%=~_()|])?/; // note: optional part of the full regex | ||
matcherRegex : (function() { | ||
var twitterRegex = /(^|[^\w])@(\w{1,15})/, // For matching a twitter handle. Ex: @gregory_jacobs | ||
emailRegex = /(?:[\-;:&=\+\$,\w\.]+@)/, // something@ for email addresses (a.k.a. local-part) | ||
protocolRegex = /(?:[A-Za-z]{3,9}:(?:\/\/)?)/, // match protocol, allow in format http:// or mailto: | ||
wwwRegex = /(?:www\.)/, // starting with 'www.' | ||
domainNameRegex = /[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/, // anything looking at all like a domain, non-unicode domains, not ending in a period | ||
tldRegex = /\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/, // match our known top level domains (TLDs) | ||
// Allow optional path, query string, and hash anchor, not ending in the following characters: "!:,.;" | ||
// http://blog.codinghorror.com/the-problem-with-urls/ | ||
urlSuffixRegex = /(?:[\-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|])?/; // note: optional part of the full regex | ||
return new RegExp( [ | ||
@@ -258,3 +178,3 @@ '(', // *** Capturing group $1, which can be used to check for a twitter handle match. Use group $3 for the actual twitter handle though. $2 may be used to reconstruct the original string in a replace() | ||
'(?:', // parens to cover match for protocol (optional), and domain | ||
'(?:', // non-capturing paren for a protocol-prefixed url (ex: http://google.com) | ||
'(?:', // non-capturing paren for a protocol-prefixed url (ex: http://google.com) | ||
protocolRegex.source, | ||
@@ -267,2 +187,3 @@ domainNameRegex.source, | ||
'(?:', // non-capturing paren for a 'www.' prefixed url (ex: www.google.com) | ||
'(.?//)?', // *** Capturing group $6 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character | ||
wwwRegex.source, | ||
@@ -275,2 +196,3 @@ domainNameRegex.source, | ||
'(?:', // non-capturing paren for known a TLD url (ex: google.com) | ||
'(.?//)?', // *** Capturing group $7 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character | ||
domainNameRegex.source, | ||
@@ -283,7 +205,399 @@ tldRegex.source, | ||
')' | ||
].join( "" ), 'gi' ); | ||
} )(), | ||
/** | ||
* @private | ||
* @property {RegExp} protocolRelativeRegex | ||
* | ||
* The regular expression used to find protocol-relative URLs. A protocol-relative URL is, for example, "//yahoo.com" | ||
* | ||
* This regular expression needs to match the character before the '//', in order to determine if we should actually | ||
* autolink a protocol-relative URL. For instance, we want to autolink something like "//google.com", but we | ||
* don't want to autolink something like "abc//google.com" | ||
*/ | ||
protocolRelativeRegex : /(.)?\/\//, | ||
/** | ||
* @private | ||
* @property {RegExp} htmlRegex | ||
* | ||
* The regular expression used to pull out HTML tags from a string. Handles namespaced HTML tags and | ||
* attribute names, as specified by http://www.w3.org/TR/html-markup/syntax.html. | ||
* | ||
* Capturing groups: | ||
* | ||
* 1. If it is an end tag, this group will have the '/'. | ||
* 2. The tag name. | ||
*/ | ||
htmlRegex : (function() { | ||
var tagNameRegex = /[0-9a-zA-Z:]+/, | ||
attrNameRegex = /[^\s\0"'>\/=\x01-\x1F\x7F]+/, // the unicode range accounts for excluding control chars, and the delete char | ||
attrValueRegex = /(?:".*?"|'.*?'|[^'"=<>`\s]+)/; // double quoted, single quoted, or unquoted attribute values | ||
return new RegExp( [ | ||
'<(/)?', // Beginning of a tag. Either '<' for a start tag, or '</' for an end tag. The slash or an empty string is Capturing Group 1. | ||
// The tag name (Capturing Group 2) | ||
'(' + tagNameRegex.source + ')', | ||
// Zero or more attributes following the tag name | ||
'(?:', | ||
'\\s+', // one or more whitespace chars before an attribute | ||
attrNameRegex.source, | ||
'(?:\\s*=\\s*' + attrValueRegex.source + ')?', // optional '=[value]' | ||
')*', | ||
'\\s*', // any trailing spaces before the closing '>' | ||
'>' | ||
].join( "" ), 'g' ); | ||
})() | ||
} )(), | ||
/** | ||
* @private | ||
* @property {RegExp} urlPrefixRegex | ||
* | ||
* A regular expression used to remove the 'http://' or 'https://' and/or the 'www.' from URLs. | ||
*/ | ||
urlPrefixRegex: /^(https?:\/\/)?(www\.)?/i, | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* | ||
* @method link | ||
* @param {String} textOrHtml The HTML or text to link URLs, email addresses, and Twitter handles within. | ||
* @return {String} The HTML, with URLs/emails/twitter handles automatically linked. | ||
*/ | ||
link : function( textOrHtml ) { | ||
return this.processHtml( textOrHtml ); | ||
}, | ||
/** | ||
* Processes the given HTML to auto-link URLs/emails/Twitter handles. | ||
* | ||
* Finds the text around any HTML elements in the input `html`, which will be the text that is processed. | ||
* Any original HTML elements will be left as-is, as well as the text that is already wrapped in anchor tags. | ||
* | ||
* @private | ||
* @method processHtml | ||
* @param {String} html The input text or HTML to process in order to auto-link. | ||
* @return {String} | ||
*/ | ||
processHtml : function( html ) { | ||
// Loop over the HTML string, ignoring HTML tags, and processing the text that lies between them, | ||
// wrapping the URLs in anchor tags | ||
var htmlRegex = this.htmlRegex, | ||
currentResult, | ||
inBetweenTagsText, | ||
lastIndex = 0, | ||
anchorTagStackCount = 0, | ||
resultHtml = []; | ||
while( ( currentResult = htmlRegex.exec( html ) ) !== null ) { | ||
var tagText = currentResult[ 0 ], | ||
tagName = currentResult[ 2 ], | ||
isClosingTag = !!currentResult[ 1 ]; | ||
inBetweenTagsText = html.substring( lastIndex, currentResult.index ); | ||
lastIndex = currentResult.index + tagText.length; | ||
// Process around anchor tags, and any inner text / html they may have | ||
if( tagName === 'a' ) { | ||
if( !isClosingTag ) { // it's the start <a> tag | ||
anchorTagStackCount++; | ||
resultHtml.push( this.processTextNode( inBetweenTagsText ) ); | ||
} else { // it's the end </a> tag | ||
anchorTagStackCount = Math.max( anchorTagStackCount - 1, 0 ); // attempt to handle extraneous </a> tags by making sure the stack count never goes below 0 | ||
if( anchorTagStackCount === 0 ) { | ||
resultHtml.push( inBetweenTagsText ); // We hit the matching </a> tag, simply add all of the text from the start <a> tag to the end </a> tag without linking it | ||
} | ||
} | ||
} else if( anchorTagStackCount === 0 ) { // not within an anchor tag, link the "in between" text | ||
resultHtml.push( this.processTextNode( inBetweenTagsText ) ); | ||
} else { | ||
// if we have a tag that is in between anchor tags (ex: <a href="..."><b>google.com</b></a>), | ||
// just append the inner text | ||
resultHtml.push( inBetweenTagsText ); | ||
} | ||
resultHtml.push( tagText ); // now add the text of the tag itself verbatim | ||
} | ||
// Process any remaining text after the last HTML element. Will process all of the text if there were no HTML elements. | ||
if( lastIndex < html.length ) { | ||
var processedTextNode = this.processTextNode( html.substring( lastIndex ) ); | ||
resultHtml.push( processedTextNode ); | ||
} | ||
return resultHtml.join( "" ); | ||
}, | ||
/** | ||
* Process the text that lies inbetween HTML tags. This method does the actual wrapping of URLs with | ||
* anchor tags. | ||
* | ||
* @private | ||
* @param {String} text The text to auto-link. | ||
* @return {String} The text with anchor tags auto-filled. | ||
*/ | ||
processTextNode : function( text ) { | ||
var me = this, // for closures | ||
matcherRegex = this.matcherRegex, | ||
enableTwitter = this.twitter, | ||
enableEmailAddresses = this.email, | ||
enableUrls = this.urls; | ||
return text.replace( matcherRegex, function( matchStr, $1, $2, $3, $4, $5, $6, $7 ) { | ||
var twitterMatch = $1, | ||
twitterHandlePrefixWhitespaceChar = $2, // The whitespace char before the @ sign in a Twitter handle match. This is needed because of no lookbehinds in JS regexes | ||
twitterHandle = $3, // The actual twitterUser (i.e the word after the @ sign in a Twitter handle match) | ||
emailAddress = $4, // For both determining if it is an email address, and stores the actual email address | ||
urlMatch = $5, // The matched URL string | ||
protocolRelativeMatch = $6 || $7, // The '//' for a protocol-relative match, with the character that comes before the '//' | ||
prefixStr = "", // A string to use to prefix the anchor tag that is created. This is needed for the Twitter handle match | ||
suffixStr = ""; // A string to suffix the anchor tag that is created. This is used if there is a trailing parenthesis that should not be auto-linked. | ||
// Early exits with no replacements for: | ||
// 1) Disabled link types | ||
// 2) URL matches which do not have at least have one period ('.') in the domain name (effectively skipping over | ||
// matches like "abc:def") | ||
// 3) A protocol-relative url match (a URL beginning with '//') whose previous character is a word character | ||
// (effectively skipping over strings like "abc//google.com") | ||
if( | ||
( twitterMatch && !enableTwitter ) || ( emailAddress && !enableEmailAddresses ) || ( urlMatch && !enableUrls ) || | ||
( urlMatch && urlMatch.indexOf( '.' ) === -1 ) || // At least one period ('.') must exist in the URL match for us to consider it an actual URL | ||
( urlMatch && /^[A-Za-z]{3,9}:/.test( urlMatch ) && !/:.*?[A-Za-z]/.test( urlMatch ) ) || // At least one letter character must exist in the domain name after a protocol match. Ex: skip over something like "git:1.0" | ||
( protocolRelativeMatch && /^[\w]\/\//.test( protocolRelativeMatch ) ) // a protocol-relative match which has a word character in front of it (so we can skip something like "abc//google.com") | ||
) { | ||
return matchStr; | ||
} | ||
// Handle a closing parenthesis at the end of the match, and exclude it if there is not a matching open parenthesis | ||
// in the match. This handles cases like the string "wikipedia.com/something_(disambiguation)" (which should be auto- | ||
// linked, and when it is enclosed in parenthesis itself, such as: "(wikipedia.com/something_(disambiguation))" (in | ||
// which the outer parens should *not* be auto-linked. | ||
var lastChar = matchStr.charAt( matchStr.length - 1 ); | ||
if( lastChar === ')' ) { | ||
var openParensMatch = matchStr.match( /\(/g ), | ||
closeParensMatch = matchStr.match( /\)/g ), | ||
numOpenParens = ( openParensMatch && openParensMatch.length ) || 0, | ||
numCloseParens = ( closeParensMatch && closeParensMatch.length ) || 0; | ||
if( numOpenParens < numCloseParens ) { | ||
matchStr = matchStr.substr( 0, matchStr.length - 1 ); // remove the trailing ")" | ||
suffixStr = ")"; // this will be added after the <a> tag | ||
} | ||
} | ||
var anchorHref = matchStr, // initialize both of these | ||
anchorText = matchStr, // values as the full match | ||
linkType; | ||
// Process the urls that are found. We need to change URLs like "www.yahoo.com" to "http://www.yahoo.com" (or the browser | ||
// will try to direct the user to "http://current-domain.com/www.yahoo.com"), and we need to prefix 'mailto:' to email addresses. | ||
if( twitterMatch ) { | ||
linkType = 'twitter'; | ||
prefixStr = twitterHandlePrefixWhitespaceChar; | ||
anchorHref = 'https://twitter.com/' + twitterHandle; | ||
anchorText = '@' + twitterHandle; | ||
} else if( emailAddress ) { | ||
linkType = 'email'; | ||
anchorHref = 'mailto:' + emailAddress; | ||
anchorText = emailAddress; | ||
} else { // url match | ||
linkType = 'url'; | ||
if( protocolRelativeMatch ) { | ||
// Strip off any protocol-relative '//' from the anchor text (leaving the previous non-word character | ||
// intact, if there is one) | ||
var protocolRelRegex = new RegExp( "^" + me.protocolRelativeRegex.source ), // for this one, we want to only match at the beginning of the string | ||
charBeforeMatch = protocolRelativeMatch.match( protocolRelRegex )[ 1 ] || ""; | ||
prefixStr = charBeforeMatch + prefixStr; // re-add the character before the '//' to what will be placed before the <a> tag | ||
anchorHref = anchorHref.replace( protocolRelRegex, "//" ); // remove the char before the match for the href | ||
anchorText = anchorText.replace( protocolRelRegex, "" ); // remove both the char before the match and the '//' for the anchor text | ||
} else if( !/^[A-Za-z]{3,9}:/i.test( anchorHref ) ) { | ||
// url string doesn't begin with a protocol, assume http:// | ||
anchorHref = 'http://' + anchorHref; | ||
} | ||
} | ||
// wrap the match in an anchor tag | ||
var anchorTag = me.createAnchorTag( linkType, anchorHref, anchorText ); | ||
return prefixStr + anchorTag + suffixStr; | ||
} ); | ||
}, | ||
/** | ||
* Generates the actual anchor (<a>) tag to use in place of a source url/email/twitter link. | ||
* | ||
* @private | ||
* @param {"url"/"email"/"twitter"} linkType The type of link that an anchor tag is being generated for. | ||
* @param {String} anchorHref The href for the anchor tag. | ||
* @param {String} anchorText The anchor tag's text (i.e. what will be displayed). | ||
* @return {String} The full HTML for the anchor tag. | ||
*/ | ||
createAnchorTag : function( linkType, anchorHref, anchorText ) { | ||
var attributesStr = this.createAnchorAttrsStr( linkType, anchorHref ); | ||
anchorText = this.processAnchorText( anchorText ); | ||
return '<a ' + attributesStr + '>' + anchorText + '</a>'; | ||
}, | ||
/** | ||
* Creates the string which will be the HTML attributes for the anchor (<a>) tag being generated. | ||
* | ||
* @private | ||
* @param {"url"/"email"/"twitter"} linkType The type of link that an anchor tag is being generated for. | ||
* @param {String} href The href for the anchor tag. | ||
* @return {String} The anchor tag's attribute. Ex: `href="http://google.com" class="myLink myLink-url" target="_blank"` | ||
*/ | ||
createAnchorAttrsStr : function( linkType, anchorHref ) { | ||
var attrs = [ 'href="' + anchorHref + '"' ]; // we'll always have the `href` attribute | ||
var cssClass = this.createCssClass( linkType ); | ||
if( cssClass ) { | ||
attrs.push( 'class="' + cssClass + '"' ); | ||
} | ||
if( this.newWindow ) { | ||
attrs.push( 'target="_blank"' ); | ||
} | ||
return attrs.join( " " ); | ||
}, | ||
/** | ||
* Creates the CSS class that will be used for a given anchor tag, based on the `linkType` and the {@link #className} | ||
* config. | ||
* | ||
* @private | ||
* @param {"url"/"email"/"twitter"} linkType The type of link that an anchor tag is being generated for. | ||
* @return {String} The CSS class string for the link. Example return: "myLink myLink-url". If no {@link #className} | ||
* was configured, returns an empty string. | ||
*/ | ||
createCssClass : function( linkType ) { | ||
var className = this.className; | ||
if( !className ) | ||
return ""; | ||
else | ||
return className + " " + className + "-" + linkType; // ex: "myLink myLink-url", "myLink myLink-email", or "myLink myLink-twitter" | ||
}, | ||
/** | ||
* Processes the `anchorText` by stripping the URL prefix (if {@link #stripPrefix} is `true`), removing | ||
* any trailing slash, and truncating the text according to the {@link #truncate} config. | ||
* | ||
* @private | ||
* @param {String} anchorText The anchor tag's text (i.e. what will be displayed). | ||
* @return {String} The processed `anchorText`. | ||
*/ | ||
processAnchorText : function( anchorText ) { | ||
if( this.stripPrefix ) { | ||
anchorText = this.stripUrlPrefix( anchorText ); | ||
} | ||
anchorText = this.removeTrailingSlash( anchorText ); // remove trailing slash, if there is one | ||
anchorText = this.doTruncate( anchorText ); | ||
return anchorText; | ||
}, | ||
/** | ||
* Strips the URL prefix (such as "http://" or "https://") from the given text. | ||
* | ||
* @private | ||
* @param {String} text The text of the anchor that is being generated, for which to strip off the | ||
* url prefix (such as stripping off "http://") | ||
* @return {String} The `anchorText`, with the prefix stripped. | ||
*/ | ||
stripUrlPrefix : function( text ) { | ||
return text.replace( this.urlPrefixRegex, '' ); | ||
}, | ||
/** | ||
* Removes any trailing slash from the given `anchorText`, in prepration for the text to be displayed. | ||
* | ||
* @private | ||
* @param {String} anchorText The text of the anchor that is being generated, for which to remove any trailing | ||
* slash ('/') that may exist. | ||
* @return {String} The `anchorText`, with the trailing slash removed. | ||
*/ | ||
removeTrailingSlash : function( anchorText ) { | ||
if( anchorText.charAt( anchorText.length - 1 ) === '/' ) { | ||
anchorText = anchorText.slice( 0, -1 ); | ||
} | ||
return anchorText; | ||
}, | ||
/** | ||
* Performs the truncation of the `anchorText`, if the `anchorText` is longer than the {@link #truncate} option. | ||
* Truncates the text to 2 characters fewer than the {@link #truncate} option, and adds ".." to the end. | ||
* | ||
* @private | ||
* @param {String} text The anchor tag's text (i.e. what will be displayed). | ||
* @return {String} The truncated anchor text. | ||
*/ | ||
doTruncate : function( anchorText ) { | ||
var truncateLen = this.truncate; | ||
// Truncate the anchor text if it is longer than the provided 'truncate' option | ||
if( truncateLen && anchorText.length > truncateLen ) { | ||
anchorText = anchorText.substring( 0, truncateLen - 2 ) + '..'; | ||
} | ||
return anchorText; | ||
} | ||
}; | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* | ||
* Example: | ||
* | ||
* var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } ); | ||
* // Produces: "Go to <a href="http://google.com">google.com</a>" | ||
* | ||
* @static | ||
* @method link | ||
* @param {String} html The HTML text to link URLs within. | ||
* @param {Object} [options] Any of the configuration options for the Autolinker class, specified in an Object (map). | ||
* See the class description for an example call. | ||
* @return {String} The HTML text, with URLs automatically linked | ||
*/ | ||
Autolinker.link = function( text, options ) { | ||
var autolinker = new Autolinker( options ); | ||
return autolinker.link( text ); | ||
}; | ||
return Autolinker; | ||
})); | ||
} ) ); |
/*global Autolinker, _, describe, beforeEach, afterEach, it, expect */ | ||
describe( "Autolinker", function() { | ||
describe( "link()", function() { | ||
describe( "instantiating and using as a class", function() { | ||
it( "should automatically link URLs in the form of http://yahoo.com", function() { | ||
var result = Autolinker.link( "Joe went to http://yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com" target="_blank">yahoo.com</a>' ); | ||
} ); | ||
it( "should configure the instance with configuration options, and then be able to execute the link() method", function() { | ||
var autolinker = new Autolinker( { newWindow: false, truncate: 25 } ); | ||
it( "should automatically link URLs in the form of http://www.yahoo.com (i.e. 'www' prefix)", function() { | ||
var result = Autolinker.link( "Joe went to http://www.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>' ); | ||
var result = autolinker.link( "Check out http://www.yahoo.com/some/long/path/to/a/file" ); | ||
expect( result ).toBe( 'Check out <a href="http://www.yahoo.com/some/long/path/to/a/file">yahoo.com/some/long/pat..</a>' ); | ||
} ); | ||
} ); | ||
describe( "link() method", function() { | ||
var autolinker; | ||
it( "should automatically link URLs in the form of www.yahoo.com, prepending the http:// in this case", function() { | ||
var result = Autolinker.link( "Joe went to www.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>' ); | ||
beforeEach( function() { | ||
autolinker = new Autolinker( { newWindow: false } ); // so that target="_blank" is not added to resulting autolinked URLs | ||
} ); | ||
it( "should automatically link URLs in the form of subdomain.yahoo.com", function() { | ||
var result = Autolinker.link( "Joe went to subdomain.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://subdomain.yahoo.com" target="_blank">subdomain.yahoo.com</a>' ); | ||
} ); | ||
describe( "URL linking", function() { | ||
describe( "protocol-prefixed URLs (i.e. URLs starting with http:// or https://)", function() { | ||
it( "should automatically link URLs in the form of http://yahoo.com", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of http://www.yahoo.com (i.e. protocol and 'www' prefix)", function() { | ||
var result = autolinker.link( "Joe went to http://www.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link https URLs in the form of https://yahoo.com", function() { | ||
var result = autolinker.link( "Joe went to https://www.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="https://www.yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link capitalized URLs", function() { | ||
var result = autolinker.link( "Joe went to HTTP://WWW.YAHOO.COM" ); | ||
expect( result ).toBe( 'Joe went to <a href="HTTP://WWW.YAHOO.COM">YAHOO.COM</a>' ); | ||
} ); | ||
it( "should automatically link 'yahoo.xyz' (a known TLD), but not 'sencha.etc' (an unknown TLD)", function() { | ||
var result = autolinker.link( "yahoo.xyz should be linked, sencha.etc should not", { newWindow: false } ); | ||
expect( result ).toBe( '<a href="http://yahoo.xyz">yahoo.xyz</a> should be linked, sencha.etc should not' ); | ||
} ); | ||
it( "should automatically link 'a.museum' (a known TLD), but not 'abc.123'", function() { | ||
var result = autolinker.link( "a.museum should be linked, but abc.123 should not", { newWindow: false } ); | ||
expect( result ).toBe( '<a href="http://a.museum">a.museum</a> should be linked, but abc.123 should not' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com, prepending the http:// in this case", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com" target="_blank">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'http://yahoo.com.', without including the trailing period", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com">yahoo.com</a>.' ); | ||
} ); | ||
it( "should automatically link URLs with a port number", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com:8000 today." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com:8000">yahoo.com:8000</a> today.' ); | ||
} ); | ||
it( "should automatically link URLs with a port number and a following slash", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com:8000/ today." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com:8000/">yahoo.com:8000</a> today.' ); | ||
} ); | ||
it( "should automatically link URLs with a port number and a path", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com:8000/mysite/page today." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com:8000/mysite/page">yahoo.com:8000/mysite/page</a> today.' ); | ||
} ); | ||
it( "should automatically link URLs with a port number and a query string", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com:8000?page=index today." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com:8000?page=index">yahoo.com:8000?page=index</a> today.' ); | ||
} ); | ||
it( "should automatically link URLs with a port number and a hash string", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com:8000#page=index today." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com:8000#page=index">yahoo.com:8000#page=index</a> today.' ); | ||
} ); | ||
it( "should NOT automatically link strings of the form 'git:d' (using the heuristic that the domain name does not have a '.' in it)", function() { | ||
var result = autolinker.link( 'Something like git:d should not be linked as a URL' ); | ||
expect( result ).toBe( 'Something like git:d should not be linked as a URL' ); | ||
} ); | ||
it( "should NOT automatically link strings of the form 'git:domain' (using the heuristic that the domain name does not have a '.' in it)", function() { | ||
var result = autolinker.link( 'Something like git:domain should not be linked as a URL' ); | ||
expect( result ).toBe( 'Something like git:domain should not be linked as a URL' ); | ||
} ); | ||
it( "should automatically link strings of the form 'git:domain.com', interpreting this as a protocol and domain name", function() { | ||
var result = autolinker.link( 'Something like git:domain.com should be linked as a URL' ); | ||
expect( result ).toBe( 'Something like <a href="git:domain.com">git:domain.com</a> should be linked as a URL' ); | ||
} ); | ||
it( "should NOT automatically link a string in the form of 'version:1.0'", function() { | ||
var result = autolinker.link( 'version:1.0' ); | ||
expect( result ).toBe( 'version:1.0' ); | ||
} ); | ||
it( "should NOT automatically link these 'abc:def' style strings", function() { | ||
var strings = [ | ||
'BEGIN:VCALENDAR', | ||
'VERSION:1.0', | ||
'BEGIN:VEVENT', | ||
'DTSTART:20140401T090000', | ||
'DTEND:20140401T100000', | ||
'SUMMARY:Some thing to do', | ||
'LOCATION:', | ||
'DESCRIPTION:Just call this guy yeah! Testings', | ||
'PRIORITY:3', | ||
'END:VEVENT', | ||
'END:VCALENDAR' | ||
]; | ||
for( var i = 0, len = strings.length; i < len; i++ ) { | ||
expect( autolinker.link( strings[ i ] ) ).toBe( strings[ i ] ); // none should be autolinked | ||
} | ||
} ); | ||
} ); | ||
describe( "'www.' prefixed URLs", function() { | ||
it( "should automatically link URLs in the form of yahoo.co.uk, prepending the http:// in this case", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.co.uk" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.co.uk" target="_blank">yahoo.co.uk</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of www.yahoo.com, prepending the http:// in this case", function() { | ||
var result = autolinker.link( "Joe went to www.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'www.yahoo.com.', without including the trailing period", function() { | ||
var result = autolinker.link( "Joe went to www.yahoo.com." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>.' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'www.yahoo.com:8000' (with a port number)", function() { | ||
var result = autolinker.link( "Joe went to www.yahoo.com:8000 today" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com:8000">yahoo.com:8000</a> today' ); | ||
} ); | ||
it( "should automatically link capitalized URLs", function() { | ||
var result = autolinker.link( "Joe went to WWW.YAHOO.COM today" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://WWW.YAHOO.COM">YAHOO.COM</a> today' ); | ||
} ); | ||
} ); | ||
describe( "URLs with no protocol prefix, and no 'www' (i.e. URLs with known TLDs)", function() { | ||
it( "should automatically link URLs in the form of yahoo.ru, prepending the http:// in this case", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.ru" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.ru" target="_blank">yahoo.ru</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com, prepending the http:// in this case", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of subdomain.yahoo.com", function() { | ||
var result = autolinker.link( "Joe went to subdomain.yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://subdomain.yahoo.com">subdomain.yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.co.uk, prepending the http:// in this case", function() { | ||
var result = autolinker.link( "Joe went to yahoo.co.uk" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.co.uk">yahoo.co.uk</a>' ); | ||
} ); | ||
it( "should automatically link 'yahoo.xyz', but not 'sencha.etc'", function() { | ||
var result = Autolinker.link( "yahoo.xyz should be linked, sencha.etc should not", { newWindow: false } ); | ||
expect( result ).toBe( '<a href="http://yahoo.xyz">yahoo.xyz</a> should be linked, sencha.etc should not' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.ru, prepending the http:// in this case", function() { | ||
var result = autolinker.link( "Joe went to yahoo.ru" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.ru">yahoo.ru</a>' ); | ||
} ); | ||
it( "should automatically link 'a.museum', but not 'abc.123'", function() { | ||
var result = Autolinker.link( "a.museum should be linked, but abc.123 should not", { newWindow: false } ); | ||
expect( result ).toBe( '<a href="http://a.museum">a.museum</a> should be linked, but abc.123 should not' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'yahoo.com.', without including the trailing period", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com">yahoo.com</a>.' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'www.yahoo.com:8000' (with a port number)", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com:8000 today" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com:8000">yahoo.com:8000</a> today' ); | ||
} ); | ||
it( "should automatically link capitalized URLs", function() { | ||
var result = autolinker.link( "Joe went to YAHOO.COM." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://YAHOO.COM">YAHOO.COM</a>.' ); | ||
} ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html, handling the path", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com/path/to/file.html" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html" target="_blank">yahoo.com/path/to/file.html</a>' ); | ||
} ); | ||
describe( "protocol-relative URLs (i.e. URLs starting with only '//')", function() { | ||
it( "should automatically link protocol-relative URLs in the form of //yahoo.com at the beginning of the string", function() { | ||
var result = autolinker.link( "//yahoo.com" ); | ||
expect( result ).toBe( '<a href="//yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link protocol-relative URLs in the form of //yahoo.com in the middle of the string", function() { | ||
var result = autolinker.link( "Joe went to //yahoo.com yesterday" ); | ||
expect( result ).toBe( 'Joe went to <a href="//yahoo.com">yahoo.com</a> yesterday' ); | ||
} ); | ||
it( "should automatically link protocol-relative URLs in the form of //yahoo.com at the end of the string", function() { | ||
var result = autolinker.link( "Joe went to //yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to <a href="//yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link capitalized protocol-relative URLs", function() { | ||
var result = autolinker.link( "Joe went to //YAHOO.COM" ); | ||
expect( result ).toBe( 'Joe went to <a href="//YAHOO.COM">YAHOO.COM</a>' ); | ||
} ); | ||
it( "should NOT automatically link supposed protocol-relative URLs in the form of abc//yahoo.com, which is most likely not supposed to be interpreted as a URL", function() { | ||
var result = autolinker.link( "Joe went to abc//yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to abc//yahoo.com' ); | ||
} ); | ||
it( "should NOT automatically link supposed protocol-relative URLs in the form of 123//yahoo.com, which is most likely not supposed to be interpreted as a URL", function() { | ||
var result = autolinker.link( "Joe went to 123//yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to 123//yahoo.com' ); | ||
} ); | ||
it( "should automatically link supposed protocol-relative URLs as long as the character before the '//' is a non-word character", function() { | ||
var result = autolinker.link( "Joe went to abc-//yahoo.com" ); | ||
expect( result ).toBe( 'Joe went to abc-<a href="//yahoo.com">yahoo.com</a>' ); | ||
} ); | ||
} ); | ||
describe( "parenthesis handling", function() { | ||
it( "should include parentheses in URLs", function() { | ||
var result = autolinker.link( "TLDs come from en.wikipedia.org/wiki/IANA_(disambiguation)." ); | ||
expect( result ).toBe( 'TLDs come from <a href="http://en.wikipedia.org/wiki/IANA_(disambiguation)">en.wikipedia.org/wiki/IANA_(disambiguation)</a>.' ); | ||
result = autolinker.link( "MSDN has a great article at http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx." ); | ||
expect( result ).toBe( 'MSDN has a great article at <a href="http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx</a>.' ); | ||
} ); | ||
it( "should include parentheses in URLs with query strings", function() { | ||
var result = autolinker.link( "TLDs come from en.wikipedia.org/wiki?IANA_(disambiguation)." ); | ||
expect( result ).toBe( 'TLDs come from <a href="http://en.wikipedia.org/wiki?IANA_(disambiguation)">en.wikipedia.org/wiki?IANA_(disambiguation)</a>.' ); | ||
result = autolinker.link( "MSDN has a great article at http://msdn.microsoft.com/en-us/library?aa752574(VS.85).aspx." ); | ||
expect( result ).toBe( 'MSDN has a great article at <a href="http://msdn.microsoft.com/en-us/library?aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library?aa752574(VS.85).aspx</a>.' ); | ||
} ); | ||
it( "should include parentheses in URLs with hash anchors", function() { | ||
var result = autolinker.link( "TLDs come from en.wikipedia.org/wiki#IANA_(disambiguation)." ); | ||
expect( result ).toBe( 'TLDs come from <a href="http://en.wikipedia.org/wiki#IANA_(disambiguation)">en.wikipedia.org/wiki#IANA_(disambiguation)</a>.' ); | ||
result = autolinker.link( "MSDN has a great article at http://msdn.microsoft.com/en-us/library#aa752574(VS.85).aspx." ); | ||
expect( result ).toBe( 'MSDN has a great article at <a href="http://msdn.microsoft.com/en-us/library#aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library#aa752574(VS.85).aspx</a>.' ); | ||
} ); | ||
it( "should include parentheses in URLs, when the URL is also in parenthesis itself", function() { | ||
var result = autolinker.link( "TLDs come from (en.wikipedia.org/wiki/IANA_(disambiguation))." ); | ||
expect( result ).toBe( 'TLDs come from (<a href="http://en.wikipedia.org/wiki/IANA_(disambiguation)">en.wikipedia.org/wiki/IANA_(disambiguation)</a>).' ); | ||
result = autolinker.link( "MSDN has a great article at (http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx)." ); | ||
expect( result ).toBe( 'MSDN has a great article at (<a href="http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx</a>).' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL, if it doesn't match an opening paren in the url", function() { | ||
var result = autolinker.link( "Click here (google.com) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com">google.com</a>) for more details' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL when a path exists", function() { | ||
var result = autolinker.link( "Click here (google.com/abc) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com/abc">google.com/abc</a>) for more details' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL when a query string exists", function() { | ||
var result = autolinker.link( "Click here (google.com?abc=1) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com?abc=1">google.com?abc=1</a>) for more details' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL when a hash anchor exists", function() { | ||
var result = autolinker.link( "Click here (google.com#abc) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com#abc">google.com#abc</a>) for more details' ); | ||
} ); | ||
it( "should include escaped parentheses in the URL", function() { | ||
var result = autolinker.link( "Here's an example from CodingHorror: http://en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29" ); | ||
expect( result ).toBe( 'Here\'s an example from CodingHorror: <a href="http://en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29">en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29</a>' ); | ||
} ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com?hi=1, handling the query string", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com?hi=1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com?hi=1" target="_blank">yahoo.com?hi=1</a>' ); | ||
} ); | ||
describe( "URL path, query string, and hash handling", function() { | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html, handling the path", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com/path/to/file.html" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html">yahoo.com/path/to/file.html</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com?hi=1, handling the query string", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com?hi=1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com?hi=1">yahoo.com?hi=1</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com#index1, handling the hash", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com#index1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com#index1">yahoo.com#index1</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html?hi=1, handling the path and the query string", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com/path/to/file.html?hi=1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html?hi=1">yahoo.com/path/to/file.html?hi=1</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html#index1, handling the path and the hash", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com/path/to/file.html#index1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html#index1">yahoo.com/path/to/file.html#index1</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html?hi=1#index1, handling the path, query string, and hash", function() { | ||
var result = autolinker.link( "Joe went to yahoo.com/path/to/file.html?hi=1#index1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html?hi=1#index1">yahoo.com/path/to/file.html?hi=1#index1</a>' ); | ||
} ); | ||
it( "should automatically link a URL with a complex hash (such as a Google Analytics url)", function() { | ||
var result = autolinker.link( "Joe went to https://www.google.com/analytics/web/?pli=1#my-reports/Obif-Y6qQB2xAJk0ZZE1Zg/a4454143w36378534p43704543/%3F.date00%3D20120314%26_.date01%3D20120314%268534-table.rowStart%3D0%268534-table.rowCount%3D25/ and analyzed his analytics" ); | ||
expect( result ).toBe( 'Joe went to <a href="https://www.google.com/analytics/web/?pli=1#my-reports/Obif-Y6qQB2xAJk0ZZE1Zg/a4454143w36378534p43704543/%3F.date00%3D20120314%26_.date01%3D20120314%268534-table.rowStart%3D0%268534-table.rowCount%3D25/">google.com/analytics/web/?pli=1#my-reports/Obif-Y6qQB2xAJk0ZZE1Zg/a4454143w36378534p43704543/%3F.date00%3D20120314%26_.date01%3D20120314%268534-table.rowStart%3D0%268534-table.rowCount%3D25</a> and analyzed his analytics' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com#index1, handling the hash", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com#index1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com#index1" target="_blank">yahoo.com#index1</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'http://yahoo.com/sports.', without including the trailing period", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com/sports." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/sports">yahoo.com/sports</a>.' ); | ||
} ); | ||
it( "should remove trailing slash from 'http://yahoo.com/'", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com/." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/">yahoo.com</a>.' ); | ||
} ); | ||
it( "should remove trailing slash from 'http://yahoo.com/sports/'", function() { | ||
var result = autolinker.link( "Joe went to http://yahoo.com/sports/." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/sports/">yahoo.com/sports</a>.' ); | ||
} ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html?hi=1, handling the path and the query string", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com/path/to/file.html?hi=1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html?hi=1" target="_blank">yahoo.com/path/to/file.html?hi=1</a>' ); | ||
} ); | ||
it( "should automatically link multiple URLs in the same input string", function() { | ||
var result = autolinker.link( 'Joe went to http://yahoo.com and http://google.com' ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com">yahoo.com</a> and <a href="http://google.com">google.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html#index1, handling the path and the hash", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com/path/to/file.html#index1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html#index1" target="_blank">yahoo.com/path/to/file.html#index1</a>' ); | ||
} ); | ||
describe( "email address linking", function() { | ||
it( "should automatically link an email address which is the only text in the string", function() { | ||
var result = autolinker.link( "joe@joe.com" ); | ||
expect( result ).toBe( '<a href="mailto:joe@joe.com">joe@joe.com</a>' ); | ||
} ); | ||
it( "should automatically link email addresses at the start of the string", function() { | ||
var result = autolinker.link( "joe@joe.com is Joe's email" ); | ||
expect( result ).toBe( '<a href="mailto:joe@joe.com">joe@joe.com</a> is Joe\'s email' ); | ||
} ); | ||
it( "should automatically link URLs in the form of yahoo.com/path/to/file.html?hi=1#index1, handling the path, query string, and hash", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com/path/to/file.html?hi=1#index1" ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/path/to/file.html?hi=1#index1" target="_blank">yahoo.com/path/to/file.html?hi=1#index1</a>' ); | ||
} ); | ||
it( "should automatically link a URL with a complex hash (such as a Google Analytics url)", function() { | ||
var result = Autolinker.link( "Joe went to https://www.google.com/analytics/web/?pli=1#my-reports/Obif-Y6qQB2xAJk0ZZE1Zg/a4454143w36378534p43704543/%3F.date00%3D20120314%26_.date01%3D20120314%268534-table.rowStart%3D0%268534-table.rowCount%3D25/ and analyzed his analytics" ); | ||
expect( result ).toBe( 'Joe went to <a href="https://www.google.com/analytics/web/?pli=1#my-reports/Obif-Y6qQB2xAJk0ZZE1Zg/a4454143w36378534p43704543/%3F.date00%3D20120314%26_.date01%3D20120314%268534-table.rowStart%3D0%268534-table.rowCount%3D25/" target="_blank">google.com/analytics/web/?pli=1#my-reports/Obif-Y6qQB2xAJk0ZZE1Zg/a4454143w36378534p43704543/%3F.date00%3D20120314%26_.date01%3D20120314%268534-table.rowStart%3D0%268534-table.rowCount%3D25</a> and analyzed his analytics' ); | ||
it( "should automatically link an email address in the middle of the string", function() { | ||
var result = autolinker.link( "Joe's email is joe@joe.com because it is" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe@joe.com">joe@joe.com</a> because it is' ); | ||
} ); | ||
it( "should automatically link email addresses at the end of the string", function() { | ||
var result = autolinker.link( "Joe's email is joe@joe.com" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe@joe.com">joe@joe.com</a>' ); | ||
} ); | ||
it( "should automatically link email addresses with a period in the 'local part'", function() { | ||
var result = autolinker.link( "Joe's email is joe.smith@joe.com" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe.smith@joe.com">joe.smith@joe.com</a>' ); | ||
} ); | ||
it( "should automatically link fully-capitalized email addresses", function() { | ||
var result = autolinker.link( "Joe's email is JOE@JOE.COM" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:JOE@JOE.COM">JOE@JOE.COM</a>' ); | ||
} ); | ||
it( "should NOT automatically link any old word with an @ character in it", function() { | ||
var result = autolinker.link( "Hi there@stuff" ); | ||
expect( result ).toBe( 'Hi there@stuff' ); | ||
} ); | ||
} ); | ||
it( "should automatically link multiple URLs", function() { | ||
var result = Autolinker.link( 'Joe went to http://yahoo.com and http://google.com' ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com" target="_blank">yahoo.com</a> and <a href="http://google.com" target="_blank">google.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'http://yahoo.com.', without including the trailing period", function() { | ||
var result = Autolinker.link( "Joe went to http://yahoo.com." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com" target="_blank">yahoo.com</a>.' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'www.yahoo.com.', without including the trailing period", function() { | ||
var result = Autolinker.link( "Joe went to www.yahoo.com." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>.' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'yahoo.com.', without including the trailing period", function() { | ||
var result = Autolinker.link( "Joe went to yahoo.com." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com" target="_blank">yahoo.com</a>.' ); | ||
} ); | ||
it( "should automatically link URLs in the form of 'http://yahoo.com/sports.', without including the trailing period", function() { | ||
var result = Autolinker.link( "Joe went to http://yahoo.com/sports." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/sports" target="_blank">yahoo.com/sports</a>.' ); | ||
} ); | ||
it( "should remove trailing slash from 'http://yahoo.com/'", function() { | ||
var result = Autolinker.link( "Joe went to http://yahoo.com/." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/" target="_blank">yahoo.com</a>.' ); | ||
} ); | ||
it( "should remove trailing slash from 'http://yahoo.com/sports/'", function() { | ||
var result = Autolinker.link( "Joe went to http://yahoo.com/sports/." ); | ||
expect( result ).toBe( 'Joe went to <a href="http://yahoo.com/sports/" target="_blank">yahoo.com/sports</a>.' ); | ||
} ); | ||
it( "should NOT automatically link URLs within HTML tags", function() { | ||
var result = Autolinker.link( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p>' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p>' ); | ||
} ); | ||
it( "should automatically link URLs past the last HTML tag", function() { | ||
var result = Autolinker.link( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p> and http://google.com' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p> and <a href="http://google.com" target="_blank">google.com</a>' ); | ||
} ); | ||
it( "should NOT automatically link a URL found within the inner text of a pre-existing anchor tag", function() { | ||
var result = Autolinker.link( '<p>Joe went to <a href="http://www.yahoo.com">yahoo.com</a></p> yesterday.' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://www.yahoo.com">yahoo.com</a></p> yesterday.' ); | ||
} ); | ||
it( "should NOT automatically link a URL found within the inner text of a pre-existing anchor tag, but link others", function() { | ||
var result = Autolinker.link( '<p>Joe went to google.com, <a href="http://www.yahoo.com">yahoo.com</a>, and weather.com</p> yesterday.' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://google.com" target="_blank">google.com</a>, <a href="http://www.yahoo.com">yahoo.com</a>, and <a href="http://weather.com" target="_blank">weather.com</a></p> yesterday.' ); | ||
} ); | ||
it( "should NOT automatically link an image tag with a URL inside it, inside an anchor tag", function() { | ||
var result = Autolinker.link( '<a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a>' ); | ||
expect( result ).toBe( '<a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a>' ); | ||
} ); | ||
it( "should NOT automatically link an image tag with a URL inside it, inside an anchor tag, but match urls around the tags", function() { | ||
var result = Autolinker.link( 'google.com looks like <a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a> (at google.com)' ); | ||
expect( result ).toBe( '<a href="http://google.com" target="_blank">google.com</a> looks like <a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a> (at <a href="http://google.com" target="_blank">google.com</a>)' ); | ||
} ); | ||
it( "should NOT automatically link URLs in the form of http://www.yahoo.com if URL support is disabled", function() { | ||
var result = Autolinker.link( "Joe went to http://www.yahoo.com", { urls: false } ); | ||
expect( result ).toBe( 'Joe went to http://www.yahoo.com' ); | ||
} ); | ||
it( "should automatically link twitter handles when URL support is disabled", function() { | ||
var result = Autolinker.link( "@greg is tweeting @joe with @josh", { urls: false } ); | ||
expect( result ).toBe( '<a href="https://twitter.com/greg" target="_blank">@greg</a> is tweeting <a href="https://twitter.com/joe" target="_blank">@joe</a> with <a href="https://twitter.com/josh" target="_blank">@josh</a>' ); | ||
} ); | ||
it( "should automatically link an email address in the middle of the string if URL support is disabled", function() { | ||
var result = Autolinker.link( "Joe's email is joe@joe.com because it is", { urls: false } ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe@joe.com" target="_blank">joe@joe.com</a> because it is' ); | ||
} ); | ||
describe( "parenthesis handling", function() { | ||
describe( "twitter handle linking", function() { | ||
it( "should include parentheses in URLs", function() { | ||
var result = Autolinker.link( "TLDs come from en.wikipedia.org/wiki/IANA_(disambiguation).", { newWindow: false } ); | ||
expect( result ).toBe( 'TLDs come from <a href="http://en.wikipedia.org/wiki/IANA_(disambiguation)">en.wikipedia.org/wiki/IANA_(disambiguation)</a>.' ); | ||
var result = Autolinker.link( "MSDN has a great article at http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx.", { newWindow: false } ); | ||
expect( result ).toBe( 'MSDN has a great article at <a href="http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx</a>.' ); | ||
it( "should automatically link a twitter handle which is the only thing in the string", function() { | ||
var result = autolinker.link( "@joe_the_man12" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/joe_the_man12">@joe_the_man12</a>' ); | ||
} ); | ||
it( "should include parentheses in URLs with query strings", function() { | ||
var result = Autolinker.link( "TLDs come from en.wikipedia.org/wiki?IANA_(disambiguation).", { newWindow: false } ); | ||
expect( result ).toBe( 'TLDs come from <a href="http://en.wikipedia.org/wiki?IANA_(disambiguation)">en.wikipedia.org/wiki?IANA_(disambiguation)</a>.' ); | ||
var result = Autolinker.link( "MSDN has a great article at http://msdn.microsoft.com/en-us/library?aa752574(VS.85).aspx.", { newWindow: false } ); | ||
expect( result ).toBe( 'MSDN has a great article at <a href="http://msdn.microsoft.com/en-us/library?aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library?aa752574(VS.85).aspx</a>.' ); | ||
it( "should automatically link twitter handles at the beginning of a string", function() { | ||
var result = autolinker.link( "@greg is my twitter handle" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/greg">@greg</a> is my twitter handle' ); | ||
} ); | ||
it( "should include parentheses in URLs with hash anchors", function() { | ||
var result = Autolinker.link( "TLDs come from en.wikipedia.org/wiki#IANA_(disambiguation).", { newWindow: false } ); | ||
expect( result ).toBe( 'TLDs come from <a href="http://en.wikipedia.org/wiki#IANA_(disambiguation)">en.wikipedia.org/wiki#IANA_(disambiguation)</a>.' ); | ||
var result = Autolinker.link( "MSDN has a great article at http://msdn.microsoft.com/en-us/library#aa752574(VS.85).aspx.", { newWindow: false } ); | ||
expect( result ).toBe( 'MSDN has a great article at <a href="http://msdn.microsoft.com/en-us/library#aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library#aa752574(VS.85).aspx</a>.' ); | ||
it( "should automatically link twitter handles in the middle of a string", function() { | ||
var result = autolinker.link( "Joe's twitter is @joe_the_man12 today, but what will it be tomorrow?" ); | ||
expect( result ).toBe( 'Joe\'s twitter is <a href="https://twitter.com/joe_the_man12">@joe_the_man12</a> today, but what will it be tomorrow?' ); | ||
} ); | ||
it( "should include parentheses in URLs, when the URL is also in parenthesis itself", function() { | ||
var result = Autolinker.link( "TLDs come from (en.wikipedia.org/wiki/IANA_(disambiguation)).", { newWindow: false } ); | ||
expect( result ).toBe( 'TLDs come from (<a href="http://en.wikipedia.org/wiki/IANA_(disambiguation)">en.wikipedia.org/wiki/IANA_(disambiguation)</a>).' ); | ||
var result = Autolinker.link( "MSDN has a great article at (http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx).", { newWindow: false } ); | ||
expect( result ).toBe( 'MSDN has a great article at (<a href="http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx">msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx</a>).' ); | ||
it( "should automatically link twitter handles at the end of a string", function() { | ||
var result = autolinker.link( "Joe's twitter is @joe_the_man12" ); | ||
expect( result ).toBe( 'Joe\'s twitter is <a href="https://twitter.com/joe_the_man12">@joe_the_man12</a>' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL, if it doesn't match an opening paren in the url", function() { | ||
var result = Autolinker.link( "Click here (google.com) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com" target="_blank">google.com</a>) for more details' ); | ||
it( "should automatically link twitter handles surrounded by parentheses", function() { | ||
var result = autolinker.link( "Joe's twitter is (@joe_the_man12)" ); | ||
expect( result ).toBe( 'Joe\'s twitter is (<a href="https://twitter.com/joe_the_man12">@joe_the_man12</a>)' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL when a path exists", function() { | ||
var result = Autolinker.link( "Click here (google.com/abc) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com/abc" target="_blank">google.com/abc</a>) for more details' ); | ||
it( "should automatically link twitter handles surrounded by braces", function() { | ||
var result = autolinker.link( "Joe's twitter is {@joe_the_man12}" ); | ||
expect( result ).toBe( 'Joe\'s twitter is {<a href="https://twitter.com/joe_the_man12">@joe_the_man12</a>}' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL when a query string exists", function() { | ||
var result = Autolinker.link( "Click here (google.com?abc=1) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com?abc=1" target="_blank">google.com?abc=1</a>) for more details' ); | ||
it( "should automatically link twitter handles surrounded by brackets", function() { | ||
var result = autolinker.link( "Joe's twitter is [@joe_the_man12]" ); | ||
expect( result ).toBe( 'Joe\'s twitter is [<a href="https://twitter.com/joe_the_man12">@joe_the_man12</a>]' ); | ||
} ); | ||
it( "should not include a final closing paren in the URL when a hash anchor exists", function() { | ||
var result = Autolinker.link( "Click here (google.com#abc) for more details" ); | ||
expect( result ).toBe( 'Click here (<a href="http://google.com#abc" target="_blank">google.com#abc</a>) for more details' ); | ||
it( "should automatically link multiple twitter handles in a string", function() { | ||
var result = autolinker.link( "@greg is tweeting @joe with @josh" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/greg">@greg</a> is tweeting <a href="https://twitter.com/joe">@joe</a> with <a href="https://twitter.com/josh">@josh</a>' ); | ||
} ); | ||
it( "should include escaped parentheses in the URL", function() { | ||
var result = Autolinker.link( "Here's an example from CodingHorror: http://en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29" ); | ||
expect( result ).toBe( 'Here\'s an example from CodingHorror: <a href="http://en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29" target="_blank">en.wikipedia.org/wiki/PC_Tools_%28Central_Point_Software%29</a>' ); | ||
it( "should automatically liny fully capitalized twitter handles", function() { | ||
var result = autolinker.link( "@GREG is tweeting @JOE with @JOSH" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/GREG">@GREG</a> is tweeting <a href="https://twitter.com/JOE">@JOE</a> with <a href="https://twitter.com/JOSH">@JOSH</a>' ); | ||
} ); | ||
} ); | ||
describe( "proper handling of HTML in the input string", function() { | ||
describe( "email address linking", function() { | ||
it( "should automatically link an email address which is the only text in the string", function() { | ||
var result = Autolinker.link( "joe@joe.com" ); | ||
expect( result ).toBe( '<a href="mailto:joe@joe.com" target="_blank">joe@joe.com</a>' ); | ||
it( "should automatically link URLs past the last HTML tag", function() { | ||
var result = autolinker.link( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p> and http://google.com' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p> and <a href="http://google.com">google.com</a>' ); | ||
} ); | ||
it( "should automatically link email addresses at the start of the string", function() { | ||
var result = Autolinker.link( "joe@joe.com is Joe's email" ); | ||
expect( result ).toBe( '<a href="mailto:joe@joe.com" target="_blank">joe@joe.com</a> is Joe\'s email' ); | ||
} ); | ||
it( "should automatically link an email address in the middle of the string", function() { | ||
var result = Autolinker.link( "Joe's email is joe@joe.com because it is" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe@joe.com" target="_blank">joe@joe.com</a> because it is' ); | ||
it( "should NOT automatically link URLs within existing HTML tags", function() { | ||
var result = autolinker.link( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p>' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://www.yahoo.com">yahoo</a></p>' ); | ||
} ); | ||
it( "should automatically link email addresses at the end of the string", function() { | ||
var result = Autolinker.link( "Joe's email is joe@joe.com" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe@joe.com" target="_blank">joe@joe.com</a>' ); | ||
it( "should NOT automatically link a URL found within the inner text of a pre-existing anchor tag", function() { | ||
var result = autolinker.link( '<p>Joe went to <a href="http://www.yahoo.com">yahoo.com</a></p> yesterday.' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://www.yahoo.com">yahoo.com</a></p> yesterday.' ); | ||
} ); | ||
it( "should automatically link email addresses with a period in the 'local part'", function() { | ||
var result = Autolinker.link( "Joe's email is joe.smith@joe.com" ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe.smith@joe.com" target="_blank">joe.smith@joe.com</a>' ); | ||
it( "should NOT automatically link a URL found within the inner text of a pre-existing anchor tag, but link others", function() { | ||
var result = autolinker.link( '<p>Joe went to google.com, <a href="http://www.yahoo.com">yahoo.com</a>, and weather.com</p> yesterday.' ); | ||
expect( result ).toBe( '<p>Joe went to <a href="http://google.com">google.com</a>, <a href="http://www.yahoo.com">yahoo.com</a>, and <a href="http://weather.com">weather.com</a></p> yesterday.' ); | ||
} ); | ||
it( "should NOT automatically link any old word with an @ character in it", function() { | ||
var result = Autolinker.link( "Hi there@stuff" ); | ||
expect( result ).toBe( 'Hi there@stuff' ); | ||
it( "should NOT automatically link an image tag with a URL inside it, inside an anchor tag", function() { | ||
var result = autolinker.link( '<a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a>' ); | ||
expect( result ).toBe( '<a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a>' ); | ||
} ); | ||
it( "should NOT automatically link an email address in the middle of the string if email usage is disabled", function() { | ||
var result = Autolinker.link( "Joe's email is joe@joe.com because it is", { email: false } ); | ||
expect( result ).toBe( 'Joe\'s email is joe@joe.com because it is' ); | ||
it( "should NOT automatically link an image tag with a URL inside it, inside an anchor tag, but match urls around the tags", function() { | ||
var result = autolinker.link( 'google.com looks like <a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a> (at google.com)' ); | ||
expect( result ).toBe( '<a href="http://google.com">google.com</a> looks like <a href="http://google.com"><img src="http://google.com/someImage.jpg" /></a> (at <a href="http://google.com">google.com</a>)' ); | ||
} ); | ||
it( "should automatically link URLs in the form of http://www.yahoo.com if email usage is disabled", function() { | ||
var result = Autolinker.link( "Joe went to http://www.yahoo.com", { email: false } ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>' ); | ||
} ); | ||
it( "should automatically link URLs in the form of www.yahoo.com, prepending the http:// in this case, if email usage is disabled", function() { | ||
var result = Autolinker.link( "Joe went to www.yahoo.com", { email: false } ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>' ); | ||
it( "should allow the full range of HTML attribute name characters as specified in the W3C HTML syntax document (http://www.w3.org/TR/html-markup/syntax.html)", function() { | ||
// Note: We aren't actually expecting the HTML to be modified by this test | ||
var inAndOutHtml = '<ns:p>Foo <a data-qux-="" href="http://www.example.com">Bar<\/a> Baz<\/ns:p>'; | ||
expect( autolinker.link( inAndOutHtml ) ).toBe( inAndOutHtml ); | ||
} ); | ||
it( "should automatically link twitter handles when email usage is disabled", function() { | ||
var result = Autolinker.link( "@greg is tweeting @joe with @josh", { email: false } ); | ||
expect( result ).toBe( '<a href="https://twitter.com/greg" target="_blank">@greg</a> is tweeting <a href="https://twitter.com/joe" target="_blank">@joe</a> with <a href="https://twitter.com/josh" target="_blank">@josh</a>' ); | ||
it( "should properly autolink text within namespaced HTML elements, skipping over html elements with urls in attribute values", function() { | ||
var html = '<ns:p>Go to google.com or <a data-qux-="test" href="http://www.example.com">Bar<\/a> Baz<\/ns:p>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( '<ns:p>Go to <a href="http://google.com">google.com</a> or <a data-qux-="test" href="http://www.example.com">Bar<\/a> Baz<\/ns:p>' ); | ||
} ); | ||
} ); | ||
describe( "twitter handle linking", function() { | ||
it( "should automatically link a twitter handle which is the only thing in the string", function() { | ||
var result = Autolinker.link( "@joe_the_man12" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/joe_the_man12" target="_blank">@joe_the_man12</a>' ); | ||
it( "should properly skip over attribute names that could be interpreted as urls, while still autolinking urls in their inner text", function() { | ||
var html = '<div google.com anotherAttr yahoo.com>My div that has an attribute of google.com</div>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( '<div google.com anotherAttr yahoo.com>My div that has an attribute of <a href="http://google.com">google.com</a></div>' ); | ||
} ); | ||
it( "should automatically link twitter handles at the beginning of a string", function() { | ||
var result = Autolinker.link( "@greg is my twitter handle" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/greg" target="_blank">@greg</a> is my twitter handle' ); | ||
it( "should properly skip over attribute names that could be interpreted as urls when they have a value, while still autolinking urls in their inner text", function() { | ||
var html = '<div google.com="yes" anotherAttr=true yahoo.com="true">My div that has an attribute of google.com</div>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( '<div google.com="yes" anotherAttr=true yahoo.com="true">My div that has an attribute of <a href="http://google.com">google.com</a></div>' ); | ||
} ); | ||
it( "should automatically link twitter handles in the middle of a string", function() { | ||
var result = Autolinker.link( "Joe's twitter is @joe_the_man12 today, but what will it be tomorrow?" ); | ||
expect( result ).toBe( 'Joe\'s twitter is <a href="https://twitter.com/joe_the_man12" target="_blank">@joe_the_man12</a> today, but what will it be tomorrow?' ); | ||
it( "should properly skip over attribute names that could be interpreted as urls when they have a value and any number of spaces between attrs, while still autolinking urls in their inner text", function() { | ||
var html = '<div google.com="yes" \t\t anotherAttr=true yahoo.com="true" \t>My div that has an attribute of google.com</div>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( '<div google.com="yes" \t\t anotherAttr=true yahoo.com="true" \t>My div that has an attribute of <a href="http://google.com">google.com</a></div>' ); | ||
} ); | ||
it( "should automatically link twitter handles at the end of a string", function() { | ||
var result = Autolinker.link( "Joe's twitter is @joe_the_man12" ); | ||
expect( result ).toBe( 'Joe\'s twitter is <a href="https://twitter.com/joe_the_man12" target="_blank">@joe_the_man12</a>' ); | ||
it( "should properly skip over attribute values that could be interpreted as urls/emails/twitter accts, while still autolinking urls in their inner text", function() { | ||
var html = '<div url="google.com" email="asdf@asdf.com" twitter="@asdf">google.com asdf@asdf.com @asdf</div>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( [ | ||
'<div url="google.com" email="asdf@asdf.com" twitter="@asdf">', | ||
'<a href="http://google.com">google.com</a> ', | ||
'<a href="mailto:asdf@asdf.com">asdf@asdf.com</a> ', | ||
'<a href="https://twitter.com/asdf">@asdf</a>', | ||
'</div>' | ||
].join( "" ) ); | ||
} ); | ||
it( "should automatically link multiple twitter handles in a string", function() { | ||
var result = Autolinker.link( "@greg is tweeting @joe with @josh" ); | ||
expect( result ).toBe( '<a href="https://twitter.com/greg" target="_blank">@greg</a> is tweeting <a href="https://twitter.com/joe" target="_blank">@joe</a> with <a href="https://twitter.com/josh" target="_blank">@josh</a>' ); | ||
it( "should properly skip over attribute names and values that could be interpreted as urls/emails/twitter accts, while still autolinking urls in their inner text", function() { | ||
var html = '<div google.com="google.com" asdf@asdf.com="asdf@asdf.com" @asdf="@asdf">google.com asdf@asdf.com @asdf</div>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( [ | ||
'<div google.com="google.com" asdf@asdf.com="asdf@asdf.com" @asdf="@asdf">', | ||
'<a href="http://google.com">google.com</a> ', | ||
'<a href="mailto:asdf@asdf.com">asdf@asdf.com</a> ', | ||
'<a href="https://twitter.com/asdf">@asdf</a>', | ||
'</div>' | ||
].join( "" ) ); | ||
} ); | ||
it( "should NOT automatically link twitter handles when twitter usage is disabled", function() { | ||
var result = Autolinker.link( "@greg is tweeting @joe with @josh", { twitter: false } ); | ||
expect( result ).toBe( '@greg is tweeting @joe with @josh' ); | ||
it( "should properly handle HTML markup + text nodes that are nested within <a> tags", function() { | ||
var html = '<a href="http://google.com"><b>google.com</b></a>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( html ); | ||
} ); | ||
it( "should automatically link URLs in the form of http://www.yahoo.com if twitter usage is disabled", function() { | ||
var result = Autolinker.link( "Joe went to http://www.yahoo.com", { twitter: false } ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>' ); | ||
it( "should attempt to handle some invalid HTML markup relating to <a> tags, esp if there are extraneous closing </a> tags", function() { | ||
var html = '</a><a href="http://google.com">google.com</a>'; | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( html ); | ||
} ); | ||
it( "should automatically link URLs in the form of www.yahoo.com, prepending the http:// in this case, if twitter usage is disabled", function() { | ||
var result = Autolinker.link( "Joe went to www.yahoo.com", { twitter: false } ); | ||
expect( result ).toBe( 'Joe went to <a href="http://www.yahoo.com" target="_blank">yahoo.com</a>' ); | ||
it( "should attempt to handle some more complex invalid HTML markup relating to <a> tags, esp if there are extraneous closing </a> tags", function() { | ||
var html = [ | ||
'</a>', // invalid | ||
'<a href="http://google.com">google.com</a>', | ||
'<div>google.com</div>', | ||
'</a>', // invalid | ||
'<a href="http://yahoo.com">yahoo.com</a>', | ||
'</a>', // invalid | ||
'</a>', // invalid | ||
'twitter.com' | ||
].join( "" ); | ||
var result = autolinker.link( html ); | ||
expect( result ).toBe( [ | ||
'</a>', // invalid - left alone | ||
'<a href="http://google.com">google.com</a>', // valid tag - left alone | ||
'<div><a href="http://google.com">google.com</a></div>', // autolinked text in <div> | ||
'</a>', // invalid - left alone | ||
'<a href="http://yahoo.com">yahoo.com</a>', // valid tag - left alone | ||
'</a>', // invalid - left alone | ||
'</a>', // invalid - left alone | ||
'<a href="http://twitter.com">twitter.com</a>' // autolinked text | ||
].join( "" ) ); | ||
} ); | ||
it( "should automatically link an email address in the middle of the string if twitter usage is disabled", function() { | ||
var result = Autolinker.link( "Joe's email is joe@joe.com because it is", { twitter: false } ); | ||
expect( result ).toBe( 'Joe\'s email is <a href="mailto:joe@joe.com" target="_blank">joe@joe.com</a> because it is' ); | ||
} ); | ||
} ); | ||
@@ -396,3 +668,9 @@ | ||
} ); | ||
it( "should add target=\"_blank\" when the 'newWindow' option is set to true", function() { | ||
var result = Autolinker.link( "Test http://url.com", { newWindow: true } ); | ||
expect( result ).toBe( 'Test <a href="http://url.com" target="_blank">url.com</a>' ); | ||
} ); | ||
} ); | ||
@@ -404,9 +682,16 @@ | ||
it( "should not remove the prefix for non-http protocols", function() { | ||
var result = Autolinker.link( "Test file://execute-virus.com" ); | ||
expect( result ).toBe( 'Test <a href="file://execute-virus.com" target="_blank">file://execute-virus.com</a>' ); | ||
var result = Autolinker.link( "Test file://execute-virus.com", { stripPrefix: true, newWindow: false } ); | ||
expect( result ).toBe( 'Test <a href="file://execute-virus.com">file://execute-virus.com</a>' ); | ||
} ); | ||
it( "should remove 'http://www.' when the 'stripPrefix' option is set to `true`", function() { | ||
var result = Autolinker.link( "Test http://www.url.com", { stripPrefix: true, newWindow: false } ); | ||
expect( result ).toBe( 'Test <a href="http://www.url.com">url.com</a>' ); | ||
} ); | ||
it( "should not remove 'http://www.' when the 'stripPrefix' option is set to `false`", function() { | ||
var result = Autolinker.link( "Test http://www.url.com", { stripPrefix: false } ); | ||
expect( result ).toBe( 'Test <a href="http://www.url.com" target="_blank">http://www.url.com</a>' ); | ||
var result = Autolinker.link( "Test http://www.url.com", { stripPrefix: false, newWindow: false } ); | ||
expect( result ).toBe( 'Test <a href="http://www.url.com">http://www.url.com</a>' ); | ||
} ); | ||
@@ -420,4 +705,5 @@ | ||
it( "should truncate long a url/email/twitter to the given number of characters with the 'truncate' option specified", function() { | ||
var result = Autolinker.link( "Test http://url.com/with/path", { truncate: 12 } ); | ||
expect( result ).toBe( 'Test <a href="http://url.com/with/path" target="_blank">url.com/wi..</a>' ); | ||
var result = Autolinker.link( "Test http://url.com/with/path", { truncate: 12, newWindow: false } ); | ||
expect( result ).toBe( 'Test <a href="http://url.com/with/path">url.com/wi..</a>' ); | ||
} ); | ||
@@ -427,4 +713,5 @@ | ||
it( "should leave a url/email/twitter alone if the length of the url is exactly equal to the length of the 'truncate' option", function() { | ||
var result = Autolinker.link( "Test http://url.com/with/path", { truncate: 'url.com/with/path'.length } ); // the exact length of the link | ||
expect( result ).toBe( 'Test <a href="http://url.com/with/path" target="_blank">url.com/with/path</a>' ); | ||
var result = Autolinker.link( "Test http://url.com/with/path", { truncate: 'url.com/with/path'.length, newWindow: false } ); // the exact length of the link | ||
expect( result ).toBe( 'Test <a href="http://url.com/with/path">url.com/with/path</a>' ); | ||
} ); | ||
@@ -434,4 +721,5 @@ | ||
it( "should leave a url/email/twitter alone if it does not exceed the given number of characters provided in the 'truncate' option", function() { | ||
var result = Autolinker.link( "Test http://url.com/with/path", { truncate: 25 } ); // just a random high number | ||
expect( result ).toBe( 'Test <a href="http://url.com/with/path" target="_blank">url.com/with/path</a>' ); | ||
var result = Autolinker.link( "Test http://url.com/with/path", { truncate: 25, newWindow: false } ); // just a random high number | ||
expect( result ).toBe( 'Test <a href="http://url.com/with/path">url.com/with/path</a>' ); | ||
} ); | ||
@@ -441,4 +729,80 @@ | ||
describe( "`className` option", function() { | ||
it( "should not add className when the 'className' option is not a string with at least 1 character", function() { | ||
var result = Autolinker.link( "Test http://url.com" ); | ||
expect( result ).toBe( 'Test <a href="http://url.com" target="_blank">url.com</a>' ); | ||
result = Autolinker.link( "Test http://url.com", { className: null } ); | ||
expect( result ).toBe( 'Test <a href="http://url.com" target="_blank">url.com</a>' ); | ||
result = Autolinker.link( "Test http://url.com", { className: "" } ); | ||
expect( result ).toBe( 'Test <a href="http://url.com" target="_blank">url.com</a>' ); | ||
} ); | ||
it( "should add className to links", function() { | ||
var result = Autolinker.link( "Test http://url.com", { className: 'myLink' } ); | ||
expect( result ).toBe( 'Test <a href="http://url.com" class="myLink myLink-url" target="_blank">url.com</a>' ); | ||
} ); | ||
it( "should add className to email links", function() { | ||
var result = Autolinker.link( "Iggy's email is mr@iggypop.com", { email: true, className: 'myLink' } ); | ||
expect( result ).toBe( 'Iggy\'s email is <a href="mailto:mr@iggypop.com" class="myLink myLink-email" target="_blank">mr@iggypop.com</a>' ); | ||
} ); | ||
it( "should add className to twitter links", function() { | ||
var result = Autolinker.link( "hi from @iggypopschest", { twitter: true, className: 'myLink' } ); | ||
expect( result ).toBe( 'hi from <a href="https://twitter.com/iggypopschest" class="myLink myLink-twitter" target="_blank">@iggypopschest</a>' ); | ||
} ); | ||
} ); | ||
describe( "`urls`, `email`, and `twitter` options", function() { | ||
it( "should link all 3 types if all 3 urls/email/twitter options are enabled", function() { | ||
var result = Autolinker.link( "Website: asdf.com, Email: asdf@asdf.com, Twitter: @asdf", { newWindow: false } ); | ||
expect( result ).toBe( [ | ||
'Website: <a href="http://asdf.com">asdf.com</a>', | ||
'Email: <a href="mailto:asdf@asdf.com">asdf@asdf.com</a>', | ||
'Twitter: <a href="https://twitter.com/asdf">@asdf</a>' | ||
].join( ", " ) ); | ||
} ); | ||
it( "should not link urls when they are disabled", function() { | ||
var result = Autolinker.link( "Website: asdf.com, Email: asdf@asdf.com, Twitter: @asdf", { newWindow: false, urls: false } ); | ||
expect( result ).toBe( [ | ||
'Website: asdf.com', | ||
'Email: <a href="mailto:asdf@asdf.com">asdf@asdf.com</a>', | ||
'Twitter: <a href="https://twitter.com/asdf">@asdf</a>' | ||
].join( ", " ) ); | ||
} ); | ||
it( "should not link email addresses when they are disabled", function() { | ||
var result = Autolinker.link( "Website: asdf.com, Email: asdf@asdf.com, Twitter: @asdf", { newWindow: false, email: false } ); | ||
expect( result ).toBe( [ | ||
'Website: <a href="http://asdf.com">asdf.com</a>', | ||
'Email: asdf@asdf.com', | ||
'Twitter: <a href="https://twitter.com/asdf">@asdf</a>' | ||
].join( ", " ) ); | ||
} ); | ||
it( "should not link Twitter handles when they are disabled", function() { | ||
var result = Autolinker.link( "Website: asdf.com, Email: asdf@asdf.com, Twitter: @asdf", { newWindow: false, twitter: false } ); | ||
expect( result ).toBe( [ | ||
'Website: <a href="http://asdf.com">asdf.com</a>', | ||
'Email: <a href="mailto:asdf@asdf.com">asdf@asdf.com</a>', | ||
'Twitter: @asdf' | ||
].join( ", " ) ); | ||
} ); | ||
} ); | ||
} ); | ||
} ); | ||
} ); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117386
1687
256
7
1