16
16
17
17
/*!
18
18
* Autolinker.js
19
- * 0.21 .0
19
+ * 0.22 .0
20
20
*
21
21
* Copyright(c) 2015 Gregory Jacobs <[email protected] >
22
22
* MIT
@@ -138,44 +138,59 @@ var Autolinker = function( cfg ) {
138
138
throw new Error ( "invalid `hashtag` cfg - see docs" ) ;
139
139
}
140
140
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 ) ;
149
144
} ;
150
145
151
146
Autolinker . prototype = {
152
147
constructor : Autolinker , // fix constructor property
153
148
154
149
/**
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`:
156
157
*
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.
158
170
*/
159
171
urls : true ,
160
172
161
173
/**
162
174
* @cfg {Boolean} email
163
175
*
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.
165
178
*/
166
179
email : true ,
167
180
168
181
/**
169
182
* @cfg {Boolean} twitter
170
183
*
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.
172
186
*/
173
187
twitter : true ,
174
188
175
189
/**
176
190
* @cfg {Boolean} phone
177
191
*
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.
179
194
*/
180
195
phone : true ,
181
196
@@ -313,6 +328,49 @@ Autolinker.prototype = {
313
328
*/
314
329
tagBuilder : undefined ,
315
330
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
+
316
374
/**
317
375
* Automatically links URLs, Email addresses, Phone numbers, Twitter
318
376
* handles, and Hashtags found in the given chunk of HTML. Does not link
@@ -589,6 +647,25 @@ Autolinker.Util = {
589
647
} ,
590
648
591
649
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
+
592
669
/**
593
670
* Extends `superclass` to create a new subclass, adding the `protoProps` to the new subclass's prototype.
594
671
*
@@ -1700,7 +1777,7 @@ Autolinker.htmlParser.TextNode = Autolinker.Util.extend( Autolinker.htmlParser.H
1700
1777
Autolinker . matchParser . MatchParser = Autolinker . Util . extend ( Object , {
1701
1778
1702
1779
/**
1703
- * @cfg {Boolean } urls
1780
+ * @cfg {Object } urls
1704
1781
* @inheritdoc Autolinker#urls
1705
1782
*/
1706
1783
urls : true ,
@@ -1764,26 +1841,31 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
1764
1841
* used to match protocol URLs with just a single word, like 'http://localhost',
1765
1842
* where we won't double check that the domain name has at least one '.'
1766
1843
* 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
1768
1848
* URL. Will be an empty string if it is not a protocol-relative match.
1769
1849
* We need to know the character before the '//' in order to determine
1770
1850
* if it is a valid match or the // was in a string we don't want to
1771
1851
* 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
1773
1855
* URL. Will be an empty string if it is not a protocol-relative match.
1774
1856
* 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
1777
1859
* 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
1779
1861
* (i.e. \#someHashtag). Simply check for its existence to determine if
1780
1862
* there is a Hashtag match. The next couple of capturing groups give
1781
1863
* 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
1783
1865
* is needed because there are no look-behinds in JS regular
1784
1866
* expressions, and can be used to reconstruct the original string in a
1785
1867
* 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
1787
1869
* '#someHashtag', the hashtag is 'someHashtag'.
1788
1870
*/
1789
1871
matcherRegex : ( function ( ) {
@@ -1821,23 +1903,23 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
1821
1903
1822
1904
'(' , // *** Capturing group $5, which is used to match a URL
1823
1905
'(?:' , // 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)
1825
1907
protocolRegex . source ,
1826
1908
domainNameRegex . source ,
1827
1909
')' ,
1828
1910
1829
1911
'|' ,
1830
1912
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
1833
1915
wwwRegex . source ,
1834
1916
domainNameRegex . source ,
1835
1917
')' ,
1836
1918
1837
1919
'|' ,
1838
1920
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
1841
1923
domainNameRegex . source ,
1842
1924
tldRegex . source ,
1843
1925
')' ,
@@ -1849,16 +1931,17 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
1849
1931
'|' ,
1850
1932
1851
1933
// 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
1853
1936
'(' ,
1854
1937
phoneRegex . source ,
1855
1938
')' ,
1856
1939
1857
1940
'|' ,
1858
1941
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
1862
1945
hashtagRegex . source ,
1863
1946
')'
1864
1947
] . join ( "" ) , 'gi' ) ;
@@ -1915,8 +1998,8 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
1915
1998
replace : function ( text , replaceFn , contextObj ) {
1916
1999
var me = this ; // for closure
1917
2000
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
1920
2003
1921
2004
// Return out with no changes for match types that are disabled (url,
1922
2005
// email, phone, etc.), or for matches that are invalid (false
@@ -1956,12 +2039,17 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
1956
2039
* @param {String } emailAddressMatch The matched email address for an email
1957
2040
* address match.
1958
2041
* @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
1960
2043
* match. Ex: 'http://yahoo.com'. This is used to match something like
1961
2044
* 'http://localhost', where we won't double check that the domain name
1962
2045
* 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.).
1963
2049
* @param {String } wwwProtocolRelativeMatch The '//' for a protocol-relative
1964
2050
* 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.
1965
2053
* @param {String } tldProtocolRelativeMatch The '//' for a protocol-relative
1966
2054
* match from a TLD (top level domain) match, with the character that
1967
2055
* comes before the '//'.
@@ -1993,8 +2081,8 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
1993
2081
*/
1994
2082
processCandidateMatch : function (
1995
2083
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 ,
1998
2086
hashtagPrefixWhitespaceChar , hashtag
1999
2087
) {
2000
2088
// 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, {
2004
2092
match , // Will be an Autolinker.match.Match object
2005
2093
2006
2094
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
2008
2098
2009
2099
// Return out with `null` for match types that are disabled (url, email,
2010
2100
// twitter, hashtag), or for matches that are invalid (false positives
2011
2101
// from the matcherRegex, which can't use look-behinds since they are
2012
2102
// unavailable in JS).
2013
2103
if (
2014
- ( urlMatch && ! this . urls ) ||
2104
+ ( schemeUrlMatch && ! urls . schemeMatches ) ||
2105
+ ( wwwMatch && ! urls . wwwMatches ) ||
2106
+ ( tldMatch && ! urls . tldMatches ) ||
2015
2107
( emailAddressMatch && ! this . email ) ||
2016
2108
( phoneMatch && ! this . phone ) ||
2017
2109
( twitterMatch && ! this . twitter ) ||
2018
2110
( hashtagMatch && ! this . hashtag ) ||
2019
- ! this . matchValidator . isValidMatch ( urlMatch , protocolUrlMatch , protocolRelativeMatch )
2111
+ ! this . matchValidator . isValidMatch ( urlMatch , schemeUrlMatch , protocolRelativeMatch )
2020
2112
) {
2021
2113
return null ;
2022
2114
}
@@ -2029,7 +2121,7 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
2029
2121
suffixStr = ")" ; // this will be added after the generated <a> tag
2030
2122
} else {
2031
2123
// Handle an invalid character after the TLD
2032
- var pos = this . matchHasInvalidCharAfterTld ( urlMatch , protocolUrlMatch ) ;
2124
+ var pos = this . matchHasInvalidCharAfterTld ( urlMatch , schemeUrlMatch ) ;
2033
2125
if ( pos > - 1 ) {
2034
2126
suffixStr = matchStr . substr ( pos ) ; // this will be added after the generated <a> tag
2035
2127
matchStr = matchStr . substr ( 0 , pos ) ; // remove the trailing invalid chars
@@ -2081,7 +2173,7 @@ Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, {
2081
2173
match = new Autolinker . match . Url ( {
2082
2174
matchedText : matchStr ,
2083
2175
url : matchStr ,
2084
- protocolUrlMatch : ! ! protocolUrlMatch ,
2176
+ protocolUrlMatch : ! ! schemeUrlMatch ,
2085
2177
protocolRelativeMatch : ! ! protocolRelativeMatch ,
2086
2178
stripPrefix : this . stripPrefix
2087
2179
} ) ;
0 commit comments