linkify.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*!
  2. * JavaScript Linkify - v0.3 - 6/27/2009
  3. * http://benalman.com/projects/javascript-linkify/
  4. *
  5. * Copyright (c) 2009 "Cowboy" Ben Alman
  6. * Dual licensed under the MIT and GPL licenses.
  7. * http://benalman.com/about/license/
  8. *
  9. * Some regexps adapted from http://userscripts.org/scripts/review/7122
  10. */
  11. // Script: JavaScript Linkify: Process links in text!
  12. //
  13. // *Version: 0.3, Last updated: 6/27/2009*
  14. //
  15. // Project Home - http://benalman.com/projects/javascript-linkify/
  16. // GitHub - http://github.com/cowboy/javascript-linkify/
  17. // Source - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.js
  18. // (Minified) - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.min.js (2.8kb)
  19. //
  20. // About: License
  21. //
  22. // Copyright (c) 2009 "Cowboy" Ben Alman,
  23. // Dual licensed under the MIT and GPL licenses.
  24. // http://benalman.com/about/license/
  25. //
  26. // About: Examples
  27. //
  28. // This working example, complete with fully commented code, illustrates one way
  29. // in which this code can be used.
  30. //
  31. // Linkify - http://benalman.com/code/projects/javascript-linkify/examples/linkify/
  32. //
  33. // About: Support and Testing
  34. //
  35. // Information about what browsers this code has been tested in.
  36. //
  37. // Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.
  38. //
  39. // About: Release History
  40. //
  41. // 0.3 - (6/27/2009) Initial release
  42. // Function: linkify
  43. //
  44. // Turn text into linkified html.
  45. //
  46. // Usage:
  47. //
  48. // > var html = linkify( text [, options ] );
  49. //
  50. // Arguments:
  51. //
  52. // text - (String) Non-HTML text containing links to be parsed.
  53. // options - (Object) An optional object containing linkify parse options.
  54. //
  55. // Options:
  56. //
  57. // callback (Function) - If specified, this will be called once for each link-
  58. // or non-link-chunk with two arguments, text and href. If the chunk is
  59. // non-link, href will be omitted. If unspecified, the default linkification
  60. // callback is used.
  61. // punct_regexp (RegExp) - A RegExp that will be used to trim trailing
  62. // punctuation from links, instead of the default. If set to null, trailing
  63. // punctuation will not be trimmed.
  64. //
  65. // Returns:
  66. //
  67. // (String) An HTML string containing links.
  68. require(["jquery"], function(){
  69. var
  70. SCHEME = "[a-z\\d.-]+://",
  71. IPV4 = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
  72. HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
  73. TLD = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|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|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|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|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
  74. HOST_OR_IP = "(?:" + HOSTNAME + TLD + "|" + IPV4 + ")",
  75. PATH = "(?:[;/][^#?<>\\s]*)?",
  76. QUERY_FRAG = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",
  77. URI1 = "\\b" + SCHEME + "[^<>\\s]+",
  78. URI2 = "\\b" + HOST_OR_IP + PATH + QUERY_FRAG + "(?!\\w)",
  79. MAILTO = "mailto:",
  80. EMAIL = "(?:" + MAILTO + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + HOST_OR_IP + QUERY_FRAG + "(?!\\w)",
  81. URI_RE = new RegExp( "(?:" + URI1 + "|" + URI2 + "|" + EMAIL + ")", "ig" ),
  82. SCHEME_RE = new RegExp( "^" + SCHEME, "i" ),
  83. quotes = {
  84. "'": "`",
  85. '>': '<',
  86. ')': '(',
  87. ']': '[',
  88. '}': '{',
  89. '»': '«',
  90. '›': '‹'
  91. },
  92. default_options = {
  93. callback: function( text, href ) {
  94. return href ? '<a href="' + href + '" title="' + href + '">' + text + '</a>' : text;
  95. },
  96. punct_regexp: /(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
  97. };
  98. window.linkify = function( txt, options ) {
  99. options = options || {};
  100. // Temp variables.
  101. var arr,
  102. i,
  103. link,
  104. href,
  105. // Output HTML.
  106. html = '',
  107. // Store text / link parts, in order, for re-combination.
  108. parts = [],
  109. // Used for keeping track of indices in the text.
  110. idx_prev,
  111. idx_last,
  112. idx,
  113. link_last,
  114. // Used for trimming trailing punctuation and quotes from links.
  115. matches_begin,
  116. matches_end,
  117. quote_begin,
  118. quote_end;
  119. // Initialize options.
  120. for ( i in default_options ) {
  121. if ( options[ i ] === undefined ) {
  122. options[ i ] = default_options[ i ];
  123. }
  124. }
  125. // Find links.
  126. while ( arr = URI_RE.exec( txt ) ) {
  127. link = arr[0];
  128. idx_last = URI_RE.lastIndex;
  129. idx = idx_last - link.length;
  130. // Not a link if preceded by certain characters.
  131. if ( /[\/:]/.test( txt.charAt( idx - 1 ) ) ) {
  132. continue;
  133. }
  134. // Trim trailing punctuation.
  135. do {
  136. // If no changes are made, we don't want to loop forever!
  137. link_last = link;
  138. quote_end = link.substr( -1 )
  139. quote_begin = quotes[ quote_end ];
  140. // Ending quote character?
  141. if ( quote_begin ) {
  142. matches_begin = link.match( new RegExp( '\\' + quote_begin + '(?!$)', 'g' ) );
  143. matches_end = link.match( new RegExp( '\\' + quote_end, 'g' ) );
  144. // If quotes are unbalanced, remove trailing quote character.
  145. if ( ( matches_begin ? matches_begin.length : 0 ) < ( matches_end ? matches_end.length : 0 ) ) {
  146. link = link.substr( 0, link.length - 1 );
  147. idx_last--;
  148. }
  149. }
  150. // Ending non-quote punctuation character?
  151. if ( options.punct_regexp ) {
  152. link = link.replace( options.punct_regexp, function(a){
  153. idx_last -= a.length;
  154. return '';
  155. });
  156. }
  157. } while ( link.length && link !== link_last );
  158. href = link;
  159. // Add appropriate protocol to naked links.
  160. if ( !SCHEME_RE.test( href ) ) {
  161. href = ( href.indexOf( '@' ) !== -1 ? ( !href.indexOf( MAILTO ) ? '' : MAILTO )
  162. : !href.indexOf( 'irc.' ) ? 'irc://'
  163. : !href.indexOf( 'ftp.' ) ? 'ftp://'
  164. : 'http://' )
  165. + href;
  166. }
  167. // Push preceding non-link text onto the array.
  168. if ( idx_prev != idx ) {
  169. parts.push([ txt.slice( idx_prev, idx ) ]);
  170. idx_prev = idx_last;
  171. }
  172. // Push massaged link onto the array
  173. parts.push([ link, href ]);
  174. };
  175. // Push remaining non-link text onto the array.
  176. parts.push([ txt.substr( idx_prev ) ]);
  177. // Process the array items.
  178. for ( i = 0; i < parts.length; i++ ) {
  179. html += options.callback.apply( window, parts[i] );
  180. }
  181. // In case of catastrophic failure, return the original text;
  182. return html || txt;
  183. };
  184. })();