1616
1717/*!
1818 * Autolinker.js
19- * 0.21 .0
19+ * 0.22 .0
2020 *
2121 * Copyright(c) 2015 Gregory Jacobs <[email protected] > 2222 * MIT
@@ -138,44 +138,59 @@ var Autolinker = function( cfg ) {
138138 throw new Error ( "invalid `hashtag` cfg - see docs" ) ;
139139 }
140140
141- // Normalize the `truncate` option
142- var truncate = this . truncate = this . truncate || { } ;
143- if ( typeof truncate === 'number' ) {
144- this . truncate = { length : truncate , location : 'end' } ;
145- } else if ( typeof truncate === 'object' ) {
146- this . truncate . length = truncate . length || Number . POSITIVE_INFINITY ;
147- this . truncate . location = truncate . location || 'end' ;
148- }
141+ // Normalize the configs
142+ this . urls = this . normalizeUrlsCfg ( this . urls ) ;
143+ this . truncate = this . normalizeTruncateCfg ( this . truncate ) ;
149144} ;
150145
151146Autolinker . prototype = {
152147 constructor : Autolinker , // fix constructor property
153148
154149 /**
155- * @cfg {Boolean} urls
150+ * @cfg {Boolean/Object} urls
151+ *
152+ * `true` if URLs should be automatically linked, `false` if they should not
153+ * be.
154+ *
155+ * This option also accepts an Object form with 3 properties, to allow for
156+ * more customization of what exactly gets linked. All default to `true`:
156157 *
157- * `true` if miscellaneous URLs should be automatically linked, `false` if they should not be.
158+ * @param {Boolean } schemeMatches `true` to match URLs found prefixed with a
159+ * scheme, i.e. `http://google.com`, or `other+scheme://google.com`,
160+ * `false` to prevent these types of matches.
161+ * @param {Boolean } wwwMatches `true` to match urls found prefixed with
162+ * `'www.'`, i.e. `www.google.com`. `false` to prevent these types of
163+ * matches. Note that if the URL had a prefixed scheme, and
164+ * `schemeMatches` is true, it will still be linked.
165+ * @param {Boolean } tldMatches `true` to match URLs with known top level
166+ * domains (.com, .net, etc.) that are not prefixed with a scheme or
167+ * `'www.'`. This option attempts to match anything that looks like a URL
168+ * in the given text. Ex: `google.com`, `asdf.org/?page=1`, etc. `false`
169+ * to prevent these types of matches.
158170 */
159171 urls : true ,
160172
161173 /**
162174 * @cfg {Boolean} email
163175 *
164- * `true` if email addresses should be automatically linked, `false` if they should not be.
176+ * `true` if email addresses should be automatically linked, `false` if they
177+ * should not be.
165178 */
166179 email : true ,
167180
168181 /**
169182 * @cfg {Boolean} twitter
170183 *
171- * `true` if Twitter handles ("@example") should be automatically linked, `false` if they should not be.
184+ * `true` if Twitter handles ("@example") should be automatically linked,
185+ * `false` if they should not be.
172186 */
173187 twitter : true ,
174188
175189 /**
176190 * @cfg {Boolean} phone
177191 *
178- * `true` if Phone numbers ("(555)555-5555") should be automatically linked, `false` if they should not be.
192+ * `true` if Phone numbers ("(555)555-5555") should be automatically linked,
193+ * `false` if they should not be.
179194 */
180195 phone : true ,
181196
@@ -313,6 +328,49 @@ Autolinker.prototype = {
313328 */
314329 tagBuilder : undefined ,
315330
331+
332+ /**
333+ * Normalizes the {@link #urls} config into an Object with 3 properties:
334+ * `schemeMatches`, `wwwMatches`, and `tldMatches`, all Booleans.
335+ *
336+ * See {@link #urls} config for details.
337+ *
338+ * @private
339+ * @param {Boolean/Object } urls
340+ * @return {Object }
341+ */
342+ normalizeUrlsCfg : function ( urls ) {
343+ if ( typeof urls === 'boolean' ) {
344+ return { schemeMatches : urls , wwwMatches : urls , tldMatches : urls } ;
345+ } else {
346+ return Autolinker . Util . defaults ( urls || { } , { schemeMatches : true , wwwMatches : true , tldMatches : true } ) ;
347+ }
348+ } ,
349+
350+
351+ /**
352+ * Normalizes the {@link #truncate} config into an Object with 2 properties:
353+ * `length` (Number), and `location` (String).
354+ *
355+ * See {@link #truncate} config for details.
356+ *
357+ * @private
358+ * @param {Number/Object } truncate
359+ * @return {Object }
360+ */
361+ normalizeTruncateCfg : function ( truncate ) {
362+ if ( typeof truncate === 'number' ) {
363+ return { length : truncate , location : 'end' } ;
364+
365+ } else { // object, or undefined/null
366+ return Autolinker . Util . defaults ( truncate || { } , {
367+ length : Number . POSITIVE_INFINITY ,
368+ location : 'end'
369+ } ) ;
370+ }
371+ } ,
372+
373+
316374 /**
317375 * Automatically links URLs, Email addresses, Phone numbers, Twitter
318376 * handles, and Hashtags found in the given chunk of HTML. Does not link
@@ -589,6 +647,25 @@ Autolinker.Util = {
589647 } ,
590648
591649
650+ /**
651+ * Assigns (shallow copies) the properties of `src` onto `dest`, if the
652+ * corresponding property on `dest` === `undefined`.
653+ *
654+ * @param {Object } dest The destination object.
655+ * @param {Object } src The source object.
656+ * @return {Object } The destination object (`dest`)
657+ */
658+ defaults : function ( dest , src ) {
659+ for ( var prop in src ) {
660+ if ( src . hasOwnProperty ( prop ) && dest [ prop ] === undefined ) {
661+ dest [ prop ] = src [ prop ] ;
662+ }
663+ }
664+
665+ return dest ;
666+ } ,
667+
668+
592669 /**
593670 * Extends `superclass` to create a new subclass, adding the `protoProps` to the new subclass's prototype.
594671 *
@@ -1700,7 +1777,7 @@ Autolinker.htmlParser.TextNode = Autolinker.Util.extend( Autolinker.htmlParser.H
17001777Autolinker . matchParser . MatchParser = Autolinker . Util . extend ( Object , {
17011778
17021779 /**
1703- * @cfg {Boolean } urls
1780+ * @cfg {Object } urls
17041781 * @inheritdoc Autolinker#urls
17051782 */
17061783 urls : true ,
@@ -1764,26 +1841,31 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
17641841 * used to match protocol URLs with just a single word, like 'http://localhost',
17651842 * where we won't double check that the domain name has at least one '.'
17661843 * in it.
1767- * 7. A protocol-relative ('//') match for the case of a 'www.' prefixed
1844+ * 7. Group that matches a 'www.' prefixed URL. This is only matched if the
1845+ * 'www.' text was not prefixed by a scheme (i.e.: not prefixed by
1846+ * 'http://', 'ftp:', etc.)
1847+ * 8. A protocol-relative ('//') match for the case of a 'www.' prefixed
17681848 * URL. Will be an empty string if it is not a protocol-relative match.
17691849 * We need to know the character before the '//' in order to determine
17701850 * if it is a valid match or the // was in a string we don't want to
17711851 * auto-link.
1772- * 8. A protocol-relative ('//') match for the case of a known TLD prefixed
1852+ * 9. Group that matches a known TLD (top level domain), when a scheme
1853+ * or 'www.'-prefixed domain is not matched.
1854+ * 10. A protocol-relative ('//') match for the case of a known TLD prefixed
17731855 * URL. Will be an empty string if it is not a protocol-relative match.
17741856 * See #6 for more info.
1775- * 9. Group that is used to determine if there is a phone number match.
1776- * 10 . If there is a phone number match, and a '+' sign was included with
1857+ * 11. Group that is used to determine if there is a phone number match.
1858+ * 12 . If there is a phone number match, and a '+' sign was included with
17771859 * the phone number, this group will be populated with the '+' sign.
1778- * 11 . Group that is used to determine if there is a Hashtag match
1860+ * 13 . Group that is used to determine if there is a Hashtag match
17791861 * (i.e. \#someHashtag). Simply check for its existence to determine if
17801862 * there is a Hashtag match. The next couple of capturing groups give
17811863 * information about the Hashtag match.
1782- * 12 . The whitespace character before the #sign in a Hashtag handle. This
1864+ * 14 . The whitespace character before the #sign in a Hashtag handle. This
17831865 * is needed because there are no look-behinds in JS regular
17841866 * expressions, and can be used to reconstruct the original string in a
17851867 * replace().
1786- * 13 . The Hashtag itself in a Hashtag match. If the match is
1868+ * 15 . The Hashtag itself in a Hashtag match. If the match is
17871869 * '#someHashtag', the hashtag is 'someHashtag'.
17881870 */
17891871 matcherRegex : ( function ( ) {
@@ -1821,23 +1903,23 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
18211903
18221904 '(' , // *** Capturing group $5, which is used to match a URL
18231905 '(?:' , // parens to cover match for protocol (optional), and domain
1824- '(' , // *** Capturing group $6, for a protocol -prefixed url (ex: http://google.com)
1906+ '(' , // *** Capturing group $6, for a scheme -prefixed url (ex: http://google.com)
18251907 protocolRegex . source ,
18261908 domainNameRegex . source ,
18271909 ')' ,
18281910
18291911 '|' ,
18301912
1831- '(?: ' , // non-capturing paren for a 'www.' prefixed url (ex: www.google.com)
1832- '(.?//)?' , // *** Capturing group $7 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character
1913+ '(' , // *** Capturing group $7, for a 'www.' prefixed url (ex: www.google.com)
1914+ '(.?//)?' , // *** Capturing group $8 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character
18331915 wwwRegex . source ,
18341916 domainNameRegex . source ,
18351917 ')' ,
18361918
18371919 '|' ,
18381920
1839- '(?: ' , // non-capturing paren for known a TLD url (ex: google.com)
1840- '(.?//)?' , // *** Capturing group $8 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character
1921+ '(' , // *** Capturing group $9, for known a TLD url (ex: google.com)
1922+ '(.?//)?' , // *** Capturing group $10 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character
18411923 domainNameRegex . source ,
18421924 tldRegex . source ,
18431925 ')' ,
@@ -1849,16 +1931,17 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
18491931 '|' ,
18501932
18511933 // this setup does not scale well for open extension :( Need to rethink design of autolinker...
1852- // *** Capturing group $9, which matches a (USA for now) phone number
1934+ // *** Capturing group $11, which matches a (USA for now) phone number, and
1935+ // *** Capturing group $12, which matches the '+' sign for international numbers, if it exists
18531936 '(' ,
18541937 phoneRegex . source ,
18551938 ')' ,
18561939
18571940 '|' ,
18581941
1859- '(' , // *** Capturing group $10 , which can be used to check for a Hashtag match. Use group $12 for the actual Hashtag though. $11 may be used to reconstruct the original string in a replace()
1860- // *** Capturing group $11 , which matches the whitespace character before the '#' sign (needed because of no lookbehinds), and
1861- // *** Capturing group $12 , which matches the actual Hashtag
1942+ '(' , // *** Capturing group $13 , which can be used to check for a Hashtag match. Use group $12 for the actual Hashtag though. $11 may be used to reconstruct the original string in a replace()
1943+ // *** Capturing group $14 , which matches the whitespace character before the '#' sign (needed because of no lookbehinds), and
1944+ // *** Capturing group $15 , which matches the actual Hashtag
18621945 hashtagRegex . source ,
18631946 ')'
18641947 ] . join ( "" ) , 'gi' ) ;
@@ -1915,8 +1998,8 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
19151998 replace : function ( text , replaceFn , contextObj ) {
19161999 var me = this ; // for closure
19172000
1918- return text . replace ( this . matcherRegex , function ( matchStr , $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10 , $11 , $12 , $13 ) {
1919- var matchDescObj = me . processCandidateMatch ( matchStr , $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10 , $11 , $12 , $13 ) ; // "match description" object
2001+ return text . replace ( this . matcherRegex , function ( matchStr /* , $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15*/ ) {
2002+ var matchDescObj = me . processCandidateMatch . apply ( me , arguments ) ; // "match description" object
19202003
19212004 // Return out with no changes for match types that are disabled (url,
19222005 // email, phone, etc.), or for matches that are invalid (false
@@ -1956,12 +2039,17 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
19562039 * @param {String } emailAddressMatch The matched email address for an email
19572040 * address match.
19582041 * @param {String } urlMatch The matched URL string for a URL match.
1959- * @param {String } protocolUrlMatch The match URL string for a protocol
2042+ * @param {String } schemeUrlMatch The match URL string for a protocol
19602043 * match. Ex: 'http://yahoo.com'. This is used to match something like
19612044 * 'http://localhost', where we won't double check that the domain name
19622045 * has at least one '.' in it.
2046+ * @param {String } wwwMatch The matched string of a 'www.'-prefixed URL that
2047+ * was matched. This is only matched if the 'www.' text was not prefixed
2048+ * by a scheme (i.e.: not prefixed by 'http://', 'ftp:', etc.).
19632049 * @param {String } wwwProtocolRelativeMatch The '//' for a protocol-relative
19642050 * match from a 'www' url, with the character that comes before the '//'.
2051+ * @param {String } tldMatch The matched string of a known TLD (top level
2052+ * domain), when a scheme or 'www.'-prefixed domain is not matched.
19652053 * @param {String } tldProtocolRelativeMatch The '//' for a protocol-relative
19662054 * match from a TLD (top level domain) match, with the character that
19672055 * comes before the '//'.
@@ -1993,8 +2081,8 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
19932081 */
19942082 processCandidateMatch : function (
19952083 matchStr , twitterMatch , twitterHandlePrefixWhitespaceChar , twitterHandle ,
1996- emailAddressMatch , urlMatch , protocolUrlMatch , wwwProtocolRelativeMatch ,
1997- tldProtocolRelativeMatch , phoneMatch , phonePlusSignMatch , hashtagMatch ,
2084+ emailAddressMatch , urlMatch , schemeUrlMatch , wwwMatch , wwwProtocolRelativeMatch ,
2085+ tldMatch , tldProtocolRelativeMatch , phoneMatch , phonePlusSignMatch , hashtagMatch ,
19982086 hashtagPrefixWhitespaceChar , hashtag
19992087 ) {
20002088 // Note: The `matchStr` variable wil be fixed up to remove characters that are no longer needed (which will
@@ -2004,19 +2092,23 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
20042092 match , // Will be an Autolinker.match.Match object
20052093
20062094 prefixStr = "" , // A string to use to prefix the anchor tag that is created. This is needed for the Twitter and Hashtag matches.
2007- 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.
2095+ 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.
2096+
2097+ urls = this . urls ; // the 'urls' config
20082098
20092099 // Return out with `null` for match types that are disabled (url, email,
20102100 // twitter, hashtag), or for matches that are invalid (false positives
20112101 // from the matcherRegex, which can't use look-behinds since they are
20122102 // unavailable in JS).
20132103 if (
2014- ( urlMatch && ! this . urls ) ||
2104+ ( schemeUrlMatch && ! urls . schemeMatches ) ||
2105+ ( wwwMatch && ! urls . wwwMatches ) ||
2106+ ( tldMatch && ! urls . tldMatches ) ||
20152107 ( emailAddressMatch && ! this . email ) ||
20162108 ( phoneMatch && ! this . phone ) ||
20172109 ( twitterMatch && ! this . twitter ) ||
20182110 ( hashtagMatch && ! this . hashtag ) ||
2019- ! this . matchValidator . isValidMatch ( urlMatch , protocolUrlMatch , protocolRelativeMatch )
2111+ ! this . matchValidator . isValidMatch ( urlMatch , schemeUrlMatch , protocolRelativeMatch )
20202112 ) {
20212113 return null ;
20222114 }
@@ -2029,7 +2121,7 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
20292121 suffixStr = ")" ; // this will be added after the generated <a> tag
20302122 } else {
20312123 // Handle an invalid character after the TLD
2032- var pos = this . matchHasInvalidCharAfterTld ( urlMatch , protocolUrlMatch ) ;
2124+ var pos = this . matchHasInvalidCharAfterTld ( urlMatch , schemeUrlMatch ) ;
20332125 if ( pos > - 1 ) {
20342126 suffixStr = matchStr . substr ( pos ) ; // this will be added after the generated <a> tag
20352127 matchStr = matchStr . substr ( 0 , pos ) ; // remove the trailing invalid chars
@@ -2081,7 +2173,7 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
20812173 match = new Autolinker . match . Url ( {
20822174 matchedText : matchStr ,
20832175 url : matchStr ,
2084- protocolUrlMatch : ! ! protocolUrlMatch ,
2176+ protocolUrlMatch : ! ! schemeUrlMatch ,
20852177 protocolRelativeMatch : ! ! protocolRelativeMatch ,
20862178 stripPrefix : this . stripPrefix
20872179 } ) ;
0 commit comments