linkify-jquery.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. 'use strict';
  2. ;(function (window, linkify, $) {
  3. var linkifyJquery = function (linkify) {
  4. 'use strict';
  5. /**
  6. Linkify a HTML DOM node
  7. */
  8. var tokenize = linkify.tokenize,
  9. options = linkify.options;
  10. var Options = options.Options;
  11. var TEXT_TOKEN = linkify.parser.TOKENS.TEXT;
  12. var HTML_NODE = 1;
  13. var TXT_NODE = 3;
  14. /**
  15. Given a parent element and child node that the parent contains, replaces
  16. that child with the given array of new children
  17. */
  18. function replaceChildWithChildren(parent, oldChild, newChildren) {
  19. var lastNewChild = newChildren[newChildren.length - 1];
  20. parent.replaceChild(lastNewChild, oldChild);
  21. for (var i = newChildren.length - 2; i >= 0; i--) {
  22. parent.insertBefore(newChildren[i], lastNewChild);
  23. lastNewChild = newChildren[i];
  24. }
  25. }
  26. /**
  27. Given an array of MultiTokens, return an array of Nodes that are either
  28. (a) Plain Text nodes (node type 3)
  29. (b) Anchor tag nodes (usually, unless tag name is overridden in the options)
  30. Takes the same options as linkifyElement and an optional doc element
  31. (this should be passed in by linkifyElement)
  32. */
  33. function tokensToNodes(tokens, opts, doc) {
  34. var result = [];
  35. for (var _iterator = tokens, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
  36. var _ref;
  37. if (_isArray) {
  38. if (_i >= _iterator.length) break;
  39. _ref = _iterator[_i++];
  40. } else {
  41. _i = _iterator.next();
  42. if (_i.done) break;
  43. _ref = _i.value;
  44. }
  45. var token = _ref;
  46. if (token.type === 'nl' && opts.nl2br) {
  47. result.push(doc.createElement('br'));
  48. continue;
  49. } else if (!token.isLink || !opts.check(token)) {
  50. result.push(doc.createTextNode(token.toString()));
  51. continue;
  52. }
  53. var _opts$resolve = opts.resolve(token),
  54. formatted = _opts$resolve.formatted,
  55. formattedHref = _opts$resolve.formattedHref,
  56. tagName = _opts$resolve.tagName,
  57. className = _opts$resolve.className,
  58. target = _opts$resolve.target,
  59. events = _opts$resolve.events,
  60. attributes = _opts$resolve.attributes;
  61. // Build the link
  62. var link = doc.createElement(tagName);
  63. link.setAttribute('href', formattedHref);
  64. if (className) {
  65. link.setAttribute('class', className);
  66. }
  67. if (target) {
  68. link.setAttribute('target', target);
  69. }
  70. // Build up additional attributes
  71. if (attributes) {
  72. for (var attr in attributes) {
  73. link.setAttribute(attr, attributes[attr]);
  74. }
  75. }
  76. if (events) {
  77. for (var event in events) {
  78. if (link.addEventListener) {
  79. link.addEventListener(event, events[event]);
  80. } else if (link.attachEvent) {
  81. link.attachEvent('on' + event, events[event]);
  82. }
  83. }
  84. }
  85. link.appendChild(doc.createTextNode(formatted));
  86. result.push(link);
  87. }
  88. return result;
  89. }
  90. // Requires document.createElement
  91. function linkifyElementHelper(element, opts, doc) {
  92. // Can the element be linkified?
  93. if (!element || element.nodeType !== HTML_NODE) {
  94. throw new Error('Cannot linkify ' + element + ' - Invalid DOM Node type');
  95. }
  96. var ignoreTags = opts.ignoreTags;
  97. // Is this element already a link?
  98. if (element.tagName === 'A' || options.contains(ignoreTags, element.tagName)) {
  99. // No need to linkify
  100. return element;
  101. }
  102. var childElement = element.firstChild;
  103. while (childElement) {
  104. var str = void 0,
  105. tokens = void 0,
  106. nodes = void 0;
  107. switch (childElement.nodeType) {
  108. case HTML_NODE:
  109. linkifyElementHelper(childElement, opts, doc);
  110. break;
  111. case TXT_NODE:
  112. {
  113. str = childElement.nodeValue;
  114. tokens = tokenize(str);
  115. if (tokens.length === 0 || tokens.length === 1 && tokens[0] instanceof TEXT_TOKEN) {
  116. // No node replacement required
  117. break;
  118. }
  119. nodes = tokensToNodes(tokens, opts, doc);
  120. // Swap out the current child for the set of nodes
  121. replaceChildWithChildren(element, childElement, nodes);
  122. // so that the correct sibling is selected next
  123. childElement = nodes[nodes.length - 1];
  124. break;
  125. }
  126. }
  127. childElement = childElement.nextSibling;
  128. }
  129. return element;
  130. }
  131. function linkifyElement(element, opts) {
  132. var doc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  133. try {
  134. doc = doc || document || window && window.document || global && global.document;
  135. } catch (e) {/* do nothing for now */}
  136. if (!doc) {
  137. throw new Error('Cannot find document implementation. ' + 'If you are in a non-browser environment like Node.js, ' + 'pass the document implementation as the third argument to linkifyElement.');
  138. }
  139. opts = new Options(opts);
  140. return linkifyElementHelper(element, opts, doc);
  141. }
  142. // Maintain reference to the recursive helper to cache option-normalization
  143. linkifyElement.helper = linkifyElementHelper;
  144. linkifyElement.normalize = function (opts) {
  145. return new Options(opts);
  146. };
  147. // Applies the plugin to jQuery
  148. function apply($) {
  149. var doc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  150. $.fn = $.fn || {};
  151. try {
  152. doc = doc || document || window && window.document || global && global.document;
  153. } catch (e) {/* do nothing for now */}
  154. if (!doc) {
  155. throw new Error('Cannot find document implementation. ' + 'If you are in a non-browser environment like Node.js, ' + 'pass the document implementation as the second argument to linkify/jquery');
  156. }
  157. if (typeof $.fn.linkify === 'function') {
  158. // Already applied
  159. return;
  160. }
  161. function jqLinkify(opts) {
  162. opts = linkifyElement.normalize(opts);
  163. return this.each(function () {
  164. linkifyElement.helper(this, opts, doc);
  165. });
  166. }
  167. $.fn.linkify = jqLinkify;
  168. $(doc).ready(function () {
  169. $('[data-linkify]').each(function () {
  170. var $this = $(this);
  171. var data = $this.data();
  172. var target = data.linkify;
  173. var nl2br = data.linkifyNlbr;
  174. var options = {
  175. nl2br: !!nl2br && nl2br !== 0 && nl2br !== 'false'
  176. };
  177. if ('linkifyAttributes' in data) {
  178. options.attributes = data.linkifyAttributes;
  179. }
  180. if ('linkifyDefaultProtocol' in data) {
  181. options.defaultProtocol = data.linkifyDefaultProtocol;
  182. }
  183. if ('linkifyEvents' in data) {
  184. options.events = data.linkifyEvents;
  185. }
  186. if ('linkifyFormat' in data) {
  187. options.format = data.linkifyFormat;
  188. }
  189. if ('linkifyFormatHref' in data) {
  190. options.formatHref = data.linkifyFormatHref;
  191. }
  192. if ('linkifyTagname' in data) {
  193. options.tagName = data.linkifyTagname;
  194. }
  195. if ('linkifyTarget' in data) {
  196. options.target = data.linkifyTarget;
  197. }
  198. if ('linkifyValidate' in data) {
  199. options.validate = data.linkifyValidate;
  200. }
  201. if ('linkifyIgnoreTags' in data) {
  202. options.ignoreTags = data.linkifyIgnoreTags;
  203. }
  204. if ('linkifyClassName' in data) {
  205. options.className = data.linkifyClassName;
  206. } else if ('linkifyLinkclass' in data) {
  207. // linkClass is deprecated
  208. options.className = data.linkifyLinkclass;
  209. }
  210. options = linkifyElement.normalize(options);
  211. var $target = target === 'this' ? $this : $this.find(target);
  212. $target.linkify(options);
  213. });
  214. });
  215. }
  216. // Try assigning linkifyElement to the browser scope
  217. try {
  218. !undefined.define && (window.linkifyElement = linkifyElement);
  219. } catch (e) {/**/}
  220. return apply;
  221. }(linkify);
  222. if (typeof $.fn.linkify !== 'function') {
  223. linkifyJquery($);
  224. }
  225. })(window, linkify, jQuery);