source: node_modules/autolinker/dist/autolinker.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 215.9 KB
Line 
1/*!
2 * Autolinker.js
3 * v3.16.2
4 *
5 * Copyright(c) 2022 Gregory Jacobs <greg@greg-jacobs.com>
6 * MIT License
7 *
8 * https://github.com/gregjacobs/Autolinker.js
9 */
10(function (global, factory) {
11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12 typeof define === 'function' && define.amd ? define(factory) :
13 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Autolinker = factory());
14})(this, (function () { 'use strict';
15
16 // Important: this file is generated from the 'build' script and should not be
17 // edited directly
18 var version = '3.16.2';
19
20 /**
21 * Assigns (shallow copies) the properties of `src` onto `dest`, if the
22 * corresponding property on `dest` === `undefined`.
23 *
24 * @param {Object} dest The destination object.
25 * @param {Object} src The source object.
26 * @return {Object} The destination object (`dest`)
27 */
28 function defaults(dest, src) {
29 for (var prop in src) {
30 if (src.hasOwnProperty(prop) && dest[prop] === undefined) {
31 dest[prop] = src[prop];
32 }
33 }
34 return dest;
35 }
36 /**
37 * Truncates the `str` at `len - ellipsisChars.length`, and adds the `ellipsisChars` to the
38 * end of the string (by default, two periods: '..'). If the `str` length does not exceed
39 * `len`, the string will be returned unchanged.
40 *
41 * @param {String} str The string to truncate and add an ellipsis to.
42 * @param {Number} truncateLen The length to truncate the string at.
43 * @param {String} [ellipsisChars=...] The ellipsis character(s) to add to the end of `str`
44 * when truncated. Defaults to '...'
45 */
46 function ellipsis(str, truncateLen, ellipsisChars) {
47 var ellipsisLength;
48 if (str.length > truncateLen) {
49 if (ellipsisChars == null) {
50 ellipsisChars = '&hellip;';
51 ellipsisLength = 3;
52 }
53 else {
54 ellipsisLength = ellipsisChars.length;
55 }
56 str = str.substring(0, truncateLen - ellipsisLength) + ellipsisChars;
57 }
58 return str;
59 }
60 /**
61 * Supports `Array.prototype.indexOf()` functionality for old IE (IE8 and below).
62 *
63 * @param {Array} arr The array to find an element of.
64 * @param {*} element The element to find in the array, and return the index of.
65 * @return {Number} The index of the `element`, or -1 if it was not found.
66 */
67 function indexOf(arr, element) {
68 // @ts-ignore - As far as TypeScript is concerned, this method will always
69 // exist (lowest "lib" in TS is "ES5"). Hence we need to ignore this error
70 // to support IE8 which only implements ES3 and doesn't have this method
71 if (Array.prototype.indexOf) {
72 return arr.indexOf(element);
73 }
74 else {
75 for (var i = 0, len = arr.length; i < len; i++) {
76 if (arr[i] === element)
77 return i;
78 }
79 return -1;
80 }
81 }
82 /**
83 * Removes array elements based on a filtering function. Mutates the input
84 * array.
85 *
86 * Using this instead of the ES5 Array.prototype.filter() function, to allow
87 * Autolinker compatibility with IE8, and also to prevent creating many new
88 * arrays in memory for filtering.
89 *
90 * @param {Array} arr The array to remove elements from. This array is
91 * mutated.
92 * @param {Function} fn A function which should return `true` to
93 * remove an element.
94 * @return {Array} The mutated input `arr`.
95 */
96 function remove(arr, fn) {
97 for (var i = arr.length - 1; i >= 0; i--) {
98 if (fn(arr[i]) === true) {
99 arr.splice(i, 1);
100 }
101 }
102 }
103 /**
104 * Performs the functionality of what modern browsers do when `String.prototype.split()` is called
105 * with a regular expression that contains capturing parenthesis.
106 *
107 * For example:
108 *
109 * // Modern browsers:
110 * "a,b,c".split( /(,)/ ); // --> [ 'a', ',', 'b', ',', 'c' ]
111 *
112 * // Old IE (including IE8):
113 * "a,b,c".split( /(,)/ ); // --> [ 'a', 'b', 'c' ]
114 *
115 * This method emulates the functionality of modern browsers for the old IE case.
116 *
117 * @param {String} str The string to split.
118 * @param {RegExp} splitRegex The regular expression to split the input `str` on. The splitting
119 * character(s) will be spliced into the array, as in the "modern browsers" example in the
120 * description of this method.
121 * Note #1: the supplied regular expression **must** have the 'g' flag specified.
122 * Note #2: for simplicity's sake, the regular expression does not need
123 * to contain capturing parenthesis - it will be assumed that any match has them.
124 * @return {String[]} The split array of strings, with the splitting character(s) included.
125 */
126 function splitAndCapture(str, splitRegex) {
127 if (!splitRegex.global)
128 throw new Error("`splitRegex` must have the 'g' flag set");
129 var result = [], lastIdx = 0, match;
130 while ((match = splitRegex.exec(str))) {
131 result.push(str.substring(lastIdx, match.index));
132 result.push(match[0]); // push the splitting char(s)
133 lastIdx = match.index + match[0].length;
134 }
135 result.push(str.substring(lastIdx));
136 return result;
137 }
138 /**
139 * Function that should never be called but is used to check that every
140 * enum value is handled using TypeScript's 'never' type.
141 */
142 function throwUnhandledCaseError(theValue) {
143 throw new Error("Unhandled case for value: '".concat(theValue, "'"));
144 }
145
146 /**
147 * @class Autolinker.HtmlTag
148 * @extends Object
149 *
150 * Represents an HTML tag, which can be used to easily build/modify HTML tags programmatically.
151 *
152 * Autolinker uses this abstraction to create HTML tags, and then write them out as strings. You may also use
153 * this class in your code, especially within a {@link Autolinker#replaceFn replaceFn}.
154 *
155 * ## Examples
156 *
157 * Example instantiation:
158 *
159 * var tag = new Autolinker.HtmlTag( {
160 * tagName : 'a',
161 * attrs : { 'href': 'http://google.com', 'class': 'external-link' },
162 * innerHtml : 'Google'
163 * } );
164 *
165 * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a>
166 *
167 * // Individual accessor methods
168 * tag.getTagName(); // 'a'
169 * tag.getAttr( 'href' ); // 'http://google.com'
170 * tag.hasClass( 'external-link' ); // true
171 *
172 *
173 * Using mutator methods (which may be used in combination with instantiation config properties):
174 *
175 * var tag = new Autolinker.HtmlTag();
176 * tag.setTagName( 'a' );
177 * tag.setAttr( 'href', 'http://google.com' );
178 * tag.addClass( 'external-link' );
179 * tag.setInnerHtml( 'Google' );
180 *
181 * tag.getTagName(); // 'a'
182 * tag.getAttr( 'href' ); // 'http://google.com'
183 * tag.hasClass( 'external-link' ); // true
184 *
185 * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a>
186 *
187 *
188 * ## Example use within a {@link Autolinker#replaceFn replaceFn}
189 *
190 * var html = Autolinker.link( "Test google.com", {
191 * replaceFn : function( match ) {
192 * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance, configured with the Match's href and anchor text
193 * tag.setAttr( 'rel', 'nofollow' );
194 *
195 * return tag;
196 * }
197 * } );
198 *
199 * // generated html:
200 * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
201 *
202 *
203 * ## Example use with a new tag for the replacement
204 *
205 * var html = Autolinker.link( "Test google.com", {
206 * replaceFn : function( match ) {
207 * var tag = new Autolinker.HtmlTag( {
208 * tagName : 'button',
209 * attrs : { 'title': 'Load URL: ' + match.getAnchorHref() },
210 * innerHtml : 'Load URL: ' + match.getAnchorText()
211 * } );
212 *
213 * return tag;
214 * }
215 * } );
216 *
217 * // generated html:
218 * // Test <button title="Load URL: http://google.com">Load URL: google.com</button>
219 */
220 var HtmlTag = /** @class */ (function () {
221 /**
222 * @method constructor
223 * @param {Object} [cfg] The configuration properties for this class, in an Object (map)
224 */
225 function HtmlTag(cfg) {
226 if (cfg === void 0) { cfg = {}; }
227 /**
228 * @cfg {String} tagName
229 *
230 * The tag name. Ex: 'a', 'button', etc.
231 *
232 * Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toAnchorString}
233 * is executed.
234 */
235 this.tagName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
236 /**
237 * @cfg {Object.<String, String>} attrs
238 *
239 * An key/value Object (map) of attributes to create the tag with. The keys are the attribute names, and the
240 * values are the attribute values.
241 */
242 this.attrs = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
243 /**
244 * @cfg {String} innerHTML
245 *
246 * The inner HTML for the tag.
247 */
248 this.innerHTML = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
249 /**
250 * @protected
251 * @property {RegExp} whitespaceRegex
252 *
253 * Regular expression used to match whitespace in a string of CSS classes.
254 */
255 this.whitespaceRegex = /\s+/; // default value just to get the above doc comment in the ES5 output and documentation generator
256 this.tagName = cfg.tagName || '';
257 this.attrs = cfg.attrs || {};
258 this.innerHTML = cfg.innerHtml || cfg.innerHTML || ''; // accept either the camelCased form or the fully capitalized acronym as in the DOM
259 }
260 /**
261 * Sets the tag name that will be used to generate the tag with.
262 *
263 * @param {String} tagName
264 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
265 */
266 HtmlTag.prototype.setTagName = function (tagName) {
267 this.tagName = tagName;
268 return this;
269 };
270 /**
271 * Retrieves the tag name.
272 *
273 * @return {String}
274 */
275 HtmlTag.prototype.getTagName = function () {
276 return this.tagName || '';
277 };
278 /**
279 * Sets an attribute on the HtmlTag.
280 *
281 * @param {String} attrName The attribute name to set.
282 * @param {String} attrValue The attribute value to set.
283 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
284 */
285 HtmlTag.prototype.setAttr = function (attrName, attrValue) {
286 var tagAttrs = this.getAttrs();
287 tagAttrs[attrName] = attrValue;
288 return this;
289 };
290 /**
291 * Retrieves an attribute from the HtmlTag. If the attribute does not exist, returns `undefined`.
292 *
293 * @param {String} attrName The attribute name to retrieve.
294 * @return {String} The attribute's value, or `undefined` if it does not exist on the HtmlTag.
295 */
296 HtmlTag.prototype.getAttr = function (attrName) {
297 return this.getAttrs()[attrName];
298 };
299 /**
300 * Sets one or more attributes on the HtmlTag.
301 *
302 * @param {Object.<String, String>} attrs A key/value Object (map) of the attributes to set.
303 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
304 */
305 HtmlTag.prototype.setAttrs = function (attrs) {
306 Object.assign(this.getAttrs(), attrs);
307 return this;
308 };
309 /**
310 * Retrieves the attributes Object (map) for the HtmlTag.
311 *
312 * @return {Object.<String, String>} A key/value object of the attributes for the HtmlTag.
313 */
314 HtmlTag.prototype.getAttrs = function () {
315 return this.attrs || (this.attrs = {});
316 };
317 /**
318 * Sets the provided `cssClass`, overwriting any current CSS classes on the HtmlTag.
319 *
320 * @param {String} cssClass One or more space-separated CSS classes to set (overwrite).
321 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
322 */
323 HtmlTag.prototype.setClass = function (cssClass) {
324 return this.setAttr('class', cssClass);
325 };
326 /**
327 * Convenience method to add one or more CSS classes to the HtmlTag. Will not add duplicate CSS classes.
328 *
329 * @param {String} cssClass One or more space-separated CSS classes to add.
330 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
331 */
332 HtmlTag.prototype.addClass = function (cssClass) {
333 var classAttr = this.getClass(), whitespaceRegex = this.whitespaceRegex, classes = !classAttr ? [] : classAttr.split(whitespaceRegex), newClasses = cssClass.split(whitespaceRegex), newClass;
334 while ((newClass = newClasses.shift())) {
335 if (indexOf(classes, newClass) === -1) {
336 classes.push(newClass);
337 }
338 }
339 this.getAttrs()['class'] = classes.join(' ');
340 return this;
341 };
342 /**
343 * Convenience method to remove one or more CSS classes from the HtmlTag.
344 *
345 * @param {String} cssClass One or more space-separated CSS classes to remove.
346 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
347 */
348 HtmlTag.prototype.removeClass = function (cssClass) {
349 var classAttr = this.getClass(), whitespaceRegex = this.whitespaceRegex, classes = !classAttr ? [] : classAttr.split(whitespaceRegex), removeClasses = cssClass.split(whitespaceRegex), removeClass;
350 while (classes.length && (removeClass = removeClasses.shift())) {
351 var idx = indexOf(classes, removeClass);
352 if (idx !== -1) {
353 classes.splice(idx, 1);
354 }
355 }
356 this.getAttrs()['class'] = classes.join(' ');
357 return this;
358 };
359 /**
360 * Convenience method to retrieve the CSS class(es) for the HtmlTag, which will each be separated by spaces when
361 * there are multiple.
362 *
363 * @return {String}
364 */
365 HtmlTag.prototype.getClass = function () {
366 return this.getAttrs()['class'] || '';
367 };
368 /**
369 * Convenience method to check if the tag has a CSS class or not.
370 *
371 * @param {String} cssClass The CSS class to check for.
372 * @return {Boolean} `true` if the HtmlTag has the CSS class, `false` otherwise.
373 */
374 HtmlTag.prototype.hasClass = function (cssClass) {
375 return (' ' + this.getClass() + ' ').indexOf(' ' + cssClass + ' ') !== -1;
376 };
377 /**
378 * Sets the inner HTML for the tag.
379 *
380 * @param {String} html The inner HTML to set.
381 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
382 */
383 HtmlTag.prototype.setInnerHTML = function (html) {
384 this.innerHTML = html;
385 return this;
386 };
387 /**
388 * Backwards compatibility method name.
389 *
390 * @param {String} html The inner HTML to set.
391 * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
392 */
393 HtmlTag.prototype.setInnerHtml = function (html) {
394 return this.setInnerHTML(html);
395 };
396 /**
397 * Retrieves the inner HTML for the tag.
398 *
399 * @return {String}
400 */
401 HtmlTag.prototype.getInnerHTML = function () {
402 return this.innerHTML || '';
403 };
404 /**
405 * Backward compatibility method name.
406 *
407 * @return {String}
408 */
409 HtmlTag.prototype.getInnerHtml = function () {
410 return this.getInnerHTML();
411 };
412 /**
413 * Override of superclass method used to generate the HTML string for the tag.
414 *
415 * @return {String}
416 */
417 HtmlTag.prototype.toAnchorString = function () {
418 var tagName = this.getTagName(), attrsStr = this.buildAttrsStr();
419 attrsStr = attrsStr ? ' ' + attrsStr : ''; // prepend a space if there are actually attributes
420 return ['<', tagName, attrsStr, '>', this.getInnerHtml(), '</', tagName, '>'].join('');
421 };
422 /**
423 * Support method for {@link #toAnchorString}, returns the string space-separated key="value" pairs, used to populate
424 * the stringified HtmlTag.
425 *
426 * @protected
427 * @return {String} Example return: `attr1="value1" attr2="value2"`
428 */
429 HtmlTag.prototype.buildAttrsStr = function () {
430 if (!this.attrs)
431 return ''; // no `attrs` Object (map) has been set, return empty string
432 var attrs = this.getAttrs(), attrsArr = [];
433 for (var prop in attrs) {
434 if (attrs.hasOwnProperty(prop)) {
435 attrsArr.push(prop + '="' + attrs[prop] + '"');
436 }
437 }
438 return attrsArr.join(' ');
439 };
440 return HtmlTag;
441 }());
442
443 /**
444 * Date: 2015-10-05
445 * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso)
446 *
447 * A truncation feature, where the ellipsis will be placed at a section within
448 * the URL making it still somewhat human readable.
449 *
450 * @param {String} url A URL.
451 * @param {Number} truncateLen The maximum length of the truncated output URL string.
452 * @param {String} ellipsisChars The characters to place within the url, e.g. "...".
453 * @return {String} The truncated URL.
454 */
455 function truncateSmart(url, truncateLen, ellipsisChars) {
456 var ellipsisLengthBeforeParsing;
457 var ellipsisLength;
458 if (ellipsisChars == null) {
459 ellipsisChars = '&hellip;';
460 ellipsisLength = 3;
461 ellipsisLengthBeforeParsing = 8;
462 }
463 else {
464 ellipsisLength = ellipsisChars.length;
465 ellipsisLengthBeforeParsing = ellipsisChars.length;
466 }
467 var parse_url = function (url) {
468 // Functionality inspired by PHP function of same name
469 var urlObj = {};
470 var urlSub = url;
471 var match = urlSub.match(/^([a-z]+):\/\//i);
472 if (match) {
473 urlObj.scheme = match[1];
474 urlSub = urlSub.substr(match[0].length);
475 }
476 match = urlSub.match(/^(.*?)(?=(\?|#|\/|$))/i);
477 if (match) {
478 urlObj.host = match[1];
479 urlSub = urlSub.substr(match[0].length);
480 }
481 match = urlSub.match(/^\/(.*?)(?=(\?|#|$))/i);
482 if (match) {
483 urlObj.path = match[1];
484 urlSub = urlSub.substr(match[0].length);
485 }
486 match = urlSub.match(/^\?(.*?)(?=(#|$))/i);
487 if (match) {
488 urlObj.query = match[1];
489 urlSub = urlSub.substr(match[0].length);
490 }
491 match = urlSub.match(/^#(.*?)$/i);
492 if (match) {
493 urlObj.fragment = match[1];
494 //urlSub = urlSub.substr(match[0].length); -- not used. Uncomment if adding another block.
495 }
496 return urlObj;
497 };
498 var buildUrl = function (urlObj) {
499 var url = '';
500 if (urlObj.scheme && urlObj.host) {
501 url += urlObj.scheme + '://';
502 }
503 if (urlObj.host) {
504 url += urlObj.host;
505 }
506 if (urlObj.path) {
507 url += '/' + urlObj.path;
508 }
509 if (urlObj.query) {
510 url += '?' + urlObj.query;
511 }
512 if (urlObj.fragment) {
513 url += '#' + urlObj.fragment;
514 }
515 return url;
516 };
517 var buildSegment = function (segment, remainingAvailableLength) {
518 var remainingAvailableLengthHalf = remainingAvailableLength / 2, startOffset = Math.ceil(remainingAvailableLengthHalf), endOffset = -1 * Math.floor(remainingAvailableLengthHalf), end = '';
519 if (endOffset < 0) {
520 end = segment.substr(endOffset);
521 }
522 return segment.substr(0, startOffset) + ellipsisChars + end;
523 };
524 if (url.length <= truncateLen) {
525 return url;
526 }
527 var availableLength = truncateLen - ellipsisLength;
528 var urlObj = parse_url(url);
529 // Clean up the URL
530 if (urlObj.query) {
531 var matchQuery = urlObj.query.match(/^(.*?)(?=(\?|\#))(.*?)$/i);
532 if (matchQuery) {
533 // Malformed URL; two or more "?". Removed any content behind the 2nd.
534 urlObj.query = urlObj.query.substr(0, matchQuery[1].length);
535 url = buildUrl(urlObj);
536 }
537 }
538 if (url.length <= truncateLen) {
539 return url;
540 }
541 if (urlObj.host) {
542 urlObj.host = urlObj.host.replace(/^www\./, '');
543 url = buildUrl(urlObj);
544 }
545 if (url.length <= truncateLen) {
546 return url;
547 }
548 // Process and build the URL
549 var str = '';
550 if (urlObj.host) {
551 str += urlObj.host;
552 }
553 if (str.length >= availableLength) {
554 if (urlObj.host.length == truncateLen) {
555 return (urlObj.host.substr(0, truncateLen - ellipsisLength) + ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing);
556 }
557 return buildSegment(str, availableLength).substr(0, availableLength + ellipsisLengthBeforeParsing);
558 }
559 var pathAndQuery = '';
560 if (urlObj.path) {
561 pathAndQuery += '/' + urlObj.path;
562 }
563 if (urlObj.query) {
564 pathAndQuery += '?' + urlObj.query;
565 }
566 if (pathAndQuery) {
567 if ((str + pathAndQuery).length >= availableLength) {
568 if ((str + pathAndQuery).length == truncateLen) {
569 return (str + pathAndQuery).substr(0, truncateLen);
570 }
571 var remainingAvailableLength = availableLength - str.length;
572 return (str + buildSegment(pathAndQuery, remainingAvailableLength)).substr(0, availableLength + ellipsisLengthBeforeParsing);
573 }
574 else {
575 str += pathAndQuery;
576 }
577 }
578 if (urlObj.fragment) {
579 var fragment = '#' + urlObj.fragment;
580 if ((str + fragment).length >= availableLength) {
581 if ((str + fragment).length == truncateLen) {
582 return (str + fragment).substr(0, truncateLen);
583 }
584 var remainingAvailableLength2 = availableLength - str.length;
585 return (str + buildSegment(fragment, remainingAvailableLength2)).substr(0, availableLength + ellipsisLengthBeforeParsing);
586 }
587 else {
588 str += fragment;
589 }
590 }
591 if (urlObj.scheme && urlObj.host) {
592 var scheme = urlObj.scheme + '://';
593 if ((str + scheme).length < availableLength) {
594 return (scheme + str).substr(0, truncateLen);
595 }
596 }
597 if (str.length <= truncateLen) {
598 return str;
599 }
600 var end = '';
601 if (availableLength > 0) {
602 end = str.substr(-1 * Math.floor(availableLength / 2));
603 }
604 return (str.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
605 }
606
607 /**
608 * Date: 2015-10-05
609 * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso)
610 *
611 * A truncation feature, where the ellipsis will be placed in the dead-center of the URL.
612 *
613 * @param {String} url A URL.
614 * @param {Number} truncateLen The maximum length of the truncated output URL string.
615 * @param {String} ellipsisChars The characters to place within the url, e.g. "..".
616 * @return {String} The truncated URL.
617 */
618 function truncateMiddle(url, truncateLen, ellipsisChars) {
619 if (url.length <= truncateLen) {
620 return url;
621 }
622 var ellipsisLengthBeforeParsing;
623 var ellipsisLength;
624 if (ellipsisChars == null) {
625 ellipsisChars = '&hellip;';
626 ellipsisLengthBeforeParsing = 8;
627 ellipsisLength = 3;
628 }
629 else {
630 ellipsisLengthBeforeParsing = ellipsisChars.length;
631 ellipsisLength = ellipsisChars.length;
632 }
633 var availableLength = truncateLen - ellipsisLength;
634 var end = '';
635 if (availableLength > 0) {
636 end = url.substr(-1 * Math.floor(availableLength / 2));
637 }
638 return (url.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
639 }
640
641 /**
642 * A truncation feature where the ellipsis will be placed at the end of the URL.
643 *
644 * @param {String} anchorText
645 * @param {Number} truncateLen The maximum length of the truncated output URL string.
646 * @param {String} ellipsisChars The characters to place within the url, e.g. "..".
647 * @return {String} The truncated URL.
648 */
649 function truncateEnd(anchorText, truncateLen, ellipsisChars) {
650 return ellipsis(anchorText, truncateLen, ellipsisChars);
651 }
652
653 /**
654 * @protected
655 * @class Autolinker.AnchorTagBuilder
656 * @extends Object
657 *
658 * Builds anchor (&lt;a&gt;) tags for the Autolinker utility when a match is
659 * found.
660 *
661 * Normally this class is instantiated, configured, and used internally by an
662 * {@link Autolinker} instance, but may actually be used indirectly in a
663 * {@link Autolinker#replaceFn replaceFn} to create {@link Autolinker.HtmlTag HtmlTag}
664 * instances which may be modified before returning from the
665 * {@link Autolinker#replaceFn replaceFn}. For example:
666 *
667 * var html = Autolinker.link( "Test google.com", {
668 * replaceFn : function( match ) {
669 * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance
670 * tag.setAttr( 'rel', 'nofollow' );
671 *
672 * return tag;
673 * }
674 * } );
675 *
676 * // generated html:
677 * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
678 */
679 var AnchorTagBuilder = /** @class */ (function () {
680 /**
681 * @method constructor
682 * @param {Object} [cfg] The configuration options for the AnchorTagBuilder instance, specified in an Object (map).
683 */
684 function AnchorTagBuilder(cfg) {
685 if (cfg === void 0) { cfg = {}; }
686 /**
687 * @cfg {Boolean} newWindow
688 * @inheritdoc Autolinker#newWindow
689 */
690 this.newWindow = false; // default value just to get the above doc comment in the ES5 output and documentation generator
691 /**
692 * @cfg {Object} truncate
693 * @inheritdoc Autolinker#truncate
694 */
695 this.truncate = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
696 /**
697 * @cfg {String} className
698 * @inheritdoc Autolinker#className
699 */
700 this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
701 this.newWindow = cfg.newWindow || false;
702 this.truncate = cfg.truncate || {};
703 this.className = cfg.className || '';
704 }
705 /**
706 * Generates the actual anchor (&lt;a&gt;) tag to use in place of the
707 * matched text, via its `match` object.
708 *
709 * @param {Autolinker.match.Match} match The Match instance to generate an
710 * anchor tag from.
711 * @return {Autolinker.HtmlTag} The HtmlTag instance for the anchor tag.
712 */
713 AnchorTagBuilder.prototype.build = function (match) {
714 return new HtmlTag({
715 tagName: 'a',
716 attrs: this.createAttrs(match),
717 innerHtml: this.processAnchorText(match.getAnchorText()),
718 });
719 };
720 /**
721 * Creates the Object (map) of the HTML attributes for the anchor (&lt;a&gt;)
722 * tag being generated.
723 *
724 * @protected
725 * @param {Autolinker.match.Match} match The Match instance to generate an
726 * anchor tag from.
727 * @return {Object} A key/value Object (map) of the anchor tag's attributes.
728 */
729 AnchorTagBuilder.prototype.createAttrs = function (match) {
730 var attrs = {
731 href: match.getAnchorHref(), // we'll always have the `href` attribute
732 };
733 var cssClass = this.createCssClass(match);
734 if (cssClass) {
735 attrs['class'] = cssClass;
736 }
737 if (this.newWindow) {
738 attrs['target'] = '_blank';
739 attrs['rel'] = 'noopener noreferrer'; // Issue #149. See https://mathiasbynens.github.io/rel-noopener/
740 }
741 if (this.truncate) {
742 if (this.truncate.length && this.truncate.length < match.getAnchorText().length) {
743 attrs['title'] = match.getAnchorHref();
744 }
745 }
746 return attrs;
747 };
748 /**
749 * Creates the CSS class that will be used for a given anchor tag, based on
750 * the `matchType` and the {@link #className} config.
751 *
752 * Example returns:
753 *
754 * - "" // no {@link #className}
755 * - "myLink myLink-url" // url match
756 * - "myLink myLink-email" // email match
757 * - "myLink myLink-phone" // phone match
758 * - "myLink myLink-hashtag" // hashtag match
759 * - "myLink myLink-mention myLink-twitter" // mention match with Twitter service
760 *
761 * @protected
762 * @param {Autolinker.match.Match} match The Match instance to generate an
763 * anchor tag from.
764 * @return {String} The CSS class string for the link. Example return:
765 * "myLink myLink-url". If no {@link #className} was configured, returns
766 * an empty string.
767 */
768 AnchorTagBuilder.prototype.createCssClass = function (match) {
769 var className = this.className;
770 if (!className) {
771 return '';
772 }
773 else {
774 var returnClasses = [className], cssClassSuffixes = match.getCssClassSuffixes();
775 for (var i = 0, len = cssClassSuffixes.length; i < len; i++) {
776 returnClasses.push(className + '-' + cssClassSuffixes[i]);
777 }
778 return returnClasses.join(' ');
779 }
780 };
781 /**
782 * Processes the `anchorText` by truncating the text according to the
783 * {@link #truncate} config.
784 *
785 * @private
786 * @param {String} anchorText The anchor tag's text (i.e. what will be
787 * displayed).
788 * @return {String} The processed `anchorText`.
789 */
790 AnchorTagBuilder.prototype.processAnchorText = function (anchorText) {
791 anchorText = this.doTruncate(anchorText);
792 return anchorText;
793 };
794 /**
795 * Performs the truncation of the `anchorText` based on the {@link #truncate}
796 * option. If the `anchorText` is longer than the length specified by the
797 * {@link #truncate} option, the truncation is performed based on the
798 * `location` property. See {@link #truncate} for details.
799 *
800 * @private
801 * @param {String} anchorText The anchor tag's text (i.e. what will be
802 * displayed).
803 * @return {String} The truncated anchor text.
804 */
805 AnchorTagBuilder.prototype.doTruncate = function (anchorText) {
806 var truncate = this.truncate;
807 if (!truncate || !truncate.length)
808 return anchorText;
809 var truncateLength = truncate.length, truncateLocation = truncate.location;
810 if (truncateLocation === 'smart') {
811 return truncateSmart(anchorText, truncateLength);
812 }
813 else if (truncateLocation === 'middle') {
814 return truncateMiddle(anchorText, truncateLength);
815 }
816 else {
817 return truncateEnd(anchorText, truncateLength);
818 }
819 };
820 return AnchorTagBuilder;
821 }());
822
823 /**
824 * @abstract
825 * @class Autolinker.match.Match
826 *
827 * Represents a match found in an input string which should be Autolinked. A Match object is what is provided in a
828 * {@link Autolinker#replaceFn replaceFn}, and may be used to query for details about the match.
829 *
830 * For example:
831 *
832 * var input = "..."; // string with URLs, Email Addresses, and Mentions (Twitter, Instagram, Soundcloud)
833 *
834 * var linkedText = Autolinker.link( input, {
835 * replaceFn : function( match ) {
836 * console.log( "href = ", match.getAnchorHref() );
837 * console.log( "text = ", match.getAnchorText() );
838 *
839 * switch( match.getType() ) {
840 * case 'url' :
841 * console.log( "url: ", match.getUrl() );
842 *
843 * case 'email' :
844 * console.log( "email: ", match.getEmail() );
845 *
846 * case 'mention' :
847 * console.log( "mention: ", match.getMention() );
848 * }
849 * }
850 * } );
851 *
852 * See the {@link Autolinker} class for more details on using the {@link Autolinker#replaceFn replaceFn}.
853 */
854 var Match = /** @class */ (function () {
855 /**
856 * @member Autolinker.match.Match
857 * @method constructor
858 * @param {Object} cfg The configuration properties for the Match
859 * instance, specified in an Object (map).
860 */
861 function Match(cfg) {
862 /**
863 * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required)
864 *
865 * Reference to the AnchorTagBuilder instance to use to generate an anchor
866 * tag for the Match.
867 */
868 // @ts-ignore
869 this.__jsduckDummyDocProp = null; // property used just to get the above doc comment into the ES5 output and documentation generator
870 /**
871 * @cfg {String} matchedText (required)
872 *
873 * The original text that was matched by the {@link Autolinker.matcher.Matcher}.
874 */
875 this.matchedText = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
876 /**
877 * @cfg {Number} offset (required)
878 *
879 * The offset of where the match was made in the input string.
880 */
881 this.offset = 0; // default value just to get the above doc comment in the ES5 output and documentation generator
882 this.tagBuilder = cfg.tagBuilder;
883 this.matchedText = cfg.matchedText;
884 this.offset = cfg.offset;
885 }
886 /**
887 * Returns the original text that was matched.
888 *
889 * @return {String}
890 */
891 Match.prototype.getMatchedText = function () {
892 return this.matchedText;
893 };
894 /**
895 * Sets the {@link #offset} of where the match was made in the input string.
896 *
897 * A {@link Autolinker.matcher.Matcher} will be fed only HTML text nodes,
898 * and will therefore set an original offset that is relative to the HTML
899 * text node itself. However, we want this offset to be relative to the full
900 * HTML input string, and thus if using {@link Autolinker#parse} (rather
901 * than calling a {@link Autolinker.matcher.Matcher} directly), then this
902 * offset is corrected after the Matcher itself has done its job.
903 *
904 * @param {Number} offset
905 */
906 Match.prototype.setOffset = function (offset) {
907 this.offset = offset;
908 };
909 /**
910 * Returns the offset of where the match was made in the input string. This
911 * is the 0-based index of the match.
912 *
913 * @return {Number}
914 */
915 Match.prototype.getOffset = function () {
916 return this.offset;
917 };
918 /**
919 * Returns the CSS class suffix(es) for this match.
920 *
921 * A CSS class suffix is appended to the {@link Autolinker#className} in
922 * the {@link Autolinker.AnchorTagBuilder} when a match is translated into
923 * an anchor tag.
924 *
925 * For example, if {@link Autolinker#className} was configured as 'myLink',
926 * and this method returns `[ 'url' ]`, the final class name of the element
927 * will become: 'myLink myLink-url'.
928 *
929 * The match may provide multiple CSS class suffixes to be appended to the
930 * {@link Autolinker#className} in order to facilitate better styling
931 * options for different match criteria. See {@link Autolinker.match.Mention}
932 * for an example.
933 *
934 * By default, this method returns a single array with the match's
935 * {@link #getType type} name, but may be overridden by subclasses.
936 *
937 * @return {String[]}
938 */
939 Match.prototype.getCssClassSuffixes = function () {
940 return [this.getType()];
941 };
942 /**
943 * Builds and returns an {@link Autolinker.HtmlTag} instance based on the
944 * Match.
945 *
946 * This can be used to easily generate anchor tags from matches, and either
947 * return their HTML string, or modify them before doing so.
948 *
949 * Example Usage:
950 *
951 * var tag = match.buildTag();
952 * tag.addClass( 'cordova-link' );
953 * tag.setAttr( 'target', '_system' );
954 *
955 * tag.toAnchorString(); // <a href="http://google.com" class="cordova-link" target="_system">Google</a>
956 *
957 * Example Usage in {@link Autolinker#replaceFn}:
958 *
959 * var html = Autolinker.link( "Test google.com", {
960 * replaceFn : function( match ) {
961 * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance
962 * tag.setAttr( 'rel', 'nofollow' );
963 *
964 * return tag;
965 * }
966 * } );
967 *
968 * // generated html:
969 * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
970 */
971 Match.prototype.buildTag = function () {
972 return this.tagBuilder.build(this);
973 };
974 return Match;
975 }());
976
977 /*! *****************************************************************************
978 Copyright (c) Microsoft Corporation.
979
980 Permission to use, copy, modify, and/or distribute this software for any
981 purpose with or without fee is hereby granted.
982
983 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
984 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
985 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
986 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
987 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
988 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
989 PERFORMANCE OF THIS SOFTWARE.
990 ***************************************************************************** */
991 /* global Reflect, Promise */
992
993 var extendStatics = function(d, b) {
994 extendStatics = Object.setPrototypeOf ||
995 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
996 function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
997 return extendStatics(d, b);
998 };
999
1000 function __extends(d, b) {
1001 if (typeof b !== "function" && b !== null)
1002 throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
1003 extendStatics(d, b);
1004 function __() { this.constructor = d; }
1005 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
1006 }
1007
1008 var __assign = function() {
1009 __assign = Object.assign || function __assign(t) {
1010 for (var s, i = 1, n = arguments.length; i < n; i++) {
1011 s = arguments[i];
1012 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
1013 }
1014 return t;
1015 };
1016 return __assign.apply(this, arguments);
1017 };
1018
1019 /**
1020 * @class Autolinker.match.Email
1021 * @extends Autolinker.match.Match
1022 *
1023 * Represents a Email match found in an input string which should be Autolinked.
1024 *
1025 * See this class's superclass ({@link Autolinker.match.Match}) for more details.
1026 */
1027 var EmailMatch = /** @class */ (function (_super) {
1028 __extends(EmailMatch, _super);
1029 /**
1030 * @method constructor
1031 * @param {Object} cfg The configuration properties for the Match
1032 * instance, specified in an Object (map).
1033 */
1034 function EmailMatch(cfg) {
1035 var _this = _super.call(this, cfg) || this;
1036 /**
1037 * @cfg {String} email (required)
1038 *
1039 * The email address that was matched.
1040 */
1041 _this.email = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
1042 _this.email = cfg.email;
1043 return _this;
1044 }
1045 /**
1046 * Returns a string name for the type of match that this class represents.
1047 * For the case of EmailMatch, returns 'email'.
1048 *
1049 * @return {String}
1050 */
1051 EmailMatch.prototype.getType = function () {
1052 return 'email';
1053 };
1054 /**
1055 * Returns the email address that was matched.
1056 *
1057 * @return {String}
1058 */
1059 EmailMatch.prototype.getEmail = function () {
1060 return this.email;
1061 };
1062 /**
1063 * Returns the anchor href that should be generated for the match.
1064 *
1065 * @return {String}
1066 */
1067 EmailMatch.prototype.getAnchorHref = function () {
1068 return 'mailto:' + this.email;
1069 };
1070 /**
1071 * Returns the anchor text that should be generated for the match.
1072 *
1073 * @return {String}
1074 */
1075 EmailMatch.prototype.getAnchorText = function () {
1076 return this.email;
1077 };
1078 return EmailMatch;
1079 }(Match));
1080
1081 /**
1082 * @class Autolinker.match.Hashtag
1083 * @extends Autolinker.match.Match
1084 *
1085 * Represents a Hashtag match found in an input string which should be
1086 * Autolinked.
1087 *
1088 * See this class's superclass ({@link Autolinker.match.Match}) for more
1089 * details.
1090 */
1091 var HashtagMatch = /** @class */ (function (_super) {
1092 __extends(HashtagMatch, _super);
1093 /**
1094 * @method constructor
1095 * @param {Object} cfg The configuration properties for the Match
1096 * instance, specified in an Object (map).
1097 */
1098 function HashtagMatch(cfg) {
1099 var _this = _super.call(this, cfg) || this;
1100 /**
1101 * @cfg {String} serviceName
1102 *
1103 * The service to point hashtag matches to. See {@link Autolinker#hashtag}
1104 * for available values.
1105 */
1106 _this.serviceName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
1107 /**
1108 * @cfg {String} hashtag (required)
1109 *
1110 * The HashtagMatch that was matched, without the '#'.
1111 */
1112 _this.hashtag = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
1113 _this.serviceName = cfg.serviceName;
1114 _this.hashtag = cfg.hashtag;
1115 return _this;
1116 }
1117 /**
1118 * Returns a string name for the type of match that this class represents.
1119 * For the case of HashtagMatch, returns 'hashtag'.
1120 *
1121 * @return {String}
1122 */
1123 HashtagMatch.prototype.getType = function () {
1124 return 'hashtag';
1125 };
1126 /**
1127 * Returns the configured {@link #serviceName} to point the HashtagMatch to.
1128 * Ex: 'facebook', 'twitter'.
1129 *
1130 * @return {String}
1131 */
1132 HashtagMatch.prototype.getServiceName = function () {
1133 return this.serviceName;
1134 };
1135 /**
1136 * Returns the matched hashtag, without the '#' character.
1137 *
1138 * @return {String}
1139 */
1140 HashtagMatch.prototype.getHashtag = function () {
1141 return this.hashtag;
1142 };
1143 /**
1144 * Returns the anchor href that should be generated for the match.
1145 *
1146 * @return {String}
1147 */
1148 HashtagMatch.prototype.getAnchorHref = function () {
1149 var serviceName = this.serviceName, hashtag = this.hashtag;
1150 switch (serviceName) {
1151 case 'twitter':
1152 return 'https://twitter.com/hashtag/' + hashtag;
1153 case 'facebook':
1154 return 'https://www.facebook.com/hashtag/' + hashtag;
1155 case 'instagram':
1156 return 'https://instagram.com/explore/tags/' + hashtag;
1157 case 'tiktok':
1158 return 'https://www.tiktok.com/tag/' + hashtag;
1159 default:
1160 // Shouldn't happen because Autolinker's constructor should block any invalid values, but just in case.
1161 throw new Error('Unknown service name to point hashtag to: ' + serviceName);
1162 }
1163 };
1164 /**
1165 * Returns the anchor text that should be generated for the match.
1166 *
1167 * @return {String}
1168 */
1169 HashtagMatch.prototype.getAnchorText = function () {
1170 return '#' + this.hashtag;
1171 };
1172 return HashtagMatch;
1173 }(Match));
1174
1175 /**
1176 * @class Autolinker.match.Mention
1177 * @extends Autolinker.match.Match
1178 *
1179 * Represents a Mention match found in an input string which should be Autolinked.
1180 *
1181 * See this class's superclass ({@link Autolinker.match.Match}) for more details.
1182 */
1183 var MentionMatch = /** @class */ (function (_super) {
1184 __extends(MentionMatch, _super);
1185 /**
1186 * @method constructor
1187 * @param {Object} cfg The configuration properties for the Match
1188 * instance, specified in an Object (map).
1189 */
1190 function MentionMatch(cfg) {
1191 var _this = _super.call(this, cfg) || this;
1192 /**
1193 * @cfg {String} serviceName
1194 *
1195 * The service to point mention matches to. See {@link Autolinker#mention}
1196 * for available values.
1197 */
1198 _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
1199 /**
1200 * @cfg {String} mention (required)
1201 *
1202 * The Mention that was matched, without the '@' character.
1203 */
1204 _this.mention = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
1205 _this.mention = cfg.mention;
1206 _this.serviceName = cfg.serviceName;
1207 return _this;
1208 }
1209 /**
1210 * Returns a string name for the type of match that this class represents.
1211 * For the case of MentionMatch, returns 'mention'.
1212 *
1213 * @return {String}
1214 */
1215 MentionMatch.prototype.getType = function () {
1216 return 'mention';
1217 };
1218 /**
1219 * Returns the mention, without the '@' character.
1220 *
1221 * @return {String}
1222 */
1223 MentionMatch.prototype.getMention = function () {
1224 return this.mention;
1225 };
1226 /**
1227 * Returns the configured {@link #serviceName} to point the mention to.
1228 * Ex: 'instagram', 'twitter', 'soundcloud'.
1229 *
1230 * @return {String}
1231 */
1232 MentionMatch.prototype.getServiceName = function () {
1233 return this.serviceName;
1234 };
1235 /**
1236 * Returns the anchor href that should be generated for the match.
1237 *
1238 * @return {String}
1239 */
1240 MentionMatch.prototype.getAnchorHref = function () {
1241 switch (this.serviceName) {
1242 case 'twitter':
1243 return 'https://twitter.com/' + this.mention;
1244 case 'instagram':
1245 return 'https://instagram.com/' + this.mention;
1246 case 'soundcloud':
1247 return 'https://soundcloud.com/' + this.mention;
1248 case 'tiktok':
1249 return 'https://www.tiktok.com/@' + this.mention;
1250 default:
1251 // Shouldn't happen because Autolinker's constructor should block any invalid values, but just in case.
1252 throw new Error('Unknown service name to point mention to: ' + this.serviceName);
1253 }
1254 };
1255 /**
1256 * Returns the anchor text that should be generated for the match.
1257 *
1258 * @return {String}
1259 */
1260 MentionMatch.prototype.getAnchorText = function () {
1261 return '@' + this.mention;
1262 };
1263 /**
1264 * Returns the CSS class suffixes that should be used on a tag built with
1265 * the match. See {@link Autolinker.match.Match#getCssClassSuffixes} for
1266 * details.
1267 *
1268 * @return {String[]}
1269 */
1270 MentionMatch.prototype.getCssClassSuffixes = function () {
1271 var cssClassSuffixes = _super.prototype.getCssClassSuffixes.call(this), serviceName = this.getServiceName();
1272 if (serviceName) {
1273 cssClassSuffixes.push(serviceName);
1274 }
1275 return cssClassSuffixes;
1276 };
1277 return MentionMatch;
1278 }(Match));
1279
1280 /**
1281 * @class Autolinker.match.Phone
1282 * @extends Autolinker.match.Match
1283 *
1284 * Represents a Phone number match found in an input string which should be
1285 * Autolinked.
1286 *
1287 * See this class's superclass ({@link Autolinker.match.Match}) for more
1288 * details.
1289 */
1290 var PhoneMatch = /** @class */ (function (_super) {
1291 __extends(PhoneMatch, _super);
1292 /**
1293 * @method constructor
1294 * @param {Object} cfg The configuration properties for the Match
1295 * instance, specified in an Object (map).
1296 */
1297 function PhoneMatch(cfg) {
1298 var _this = _super.call(this, cfg) || this;
1299 /**
1300 * @protected
1301 * @property {String} number (required)
1302 *
1303 * The phone number that was matched, without any delimiter characters.
1304 *
1305 * Note: This is a string to allow for prefixed 0's.
1306 */
1307 _this.number = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
1308 /**
1309 * @protected
1310 * @property {Boolean} plusSign (required)
1311 *
1312 * `true` if the matched phone number started with a '+' sign. We'll include
1313 * it in the `tel:` URL if so, as this is needed for international numbers.
1314 *
1315 * Ex: '+1 (123) 456 7879'
1316 */
1317 _this.plusSign = false; // default value just to get the above doc comment in the ES5 output and documentation generator
1318 _this.number = cfg.number;
1319 _this.plusSign = cfg.plusSign;
1320 return _this;
1321 }
1322 /**
1323 * Returns a string name for the type of match that this class represents.
1324 * For the case of PhoneMatch, returns 'phone'.
1325 *
1326 * @return {String}
1327 */
1328 PhoneMatch.prototype.getType = function () {
1329 return 'phone';
1330 };
1331 /**
1332 * Returns the phone number that was matched as a string, without any
1333 * delimiter characters.
1334 *
1335 * Note: This is a string to allow for prefixed 0's.
1336 *
1337 * @return {String}
1338 */
1339 PhoneMatch.prototype.getPhoneNumber = function () {
1340 return this.number;
1341 };
1342 /**
1343 * Alias of {@link #getPhoneNumber}, returns the phone number that was
1344 * matched as a string, without any delimiter characters.
1345 *
1346 * Note: This is a string to allow for prefixed 0's.
1347 *
1348 * @return {String}
1349 */
1350 PhoneMatch.prototype.getNumber = function () {
1351 return this.getPhoneNumber();
1352 };
1353 /**
1354 * Returns the anchor href that should be generated for the match.
1355 *
1356 * @return {String}
1357 */
1358 PhoneMatch.prototype.getAnchorHref = function () {
1359 return 'tel:' + (this.plusSign ? '+' : '') + this.number;
1360 };
1361 /**
1362 * Returns the anchor text that should be generated for the match.
1363 *
1364 * @return {String}
1365 */
1366 PhoneMatch.prototype.getAnchorText = function () {
1367 return this.matchedText;
1368 };
1369 return PhoneMatch;
1370 }(Match));
1371
1372 /**
1373 * @class Autolinker.match.Url
1374 * @extends Autolinker.match.Match
1375 *
1376 * Represents a Url match found in an input string which should be Autolinked.
1377 *
1378 * See this class's superclass ({@link Autolinker.match.Match}) for more details.
1379 */
1380 var UrlMatch = /** @class */ (function (_super) {
1381 __extends(UrlMatch, _super);
1382 /**
1383 * @method constructor
1384 * @param {Object} cfg The configuration properties for the Match
1385 * instance, specified in an Object (map).
1386 */
1387 function UrlMatch(cfg) {
1388 var _this = _super.call(this, cfg) || this;
1389 /**
1390 * @cfg {String} url (required)
1391 *
1392 * The url that was matched.
1393 */
1394 _this.url = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
1395 /**
1396 * @cfg {"scheme"/"www"/"tld"} urlMatchType (required)
1397 *
1398 * The type of URL match that this class represents. This helps to determine
1399 * if the match was made in the original text with a prefixed scheme (ex:
1400 * 'http://www.google.com'), a prefixed 'www' (ex: 'www.google.com'), or
1401 * was matched by a known top-level domain (ex: 'google.com').
1402 */
1403 _this.urlMatchType = 'scheme'; // default value just to get the above doc comment in the ES5 output and documentation generator
1404 /**
1405 * @cfg {Boolean} protocolUrlMatch (required)
1406 *
1407 * `true` if the URL is a match which already has a protocol (i.e.
1408 * 'http://'), `false` if the match was from a 'www' or known TLD match.
1409 */
1410 _this.protocolUrlMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator
1411 /**
1412 * @cfg {Boolean} protocolRelativeMatch (required)
1413 *
1414 * `true` if the URL is a protocol-relative match. A protocol-relative match
1415 * is a URL that starts with '//', and will be either http:// or https://
1416 * based on the protocol that the site is loaded under.
1417 */
1418 _this.protocolRelativeMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator
1419 /**
1420 * @cfg {Object} stripPrefix (required)
1421 *
1422 * The Object form of {@link Autolinker#cfg-stripPrefix}.
1423 */
1424 _this.stripPrefix = {
1425 scheme: true,
1426 www: true,
1427 }; // default value just to get the above doc comment in the ES5 output and documentation generator
1428 /**
1429 * @cfg {Boolean} stripTrailingSlash (required)
1430 * @inheritdoc Autolinker#cfg-stripTrailingSlash
1431 */
1432 _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
1433 /**
1434 * @cfg {Boolean} decodePercentEncoding (required)
1435 * @inheritdoc Autolinker#cfg-decodePercentEncoding
1436 */
1437 _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
1438 /**
1439 * @private
1440 * @property {RegExp} schemePrefixRegex
1441 *
1442 * A regular expression used to remove the 'http://' or 'https://' from
1443 * URLs.
1444 */
1445 _this.schemePrefixRegex = /^(https?:\/\/)?/i;
1446 /**
1447 * @private
1448 * @property {RegExp} wwwPrefixRegex
1449 *
1450 * A regular expression used to remove the 'www.' from URLs.
1451 */
1452 _this.wwwPrefixRegex = /^(https?:\/\/)?(www\.)?/i;
1453 /**
1454 * @private
1455 * @property {RegExp} protocolRelativeRegex
1456 *
1457 * The regular expression used to remove the protocol-relative '//' from the {@link #url} string, for purposes
1458 * of {@link #getAnchorText}. A protocol-relative URL is, for example, "//yahoo.com"
1459 */
1460 _this.protocolRelativeRegex = /^\/\//;
1461 /**
1462 * @private
1463 * @property {Boolean} protocolPrepended
1464 *
1465 * Will be set to `true` if the 'http://' protocol has been prepended to the {@link #url} (because the
1466 * {@link #url} did not have a protocol)
1467 */
1468 _this.protocolPrepended = false;
1469 _this.urlMatchType = cfg.urlMatchType;
1470 _this.url = cfg.url;
1471 _this.protocolUrlMatch = cfg.protocolUrlMatch;
1472 _this.protocolRelativeMatch = cfg.protocolRelativeMatch;
1473 _this.stripPrefix = cfg.stripPrefix;
1474 _this.stripTrailingSlash = cfg.stripTrailingSlash;
1475 _this.decodePercentEncoding = cfg.decodePercentEncoding;
1476 return _this;
1477 }
1478 /**
1479 * Returns a string name for the type of match that this class represents.
1480 * For the case of UrlMatch, returns 'url'.
1481 *
1482 * @return {String}
1483 */
1484 UrlMatch.prototype.getType = function () {
1485 return 'url';
1486 };
1487 /**
1488 * Returns a string name for the type of URL match that this class
1489 * represents.
1490 *
1491 * This helps to determine if the match was made in the original text with a
1492 * prefixed scheme (ex: 'http://www.google.com'), a prefixed 'www' (ex:
1493 * 'www.google.com'), or was matched by a known top-level domain (ex:
1494 * 'google.com').
1495 *
1496 * @return {"scheme"/"www"/"tld"}
1497 */
1498 UrlMatch.prototype.getUrlMatchType = function () {
1499 return this.urlMatchType;
1500 };
1501 /**
1502 * Returns the url that was matched, assuming the protocol to be 'http://' if the original
1503 * match was missing a protocol.
1504 *
1505 * @return {String}
1506 */
1507 UrlMatch.prototype.getUrl = function () {
1508 var url = this.url;
1509 // if the url string doesn't begin with a protocol, assume 'http://'
1510 if (!this.protocolRelativeMatch && !this.protocolUrlMatch && !this.protocolPrepended) {
1511 url = this.url = 'http://' + url;
1512 this.protocolPrepended = true;
1513 }
1514 return url;
1515 };
1516 /**
1517 * Returns the anchor href that should be generated for the match.
1518 *
1519 * @return {String}
1520 */
1521 UrlMatch.prototype.getAnchorHref = function () {
1522 var url = this.getUrl();
1523 return url.replace(/&amp;/g, '&'); // any &amp;'s in the URL should be converted back to '&' if they were displayed as &amp; in the source html
1524 };
1525 /**
1526 * Returns the anchor text that should be generated for the match.
1527 *
1528 * @return {String}
1529 */
1530 UrlMatch.prototype.getAnchorText = function () {
1531 var anchorText = this.getMatchedText();
1532 if (this.protocolRelativeMatch) {
1533 // Strip off any protocol-relative '//' from the anchor text
1534 anchorText = this.stripProtocolRelativePrefix(anchorText);
1535 }
1536 if (this.stripPrefix.scheme) {
1537 anchorText = this.stripSchemePrefix(anchorText);
1538 }
1539 if (this.stripPrefix.www) {
1540 anchorText = this.stripWwwPrefix(anchorText);
1541 }
1542 if (this.stripTrailingSlash) {
1543 anchorText = this.removeTrailingSlash(anchorText); // remove trailing slash, if there is one
1544 }
1545 if (this.decodePercentEncoding) {
1546 anchorText = this.removePercentEncoding(anchorText);
1547 }
1548 return anchorText;
1549 };
1550 // ---------------------------------------
1551 // Utility Functionality
1552 /**
1553 * Strips the scheme prefix (such as "http://" or "https://") from the given
1554 * `url`.
1555 *
1556 * @private
1557 * @param {String} url The text of the anchor that is being generated, for
1558 * which to strip off the url scheme.
1559 * @return {String} The `url`, with the scheme stripped.
1560 */
1561 UrlMatch.prototype.stripSchemePrefix = function (url) {
1562 return url.replace(this.schemePrefixRegex, '');
1563 };
1564 /**
1565 * Strips the 'www' prefix from the given `url`.
1566 *
1567 * @private
1568 * @param {String} url The text of the anchor that is being generated, for
1569 * which to strip off the 'www' if it exists.
1570 * @return {String} The `url`, with the 'www' stripped.
1571 */
1572 UrlMatch.prototype.stripWwwPrefix = function (url) {
1573 return url.replace(this.wwwPrefixRegex, '$1'); // leave any scheme ($1), it one exists
1574 };
1575 /**
1576 * Strips any protocol-relative '//' from the anchor text.
1577 *
1578 * @private
1579 * @param {String} text The text of the anchor that is being generated, for which to strip off the
1580 * protocol-relative prefix (such as stripping off "//")
1581 * @return {String} The `anchorText`, with the protocol-relative prefix stripped.
1582 */
1583 UrlMatch.prototype.stripProtocolRelativePrefix = function (text) {
1584 return text.replace(this.protocolRelativeRegex, '');
1585 };
1586 /**
1587 * Removes any trailing slash from the given `anchorText`, in preparation for the text to be displayed.
1588 *
1589 * @private
1590 * @param {String} anchorText The text of the anchor that is being generated, for which to remove any trailing
1591 * slash ('/') that may exist.
1592 * @return {String} The `anchorText`, with the trailing slash removed.
1593 */
1594 UrlMatch.prototype.removeTrailingSlash = function (anchorText) {
1595 if (anchorText.charAt(anchorText.length - 1) === '/') {
1596 anchorText = anchorText.slice(0, -1);
1597 }
1598 return anchorText;
1599 };
1600 /**
1601 * Decodes percent-encoded characters from the given `anchorText`, in
1602 * preparation for the text to be displayed.
1603 *
1604 * @private
1605 * @param {String} anchorText The text of the anchor that is being
1606 * generated, for which to decode any percent-encoded characters.
1607 * @return {String} The `anchorText`, with the percent-encoded characters
1608 * decoded.
1609 */
1610 UrlMatch.prototype.removePercentEncoding = function (anchorText) {
1611 // First, convert a few of the known % encodings to the corresponding
1612 // HTML entities that could accidentally be interpretted as special
1613 // HTML characters
1614 var preProcessedEntityAnchorText = anchorText
1615 .replace(/%22/gi, '&quot;') // " char
1616 .replace(/%26/gi, '&amp;') // & char
1617 .replace(/%27/gi, '&#39;') // ' char
1618 .replace(/%3C/gi, '&lt;') // < char
1619 .replace(/%3E/gi, '&gt;'); // > char
1620 try {
1621 // Now attempt to decode the rest of the anchor text
1622 return decodeURIComponent(preProcessedEntityAnchorText);
1623 }
1624 catch (e) {
1625 // Invalid % escape sequence in the anchor text
1626 return preProcessedEntityAnchorText;
1627 }
1628 };
1629 return UrlMatch;
1630 }(Match));
1631
1632 /**
1633 * @abstract
1634 * @class Autolinker.matcher.Matcher
1635 *
1636 * An abstract class and interface for individual matchers to find matches in
1637 * an input string with linkified versions of them.
1638 *
1639 * Note that Matchers do not take HTML into account - they must be fed the text
1640 * nodes of any HTML string, which is handled by {@link Autolinker#parse}.
1641 */
1642 var Matcher = /** @class */ (function () {
1643 /**
1644 * @method constructor
1645 * @param {Object} cfg The configuration properties for the Matcher
1646 * instance, specified in an Object (map).
1647 */
1648 function Matcher(cfg) {
1649 /**
1650 * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required)
1651 *
1652 * Reference to the AnchorTagBuilder instance to use to generate HTML tags
1653 * for {@link Autolinker.match.Match Matches}.
1654 */
1655 // @ts-ignore
1656 this.__jsduckDummyDocProp = null; // property used just to get the above doc comment into the ES5 output and documentation generator
1657 this.tagBuilder = cfg.tagBuilder;
1658 }
1659 return Matcher;
1660 }());
1661
1662 /*
1663 * This file builds and stores a library of the common regular expressions used
1664 * by the Autolinker utility.
1665 *
1666 * Other regular expressions may exist ad-hoc, but these are generally the
1667 * regular expressions that are shared between source files.
1668 */
1669 /**
1670 * Regular expression to match upper and lowercase ASCII letters
1671 */
1672 var letterRe = /[A-Za-z]/;
1673 /**
1674 * Regular expression to match ASCII digits
1675 */
1676 var digitRe = /[\d]/;
1677 /**
1678 * Regular expression to match everything *except* ASCII digits
1679 */
1680 var nonDigitRe = /[\D]/;
1681 /**
1682 * Regular expression to match whitespace
1683 */
1684 var whitespaceRe = /\s/;
1685 /**
1686 * Regular expression to match quote characters
1687 */
1688 var quoteRe = /['"]/;
1689 /**
1690 * Regular expression to match the range of ASCII control characters (0-31), and
1691 * the backspace char (127)
1692 */
1693 var controlCharsRe = /[\x00-\x1F\x7F]/;
1694 /**
1695 * The string form of a regular expression that would match all of the
1696 * alphabetic ("letter") chars in the unicode character set when placed in a
1697 * RegExp character class (`[]`). This includes all international alphabetic
1698 * characters.
1699 *
1700 * These would be the characters matched by unicode regex engines `\p{L}`
1701 * escape ("all letters").
1702 *
1703 * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan)
1704 * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Letter'
1705 * regex's bmp
1706 *
1707 * VERY IMPORTANT: This set of characters is defined inside of a Regular
1708 * Expression literal rather than a string literal to prevent UglifyJS from
1709 * compressing the unicode escape sequences into their actual unicode
1710 * characters. If Uglify compresses these into the unicode characters
1711 * themselves, this results in the error "Range out of order in character
1712 * class" when these characters are used inside of a Regular Expression
1713 * character class (`[]`). See usages of this const. Alternatively, we can set
1714 * the UglifyJS option `ascii_only` to true for the build, but that doesn't
1715 * help others who are pulling in Autolinker into their own build and running
1716 * UglifyJS themselves.
1717 */
1718 // prettier-ignore
1719 var alphaCharsStr = /A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC/
1720 .source; // see note in above variable description
1721 /**
1722 * The string form of a regular expression that would match all emoji characters
1723 * Based on the emoji regex defined in this article: https://thekevinscott.com/emojis-in-javascript/
1724 */
1725 var emojiStr = /\u2700-\u27bf\udde6-\uddff\ud800-\udbff\udc00-\udfff\ufe0e\ufe0f\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0\ud83c\udffb-\udfff\u200d\u3299\u3297\u303d\u3030\u24c2\ud83c\udd70-\udd71\udd7e-\udd7f\udd8e\udd91-\udd9a\udde6-\uddff\ude01-\ude02\ude1a\ude2f\ude32-\ude3a\ude50-\ude51\u203c\u2049\u25aa-\u25ab\u25b6\u25c0\u25fb-\u25fe\u00a9\u00ae\u2122\u2139\udc04\u2600-\u26FF\u2b05\u2b06\u2b07\u2b1b\u2b1c\u2b50\u2b55\u231a\u231b\u2328\u23cf\u23e9-\u23f3\u23f8-\u23fa\udccf\u2935\u2934\u2190-\u21ff/
1726 .source;
1727 /**
1728 * The string form of a regular expression that would match all of the
1729 * combining mark characters in the unicode character set when placed in a
1730 * RegExp character class (`[]`).
1731 *
1732 * These would be the characters matched by unicode regex engines `\p{M}`
1733 * escape ("all marks").
1734 *
1735 * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan)
1736 * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Mark'
1737 * regex's bmp
1738 *
1739 * VERY IMPORTANT: This set of characters is defined inside of a Regular
1740 * Expression literal rather than a string literal to prevent UglifyJS from
1741 * compressing the unicode escape sequences into their actual unicode
1742 * characters. If Uglify compresses these into the unicode characters
1743 * themselves, this results in the error "Range out of order in character
1744 * class" when these characters are used inside of a Regular Expression
1745 * character class (`[]`). See usages of this const. Alternatively, we can set
1746 * the UglifyJS option `ascii_only` to true for the build, but that doesn't
1747 * help others who are pulling in Autolinker into their own build and running
1748 * UglifyJS themselves.
1749 */
1750 // prettier-ignore
1751 var marksStr = /\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F/
1752 .source; // see note in above variable description
1753 /**
1754 * The string form of a regular expression that would match all of the
1755 * alphabetic ("letter") chars, emoji, and combining marks in the unicode character set
1756 * when placed in a RegExp character class (`[]`). This includes all
1757 * international alphabetic characters.
1758 *
1759 * These would be the characters matched by unicode regex engines `\p{L}\p{M}`
1760 * escapes and emoji characters.
1761 */
1762 var alphaCharsAndMarksStr = alphaCharsStr + emojiStr + marksStr;
1763 /**
1764 * The string form of a regular expression that would match all of the
1765 * decimal number chars in the unicode character set when placed in a RegExp
1766 * character class (`[]`).
1767 *
1768 * These would be the characters matched by unicode regex engines `\p{Nd}`
1769 * escape ("all decimal numbers")
1770 *
1771 * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan)
1772 * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Decimal_Number'
1773 * regex's bmp
1774 *
1775 * VERY IMPORTANT: This set of characters is defined inside of a Regular
1776 * Expression literal rather than a string literal to prevent UglifyJS from
1777 * compressing the unicode escape sequences into their actual unicode
1778 * characters. If Uglify compresses these into the unicode characters
1779 * themselves, this results in the error "Range out of order in character
1780 * class" when these characters are used inside of a Regular Expression
1781 * character class (`[]`). See usages of this const. Alternatively, we can set
1782 * the UglifyJS option `ascii_only` to true for the build, but that doesn't
1783 * help others who are pulling in Autolinker into their own build and running
1784 * UglifyJS themselves.
1785 */
1786 // prettier-ignore
1787 var decimalNumbersStr = /0-9\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE6-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29\u1040-\u1049\u1090-\u1099\u17E0-\u17E9\u1810-\u1819\u1946-\u194F\u19D0-\u19D9\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\uA620-\uA629\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19/
1788 .source; // see note in above variable description
1789 /**
1790 * The string form of a regular expression that would match all of the
1791 * letters and decimal number chars in the unicode character set when placed in
1792 * a RegExp character class (`[]`).
1793 *
1794 * These would be the characters matched by unicode regex engines
1795 * `[\p{L}\p{Nd}]` escape ("all letters and decimal numbers")
1796 */
1797 var alphaNumericCharsStr = alphaCharsAndMarksStr + decimalNumbersStr;
1798 /**
1799 * The string form of a regular expression that would match all of the
1800 * letters, combining marks, and decimal number chars in the unicode character
1801 * set when placed in a RegExp character class (`[]`).
1802 *
1803 * These would be the characters matched by unicode regex engines
1804 * `[\p{L}\p{M}\p{Nd}]` escape ("all letters, combining marks, and decimal
1805 * numbers")
1806 */
1807 var alphaNumericAndMarksCharsStr = alphaCharsAndMarksStr + decimalNumbersStr;
1808 /**
1809 * The regular expression that will match a single letter of the
1810 * {@link #alphaNumericAndMarksCharsStr}.
1811 */
1812 var alphaNumericAndMarksCharRe = new RegExp("[".concat(alphaNumericAndMarksCharsStr, "]"));
1813 // Simplified IP regular expression
1814 var ipStr = '(?:[' + decimalNumbersStr + ']{1,3}\\.){3}[' + decimalNumbersStr + ']{1,3}';
1815 // Protected domain label which do not allow "-" or "_" character on the beginning and the end of a single label
1816 // prettier-ignore
1817 var domainLabelStr = '[' + alphaNumericAndMarksCharsStr + '](?:[' + alphaNumericAndMarksCharsStr + '\\-_]{0,61}[' + alphaNumericAndMarksCharsStr + '])?';
1818 var getDomainLabelStr = function (group) {
1819 return '(?=(' + domainLabelStr + '))\\' + group;
1820 };
1821 /**
1822 * A function to match domain names of a URL or email address.
1823 * Ex: 'google', 'yahoo', 'some-other-company', etc.
1824 */
1825 // prettier-ignore
1826 var getDomainNameStr = function (group) {
1827 return '(?:' + getDomainLabelStr(group) + '(?:\\.' + getDomainLabelStr(group + 1) + '){0,126}|' + ipStr + ')';
1828 };
1829 /**
1830 * A regular expression that is simply the character class of the characters
1831 * that may be used in a domain name, minus the '-' or '.'
1832 */
1833 var domainNameCharRegex = alphaNumericAndMarksCharRe;
1834
1835 // NOTE: THIS IS A GENERATED FILE
1836 // To update with the latest TLD list, run `npm run update-tld-regex`
1837 var tldRegex = /(?:xn--vermgensberatung-pwb|xn--vermgensberater-ctb|xn--clchc0ea0b2g2a9gcd|xn--w4r85el8fhu5dnra|northwesternmutual|travelersinsurance|vermögensberatung|xn--5su34j936bgsg|xn--bck1b9a5dre4c|xn--mgbah1a3hjkrd|xn--mgbai9azgqp6j|xn--mgberp4a5d4ar|xn--xkc2dl3a5ee0h|vermögensberater|xn--fzys8d69uvgm|xn--mgba7c0bbn0a|xn--mgbcpq6gpa1a|xn--xkc2al3hye2a|americanexpress|kerryproperties|sandvikcoromant|xn--i1b6b1a6a2e|xn--kcrx77d1x4a|xn--lgbbat1ad8j|xn--mgba3a4f16a|xn--mgbaakc7dvf|xn--mgbc0a9azcg|xn--nqv7fs00ema|americanfamily|bananarepublic|cancerresearch|cookingchannel|kerrylogistics|weatherchannel|xn--54b7fta0cc|xn--6qq986b3xl|xn--80aqecdr1a|xn--b4w605ferd|xn--fiq228c5hs|xn--h2breg3eve|xn--jlq480n2rg|xn--jlq61u9w7b|xn--mgba3a3ejt|xn--mgbaam7a8h|xn--mgbayh7gpa|xn--mgbbh1a71e|xn--mgbca7dzdo|xn--mgbi4ecexp|xn--mgbx4cd0ab|xn--rvc1e0am3e|international|lifeinsurance|travelchannel|wolterskluwer|xn--cckwcxetd|xn--eckvdtc9d|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--h2brj9c8c|xn--tiq49xqyj|xn--yfro4i67o|xn--ygbi2ammx|construction|lplfinancial|scholarships|versicherung|xn--3e0b707e|xn--45br5cyl|xn--4dbrk0ce|xn--80adxhks|xn--80asehdb|xn--8y0a063a|xn--gckr3f0f|xn--mgb9awbf|xn--mgbab2bd|xn--mgbgu82a|xn--mgbpl2fh|xn--mgbt3dhd|xn--mk1bu44c|xn--ngbc5azd|xn--ngbe9e0a|xn--ogbpf8fl|xn--qcka1pmc|accountants|barclaycard|blackfriday|blockbuster|bridgestone|calvinklein|contractors|creditunion|engineering|enterprises|foodnetwork|investments|kerryhotels|lamborghini|motorcycles|olayangroup|photography|playstation|productions|progressive|redumbrella|williamhill|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--2scrj9c|xn--3bst00m|xn--3ds443g|xn--3hcrj9c|xn--42c2d9a|xn--45brj9c|xn--55qw42g|xn--6frz82g|xn--80ao21a|xn--9krt00a|xn--cck2b3b|xn--czr694b|xn--d1acj3b|xn--efvy88h|xn--fct429k|xn--fjq720a|xn--flw351e|xn--g2xx48c|xn--gecrj9c|xn--gk3at1e|xn--h2brj9c|xn--hxt814e|xn--imr513n|xn--j6w193g|xn--jvr189m|xn--kprw13d|xn--kpry57d|xn--mgbbh1a|xn--mgbtx2b|xn--mix891f|xn--nyqy26a|xn--otu796d|xn--pgbs0dh|xn--q9jyb4c|xn--rhqv96g|xn--rovu88b|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--vuq861b|xn--w4rs40l|xn--xhq521b|xn--zfr164b|சிங்கப்பூர்|accountant|apartments|associates|basketball|bnpparibas|boehringer|capitalone|consulting|creditcard|cuisinella|eurovision|extraspace|foundation|healthcare|immobilien|industries|management|mitsubishi|nextdirect|properties|protection|prudential|realestate|republican|restaurant|schaeffler|tatamotors|technology|university|vlaanderen|volkswagen|xn--30rr7y|xn--3pxu8k|xn--45q11c|xn--4gbrim|xn--55qx5d|xn--5tzm5g|xn--80aswg|xn--90a3ac|xn--9dbq2a|xn--9et52u|xn--c2br7g|xn--cg4bki|xn--czrs0t|xn--czru2d|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--io0a7i|xn--kput3i|xn--mxtq1m|xn--o3cw4h|xn--pssy2u|xn--q7ce6a|xn--unup4y|xn--wgbh1c|xn--wgbl6a|xn--y9a3aq|accenture|alfaromeo|allfinanz|amsterdam|analytics|aquarelle|barcelona|bloomberg|christmas|community|directory|education|equipment|fairwinds|financial|firestone|fresenius|frontdoor|furniture|goldpoint|hisamitsu|homedepot|homegoods|homesense|institute|insurance|kuokgroup|lancaster|landrover|lifestyle|marketing|marshalls|melbourne|microsoft|panasonic|passagens|pramerica|richardli|shangrila|solutions|statebank|statefarm|stockholm|travelers|vacations|xn--90ais|xn--c1avg|xn--d1alf|xn--e1a4c|xn--fhbei|xn--j1aef|xn--j1amh|xn--l1acc|xn--ngbrx|xn--nqv7f|xn--p1acf|xn--qxa6a|xn--tckwe|xn--vhquv|yodobashi|موريتانيا|abudhabi|airforce|allstate|attorney|barclays|barefoot|bargains|baseball|boutique|bradesco|broadway|brussels|builders|business|capetown|catering|catholic|cipriani|cityeats|cleaning|clinique|clothing|commbank|computer|delivery|deloitte|democrat|diamonds|discount|discover|download|engineer|ericsson|etisalat|exchange|feedback|fidelity|firmdale|football|frontier|goodyear|grainger|graphics|guardian|hdfcbank|helsinki|holdings|hospital|infiniti|ipiranga|istanbul|jpmorgan|lighting|lundbeck|marriott|maserati|mckinsey|memorial|merckmsd|mortgage|observer|partners|pharmacy|pictures|plumbing|property|redstone|reliance|saarland|samsclub|security|services|shopping|showtime|softbank|software|stcgroup|supplies|training|vanguard|ventures|verisign|woodside|xn--90ae|xn--node|xn--p1ai|xn--qxam|yokohama|السعودية|abogado|academy|agakhan|alibaba|android|athleta|auction|audible|auspost|avianca|banamex|bauhaus|bentley|bestbuy|booking|brother|bugatti|capital|caravan|careers|channel|charity|chintai|citadel|clubmed|college|cologne|comcast|company|compare|contact|cooking|corsica|country|coupons|courses|cricket|cruises|dentist|digital|domains|exposed|express|farmers|fashion|ferrari|ferrero|finance|fishing|fitness|flights|florist|flowers|forsale|frogans|fujitsu|gallery|genting|godaddy|grocery|guitars|hamburg|hangout|hitachi|holiday|hosting|hoteles|hotmail|hyundai|ismaili|jewelry|juniper|kitchen|komatsu|lacaixa|lanxess|lasalle|latrobe|leclerc|limited|lincoln|markets|monster|netbank|netflix|network|neustar|okinawa|oldnavy|organic|origins|philips|pioneer|politie|realtor|recipes|rentals|reviews|rexroth|samsung|sandvik|schmidt|schwarz|science|shiksha|singles|staples|storage|support|surgery|systems|temasek|theater|theatre|tickets|tiffany|toshiba|trading|walmart|wanggou|watches|weather|website|wedding|whoswho|windows|winners|xfinity|yamaxun|youtube|zuerich|католик|اتصالات|البحرين|الجزائر|العليان|پاکستان|كاثوليك|இந்தியா|abarth|abbott|abbvie|africa|agency|airbus|airtel|alipay|alsace|alstom|amazon|anquan|aramco|author|bayern|beauty|berlin|bharti|bostik|boston|broker|camera|career|casino|center|chanel|chrome|church|circle|claims|clinic|coffee|comsec|condos|coupon|credit|cruise|dating|datsun|dealer|degree|dental|design|direct|doctor|dunlop|dupont|durban|emerck|energy|estate|events|expert|family|flickr|futbol|gallup|garden|george|giving|global|google|gratis|health|hermes|hiphop|hockey|hotels|hughes|imamat|insure|intuit|jaguar|joburg|juegos|kaufen|kinder|kindle|kosher|lancia|latino|lawyer|lefrak|living|locker|london|luxury|madrid|maison|makeup|market|mattel|mobile|monash|mormon|moscow|museum|mutual|nagoya|natura|nissan|nissay|norton|nowruz|office|olayan|online|oracle|orange|otsuka|pfizer|photos|physio|pictet|quebec|racing|realty|reisen|repair|report|review|rocher|rogers|ryukyu|safety|sakura|sanofi|school|schule|search|secure|select|shouji|soccer|social|stream|studio|supply|suzuki|swatch|sydney|taipei|taobao|target|tattoo|tennis|tienda|tjmaxx|tkmaxx|toyota|travel|unicom|viajes|viking|villas|virgin|vision|voting|voyage|vuelos|walter|webcam|xihuan|yachts|yandex|zappos|москва|онлайн|ابوظبي|ارامكو|الاردن|المغرب|امارات|فلسطين|مليسيا|भारतम्|இலங்கை|ファッション|actor|adult|aetna|amfam|amica|apple|archi|audio|autos|azure|baidu|beats|bible|bingo|black|boats|bosch|build|canon|cards|chase|cheap|cisco|citic|click|cloud|coach|codes|crown|cymru|dabur|dance|deals|delta|drive|dubai|earth|edeka|email|epson|faith|fedex|final|forex|forum|gallo|games|gifts|gives|glass|globo|gmail|green|gripe|group|gucci|guide|homes|honda|horse|house|hyatt|ikano|irish|jetzt|koeln|kyoto|lamer|lease|legal|lexus|lilly|linde|lipsy|loans|locus|lotte|lotto|macys|mango|media|miami|money|movie|music|nexus|nikon|ninja|nokia|nowtv|omega|osaka|paris|parts|party|phone|photo|pizza|place|poker|praxi|press|prime|promo|quest|radio|rehab|reise|ricoh|rocks|rodeo|rugby|salon|sener|seven|sharp|shell|shoes|skype|sling|smart|smile|solar|space|sport|stada|store|study|style|sucks|swiss|tatar|tires|tirol|tmall|today|tokyo|tools|toray|total|tours|trade|trust|tunes|tushu|ubank|vegas|video|vodka|volvo|wales|watch|weber|weibo|works|world|xerox|yahoo|ישראל|ایران|بازار|بھارت|سودان|سورية|همراه|भारोत|संगठन|বাংলা|భారత్|ഭാരതം|嘉里大酒店|aarp|able|adac|aero|akdn|ally|amex|arab|army|arpa|arte|asda|asia|audi|auto|baby|band|bank|bbva|beer|best|bike|bing|blog|blue|bofa|bond|book|buzz|cafe|call|camp|care|cars|casa|case|cash|cbre|cern|chat|citi|city|club|cool|coop|cyou|data|date|dclk|deal|dell|desi|diet|dish|docs|dvag|erni|fage|fail|fans|farm|fast|fiat|fido|film|fire|fish|flir|food|ford|free|fund|game|gbiz|gent|ggee|gift|gmbh|gold|golf|goog|guge|guru|hair|haus|hdfc|help|here|hgtv|host|hsbc|icbc|ieee|imdb|immo|info|itau|java|jeep|jobs|jprs|kddi|kids|kiwi|kpmg|kred|land|lego|lgbt|lidl|life|like|limo|link|live|loan|loft|love|ltda|luxe|maif|meet|meme|menu|mini|mint|mobi|moda|moto|name|navy|news|next|nico|nike|ollo|open|page|pars|pccw|pics|ping|pink|play|plus|pohl|porn|post|prod|prof|qpon|read|reit|rent|rest|rich|room|rsvp|ruhr|safe|sale|sarl|save|saxo|scot|seat|seek|sexy|shaw|shia|shop|show|silk|sina|site|skin|sncf|sohu|song|sony|spot|star|surf|talk|taxi|team|tech|teva|tiaa|tips|town|toys|tube|vana|visa|viva|vivo|vote|voto|wang|weir|wien|wiki|wine|work|xbox|yoga|zara|zero|zone|дети|сайт|بارت|بيتك|ڀارت|تونس|شبكة|عراق|عمان|موقع|भारत|ভারত|ভাৰত|ਭਾਰਤ|ભારત|ଭାରତ|ಭಾರತ|ලංකා|アマゾン|グーグル|クラウド|ポイント|组织机构|電訊盈科|香格里拉|aaa|abb|abc|aco|ads|aeg|afl|aig|anz|aol|app|art|aws|axa|bar|bbc|bbt|bcg|bcn|bet|bid|bio|biz|bms|bmw|bom|boo|bot|box|buy|bzh|cab|cal|cam|car|cat|cba|cbn|cbs|ceo|cfa|cfd|com|cpa|crs|dad|day|dds|dev|dhl|diy|dnp|dog|dot|dtv|dvr|eat|eco|edu|esq|eus|fan|fit|fly|foo|fox|frl|ftr|fun|fyi|gal|gap|gay|gdn|gea|gle|gmo|gmx|goo|gop|got|gov|hbo|hiv|hkt|hot|how|ibm|ice|icu|ifm|inc|ing|ink|int|ist|itv|jcb|jio|jll|jmp|jnj|jot|joy|kfh|kia|kim|kpn|krd|lat|law|lds|llc|llp|lol|lpl|ltd|man|map|mba|med|men|mil|mit|mlb|mls|mma|moe|moi|mom|mov|msd|mtn|mtr|nab|nba|nec|net|new|nfl|ngo|nhk|now|nra|nrw|ntt|nyc|obi|one|ong|onl|ooo|org|ott|ovh|pay|pet|phd|pid|pin|pnc|pro|pru|pub|pwc|red|ren|ril|rio|rip|run|rwe|sap|sas|sbi|sbs|sca|scb|ses|sew|sex|sfr|ski|sky|soy|spa|srl|stc|tab|tax|tci|tdk|tel|thd|tjx|top|trv|tui|tvs|ubs|uno|uol|ups|vet|vig|vin|vip|wed|win|wme|wow|wtc|wtf|xin|xxx|xyz|you|yun|zip|бел|ком|қаз|мкд|мон|орг|рус|срб|укр|հայ|קום|عرب|قطر|كوم|مصر|कॉम|नेट|คอม|ไทย|ລາວ|ストア|セール|みんな|中文网|亚马逊|天主教|我爱你|新加坡|淡马锡|诺基亚|飞利浦|ac|ad|ae|af|ag|ai|al|am|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|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|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|ελ|ευ|бг|ею|рф|გე|닷넷|닷컴|삼성|한국|コム|世界|中信|中国|中國|企业|佛山|信息|健康|八卦|公司|公益|台湾|台灣|商城|商店|商标|嘉里|在线|大拿|娱乐|家電|广东|微博|慈善|手机|招聘|政务|政府|新闻|时尚|書籍|机构|游戏|澳門|点看|移动|网址|网店|网站|网络|联通|谷歌|购物|通販|集团|食品|餐厅|香港)/;
1838
1839 // For debugging: search for other "For debugging" lines
1840 // import CliTable from 'cli-table';
1841 // RegExp objects which are shared by all instances of EmailMatcher. These are
1842 // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
1843 // called multiple times, thus instantiating EmailMatcher and its RegExp
1844 // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
1845 // See descriptions of the properties where they are used for details about them
1846 var localPartCharRegex = new RegExp("[".concat(alphaNumericAndMarksCharsStr, "!#$%&'*+/=?^_`{|}~-]"));
1847 var strictTldRegex = new RegExp("^".concat(tldRegex.source, "$"));
1848 /**
1849 * @class Autolinker.matcher.Email
1850 * @extends Autolinker.matcher.Matcher
1851 *
1852 * Matcher to find email matches in an input string.
1853 *
1854 * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details.
1855 */
1856 var EmailMatcher = /** @class */ (function (_super) {
1857 __extends(EmailMatcher, _super);
1858 function EmailMatcher() {
1859 var _this = _super !== null && _super.apply(this, arguments) || this;
1860 /**
1861 * Valid characters that can be used in the "local" part of an email address,
1862 * i.e. the "name" part of "name@site.com"
1863 */
1864 _this.localPartCharRegex = localPartCharRegex;
1865 /**
1866 * Stricter TLD regex which adds a beginning and end check to ensure
1867 * the string is a valid TLD
1868 */
1869 _this.strictTldRegex = strictTldRegex;
1870 return _this;
1871 }
1872 /**
1873 * @inheritdoc
1874 */
1875 EmailMatcher.prototype.parseMatches = function (text) {
1876 var tagBuilder = this.tagBuilder, localPartCharRegex = this.localPartCharRegex, strictTldRegex = this.strictTldRegex, matches = [], len = text.length, noCurrentEmailMatch = new CurrentEmailMatch();
1877 // for matching a 'mailto:' prefix
1878 var mailtoTransitions = {
1879 m: 'a',
1880 a: 'i',
1881 i: 'l',
1882 l: 't',
1883 t: 'o',
1884 o: ':',
1885 };
1886 var charIdx = 0, state = 0 /* NonEmailMatch */, currentEmailMatch = noCurrentEmailMatch;
1887 // For debugging: search for other "For debugging" lines
1888 // const table = new CliTable( {
1889 // head: [ 'charIdx', 'char', 'state', 'charIdx', 'currentEmailAddress.idx', 'hasDomainDot' ]
1890 // } );
1891 while (charIdx < len) {
1892 var char = text.charAt(charIdx);
1893 // For debugging: search for other "For debugging" lines
1894 // table.push(
1895 // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ]
1896 // );
1897 switch (state) {
1898 case 0 /* NonEmailMatch */:
1899 stateNonEmailAddress(char);
1900 break;
1901 case 1 /* Mailto */:
1902 stateMailTo(text.charAt(charIdx - 1), char);
1903 break;
1904 case 2 /* LocalPart */:
1905 stateLocalPart(char);
1906 break;
1907 case 3 /* LocalPartDot */:
1908 stateLocalPartDot(char);
1909 break;
1910 case 4 /* AtSign */:
1911 stateAtSign(char);
1912 break;
1913 case 5 /* DomainChar */:
1914 stateDomainChar(char);
1915 break;
1916 case 6 /* DomainHyphen */:
1917 stateDomainHyphen(char);
1918 break;
1919 case 7 /* DomainDot */:
1920 stateDomainDot(char);
1921 break;
1922 default:
1923 throwUnhandledCaseError(state);
1924 }
1925 // For debugging: search for other "For debugging" lines
1926 // table.push(
1927 // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ]
1928 // );
1929 charIdx++;
1930 }
1931 // Capture any valid match at the end of the string
1932 captureMatchIfValidAndReset();
1933 // For debugging: search for other "For debugging" lines
1934 //console.log( '\n' + table.toString() );
1935 return matches;
1936 // Handles the state when we're not in an email address
1937 function stateNonEmailAddress(char) {
1938 if (char === 'm') {
1939 beginEmailMatch(1 /* Mailto */);
1940 }
1941 else if (localPartCharRegex.test(char)) {
1942 beginEmailMatch();
1943 }
1944 else ;
1945 }
1946 // Handles if we're reading a 'mailto:' prefix on the string
1947 function stateMailTo(prevChar, char) {
1948 if (prevChar === ':') {
1949 // We've reached the end of the 'mailto:' prefix
1950 if (localPartCharRegex.test(char)) {
1951 state = 2 /* LocalPart */;
1952 currentEmailMatch = new CurrentEmailMatch(__assign(__assign({}, currentEmailMatch), { hasMailtoPrefix: true }));
1953 }
1954 else {
1955 // we've matched 'mailto:' but didn't get anything meaningful
1956 // immediately afterwards (for example, we encountered a
1957 // space character, or an '@' character which formed 'mailto:@'
1958 resetToNonEmailMatchState();
1959 }
1960 }
1961 else if (mailtoTransitions[prevChar] === char) ;
1962 else if (localPartCharRegex.test(char)) {
1963 // We we're reading a prefix of 'mailto:', but encountered a
1964 // different character that didn't continue the prefix
1965 state = 2 /* LocalPart */;
1966 }
1967 else if (char === '.') {
1968 // We we're reading a prefix of 'mailto:', but encountered a
1969 // dot character
1970 state = 3 /* LocalPartDot */;
1971 }
1972 else if (char === '@') {
1973 // We we're reading a prefix of 'mailto:', but encountered a
1974 // an @ character
1975 state = 4 /* AtSign */;
1976 }
1977 else {
1978 // not an email address character, return to "NonEmailAddress" state
1979 resetToNonEmailMatchState();
1980 }
1981 }
1982 // Handles the state when we're currently in the "local part" of an
1983 // email address (as opposed to the "domain part")
1984 function stateLocalPart(char) {
1985 if (char === '.') {
1986 state = 3 /* LocalPartDot */;
1987 }
1988 else if (char === '@') {
1989 state = 4 /* AtSign */;
1990 }
1991 else if (localPartCharRegex.test(char)) ;
1992 else {
1993 // not an email address character, return to "NonEmailAddress" state
1994 resetToNonEmailMatchState();
1995 }
1996 }
1997 // Handles the state where we've read
1998 function stateLocalPartDot(char) {
1999 if (char === '.') {
2000 // We read a second '.' in a row, not a valid email address
2001 // local part
2002 resetToNonEmailMatchState();
2003 }
2004 else if (char === '@') {
2005 // We read the '@' character immediately after a dot ('.'), not
2006 // an email address
2007 resetToNonEmailMatchState();
2008 }
2009 else if (localPartCharRegex.test(char)) {
2010 state = 2 /* LocalPart */;
2011 }
2012 else {
2013 // Anything else, not an email address
2014 resetToNonEmailMatchState();
2015 }
2016 }
2017 function stateAtSign(char) {
2018 if (domainNameCharRegex.test(char)) {
2019 state = 5 /* DomainChar */;
2020 }
2021 else {
2022 // Anything else, not an email address
2023 resetToNonEmailMatchState();
2024 }
2025 }
2026 function stateDomainChar(char) {
2027 if (char === '.') {
2028 state = 7 /* DomainDot */;
2029 }
2030 else if (char === '-') {
2031 state = 6 /* DomainHyphen */;
2032 }
2033 else if (domainNameCharRegex.test(char)) ;
2034 else {
2035 // Anything else, we potentially matched if the criteria has
2036 // been met
2037 captureMatchIfValidAndReset();
2038 }
2039 }
2040 function stateDomainHyphen(char) {
2041 if (char === '-' || char === '.') {
2042 // Not valid to have two hyphens ("--") or hypen+dot ("-.")
2043 captureMatchIfValidAndReset();
2044 }
2045 else if (domainNameCharRegex.test(char)) {
2046 state = 5 /* DomainChar */;
2047 }
2048 else {
2049 // Anything else
2050 captureMatchIfValidAndReset();
2051 }
2052 }
2053 function stateDomainDot(char) {
2054 if (char === '.' || char === '-') {
2055 // not valid to have two dots ("..") or dot+hypen (".-")
2056 captureMatchIfValidAndReset();
2057 }
2058 else if (domainNameCharRegex.test(char)) {
2059 state = 5 /* DomainChar */;
2060 // After having read a '.' and then a valid domain character,
2061 // we now know that the domain part of the email is valid, and
2062 // we have found at least a partial EmailMatch (however, the
2063 // email address may have additional characters from this point)
2064 currentEmailMatch = new CurrentEmailMatch(__assign(__assign({}, currentEmailMatch), { hasDomainDot: true }));
2065 }
2066 else {
2067 // Anything else
2068 captureMatchIfValidAndReset();
2069 }
2070 }
2071 function beginEmailMatch(newState) {
2072 if (newState === void 0) { newState = 2 /* LocalPart */; }
2073 state = newState;
2074 currentEmailMatch = new CurrentEmailMatch({ idx: charIdx });
2075 }
2076 function resetToNonEmailMatchState() {
2077 state = 0 /* NonEmailMatch */;
2078 currentEmailMatch = noCurrentEmailMatch;
2079 }
2080 /*
2081 * Captures the current email address as an EmailMatch if it's valid,
2082 * and resets the state to read another email address.
2083 */
2084 function captureMatchIfValidAndReset() {
2085 if (currentEmailMatch.hasDomainDot) {
2086 // we need at least one dot in the domain to be considered a valid email address
2087 var matchedText = text.slice(currentEmailMatch.idx, charIdx);
2088 // If we read a '.' or '-' char that ended the email address
2089 // (valid domain name characters, but only valid email address
2090 // characters if they are followed by something else), strip
2091 // it off now
2092 if (/[-.]$/.test(matchedText)) {
2093 matchedText = matchedText.slice(0, -1);
2094 }
2095 var emailAddress = currentEmailMatch.hasMailtoPrefix
2096 ? matchedText.slice('mailto:'.length)
2097 : matchedText;
2098 // if the email address has a valid TLD, add it to the list of matches
2099 if (doesEmailHaveValidTld(emailAddress)) {
2100 matches.push(new EmailMatch({
2101 tagBuilder: tagBuilder,
2102 matchedText: matchedText,
2103 offset: currentEmailMatch.idx,
2104 email: emailAddress,
2105 }));
2106 }
2107 }
2108 resetToNonEmailMatchState();
2109 /**
2110 * Determines if the given email address has a valid TLD or not
2111 * @param {string} emailAddress - email address
2112 * @return {Boolean} - true is email have valid TLD, false otherwise
2113 */
2114 function doesEmailHaveValidTld(emailAddress) {
2115 var emailAddressTld = emailAddress.split('.').pop() || '';
2116 var emailAddressNormalized = emailAddressTld.toLowerCase();
2117 var isValidTld = strictTldRegex.test(emailAddressNormalized);
2118 return isValidTld;
2119 }
2120 }
2121 };
2122 return EmailMatcher;
2123 }(Matcher));
2124 var CurrentEmailMatch = /** @class */ (function () {
2125 function CurrentEmailMatch(cfg) {
2126 if (cfg === void 0) { cfg = {}; }
2127 this.idx = cfg.idx !== undefined ? cfg.idx : -1;
2128 this.hasMailtoPrefix = !!cfg.hasMailtoPrefix;
2129 this.hasDomainDot = !!cfg.hasDomainDot;
2130 }
2131 return CurrentEmailMatch;
2132 }());
2133
2134 /**
2135 * @private
2136 * @class Autolinker.matcher.UrlMatchValidator
2137 * @singleton
2138 *
2139 * Used by Autolinker to filter out false URL positives from the
2140 * {@link Autolinker.matcher.Url UrlMatcher}.
2141 *
2142 * Due to the limitations of regular expressions (including the missing feature
2143 * of look-behinds in JS regular expressions), we cannot always determine the
2144 * validity of a given match. This class applies a bit of additional logic to
2145 * filter out any false positives that have been matched by the
2146 * {@link Autolinker.matcher.Url UrlMatcher}.
2147 */
2148 var UrlMatchValidator = /** @class */ (function () {
2149 function UrlMatchValidator() {
2150 }
2151 /**
2152 * Determines if a given URL match found by the {@link Autolinker.matcher.Url UrlMatcher}
2153 * is valid. Will return `false` for:
2154 *
2155 * 1) URL matches which do not have at least have one period ('.') in the
2156 * domain name (effectively skipping over matches like "abc:def").
2157 * However, URL matches with a protocol will be allowed (ex: 'http://localhost')
2158 * 2) URL matches which do not have at least one word character in the
2159 * domain name (effectively skipping over matches like "git:1.0").
2160 * However, URL matches with a protocol will be allowed (ex: 'intra-net://271219.76')
2161 * 3) A protocol-relative url match (a URL beginning with '//') whose
2162 * previous character is a word character (effectively skipping over
2163 * strings like "abc//google.com")
2164 *
2165 * Otherwise, returns `true`.
2166 *
2167 * @param {String} urlMatch The matched URL, if there was one. Will be an
2168 * empty string if the match is not a URL match.
2169 * @param {String} protocolUrlMatch The match URL string for a protocol
2170 * match. Ex: 'http://yahoo.com'. This is used to match something like
2171 * 'http://localhost', where we won't double check that the domain name
2172 * has at least one '.' in it.
2173 * @return {Boolean} `true` if the match given is valid and should be
2174 * processed, or `false` if the match is invalid and/or should just not be
2175 * processed.
2176 */
2177 UrlMatchValidator.isValid = function (urlMatch, protocolUrlMatch) {
2178 if ((protocolUrlMatch && !this.isValidUriScheme(protocolUrlMatch)) ||
2179 this.urlMatchDoesNotHaveProtocolOrDot(urlMatch, protocolUrlMatch) || // At least one period ('.') must exist in the URL match for us to consider it an actual URL, *unless* it was a full protocol match (like 'http://localhost')
2180 (this.urlMatchDoesNotHaveAtLeastOneWordChar(urlMatch, protocolUrlMatch) && // At least one letter character must exist in the domain name after a protocol match. Ex: skip over something like "git:1.0"
2181 !this.isValidIpAddress(urlMatch)) || // Except if it's an IP address
2182 this.containsMultipleDots(urlMatch)) {
2183 return false;
2184 }
2185 return true;
2186 };
2187 UrlMatchValidator.isValidIpAddress = function (uriSchemeMatch) {
2188 var newRegex = new RegExp(this.hasFullProtocolRegex.source + this.ipRegex.source);
2189 var uriScheme = uriSchemeMatch.match(newRegex);
2190 return uriScheme !== null;
2191 };
2192 UrlMatchValidator.containsMultipleDots = function (urlMatch) {
2193 var stringBeforeSlash = urlMatch;
2194 if (this.hasFullProtocolRegex.test(urlMatch)) {
2195 stringBeforeSlash = urlMatch.split('://')[1];
2196 }
2197 return stringBeforeSlash.split('/')[0].indexOf('..') > -1;
2198 };
2199 /**
2200 * Determines if the URI scheme is a valid scheme to be autolinked. Returns
2201 * `false` if the scheme is 'javascript:' or 'vbscript:'
2202 *
2203 * @private
2204 * @param {String} uriSchemeMatch The match URL string for a full URI scheme
2205 * match. Ex: 'http://yahoo.com' or 'mailto:a@a.com'.
2206 * @return {Boolean} `true` if the scheme is a valid one, `false` otherwise.
2207 */
2208 UrlMatchValidator.isValidUriScheme = function (uriSchemeMatch) {
2209 var uriSchemeMatchArr = uriSchemeMatch.match(this.uriSchemeRegex), uriScheme = uriSchemeMatchArr && uriSchemeMatchArr[0].toLowerCase();
2210 return uriScheme !== 'javascript:' && uriScheme !== 'vbscript:';
2211 };
2212 /**
2213 * Determines if a URL match does not have either:
2214 *
2215 * a) a full protocol (i.e. 'http://'), or
2216 * b) at least one dot ('.') in the domain name (for a non-full-protocol
2217 * match).
2218 *
2219 * Either situation is considered an invalid URL (ex: 'git:d' does not have
2220 * either the '://' part, or at least one dot in the domain name. If the
2221 * match was 'git:abc.com', we would consider this valid.)
2222 *
2223 * @private
2224 * @param {String} urlMatch The matched URL, if there was one. Will be an
2225 * empty string if the match is not a URL match.
2226 * @param {String} protocolUrlMatch The match URL string for a protocol
2227 * match. Ex: 'http://yahoo.com'. This is used to match something like
2228 * 'http://localhost', where we won't double check that the domain name
2229 * has at least one '.' in it.
2230 * @return {Boolean} `true` if the URL match does not have a full protocol,
2231 * or at least one dot ('.') in a non-full-protocol match.
2232 */
2233 UrlMatchValidator.urlMatchDoesNotHaveProtocolOrDot = function (urlMatch, protocolUrlMatch) {
2234 return (!!urlMatch &&
2235 (!protocolUrlMatch || !this.hasFullProtocolRegex.test(protocolUrlMatch)) &&
2236 urlMatch.indexOf('.') === -1);
2237 };
2238 /**
2239 * Determines if a URL match does not have either:
2240 *
2241 * a) a full protocol (i.e. 'http://'), or
2242 * b) at least one word character after the protocol (i.e. in the domain name)
2243 *
2244 * At least one letter character must exist in the domain name after a
2245 * protocol match. Ex: skip over something like "git:1.0"
2246 *
2247 * @private
2248 * @param {String} urlMatch The matched URL, if there was one. Will be an
2249 * empty string if the match is not a URL match.
2250 * @param {String} protocolUrlMatch The match URL string for a protocol
2251 * match. Ex: 'http://yahoo.com'. This is used to know whether or not we
2252 * have a protocol in the URL string, in order to check for a word
2253 * character after the protocol separator (':').
2254 * @return {Boolean} `true` if the URL match does not have a full protocol, or
2255 * at least one word character in it, `false` otherwise.
2256 */
2257 UrlMatchValidator.urlMatchDoesNotHaveAtLeastOneWordChar = function (urlMatch, protocolUrlMatch) {
2258 if (urlMatch && protocolUrlMatch) {
2259 return (!this.hasFullProtocolRegex.test(protocolUrlMatch) &&
2260 !this.hasWordCharAfterProtocolRegex.test(urlMatch));
2261 }
2262 else {
2263 return false;
2264 }
2265 };
2266 /**
2267 * Regex to test for a full protocol, with the two trailing slashes. Ex: 'http://'
2268 *
2269 * @private
2270 * @property {RegExp} hasFullProtocolRegex
2271 */
2272 UrlMatchValidator.hasFullProtocolRegex = /^[A-Za-z][-.+A-Za-z0-9]*:\/\//;
2273 /**
2274 * Regex to find the URI scheme, such as 'mailto:'.
2275 *
2276 * This is used to filter out 'javascript:' and 'vbscript:' schemes.
2277 *
2278 * @private
2279 * @property {RegExp} uriSchemeRegex
2280 */
2281 UrlMatchValidator.uriSchemeRegex = /^[A-Za-z][-.+A-Za-z0-9]*:/;
2282 /**
2283 * Regex to determine if at least one word char exists after the protocol (i.e. after the ':')
2284 *
2285 * @private
2286 * @property {RegExp} hasWordCharAfterProtocolRegex
2287 */
2288 UrlMatchValidator.hasWordCharAfterProtocolRegex = new RegExp(':[^\\s]*?[' + alphaCharsStr + ']');
2289 /**
2290 * Regex to determine if the string is a valid IP address
2291 *
2292 * @private
2293 * @property {RegExp} ipRegex
2294 */
2295 UrlMatchValidator.ipRegex = /[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/;
2296 return UrlMatchValidator;
2297 }());
2298
2299 // RegExp objects which are shared by all instances of UrlMatcher. These are
2300 // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
2301 // called multiple times, thus instantiating UrlMatcher and its RegExp
2302 // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
2303 // See descriptions of the properties where they are used for details about them
2304 // prettier-ignore
2305 var matcherRegex = (function () {
2306 var schemeRegex = /(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/, // match protocol, allow in format "http://" or "mailto:". However, do not match the first part of something like 'link:http://www.google.com' (i.e. don't match "link:"). Also, make sure we don't interpret 'google.com:8000' as if 'google.com' was a protocol here (i.e. ignore a trailing port number in this regex)
2307 wwwRegex = /(?:www\.)/, // starting with 'www.'
2308 // Allow optional path, query string, and hash anchor, not ending in the following characters: "?!:,.;"
2309 // http://blog.codinghorror.com/the-problem-with-urls/
2310 urlSuffixRegex = new RegExp('[/?#](?:[' + alphaNumericAndMarksCharsStr + '\\-+&@#/%=~_()|\'$*\\[\\]{}?!:,.;^\u2713]*[' + alphaNumericAndMarksCharsStr + '\\-+&@#/%=~_()|\'$*\\[\\]{}\u2713])?');
2311 return new RegExp([
2312 '(?:',
2313 '(',
2314 schemeRegex.source,
2315 getDomainNameStr(2),
2316 ')',
2317 '|',
2318 '(',
2319 '(//)?',
2320 wwwRegex.source,
2321 getDomainNameStr(6),
2322 ')',
2323 '|',
2324 '(',
2325 '(//)?',
2326 getDomainNameStr(10) + '\\.',
2327 tldRegex.source,
2328 '(?![-' + alphaNumericCharsStr + '])',
2329 ')',
2330 ')',
2331 '(?::[0-9]+)?',
2332 '(?:' + urlSuffixRegex.source + ')?' // match for path, query string, and/or hash anchor - optional
2333 ].join(""), 'gi');
2334 })();
2335 var wordCharRegExp = new RegExp('[' + alphaNumericAndMarksCharsStr + ']');
2336 /**
2337 * @class Autolinker.matcher.Url
2338 * @extends Autolinker.matcher.Matcher
2339 *
2340 * Matcher to find URL matches in an input string.
2341 *
2342 * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details.
2343 */
2344 var UrlMatcher = /** @class */ (function (_super) {
2345 __extends(UrlMatcher, _super);
2346 /**
2347 * @method constructor
2348 * @param {Object} cfg The configuration properties for the Match instance,
2349 * specified in an Object (map).
2350 */
2351 function UrlMatcher(cfg) {
2352 var _this = _super.call(this, cfg) || this;
2353 /**
2354 * @cfg {Object} stripPrefix (required)
2355 *
2356 * The Object form of {@link Autolinker#cfg-stripPrefix}.
2357 */
2358 _this.stripPrefix = {
2359 scheme: true,
2360 www: true,
2361 }; // default value just to get the above doc comment in the ES5 output and documentation generator
2362 /**
2363 * @cfg {Boolean} stripTrailingSlash (required)
2364 * @inheritdoc Autolinker#stripTrailingSlash
2365 */
2366 _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
2367 /**
2368 * @cfg {Boolean} decodePercentEncoding (required)
2369 * @inheritdoc Autolinker#decodePercentEncoding
2370 */
2371 _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
2372 /**
2373 * @protected
2374 * @property {RegExp} matcherRegex
2375 *
2376 * The regular expression to match URLs with an optional scheme, port
2377 * number, path, query string, and hash anchor.
2378 *
2379 * Example matches:
2380 *
2381 * http://google.com
2382 * www.google.com
2383 * google.com/path/to/file?q1=1&q2=2#myAnchor
2384 *
2385 *
2386 * This regular expression will have the following capturing groups:
2387 *
2388 * 1. Group that matches a scheme-prefixed URL (i.e. 'http://google.com').
2389 * This is used to match scheme URLs with just a single word, such as
2390 * 'http://localhost', where we won't double check that the domain name
2391 * has at least one dot ('.') in it.
2392 * 2. Group that matches a 'www.' prefixed URL. This is only matched if the
2393 * 'www.' text was not prefixed by a scheme (i.e.: not prefixed by
2394 * 'http://', 'ftp:', etc.)
2395 * 3. A protocol-relative ('//') match for the case of a 'www.' prefixed
2396 * URL. Will be an empty string if it is not a protocol-relative match.
2397 * We need to know the character before the '//' in order to determine
2398 * if it is a valid match or the // was in a string we don't want to
2399 * auto-link.
2400 * 4. Group that matches a known TLD (top level domain), when a scheme
2401 * or 'www.'-prefixed domain is not matched.
2402 * 5. A protocol-relative ('//') match for the case of a known TLD prefixed
2403 * URL. Will be an empty string if it is not a protocol-relative match.
2404 * See #3 for more info.
2405 */
2406 _this.matcherRegex = matcherRegex;
2407 /**
2408 * A regular expression to use to check the character before a protocol-relative
2409 * URL match. We don't want to match a protocol-relative URL if it is part
2410 * of another word.
2411 *
2412 * For example, we want to match something like "Go to: //google.com",
2413 * but we don't want to match something like "abc//google.com"
2414 *
2415 * This regular expression is used to test the character before the '//'.
2416 *
2417 * @protected
2418 * @type {RegExp} wordCharRegExp
2419 */
2420 _this.wordCharRegExp = wordCharRegExp;
2421 _this.stripPrefix = cfg.stripPrefix;
2422 _this.stripTrailingSlash = cfg.stripTrailingSlash;
2423 _this.decodePercentEncoding = cfg.decodePercentEncoding;
2424 return _this;
2425 }
2426 /**
2427 * @inheritdoc
2428 */
2429 UrlMatcher.prototype.parseMatches = function (text) {
2430 var matcherRegex = this.matcherRegex, stripPrefix = this.stripPrefix, stripTrailingSlash = this.stripTrailingSlash, decodePercentEncoding = this.decodePercentEncoding, tagBuilder = this.tagBuilder, matches = [], match;
2431 var _loop_1 = function () {
2432 var matchStr = match[0], schemeUrlMatch = match[1], wwwUrlMatch = match[4], wwwProtocolRelativeMatch = match[5],
2433 //tldUrlMatch = match[ 8 ], -- not needed at the moment
2434 tldProtocolRelativeMatch = match[9], offset = match.index, protocolRelativeMatch = wwwProtocolRelativeMatch || tldProtocolRelativeMatch, prevChar = text.charAt(offset - 1);
2435 if (!UrlMatchValidator.isValid(matchStr, schemeUrlMatch)) {
2436 return "continue";
2437 }
2438 // If the match is preceded by an '@' character, then it is either
2439 // an email address or a username. Skip these types of matches.
2440 if (offset > 0 && prevChar === '@') {
2441 return "continue";
2442 }
2443 // If it's a protocol-relative '//' match, but the character before the '//'
2444 // was a word character (i.e. a letter/number), then we found the '//' in the
2445 // middle of another word (such as "asdf//asdf.com"). In this case, skip the
2446 // match.
2447 if (offset > 0 && protocolRelativeMatch && this_1.wordCharRegExp.test(prevChar)) {
2448 return "continue";
2449 }
2450 // If the URL ends with a question mark, don't include the question
2451 // mark as part of the URL. We'll assume the question mark was the
2452 // end of a sentence, such as: "Going to google.com?"
2453 if (/\?$/.test(matchStr)) {
2454 matchStr = matchStr.substr(0, matchStr.length - 1);
2455 }
2456 // Handle a closing parenthesis or square bracket at the end of the
2457 // match, and exclude it if there is not a matching open parenthesis
2458 // or square bracket in the match itself.
2459 if (this_1.matchHasUnbalancedClosingParen(matchStr)) {
2460 matchStr = matchStr.substr(0, matchStr.length - 1); // remove the trailing ")"
2461 }
2462 else {
2463 // Handle an invalid character after the TLD
2464 var pos = this_1.matchHasInvalidCharAfterTld(matchStr, schemeUrlMatch);
2465 if (pos > -1) {
2466 matchStr = matchStr.substr(0, pos); // remove the trailing invalid chars
2467 }
2468 }
2469 // The autolinker accepts many characters in a url's scheme (like `fake://test.com`).
2470 // However, in cases where a URL is missing whitespace before an obvious link,
2471 // (for example: `nowhitespacehttp://www.test.com`), we only want the match to start
2472 // at the http:// part. We will check if the match contains a common scheme and then
2473 // shift the match to start from there.
2474 var foundCommonScheme = ['http://', 'https://'].find(function (commonScheme) { return !!schemeUrlMatch && schemeUrlMatch.indexOf(commonScheme) !== -1; });
2475 if (foundCommonScheme) {
2476 // If we found an overmatched URL, we want to find the index
2477 // of where the match should start and shift the match to
2478 // start from the beginning of the common scheme
2479 var indexOfSchemeStart = matchStr.indexOf(foundCommonScheme);
2480 matchStr = matchStr.substr(indexOfSchemeStart);
2481 schemeUrlMatch = schemeUrlMatch.substr(indexOfSchemeStart);
2482 offset = offset + indexOfSchemeStart;
2483 }
2484 var urlMatchType = schemeUrlMatch
2485 ? 'scheme'
2486 : wwwUrlMatch
2487 ? 'www'
2488 : 'tld', protocolUrlMatch = !!schemeUrlMatch;
2489 matches.push(new UrlMatch({
2490 tagBuilder: tagBuilder,
2491 matchedText: matchStr,
2492 offset: offset,
2493 urlMatchType: urlMatchType,
2494 url: matchStr,
2495 protocolUrlMatch: protocolUrlMatch,
2496 protocolRelativeMatch: !!protocolRelativeMatch,
2497 stripPrefix: stripPrefix,
2498 stripTrailingSlash: stripTrailingSlash,
2499 decodePercentEncoding: decodePercentEncoding,
2500 }));
2501 };
2502 var this_1 = this;
2503 while ((match = matcherRegex.exec(text)) !== null) {
2504 _loop_1();
2505 }
2506 return matches;
2507 };
2508 /**
2509 * Determines if a match found has an unmatched closing parenthesis,
2510 * square bracket or curly bracket. If so, the symbol will be removed
2511 * from the match itself, and appended after the generated anchor tag.
2512 *
2513 * A match may have an extra closing parenthesis at the end of the match
2514 * because the regular expression must include parenthesis for URLs such as
2515 * "wikipedia.com/something_(disambiguation)", which should be auto-linked.
2516 *
2517 * However, an extra parenthesis *will* be included when the URL itself is
2518 * wrapped in parenthesis, such as in the case of:
2519 * "(wikipedia.com/something_(disambiguation))"
2520 * In this case, the last closing parenthesis should *not* be part of the
2521 * URL itself, and this method will return `true`.
2522 *
2523 * For square brackets in URLs such as in PHP arrays, the same behavior as
2524 * parenthesis discussed above should happen:
2525 * "[http://www.example.com/foo.php?bar[]=1&bar[]=2&bar[]=3]"
2526 * The closing square bracket should not be part of the URL itself, and this
2527 * method will return `true`.
2528 *
2529 * @protected
2530 * @param {String} matchStr The full match string from the {@link #matcherRegex}.
2531 * @return {Boolean} `true` if there is an unbalanced closing parenthesis or
2532 * square bracket at the end of the `matchStr`, `false` otherwise.
2533 */
2534 UrlMatcher.prototype.matchHasUnbalancedClosingParen = function (matchStr) {
2535 var endChar = matchStr.charAt(matchStr.length - 1);
2536 var startChar;
2537 if (endChar === ')') {
2538 startChar = '(';
2539 }
2540 else if (endChar === ']') {
2541 startChar = '[';
2542 }
2543 else if (endChar === '}') {
2544 startChar = '{';
2545 }
2546 else {
2547 return false; // not a close parenthesis or square bracket
2548 }
2549 // Find if there are the same number of open braces as close braces in
2550 // the URL string, minus the last character (which we have already
2551 // determined to be either ')', ']' or '}'
2552 var numOpenBraces = 0;
2553 for (var i = 0, len = matchStr.length - 1; i < len; i++) {
2554 var char = matchStr.charAt(i);
2555 if (char === startChar) {
2556 numOpenBraces++;
2557 }
2558 else if (char === endChar) {
2559 numOpenBraces = Math.max(numOpenBraces - 1, 0);
2560 }
2561 }
2562 // If the number of open braces matches the number of close braces in
2563 // the URL minus the last character, then the match has *unbalanced*
2564 // braces because of the last character. Example of unbalanced braces
2565 // from the regex match:
2566 // "http://example.com?a[]=1]"
2567 if (numOpenBraces === 0) {
2568 return true;
2569 }
2570 return false;
2571 };
2572 /**
2573 * Determine if there's an invalid character after the TLD in a URL. Valid
2574 * characters after TLD are ':/?#'. Exclude scheme matched URLs from this
2575 * check.
2576 *
2577 * @protected
2578 * @param {String} urlMatch The matched URL, if there was one. Will be an
2579 * empty string if the match is not a URL match.
2580 * @param {String} schemeUrlMatch The match URL string for a scheme
2581 * match. Ex: 'http://yahoo.com'. This is used to match something like
2582 * 'http://localhost', where we won't double check that the domain name
2583 * has at least one '.' in it.
2584 * @return {Number} the position where the invalid character was found. If
2585 * no such character was found, returns -1
2586 */
2587 UrlMatcher.prototype.matchHasInvalidCharAfterTld = function (urlMatch, schemeUrlMatch) {
2588 if (!urlMatch) {
2589 return -1;
2590 }
2591 var offset = 0;
2592 if (schemeUrlMatch) {
2593 offset = urlMatch.indexOf(':');
2594 urlMatch = urlMatch.slice(offset);
2595 }
2596 // prettier-ignore
2597 var re = new RegExp("^((.?\/\/)?[-." + alphaNumericAndMarksCharsStr + "]*[-" + alphaNumericAndMarksCharsStr + "]\\.[-" + alphaNumericAndMarksCharsStr + "]+)");
2598 var res = re.exec(urlMatch);
2599 if (res === null) {
2600 return -1;
2601 }
2602 offset += res[1].length;
2603 urlMatch = urlMatch.slice(res[1].length);
2604 if (/^[^-.A-Za-z0-9:\/?#]/.test(urlMatch)) {
2605 return offset;
2606 }
2607 return -1;
2608 };
2609 return UrlMatcher;
2610 }(Matcher));
2611
2612 // For debugging: search for other "For debugging" lines
2613 // import CliTable from 'cli-table';
2614 var hashtagTextCharRe = new RegExp("[_".concat(alphaNumericAndMarksCharsStr, "]"));
2615 /**
2616 * @class Autolinker.matcher.Hashtag
2617 * @extends Autolinker.matcher.Matcher
2618 *
2619 * Matcher to find HashtagMatch matches in an input string.
2620 */
2621 var HashtagMatcher = /** @class */ (function (_super) {
2622 __extends(HashtagMatcher, _super);
2623 /**
2624 * @method constructor
2625 * @param {Object} cfg The configuration properties for the Match instance,
2626 * specified in an Object (map).
2627 */
2628 function HashtagMatcher(cfg) {
2629 var _this = _super.call(this, cfg) || this;
2630 /**
2631 * @cfg {String} service
2632 *
2633 * A string for the service name to have hashtags (ex: "#myHashtag")
2634 * auto-linked to. The currently-supported values are:
2635 *
2636 * - 'twitter'
2637 * - 'facebook'
2638 * - 'instagram'
2639 * - 'tiktok'
2640 */
2641 _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
2642 _this.serviceName = cfg.serviceName;
2643 return _this;
2644 }
2645 /**
2646 * @inheritdoc
2647 */
2648 HashtagMatcher.prototype.parseMatches = function (text) {
2649 var tagBuilder = this.tagBuilder;
2650 var serviceName = this.serviceName;
2651 var matches = [];
2652 var len = text.length;
2653 var charIdx = 0, hashCharIdx = -1, state = 0 /* None */;
2654 // For debugging: search for other "For debugging" lines
2655 // const table = new CliTable( {
2656 // head: [ 'charIdx', 'char', 'state', 'charIdx', 'currentEmailAddress.idx', 'hasDomainDot' ]
2657 // } );
2658 while (charIdx < len) {
2659 var char = text.charAt(charIdx);
2660 // For debugging: search for other "For debugging" lines
2661 // table.push(
2662 // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ]
2663 // );
2664 switch (state) {
2665 case 0 /* None */:
2666 stateNone(char);
2667 break;
2668 case 1 /* NonHashtagWordChar */:
2669 stateNonHashtagWordChar(char);
2670 break;
2671 case 2 /* HashtagHashChar */:
2672 stateHashtagHashChar(char);
2673 break;
2674 case 3 /* HashtagTextChar */:
2675 stateHashtagTextChar(char);
2676 break;
2677 default:
2678 throwUnhandledCaseError(state);
2679 }
2680 // For debugging: search for other "For debugging" lines
2681 // table.push(
2682 // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ]
2683 // );
2684 charIdx++;
2685 }
2686 // Capture any valid match at the end of the string
2687 captureMatchIfValid();
2688 // For debugging: search for other "For debugging" lines
2689 //console.log( '\n' + table.toString() );
2690 return matches;
2691 // Handles the state when we're not in a hashtag or any word
2692 function stateNone(char) {
2693 if (char === '#') {
2694 state = 2 /* HashtagHashChar */;
2695 hashCharIdx = charIdx;
2696 }
2697 else if (alphaNumericAndMarksCharRe.test(char)) {
2698 state = 1 /* NonHashtagWordChar */;
2699 }
2700 else ;
2701 }
2702 // Handles the state when we've encountered a word character but are not
2703 // in a hashtag. This is used to distinguish between a standalone
2704 // hashtag such as '#Stuff' vs a hash char that is part of a word like
2705 // 'asdf#stuff' (the latter of which would not be a match)
2706 function stateNonHashtagWordChar(char) {
2707 if (alphaNumericAndMarksCharRe.test(char)) ;
2708 else {
2709 state = 0 /* None */;
2710 }
2711 }
2712 // Handles the state when we've just encountered a '#' character
2713 function stateHashtagHashChar(char) {
2714 if (hashtagTextCharRe.test(char)) {
2715 // '#' char with valid hash text char following
2716 state = 3 /* HashtagTextChar */;
2717 }
2718 else if (alphaNumericAndMarksCharRe.test(char)) {
2719 state = 1 /* NonHashtagWordChar */;
2720 }
2721 else {
2722 state = 0 /* None */;
2723 }
2724 }
2725 // Handles the state when we're currently in the hash tag's text chars
2726 function stateHashtagTextChar(char) {
2727 if (hashtagTextCharRe.test(char)) ;
2728 else {
2729 captureMatchIfValid();
2730 hashCharIdx = -1;
2731 if (alphaNumericAndMarksCharRe.test(char)) {
2732 state = 1 /* NonHashtagWordChar */;
2733 }
2734 else {
2735 state = 0 /* None */;
2736 }
2737 }
2738 }
2739 /*
2740 * Captures the current hashtag as a HashtagMatch if it's valid.
2741 */
2742 function captureMatchIfValid() {
2743 if (hashCharIdx > -1 && charIdx - hashCharIdx <= 140) {
2744 // Max length of 140 for a hashtag ('#' char + 139 word chars)
2745 var matchedText = text.slice(hashCharIdx, charIdx);
2746 var match = new HashtagMatch({
2747 tagBuilder: tagBuilder,
2748 matchedText: matchedText,
2749 offset: hashCharIdx,
2750 serviceName: serviceName,
2751 hashtag: matchedText.slice(1),
2752 });
2753 matches.push(match);
2754 }
2755 }
2756 };
2757 return HashtagMatcher;
2758 }(Matcher));
2759 var hashtagServices = ['twitter', 'facebook', 'instagram', 'tiktok'];
2760
2761 // RegExp objects which are shared by all instances of PhoneMatcher. These are
2762 // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
2763 // called multiple times, thus instantiating PhoneMatcher and its RegExp
2764 // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
2765 // See descriptions of the properties where they are used for details about them
2766 // Over the years, many people have added to this regex, but it should have been
2767 // split up by country. Maybe one day we can break this down.
2768 var mostPhoneNumbers = /(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/;
2769 // Regex for Japanese phone numbers
2770 var japanesePhoneRe = /(0([1-9]{1}-?[1-9]\d{3}|[1-9]{2}-?\d{3}|[1-9]{2}\d{1}-?\d{2}|[1-9]{2}\d{2}-?\d{1})-?\d{4}|0[789]0-?\d{4}-?\d{4}|050-?\d{4}-?\d{4})/;
2771 // Combined regex
2772 var phoneMatcherRegex = new RegExp("".concat(mostPhoneNumbers.source, "|").concat(japanesePhoneRe.source), 'g');
2773 /**
2774 * @class Autolinker.matcher.Phone
2775 * @extends Autolinker.matcher.Matcher
2776 *
2777 * Matcher to find Phone number matches in an input string.
2778 *
2779 * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more
2780 * details.
2781 */
2782 var PhoneMatcher = /** @class */ (function (_super) {
2783 __extends(PhoneMatcher, _super);
2784 function PhoneMatcher() {
2785 var _this = _super !== null && _super.apply(this, arguments) || this;
2786 /**
2787 * The regular expression to match Phone numbers. Example matches:
2788 *
2789 * (123) 456-7890
2790 * 123 456 7890
2791 * 123-456-7890
2792 * +18004441234,,;,10226420346#
2793 * +1 (800) 444 1234
2794 * 10226420346#
2795 * 1-800-444-1234,1022,64,20346#
2796 *
2797 * This regular expression has the following capturing groups:
2798 *
2799 * 1 or 2. The prefixed '+' sign, if there is one.
2800 *
2801 * @protected
2802 * @property {RegExp} matcherRegex
2803 */
2804 _this.matcherRegex = phoneMatcherRegex;
2805 return _this;
2806 }
2807 /**
2808 * @inheritdoc
2809 */
2810 PhoneMatcher.prototype.parseMatches = function (text) {
2811 var matcherRegex = this.matcherRegex, tagBuilder = this.tagBuilder, matches = [], match;
2812 while ((match = matcherRegex.exec(text)) !== null) {
2813 // Remove non-numeric values from phone number string
2814 var matchedText = match[0], cleanNumber = matchedText.replace(/[^0-9,;#]/g, ''), // strip out non-digit characters exclude comma semicolon and #
2815 plusSign = !!(match[1] || match[2]), // match[ 1 ] or match[ 2 ] is the prefixed plus sign, if there is one
2816 before = match.index == 0 ? '' : text.substr(match.index - 1, 1), after = text.substr(match.index + matchedText.length, 1), contextClear = !before.match(/\d/) && !after.match(/\d/);
2817 if (this.testMatch(match[3]) && this.testMatch(matchedText) && contextClear) {
2818 matches.push(new PhoneMatch({
2819 tagBuilder: tagBuilder,
2820 matchedText: matchedText,
2821 offset: match.index,
2822 number: cleanNumber,
2823 plusSign: plusSign,
2824 }));
2825 }
2826 }
2827 return matches;
2828 };
2829 PhoneMatcher.prototype.testMatch = function (text) {
2830 return nonDigitRe.test(text);
2831 };
2832 return PhoneMatcher;
2833 }(Matcher));
2834
2835 // RegExp objects which are shared by all instances of MentionMatcher. These are
2836 // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
2837 // called multiple times, thus instantiating MentionMatcher and its RegExp
2838 // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
2839 // See descriptions of the properties where they are used for details about them
2840 var twitterRegex = new RegExp("@[_".concat(alphaNumericAndMarksCharsStr, "]{1,50}(?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 50 characters
2841 var instagramRegex = new RegExp("@[_.".concat(alphaNumericAndMarksCharsStr, "]{1,30}(?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 30 characters
2842 var soundcloudRegex = new RegExp("@[-_.".concat(alphaNumericAndMarksCharsStr, "]{1,50}(?![-_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 50 characters
2843 // TikTok usernames are 1-24 characters containing letters, numbers, underscores
2844 // and periods, but cannot end in a period: https://support.tiktok.com/en/getting-started/setting-up-your-profile/changing-your-username
2845 var tiktokRegex = new RegExp("@[_.".concat(alphaNumericAndMarksCharsStr, "]{1,23}[_").concat(alphaNumericAndMarksCharsStr, "](?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 24 characters
2846 var nonWordCharRegex = new RegExp('[^' + alphaNumericAndMarksCharsStr + ']');
2847 /**
2848 * @class Autolinker.matcher.Mention
2849 * @extends Autolinker.matcher.Matcher
2850 *
2851 * Matcher to find/replace username matches in an input string.
2852 */
2853 var MentionMatcher = /** @class */ (function (_super) {
2854 __extends(MentionMatcher, _super);
2855 /**
2856 * @method constructor
2857 * @param {Object} cfg The configuration properties for the Match instance,
2858 * specified in an Object (map).
2859 */
2860 function MentionMatcher(cfg) {
2861 var _this = _super.call(this, cfg) || this;
2862 /**
2863 * @cfg {'twitter'/'instagram'/'soundcloud'} protected
2864 *
2865 * The name of service to link @mentions to.
2866 *
2867 * Valid values are: 'twitter', 'instagram', 'soundcloud', or 'tiktok'
2868 */
2869 _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
2870 /**
2871 * Hash of regular expression to match username handles. Example match:
2872 *
2873 * @asdf
2874 *
2875 * @private
2876 * @property {Object} matcherRegexes
2877 */
2878 _this.matcherRegexes = {
2879 twitter: twitterRegex,
2880 instagram: instagramRegex,
2881 soundcloud: soundcloudRegex,
2882 tiktok: tiktokRegex,
2883 };
2884 /**
2885 * The regular expression to use to check the character before a username match to
2886 * make sure we didn't accidentally match an email address.
2887 *
2888 * For example, the string "asdf@asdf.com" should not match "@asdf" as a username.
2889 *
2890 * @private
2891 * @property {RegExp} nonWordCharRegex
2892 */
2893 _this.nonWordCharRegex = nonWordCharRegex;
2894 _this.serviceName = cfg.serviceName;
2895 return _this;
2896 }
2897 /**
2898 * @inheritdoc
2899 */
2900 MentionMatcher.prototype.parseMatches = function (text) {
2901 var serviceName = this.serviceName, matcherRegex = this.matcherRegexes[this.serviceName], nonWordCharRegex = this.nonWordCharRegex, tagBuilder = this.tagBuilder, matches = [], match;
2902 if (!matcherRegex) {
2903 return matches;
2904 }
2905 while ((match = matcherRegex.exec(text)) !== null) {
2906 var offset = match.index, prevChar = text.charAt(offset - 1);
2907 // If we found the match at the beginning of the string, or we found the match
2908 // and there is a whitespace char in front of it (meaning it is not an email
2909 // address), then it is a username match.
2910 if (offset === 0 || nonWordCharRegex.test(prevChar)) {
2911 var matchedText = match[0].replace(/\.+$/g, ''), // strip off trailing .
2912 mention = matchedText.slice(1); // strip off the '@' character at the beginning
2913 matches.push(new MentionMatch({
2914 tagBuilder: tagBuilder,
2915 matchedText: matchedText,
2916 offset: offset,
2917 serviceName: serviceName,
2918 mention: mention,
2919 }));
2920 }
2921 }
2922 return matches;
2923 };
2924 return MentionMatcher;
2925 }(Matcher));
2926
2927 // For debugging: search for other "For debugging" lines
2928 // import CliTable from 'cli-table';
2929 /**
2930 * Parses an HTML string, calling the callbacks to notify of tags and text.
2931 *
2932 * ## History
2933 *
2934 * This file previously used a regular expression to find html tags in the input
2935 * text. Unfortunately, we ran into a bunch of catastrophic backtracking issues
2936 * with certain input text, causing Autolinker to either hang or just take a
2937 * really long time to parse the string.
2938 *
2939 * The current code is intended to be a O(n) algorithm that walks through
2940 * the string in one pass, and tries to be as cheap as possible. We don't need
2941 * to implement the full HTML spec, but rather simply determine where the string
2942 * looks like an HTML tag, and where it looks like text (so that we can autolink
2943 * that).
2944 *
2945 * This state machine parser is intended just to be a simple but performant
2946 * parser of HTML for the subset of requirements we have. We simply need to:
2947 *
2948 * 1. Determine where HTML tags are
2949 * 2. Determine the tag name (Autolinker specifically only cares about <a>,
2950 * <script>, and <style> tags, so as not to link any text within them)
2951 *
2952 * We don't need to:
2953 *
2954 * 1. Create a parse tree
2955 * 2. Auto-close tags with invalid markup
2956 * 3. etc.
2957 *
2958 * The other intention behind this is that we didn't want to add external
2959 * dependencies on the Autolinker utility which would increase its size. For
2960 * instance, adding htmlparser2 adds 125kb to the minified output file,
2961 * increasing its final size from 47kb to 172kb (at the time of writing). It
2962 * also doesn't work exactly correctly, treating the string "<3 blah blah blah"
2963 * as an HTML tag.
2964 *
2965 * Reference for HTML spec:
2966 *
2967 * https://www.w3.org/TR/html51/syntax.html#sec-tokenization
2968 *
2969 * @param {String} html The HTML to parse
2970 * @param {Object} callbacks
2971 * @param {Function} callbacks.onOpenTag Callback function to call when an open
2972 * tag is parsed. Called with the tagName as its argument.
2973 * @param {Function} callbacks.onCloseTag Callback function to call when a close
2974 * tag is parsed. Called with the tagName as its argument. If a self-closing
2975 * tag is found, `onCloseTag` is called immediately after `onOpenTag`.
2976 * @param {Function} callbacks.onText Callback function to call when text (i.e
2977 * not an HTML tag) is parsed. Called with the text (string) as its first
2978 * argument, and offset (number) into the string as its second.
2979 */
2980 function parseHtml(html, _a) {
2981 var onOpenTag = _a.onOpenTag, onCloseTag = _a.onCloseTag, onText = _a.onText, onComment = _a.onComment, onDoctype = _a.onDoctype;
2982 var noCurrentTag = new CurrentTag();
2983 var charIdx = 0, len = html.length, state = 0 /* Data */, currentDataIdx = 0, // where the current data start index is
2984 currentTag = noCurrentTag; // describes the current tag that is being read
2985 // For debugging: search for other "For debugging" lines
2986 // const table = new CliTable( {
2987 // head: [ 'charIdx', 'char', 'state', 'currentDataIdx', 'currentOpenTagIdx', 'tag.type' ]
2988 // } );
2989 while (charIdx < len) {
2990 var char = html.charAt(charIdx);
2991 // For debugging: search for other "For debugging" lines
2992 // ALSO: Temporarily remove the 'const' keyword on the State enum
2993 // table.push(
2994 // [ charIdx, char, State[ state ], currentDataIdx, currentTag.idx, currentTag.idx === -1 ? '' : currentTag.type ]
2995 // );
2996 switch (state) {
2997 case 0 /* Data */:
2998 stateData(char);
2999 break;
3000 case 1 /* TagOpen */:
3001 stateTagOpen(char);
3002 break;
3003 case 2 /* EndTagOpen */:
3004 stateEndTagOpen(char);
3005 break;
3006 case 3 /* TagName */:
3007 stateTagName(char);
3008 break;
3009 case 4 /* BeforeAttributeName */:
3010 stateBeforeAttributeName(char);
3011 break;
3012 case 5 /* AttributeName */:
3013 stateAttributeName(char);
3014 break;
3015 case 6 /* AfterAttributeName */:
3016 stateAfterAttributeName(char);
3017 break;
3018 case 7 /* BeforeAttributeValue */:
3019 stateBeforeAttributeValue(char);
3020 break;
3021 case 8 /* AttributeValueDoubleQuoted */:
3022 stateAttributeValueDoubleQuoted(char);
3023 break;
3024 case 9 /* AttributeValueSingleQuoted */:
3025 stateAttributeValueSingleQuoted(char);
3026 break;
3027 case 10 /* AttributeValueUnquoted */:
3028 stateAttributeValueUnquoted(char);
3029 break;
3030 case 11 /* AfterAttributeValueQuoted */:
3031 stateAfterAttributeValueQuoted(char);
3032 break;
3033 case 12 /* SelfClosingStartTag */:
3034 stateSelfClosingStartTag(char);
3035 break;
3036 case 13 /* MarkupDeclarationOpenState */:
3037 stateMarkupDeclarationOpen();
3038 break;
3039 case 14 /* CommentStart */:
3040 stateCommentStart(char);
3041 break;
3042 case 15 /* CommentStartDash */:
3043 stateCommentStartDash(char);
3044 break;
3045 case 16 /* Comment */:
3046 stateComment(char);
3047 break;
3048 case 17 /* CommentEndDash */:
3049 stateCommentEndDash(char);
3050 break;
3051 case 18 /* CommentEnd */:
3052 stateCommentEnd(char);
3053 break;
3054 case 19 /* CommentEndBang */:
3055 stateCommentEndBang(char);
3056 break;
3057 case 20 /* Doctype */:
3058 stateDoctype(char);
3059 break;
3060 default:
3061 throwUnhandledCaseError(state);
3062 }
3063 // For debugging: search for other "For debugging" lines
3064 // ALSO: Temporarily remove the 'const' keyword on the State enum
3065 // table.push(
3066 // [ charIdx, char, State[ state ], currentDataIdx, currentTag.idx, currentTag.idx === -1 ? '' : currentTag.type ]
3067 // );
3068 charIdx++;
3069 }
3070 if (currentDataIdx < charIdx) {
3071 emitText();
3072 }
3073 // For debugging: search for other "For debugging" lines
3074 // console.log( '\n' + table.toString() );
3075 // Called when non-tags are being read (i.e. the text around HTML †ags)
3076 // https://www.w3.org/TR/html51/syntax.html#data-state
3077 function stateData(char) {
3078 if (char === '<') {
3079 startNewTag();
3080 }
3081 }
3082 // Called after a '<' is read from the Data state
3083 // https://www.w3.org/TR/html51/syntax.html#tag-open-state
3084 function stateTagOpen(char) {
3085 if (char === '!') {
3086 state = 13 /* MarkupDeclarationOpenState */;
3087 }
3088 else if (char === '/') {
3089 state = 2 /* EndTagOpen */;
3090 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { isClosing: true }));
3091 }
3092 else if (char === '<') {
3093 // start of another tag (ignore the previous, incomplete one)
3094 startNewTag();
3095 }
3096 else if (letterRe.test(char)) {
3097 // tag name start (and no '/' read)
3098 state = 3 /* TagName */;
3099 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { isOpening: true }));
3100 }
3101 else {
3102 // Any other
3103 state = 0 /* Data */;
3104 currentTag = noCurrentTag;
3105 }
3106 }
3107 // After a '<x', '</x' sequence is read (where 'x' is a letter character),
3108 // this is to continue reading the tag name
3109 // https://www.w3.org/TR/html51/syntax.html#tag-name-state
3110 function stateTagName(char) {
3111 if (whitespaceRe.test(char)) {
3112 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { name: captureTagName() }));
3113 state = 4 /* BeforeAttributeName */;
3114 }
3115 else if (char === '<') {
3116 // start of another tag (ignore the previous, incomplete one)
3117 startNewTag();
3118 }
3119 else if (char === '/') {
3120 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { name: captureTagName() }));
3121 state = 12 /* SelfClosingStartTag */;
3122 }
3123 else if (char === '>') {
3124 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { name: captureTagName() }));
3125 emitTagAndPreviousTextNode(); // resets to Data state as well
3126 }
3127 else if (!letterRe.test(char) && !digitRe.test(char) && char !== ':') {
3128 // Anything else that does not form an html tag. Note: the colon
3129 // character is accepted for XML namespaced tags
3130 resetToDataState();
3131 }
3132 else ;
3133 }
3134 // Called after the '/' is read from a '</' sequence
3135 // https://www.w3.org/TR/html51/syntax.html#end-tag-open-state
3136 function stateEndTagOpen(char) {
3137 if (char === '>') {
3138 // parse error. Encountered "</>". Skip it without treating as a tag
3139 resetToDataState();
3140 }
3141 else if (letterRe.test(char)) {
3142 state = 3 /* TagName */;
3143 }
3144 else {
3145 // some other non-tag-like character, don't treat this as a tag
3146 resetToDataState();
3147 }
3148 }
3149 // https://www.w3.org/TR/html51/syntax.html#before-attribute-name-state
3150 function stateBeforeAttributeName(char) {
3151 if (whitespaceRe.test(char)) ;
3152 else if (char === '/') {
3153 state = 12 /* SelfClosingStartTag */;
3154 }
3155 else if (char === '>') {
3156 emitTagAndPreviousTextNode(); // resets to Data state as well
3157 }
3158 else if (char === '<') {
3159 // start of another tag (ignore the previous, incomplete one)
3160 startNewTag();
3161 }
3162 else if (char === "=" || quoteRe.test(char) || controlCharsRe.test(char)) {
3163 // "Parse error" characters that, according to the spec, should be
3164 // appended to the attribute name, but we'll treat these characters
3165 // as not forming a real HTML tag
3166 resetToDataState();
3167 }
3168 else {
3169 // Any other char, start of a new attribute name
3170 state = 5 /* AttributeName */;
3171 }
3172 }
3173 // https://www.w3.org/TR/html51/syntax.html#attribute-name-state
3174 function stateAttributeName(char) {
3175 if (whitespaceRe.test(char)) {
3176 state = 6 /* AfterAttributeName */;
3177 }
3178 else if (char === '/') {
3179 state = 12 /* SelfClosingStartTag */;
3180 }
3181 else if (char === '=') {
3182 state = 7 /* BeforeAttributeValue */;
3183 }
3184 else if (char === '>') {
3185 emitTagAndPreviousTextNode(); // resets to Data state as well
3186 }
3187 else if (char === '<') {
3188 // start of another tag (ignore the previous, incomplete one)
3189 startNewTag();
3190 }
3191 else if (quoteRe.test(char)) {
3192 // "Parse error" characters that, according to the spec, should be
3193 // appended to the attribute name, but we'll treat these characters
3194 // as not forming a real HTML tag
3195 resetToDataState();
3196 }
3197 else ;
3198 }
3199 // https://www.w3.org/TR/html51/syntax.html#after-attribute-name-state
3200 function stateAfterAttributeName(char) {
3201 if (whitespaceRe.test(char)) ;
3202 else if (char === '/') {
3203 state = 12 /* SelfClosingStartTag */;
3204 }
3205 else if (char === '=') {
3206 state = 7 /* BeforeAttributeValue */;
3207 }
3208 else if (char === '>') {
3209 emitTagAndPreviousTextNode();
3210 }
3211 else if (char === '<') {
3212 // start of another tag (ignore the previous, incomplete one)
3213 startNewTag();
3214 }
3215 else if (quoteRe.test(char)) {
3216 // "Parse error" characters that, according to the spec, should be
3217 // appended to the attribute name, but we'll treat these characters
3218 // as not forming a real HTML tag
3219 resetToDataState();
3220 }
3221 else {
3222 // Any other character, start a new attribute in the current tag
3223 state = 5 /* AttributeName */;
3224 }
3225 }
3226 // https://www.w3.org/TR/html51/syntax.html#before-attribute-value-state
3227 function stateBeforeAttributeValue(char) {
3228 if (whitespaceRe.test(char)) ;
3229 else if (char === "\"") {
3230 state = 8 /* AttributeValueDoubleQuoted */;
3231 }
3232 else if (char === "'") {
3233 state = 9 /* AttributeValueSingleQuoted */;
3234 }
3235 else if (/[>=`]/.test(char)) {
3236 // Invalid chars after an '=' for an attribute value, don't count
3237 // the current tag as an HTML tag
3238 resetToDataState();
3239 }
3240 else if (char === '<') {
3241 // start of another tag (ignore the previous, incomplete one)
3242 startNewTag();
3243 }
3244 else {
3245 // Any other character, consider it an unquoted attribute value
3246 state = 10 /* AttributeValueUnquoted */;
3247 }
3248 }
3249 // https://www.w3.org/TR/html51/syntax.html#attribute-value-double-quoted-state
3250 function stateAttributeValueDoubleQuoted(char) {
3251 if (char === "\"") {
3252 // end the current double-quoted attribute
3253 state = 11 /* AfterAttributeValueQuoted */;
3254 }
3255 }
3256 // https://www.w3.org/TR/html51/syntax.html#attribute-value-single-quoted-state
3257 function stateAttributeValueSingleQuoted(char) {
3258 if (char === "'") {
3259 // end the current single-quoted attribute
3260 state = 11 /* AfterAttributeValueQuoted */;
3261 }
3262 }
3263 // https://www.w3.org/TR/html51/syntax.html#attribute-value-unquoted-state
3264 function stateAttributeValueUnquoted(char) {
3265 if (whitespaceRe.test(char)) {
3266 state = 4 /* BeforeAttributeName */;
3267 }
3268 else if (char === '>') {
3269 emitTagAndPreviousTextNode();
3270 }
3271 else if (char === '<') {
3272 // start of another tag (ignore the previous, incomplete one)
3273 startNewTag();
3274 }
3275 else ;
3276 }
3277 // https://www.w3.org/TR/html51/syntax.html#after-attribute-value-quoted-state
3278 function stateAfterAttributeValueQuoted(char) {
3279 if (whitespaceRe.test(char)) {
3280 state = 4 /* BeforeAttributeName */;
3281 }
3282 else if (char === '/') {
3283 state = 12 /* SelfClosingStartTag */;
3284 }
3285 else if (char === '>') {
3286 emitTagAndPreviousTextNode();
3287 }
3288 else if (char === '<') {
3289 // start of another tag (ignore the previous, incomplete one)
3290 startNewTag();
3291 }
3292 else {
3293 // Any other character, "parse error". Spec says to switch to the
3294 // BeforeAttributeState and re-consume the character, as it may be
3295 // the start of a new attribute name
3296 state = 4 /* BeforeAttributeName */;
3297 reconsumeCurrentCharacter();
3298 }
3299 }
3300 // A '/' has just been read in the current tag (presumably for '/>'), and
3301 // this handles the next character
3302 // https://www.w3.org/TR/html51/syntax.html#self-closing-start-tag-state
3303 function stateSelfClosingStartTag(char) {
3304 if (char === '>') {
3305 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { isClosing: true }));
3306 emitTagAndPreviousTextNode(); // resets to Data state as well
3307 }
3308 else {
3309 state = 4 /* BeforeAttributeName */;
3310 }
3311 }
3312 // https://www.w3.org/TR/html51/syntax.html#markup-declaration-open-state
3313 // (HTML Comments or !DOCTYPE)
3314 function stateMarkupDeclarationOpen(char) {
3315 if (html.substr(charIdx, 2) === '--') {
3316 // html comment
3317 charIdx += 2; // "consume" characters
3318 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { type: 'comment' }));
3319 state = 14 /* CommentStart */;
3320 }
3321 else if (html.substr(charIdx, 7).toUpperCase() === 'DOCTYPE') {
3322 charIdx += 7; // "consume" characters
3323 currentTag = new CurrentTag(__assign(__assign({}, currentTag), { type: 'doctype' }));
3324 state = 20 /* Doctype */;
3325 }
3326 else {
3327 // At this point, the spec specifies that the state machine should
3328 // enter the "bogus comment" state, in which case any character(s)
3329 // after the '<!' that were read should become an HTML comment up
3330 // until the first '>' that is read (or EOF). Instead, we'll assume
3331 // that a user just typed '<!' as part of text data
3332 resetToDataState();
3333 }
3334 }
3335 // Handles after the sequence '<!--' has been read
3336 // https://www.w3.org/TR/html51/syntax.html#comment-start-state
3337 function stateCommentStart(char) {
3338 if (char === '-') {
3339 // We've read the sequence '<!---' at this point (3 dashes)
3340 state = 15 /* CommentStartDash */;
3341 }
3342 else if (char === '>') {
3343 // At this point, we'll assume the comment wasn't a real comment
3344 // so we'll just emit it as data. We basically read the sequence
3345 // '<!-->'
3346 resetToDataState();
3347 }
3348 else {
3349 // Any other char, take it as part of the comment
3350 state = 16 /* Comment */;
3351 }
3352 }
3353 // We've read the sequence '<!---' at this point (3 dashes)
3354 // https://www.w3.org/TR/html51/syntax.html#comment-start-dash-state
3355 function stateCommentStartDash(char) {
3356 if (char === '-') {
3357 // We've read '<!----' (4 dashes) at this point
3358 state = 18 /* CommentEnd */;
3359 }
3360 else if (char === '>') {
3361 // At this point, we'll assume the comment wasn't a real comment
3362 // so we'll just emit it as data. We basically read the sequence
3363 // '<!--->'
3364 resetToDataState();
3365 }
3366 else {
3367 // Anything else, take it as a valid comment
3368 state = 16 /* Comment */;
3369 }
3370 }
3371 // Currently reading the comment's text (data)
3372 // https://www.w3.org/TR/html51/syntax.html#comment-state
3373 function stateComment(char) {
3374 if (char === '-') {
3375 state = 17 /* CommentEndDash */;
3376 }
3377 }
3378 // When we we've read the first dash inside a comment, it may signal the
3379 // end of the comment if we read another dash
3380 // https://www.w3.org/TR/html51/syntax.html#comment-end-dash-state
3381 function stateCommentEndDash(char) {
3382 if (char === '-') {
3383 state = 18 /* CommentEnd */;
3384 }
3385 else {
3386 // Wasn't a dash, must still be part of the comment
3387 state = 16 /* Comment */;
3388 }
3389 }
3390 // After we've read two dashes inside a comment, it may signal the end of
3391 // the comment if we then read a '>' char
3392 // https://www.w3.org/TR/html51/syntax.html#comment-end-state
3393 function stateCommentEnd(char) {
3394 if (char === '>') {
3395 emitTagAndPreviousTextNode();
3396 }
3397 else if (char === '!') {
3398 state = 19 /* CommentEndBang */;
3399 }
3400 else if (char === '-') ;
3401 else {
3402 // Anything else, switch back to the comment state since we didn't
3403 // read the full "end comment" sequence (i.e. '-->')
3404 state = 16 /* Comment */;
3405 }
3406 }
3407 // We've read the sequence '--!' inside of a comment
3408 // https://www.w3.org/TR/html51/syntax.html#comment-end-bang-state
3409 function stateCommentEndBang(char) {
3410 if (char === '-') {
3411 // We read the sequence '--!-' inside of a comment. The last dash
3412 // could signify that the comment is going to close
3413 state = 17 /* CommentEndDash */;
3414 }
3415 else if (char === '>') {
3416 // End of comment with the sequence '--!>'
3417 emitTagAndPreviousTextNode();
3418 }
3419 else {
3420 // The '--!' was not followed by a '>', continue reading the
3421 // comment's text
3422 state = 16 /* Comment */;
3423 }
3424 }
3425 /**
3426 * For DOCTYPES in particular, we don't care about the attributes. Just
3427 * advance to the '>' character and emit the tag, unless we find a '<'
3428 * character in which case we'll start a new tag.
3429 *
3430 * Example doctype tag:
3431 * <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
3432 *
3433 * Actual spec: https://www.w3.org/TR/html51/syntax.html#doctype-state
3434 */
3435 function stateDoctype(char) {
3436 if (char === '>') {
3437 emitTagAndPreviousTextNode();
3438 }
3439 else if (char === '<') {
3440 startNewTag();
3441 }
3442 else ;
3443 }
3444 /**
3445 * Resets the state back to the Data state, and removes the current tag.
3446 *
3447 * We'll generally run this function whenever a "parse error" is
3448 * encountered, where the current tag that is being read no longer looks
3449 * like a real HTML tag.
3450 */
3451 function resetToDataState() {
3452 state = 0 /* Data */;
3453 currentTag = noCurrentTag;
3454 }
3455 /**
3456 * Starts a new HTML tag at the current index, ignoring any previous HTML
3457 * tag that was being read.
3458 *
3459 * We'll generally run this function whenever we read a new '<' character,
3460 * including when we read a '<' character inside of an HTML tag that we were
3461 * previously reading.
3462 */
3463 function startNewTag() {
3464 state = 1 /* TagOpen */;
3465 currentTag = new CurrentTag({ idx: charIdx });
3466 }
3467 /**
3468 * Once we've decided to emit an open tag, that means we can also emit the
3469 * text node before it.
3470 */
3471 function emitTagAndPreviousTextNode() {
3472 var textBeforeTag = html.slice(currentDataIdx, currentTag.idx);
3473 if (textBeforeTag) {
3474 // the html tag was the first element in the html string, or two
3475 // tags next to each other, in which case we should not emit a text
3476 // node
3477 onText(textBeforeTag, currentDataIdx);
3478 }
3479 if (currentTag.type === 'comment') {
3480 onComment(currentTag.idx);
3481 }
3482 else if (currentTag.type === 'doctype') {
3483 onDoctype(currentTag.idx);
3484 }
3485 else {
3486 if (currentTag.isOpening) {
3487 onOpenTag(currentTag.name, currentTag.idx);
3488 }
3489 if (currentTag.isClosing) {
3490 // note: self-closing tags will emit both opening and closing
3491 onCloseTag(currentTag.name, currentTag.idx);
3492 }
3493 }
3494 // Since we just emitted a tag, reset to the data state for the next char
3495 resetToDataState();
3496 currentDataIdx = charIdx + 1;
3497 }
3498 function emitText() {
3499 var text = html.slice(currentDataIdx, charIdx);
3500 onText(text, currentDataIdx);
3501 currentDataIdx = charIdx + 1;
3502 }
3503 /**
3504 * Captures the tag name from the start of the tag to the current character
3505 * index, and converts it to lower case
3506 */
3507 function captureTagName() {
3508 var startIdx = currentTag.idx + (currentTag.isClosing ? 2 : 1);
3509 return html.slice(startIdx, charIdx).toLowerCase();
3510 }
3511 /**
3512 * Causes the main loop to re-consume the current character, such as after
3513 * encountering a "parse error" that changed state and needs to reconsume
3514 * the same character in that new state.
3515 */
3516 function reconsumeCurrentCharacter() {
3517 charIdx--;
3518 }
3519 }
3520 var CurrentTag = /** @class */ (function () {
3521 function CurrentTag(cfg) {
3522 if (cfg === void 0) { cfg = {}; }
3523 this.idx = cfg.idx !== undefined ? cfg.idx : -1;
3524 this.type = cfg.type || 'tag';
3525 this.name = cfg.name || '';
3526 this.isOpening = !!cfg.isOpening;
3527 this.isClosing = !!cfg.isClosing;
3528 }
3529 return CurrentTag;
3530 }());
3531
3532 /**
3533 * @class Autolinker
3534 * @extends Object
3535 *
3536 * Utility class used to process a given string of text, and wrap the matches in
3537 * the appropriate anchor (&lt;a&gt;) tags to turn them into links.
3538 *
3539 * Any of the configuration options may be provided in an Object provided
3540 * to the Autolinker constructor, which will configure how the {@link #link link()}
3541 * method will process the links.
3542 *
3543 * For example:
3544 *
3545 * var autolinker = new Autolinker( {
3546 * newWindow : false,
3547 * truncate : 30
3548 * } );
3549 *
3550 * var html = autolinker.link( "Joe went to www.yahoo.com" );
3551 * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
3552 *
3553 *
3554 * The {@link #static-link static link()} method may also be used to inline
3555 * options into a single call, which may be more convenient for one-off uses.
3556 * For example:
3557 *
3558 * var html = Autolinker.link( "Joe went to www.yahoo.com", {
3559 * newWindow : false,
3560 * truncate : 30
3561 * } );
3562 * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
3563 *
3564 *
3565 * ## Custom Replacements of Links
3566 *
3567 * If the configuration options do not provide enough flexibility, a {@link #replaceFn}
3568 * may be provided to fully customize the output of Autolinker. This function is
3569 * called once for each URL/Email/Phone#/Hashtag/Mention (Twitter, Instagram, Soundcloud)
3570 * match that is encountered.
3571 *
3572 * For example:
3573 *
3574 * var input = "..."; // string with URLs, Email Addresses, Phone #s, Hashtags, and Mentions (Twitter, Instagram, Soundcloud)
3575 *
3576 * var linkedText = Autolinker.link( input, {
3577 * replaceFn : function( match ) {
3578 * console.log( "href = ", match.getAnchorHref() );
3579 * console.log( "text = ", match.getAnchorText() );
3580 *
3581 * switch( match.getType() ) {
3582 * case 'url' :
3583 * console.log( "url: ", match.getUrl() );
3584 *
3585 * if( match.getUrl().indexOf( 'mysite.com' ) === -1 ) {
3586 * var tag = match.buildTag(); // returns an `Autolinker.HtmlTag` instance, which provides mutator methods for easy changes
3587 * tag.setAttr( 'rel', 'nofollow' );
3588 * tag.addClass( 'external-link' );
3589 *
3590 * return tag;
3591 *
3592 * } else {
3593 * return true; // let Autolinker perform its normal anchor tag replacement
3594 * }
3595 *
3596 * case 'email' :
3597 * var email = match.getEmail();
3598 * console.log( "email: ", email );
3599 *
3600 * if( email === "my@own.address" ) {
3601 * return false; // don't auto-link this particular email address; leave as-is
3602 * } else {
3603 * return; // no return value will have Autolinker perform its normal anchor tag replacement (same as returning `true`)
3604 * }
3605 *
3606 * case 'phone' :
3607 * var phoneNumber = match.getPhoneNumber();
3608 * console.log( phoneNumber );
3609 *
3610 * return '<a href="http://newplace.to.link.phone.numbers.to/">' + phoneNumber + '</a>';
3611 *
3612 * case 'hashtag' :
3613 * var hashtag = match.getHashtag();
3614 * console.log( hashtag );
3615 *
3616 * return '<a href="http://newplace.to.link.hashtag.handles.to/">' + hashtag + '</a>';
3617 *
3618 * case 'mention' :
3619 * var mention = match.getMention();
3620 * console.log( mention );
3621 *
3622 * return '<a href="http://newplace.to.link.mention.to/">' + mention + '</a>';
3623 * }
3624 * }
3625 * } );
3626 *
3627 *
3628 * The function may return the following values:
3629 *
3630 * - `true` (Boolean): Allow Autolinker to replace the match as it normally
3631 * would.
3632 * - `false` (Boolean): Do not replace the current match at all - leave as-is.
3633 * - Any String: If a string is returned from the function, the string will be
3634 * used directly as the replacement HTML for the match.
3635 * - An {@link Autolinker.HtmlTag} instance, which can be used to build/modify
3636 * an HTML tag before writing out its HTML text.
3637 */
3638 var Autolinker = /** @class */ (function () {
3639 /**
3640 * @method constructor
3641 * @param {Object} [cfg] The configuration options for the Autolinker instance,
3642 * specified in an Object (map).
3643 */
3644 function Autolinker(cfg) {
3645 if (cfg === void 0) { cfg = {}; }
3646 /**
3647 * The Autolinker version number exposed on the instance itself.
3648 *
3649 * Ex: 0.25.1
3650 */
3651 this.version = Autolinker.version;
3652 /**
3653 * @cfg {Boolean/Object} [urls]
3654 *
3655 * `true` if URLs should be automatically linked, `false` if they should not
3656 * be. Defaults to `true`.
3657 *
3658 * Examples:
3659 *
3660 * urls: true
3661 *
3662 * // or
3663 *
3664 * urls: {
3665 * schemeMatches : true,
3666 * wwwMatches : true,
3667 * tldMatches : true
3668 * }
3669 *
3670 * As shown above, this option also accepts an Object form with 3 properties
3671 * to allow for more customization of what exactly gets linked. All default
3672 * to `true`:
3673 *
3674 * @cfg {Boolean} [urls.schemeMatches] `true` to match URLs found prefixed
3675 * with a scheme, i.e. `http://google.com`, or `other+scheme://google.com`,
3676 * `false` to prevent these types of matches.
3677 * @cfg {Boolean} [urls.wwwMatches] `true` to match urls found prefixed with
3678 * `'www.'`, i.e. `www.google.com`. `false` to prevent these types of
3679 * matches. Note that if the URL had a prefixed scheme, and
3680 * `schemeMatches` is true, it will still be linked.
3681 * @cfg {Boolean} [urls.tldMatches] `true` to match URLs with known top
3682 * level domains (.com, .net, etc.) that are not prefixed with a scheme or
3683 * `'www.'`. This option attempts to match anything that looks like a URL
3684 * in the given text. Ex: `google.com`, `asdf.org/?page=1`, etc. `false`
3685 * to prevent these types of matches.
3686 */
3687 this.urls = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
3688 /**
3689 * @cfg {Boolean} [email=true]
3690 *
3691 * `true` if email addresses should be automatically linked, `false` if they
3692 * should not be.
3693 */
3694 this.email = true; // default value just to get the above doc comment in the ES5 output and documentation generator
3695 /**
3696 * @cfg {Boolean} [phone=true]
3697 *
3698 * `true` if Phone numbers ("(555)555-5555") should be automatically linked,
3699 * `false` if they should not be.
3700 */
3701 this.phone = true; // default value just to get the above doc comment in the ES5 output and documentation generator
3702 /**
3703 * @cfg {Boolean/String} [hashtag=false]
3704 *
3705 * A string for the service name to have hashtags (ex: "#myHashtag")
3706 * auto-linked to. The currently-supported values are:
3707 *
3708 * - 'twitter'
3709 * - 'facebook'
3710 * - 'instagram'
3711 *
3712 * Pass `false` to skip auto-linking of hashtags.
3713 */
3714 this.hashtag = false; // default value just to get the above doc comment in the ES5 output and documentation generator
3715 /**
3716 * @cfg {String/Boolean} [mention=false]
3717 *
3718 * A string for the service name to have mentions (ex: "@myuser")
3719 * auto-linked to. The currently supported values are:
3720 *
3721 * - 'twitter'
3722 * - 'instagram'
3723 * - 'soundcloud'
3724 *
3725 * Defaults to `false` to skip auto-linking of mentions.
3726 */
3727 this.mention = false; // default value just to get the above doc comment in the ES5 output and documentation generator
3728 /**
3729 * @cfg {Boolean} [newWindow=true]
3730 *
3731 * `true` if the links should open in a new window, `false` otherwise.
3732 */
3733 this.newWindow = true; // default value just to get the above doc comment in the ES5 output and documentation generator
3734 /**
3735 * @cfg {Boolean/Object} [stripPrefix=true]
3736 *
3737 * `true` if 'http://' (or 'https://') and/or the 'www.' should be stripped
3738 * from the beginning of URL links' text, `false` otherwise. Defaults to
3739 * `true`.
3740 *
3741 * Examples:
3742 *
3743 * stripPrefix: true
3744 *
3745 * // or
3746 *
3747 * stripPrefix: {
3748 * scheme : true,
3749 * www : true
3750 * }
3751 *
3752 * As shown above, this option also accepts an Object form with 2 properties
3753 * to allow for more customization of what exactly is prevented from being
3754 * displayed. Both default to `true`:
3755 *
3756 * @cfg {Boolean} [stripPrefix.scheme] `true` to prevent the scheme part of
3757 * a URL match from being displayed to the user. Example:
3758 * `'http://google.com'` will be displayed as `'google.com'`. `false` to
3759 * not strip the scheme. NOTE: Only an `'http://'` or `'https://'` scheme
3760 * will be removed, so as not to remove a potentially dangerous scheme
3761 * (such as `'file://'` or `'javascript:'`)
3762 * @cfg {Boolean} [stripPrefix.www] www (Boolean): `true` to prevent the
3763 * `'www.'` part of a URL match from being displayed to the user. Ex:
3764 * `'www.google.com'` will be displayed as `'google.com'`. `false` to not
3765 * strip the `'www'`.
3766 */
3767 this.stripPrefix = {
3768 scheme: true,
3769 www: true,
3770 }; // default value just to get the above doc comment in the ES5 output and documentation generator
3771 /**
3772 * @cfg {Boolean} [stripTrailingSlash=true]
3773 *
3774 * `true` to remove the trailing slash from URL matches, `false` to keep
3775 * the trailing slash.
3776 *
3777 * Example when `true`: `http://google.com/` will be displayed as
3778 * `http://google.com`.
3779 */
3780 this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
3781 /**
3782 * @cfg {Boolean} [decodePercentEncoding=true]
3783 *
3784 * `true` to decode percent-encoded characters in URL matches, `false` to keep
3785 * the percent-encoded characters.
3786 *
3787 * Example when `true`: `https://en.wikipedia.org/wiki/San_Jos%C3%A9` will
3788 * be displayed as `https://en.wikipedia.org/wiki/San_José`.
3789 */
3790 this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
3791 /**
3792 * @cfg {Number/Object} [truncate=0]
3793 *
3794 * ## Number Form
3795 *
3796 * A number for how many characters matched text should be truncated to
3797 * inside the text of a link. If the matched text is over this number of
3798 * characters, it will be truncated to this length by adding a two period
3799 * ellipsis ('..') to the end of the string.
3800 *
3801 * For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file'
3802 * truncated to 25 characters might look something like this:
3803 * 'yahoo.com/some/long/pat..'
3804 *
3805 * Example Usage:
3806 *
3807 * truncate: 25
3808 *
3809 *
3810 * Defaults to `0` for "no truncation."
3811 *
3812 *
3813 * ## Object Form
3814 *
3815 * An Object may also be provided with two properties: `length` (Number) and
3816 * `location` (String). `location` may be one of the following: 'end'
3817 * (default), 'middle', or 'smart'.
3818 *
3819 * Example Usage:
3820 *
3821 * truncate: { length: 25, location: 'middle' }
3822 *
3823 * @cfg {Number} [truncate.length=0] How many characters to allow before
3824 * truncation will occur. Defaults to `0` for "no truncation."
3825 * @cfg {"end"/"middle"/"smart"} [truncate.location="end"]
3826 *
3827 * - 'end' (default): will truncate up to the number of characters, and then
3828 * add an ellipsis at the end. Ex: 'yahoo.com/some/long/pat..'
3829 * - 'middle': will truncate and add the ellipsis in the middle. Ex:
3830 * 'yahoo.com/s..th/to/a/file'
3831 * - 'smart': for URLs where the algorithm attempts to strip out unnecessary
3832 * parts first (such as the 'www.', then URL scheme, hash, etc.),
3833 * attempting to make the URL human-readable before looking for a good
3834 * point to insert the ellipsis if it is still too long. Ex:
3835 * 'yahoo.com/some..to/a/file'. For more details, see
3836 * {@link Autolinker.truncate.TruncateSmart}.
3837 */
3838 this.truncate = {
3839 length: 0,
3840 location: 'end',
3841 }; // default value just to get the above doc comment in the ES5 output and documentation generator
3842 /**
3843 * @cfg {String} className
3844 *
3845 * A CSS class name to add to the generated links. This class will be added
3846 * to all links, as well as this class plus match suffixes for styling
3847 * url/email/phone/hashtag/mention links differently.
3848 *
3849 * For example, if this config is provided as "myLink", then:
3850 *
3851 * - URL links will have the CSS classes: "myLink myLink-url"
3852 * - Email links will have the CSS classes: "myLink myLink-email", and
3853 * - Phone links will have the CSS classes: "myLink myLink-phone"
3854 * - Hashtag links will have the CSS classes: "myLink myLink-hashtag"
3855 * - Mention links will have the CSS classes: "myLink myLink-mention myLink-[type]"
3856 * where [type] is either "instagram", "twitter" or "soundcloud"
3857 */
3858 this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
3859 /**
3860 * @cfg {Function} replaceFn
3861 *
3862 * A function to individually process each match found in the input string.
3863 *
3864 * See the class's description for usage.
3865 *
3866 * The `replaceFn` can be called with a different context object (`this`
3867 * reference) using the {@link #context} cfg.
3868 *
3869 * This function is called with the following parameter:
3870 *
3871 * @cfg {Autolinker.match.Match} replaceFn.match The Match instance which
3872 * can be used to retrieve information about the match that the `replaceFn`
3873 * is currently processing. See {@link Autolinker.match.Match} subclasses
3874 * for details.
3875 */
3876 this.replaceFn = null; // default value just to get the above doc comment in the ES5 output and documentation generator
3877 /**
3878 * @cfg {Object} context
3879 *
3880 * The context object (`this` reference) to call the `replaceFn` with.
3881 *
3882 * Defaults to this Autolinker instance.
3883 */
3884 this.context = undefined; // default value just to get the above doc comment in the ES5 output and documentation generator
3885 /**
3886 * @cfg {Boolean} [sanitizeHtml=false]
3887 *
3888 * `true` to HTML-encode the start and end brackets of existing HTML tags found
3889 * in the input string. This will escape `<` and `>` characters to `&lt;` and
3890 * `&gt;`, respectively.
3891 *
3892 * Setting this to `true` will prevent XSS (Cross-site Scripting) attacks,
3893 * but will remove the significance of existing HTML tags in the input string. If
3894 * you would like to maintain the significance of existing HTML tags while also
3895 * making the output HTML string safe, leave this option as `false` and use a
3896 * tool like https://github.com/cure53/DOMPurify (or others) on the input string
3897 * before running Autolinker.
3898 */
3899 this.sanitizeHtml = false; // default value just to get the above doc comment in the ES5 output and documentation generator
3900 /**
3901 * @private
3902 * @property {Autolinker.matcher.Matcher[]} matchers
3903 *
3904 * The {@link Autolinker.matcher.Matcher} instances for this Autolinker
3905 * instance.
3906 *
3907 * This is lazily created in {@link #getMatchers}.
3908 */
3909 this.matchers = null;
3910 /**
3911 * @private
3912 * @property {Autolinker.AnchorTagBuilder} tagBuilder
3913 *
3914 * The AnchorTagBuilder instance used to build match replacement anchor tags.
3915 * Note: this is lazily instantiated in the {@link #getTagBuilder} method.
3916 */
3917 this.tagBuilder = null;
3918 // Note: when `this.something` is used in the rhs of these assignments,
3919 // it refers to the default values set above the constructor
3920 this.urls = this.normalizeUrlsCfg(cfg.urls);
3921 this.email = typeof cfg.email === 'boolean' ? cfg.email : this.email;
3922 this.phone = typeof cfg.phone === 'boolean' ? cfg.phone : this.phone;
3923 this.hashtag = cfg.hashtag || this.hashtag;
3924 this.mention = cfg.mention || this.mention;
3925 this.newWindow = typeof cfg.newWindow === 'boolean' ? cfg.newWindow : this.newWindow;
3926 this.stripPrefix = this.normalizeStripPrefixCfg(cfg.stripPrefix);
3927 this.stripTrailingSlash =
3928 typeof cfg.stripTrailingSlash === 'boolean'
3929 ? cfg.stripTrailingSlash
3930 : this.stripTrailingSlash;
3931 this.decodePercentEncoding =
3932 typeof cfg.decodePercentEncoding === 'boolean'
3933 ? cfg.decodePercentEncoding
3934 : this.decodePercentEncoding;
3935 this.sanitizeHtml = cfg.sanitizeHtml || false;
3936 // Validate the value of the `mention` cfg
3937 var mention = this.mention;
3938 if (mention !== false &&
3939 ['twitter', 'instagram', 'soundcloud', 'tiktok'].indexOf(mention) === -1) {
3940 throw new Error("invalid `mention` cfg '".concat(mention, "' - see docs"));
3941 }
3942 // Validate the value of the `hashtag` cfg
3943 var hashtag = this.hashtag;
3944 if (hashtag !== false && hashtagServices.indexOf(hashtag) === -1) {
3945 throw new Error("invalid `hashtag` cfg '".concat(hashtag, "' - see docs"));
3946 }
3947 this.truncate = this.normalizeTruncateCfg(cfg.truncate);
3948 this.className = cfg.className || this.className;
3949 this.replaceFn = cfg.replaceFn || this.replaceFn;
3950 this.context = cfg.context || this;
3951 }
3952 /**
3953 * Automatically links URLs, Email addresses, Phone Numbers, Twitter handles,
3954 * Hashtags, and Mentions found in the given chunk of HTML. Does not link URLs
3955 * found within HTML tags.
3956 *
3957 * For instance, if given the text: `You should go to http://www.yahoo.com`,
3958 * then the result will be `You should go to &lt;a href="http://www.yahoo.com"&gt;http://www.yahoo.com&lt;/a&gt;`
3959 *
3960 * Example:
3961 *
3962 * var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } );
3963 * // Produces: "Go to <a href="http://google.com">google.com</a>"
3964 *
3965 * @static
3966 * @param {String} textOrHtml The HTML or text to find matches within (depending
3967 * on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #mention},
3968 * {@link #hashtag}, and {@link #mention} options are enabled).
3969 * @param {Object} [options] Any of the configuration options for the Autolinker
3970 * class, specified in an Object (map). See the class description for an
3971 * example call.
3972 * @return {String} The HTML text, with matches automatically linked.
3973 */
3974 Autolinker.link = function (textOrHtml, options) {
3975 var autolinker = new Autolinker(options);
3976 return autolinker.link(textOrHtml);
3977 };
3978 /**
3979 * Parses the input `textOrHtml` looking for URLs, email addresses, phone
3980 * numbers, username handles, and hashtags (depending on the configuration
3981 * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
3982 * objects describing those matches (without making any replacements).
3983 *
3984 * Note that if parsing multiple pieces of text, it is slightly more efficient
3985 * to create an Autolinker instance, and use the instance-level {@link #parse}
3986 * method.
3987 *
3988 * Example:
3989 *
3990 * var matches = Autolinker.parse( "Hello google.com, I am asdf@asdf.com", {
3991 * urls: true,
3992 * email: true
3993 * } );
3994 *
3995 * console.log( matches.length ); // 2
3996 * console.log( matches[ 0 ].getType() ); // 'url'
3997 * console.log( matches[ 0 ].getUrl() ); // 'google.com'
3998 * console.log( matches[ 1 ].getType() ); // 'email'
3999 * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
4000 *
4001 * @static
4002 * @param {String} textOrHtml The HTML or text to find matches within
4003 * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
4004 * {@link #hashtag}, and {@link #mention} options are enabled).
4005 * @param {Object} [options] Any of the configuration options for the Autolinker
4006 * class, specified in an Object (map). See the class description for an
4007 * example call.
4008 * @return {Autolinker.match.Match[]} The array of Matches found in the
4009 * given input `textOrHtml`.
4010 */
4011 Autolinker.parse = function (textOrHtml, options) {
4012 var autolinker = new Autolinker(options);
4013 return autolinker.parse(textOrHtml);
4014 };
4015 /**
4016 * Normalizes the {@link #urls} config into an Object with 3 properties:
4017 * `schemeMatches`, `wwwMatches`, and `tldMatches`, all Booleans.
4018 *
4019 * See {@link #urls} config for details.
4020 *
4021 * @private
4022 * @param {Boolean/Object} urls
4023 * @return {Object}
4024 */
4025 Autolinker.prototype.normalizeUrlsCfg = function (urls) {
4026 if (urls == null)
4027 urls = true; // default to `true`
4028 if (typeof urls === 'boolean') {
4029 return { schemeMatches: urls, wwwMatches: urls, tldMatches: urls };
4030 }
4031 else {
4032 // object form
4033 return {
4034 schemeMatches: typeof urls.schemeMatches === 'boolean' ? urls.schemeMatches : true,
4035 wwwMatches: typeof urls.wwwMatches === 'boolean' ? urls.wwwMatches : true,
4036 tldMatches: typeof urls.tldMatches === 'boolean' ? urls.tldMatches : true,
4037 };
4038 }
4039 };
4040 /**
4041 * Normalizes the {@link #stripPrefix} config into an Object with 2
4042 * properties: `scheme`, and `www` - both Booleans.
4043 *
4044 * See {@link #stripPrefix} config for details.
4045 *
4046 * @private
4047 * @param {Boolean/Object} stripPrefix
4048 * @return {Object}
4049 */
4050 Autolinker.prototype.normalizeStripPrefixCfg = function (stripPrefix) {
4051 if (stripPrefix == null)
4052 stripPrefix = true; // default to `true`
4053 if (typeof stripPrefix === 'boolean') {
4054 return { scheme: stripPrefix, www: stripPrefix };
4055 }
4056 else {
4057 // object form
4058 return {
4059 scheme: typeof stripPrefix.scheme === 'boolean' ? stripPrefix.scheme : true,
4060 www: typeof stripPrefix.www === 'boolean' ? stripPrefix.www : true,
4061 };
4062 }
4063 };
4064 /**
4065 * Normalizes the {@link #truncate} config into an Object with 2 properties:
4066 * `length` (Number), and `location` (String).
4067 *
4068 * See {@link #truncate} config for details.
4069 *
4070 * @private
4071 * @param {Number/Object} truncate
4072 * @return {Object}
4073 */
4074 Autolinker.prototype.normalizeTruncateCfg = function (truncate) {
4075 if (typeof truncate === 'number') {
4076 return { length: truncate, location: 'end' };
4077 }
4078 else {
4079 // object, or undefined/null
4080 return defaults(truncate || {}, {
4081 length: Number.POSITIVE_INFINITY,
4082 location: 'end',
4083 });
4084 }
4085 };
4086 /**
4087 * Parses the input `textOrHtml` looking for URLs, email addresses, phone
4088 * numbers, username handles, and hashtags (depending on the configuration
4089 * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
4090 * objects describing those matches (without making any replacements).
4091 *
4092 * This method is used by the {@link #link} method, but can also be used to
4093 * simply do parsing of the input in order to discover what kinds of links
4094 * there are and how many.
4095 *
4096 * Example usage:
4097 *
4098 * var autolinker = new Autolinker( {
4099 * urls: true,
4100 * email: true
4101 * } );
4102 *
4103 * var matches = autolinker.parse( "Hello google.com, I am asdf@asdf.com" );
4104 *
4105 * console.log( matches.length ); // 2
4106 * console.log( matches[ 0 ].getType() ); // 'url'
4107 * console.log( matches[ 0 ].getUrl() ); // 'google.com'
4108 * console.log( matches[ 1 ].getType() ); // 'email'
4109 * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
4110 *
4111 * @param {String} textOrHtml The HTML or text to find matches within
4112 * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
4113 * {@link #hashtag}, and {@link #mention} options are enabled).
4114 * @return {Autolinker.match.Match[]} The array of Matches found in the
4115 * given input `textOrHtml`.
4116 */
4117 Autolinker.prototype.parse = function (textOrHtml) {
4118 var _this = this;
4119 var skipTagNames = ['a', 'style', 'script'], skipTagsStackCount = 0, // used to only Autolink text outside of anchor/script/style tags. We don't want to autolink something that is already linked inside of an <a> tag, for instance
4120 matches = [];
4121 // Find all matches within the `textOrHtml` (but not matches that are
4122 // already nested within <a>, <style> and <script> tags)
4123 parseHtml(textOrHtml, {
4124 onOpenTag: function (tagName) {
4125 if (skipTagNames.indexOf(tagName) >= 0) {
4126 skipTagsStackCount++;
4127 }
4128 },
4129 onText: function (text, offset) {
4130 // Only process text nodes that are not within an <a>, <style> or <script> tag
4131 if (skipTagsStackCount === 0) {
4132 // "Walk around" common HTML entities. An '&nbsp;' (for example)
4133 // could be at the end of a URL, but we don't want to
4134 // include the trailing '&' in the URL. See issue #76
4135 // TODO: Handle HTML entities separately in parseHtml() and
4136 // don't emit them as "text" except for &amp; entities
4137 var htmlCharacterEntitiesRegex = /(&nbsp;|&#160;|&lt;|&#60;|&gt;|&#62;|&quot;|&#34;|&#39;)/gi;
4138 var textSplit = splitAndCapture(text, htmlCharacterEntitiesRegex);
4139 var currentOffset_1 = offset;
4140 textSplit.forEach(function (splitText, i) {
4141 // even number matches are text, odd numbers are html entities
4142 if (i % 2 === 0) {
4143 var textNodeMatches = _this.parseText(splitText, currentOffset_1);
4144 matches.push.apply(matches, textNodeMatches);
4145 }
4146 currentOffset_1 += splitText.length;
4147 });
4148 }
4149 },
4150 onCloseTag: function (tagName) {
4151 if (skipTagNames.indexOf(tagName) >= 0) {
4152 skipTagsStackCount = Math.max(skipTagsStackCount - 1, 0); // attempt to handle extraneous </a> tags by making sure the stack count never goes below 0
4153 }
4154 },
4155 onComment: function (offset) { },
4156 onDoctype: function (offset) { }, // no need to process doctype nodes
4157 });
4158 // After we have found all matches, remove subsequent matches that
4159 // overlap with a previous match. This can happen for instance with URLs,
4160 // where the url 'google.com/#link' would match '#link' as a hashtag.
4161 matches = this.compactMatches(matches);
4162 // And finally, remove matches for match types that have been turned
4163 // off. We needed to have all match types turned on initially so that
4164 // things like hashtags could be filtered out if they were really just
4165 // part of a URL match (for instance, as a named anchor).
4166 matches = this.removeUnwantedMatches(matches);
4167 return matches;
4168 };
4169 /**
4170 * After we have found all matches, we need to remove matches that overlap
4171 * with a previous match. This can happen for instance with URLs, where the
4172 * url 'google.com/#link' would match '#link' as a hashtag. Because the
4173 * '#link' part is contained in a larger match that comes before the HashTag
4174 * match, we'll remove the HashTag match.
4175 *
4176 * @private
4177 * @param {Autolinker.match.Match[]} matches
4178 * @return {Autolinker.match.Match[]}
4179 */
4180 Autolinker.prototype.compactMatches = function (matches) {
4181 // First, the matches need to be sorted in order of offset
4182 matches.sort(function (a, b) {
4183 return a.getOffset() - b.getOffset();
4184 });
4185 var i = 0;
4186 while (i < matches.length - 1) {
4187 var match = matches[i], offset = match.getOffset(), matchedTextLength = match.getMatchedText().length, endIdx = offset + matchedTextLength;
4188 if (i + 1 < matches.length) {
4189 // Remove subsequent matches that equal offset with current match
4190 if (matches[i + 1].getOffset() === offset) {
4191 var removeIdx = matches[i + 1].getMatchedText().length > matchedTextLength ? i : i + 1;
4192 matches.splice(removeIdx, 1);
4193 continue;
4194 }
4195 // Remove subsequent matches that overlap with the current match
4196 if (matches[i + 1].getOffset() < endIdx) {
4197 matches.splice(i + 1, 1);
4198 continue;
4199 }
4200 }
4201 i++;
4202 }
4203 return matches;
4204 };
4205 /**
4206 * Removes matches for matchers that were turned off in the options. For
4207 * example, if {@link #hashtag hashtags} were not to be matched, we'll
4208 * remove them from the `matches` array here.
4209 *
4210 * Note: we *must* use all Matchers on the input string, and then filter
4211 * them out later. For example, if the options were `{ url: false, hashtag: true }`,
4212 * we wouldn't want to match the text '#link' as a HashTag inside of the text
4213 * 'google.com/#link'. The way the algorithm works is that we match the full
4214 * URL first (which prevents the accidental HashTag match), and then we'll
4215 * simply throw away the URL match.
4216 *
4217 * @private
4218 * @param {Autolinker.match.Match[]} matches The array of matches to remove
4219 * the unwanted matches from. Note: this array is mutated for the
4220 * removals.
4221 * @return {Autolinker.match.Match[]} The mutated input `matches` array.
4222 */
4223 Autolinker.prototype.removeUnwantedMatches = function (matches) {
4224 if (!this.hashtag)
4225 remove(matches, function (match) {
4226 return match.getType() === 'hashtag';
4227 });
4228 if (!this.email)
4229 remove(matches, function (match) {
4230 return match.getType() === 'email';
4231 });
4232 if (!this.phone)
4233 remove(matches, function (match) {
4234 return match.getType() === 'phone';
4235 });
4236 if (!this.mention)
4237 remove(matches, function (match) {
4238 return match.getType() === 'mention';
4239 });
4240 if (!this.urls.schemeMatches) {
4241 remove(matches, function (m) {
4242 return m.getType() === 'url' && m.getUrlMatchType() === 'scheme';
4243 });
4244 }
4245 if (!this.urls.wwwMatches) {
4246 remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'www'; });
4247 }
4248 if (!this.urls.tldMatches) {
4249 remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'tld'; });
4250 }
4251 return matches;
4252 };
4253 /**
4254 * Parses the input `text` looking for URLs, email addresses, phone
4255 * numbers, username handles, and hashtags (depending on the configuration
4256 * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
4257 * objects describing those matches.
4258 *
4259 * This method processes a **non-HTML string**, and is used to parse and
4260 * match within the text nodes of an HTML string. This method is used
4261 * internally by {@link #parse}.
4262 *
4263 * @private
4264 * @param {String} text The text to find matches within (depending on if the
4265 * {@link #urls}, {@link #email}, {@link #phone},
4266 * {@link #hashtag}, and {@link #mention} options are enabled). This must be a non-HTML string.
4267 * @param {Number} [offset=0] The offset of the text node within the
4268 * original string. This is used when parsing with the {@link #parse}
4269 * method to generate correct offsets within the {@link Autolinker.match.Match}
4270 * instances, but may be omitted if calling this method publicly.
4271 * @return {Autolinker.match.Match[]} The array of Matches found in the
4272 * given input `text`.
4273 */
4274 Autolinker.prototype.parseText = function (text, offset) {
4275 if (offset === void 0) { offset = 0; }
4276 offset = offset || 0;
4277 var matchers = this.getMatchers(), matches = [];
4278 for (var i = 0, numMatchers = matchers.length; i < numMatchers; i++) {
4279 var textMatches = matchers[i].parseMatches(text);
4280 // Correct the offset of each of the matches. They are originally
4281 // the offset of the match within the provided text node, but we
4282 // need to correct them to be relative to the original HTML input
4283 // string (i.e. the one provided to #parse).
4284 for (var j = 0, numTextMatches = textMatches.length; j < numTextMatches; j++) {
4285 textMatches[j].setOffset(offset + textMatches[j].getOffset());
4286 }
4287 matches.push.apply(matches, textMatches);
4288 }
4289 return matches;
4290 };
4291 /**
4292 * Automatically links URLs, Email addresses, Phone numbers, Hashtags,
4293 * and Mentions (Twitter, Instagram, Soundcloud) found in the given chunk of HTML. Does not link
4294 * URLs found within HTML tags.
4295 *
4296 * For instance, if given the text: `You should go to http://www.yahoo.com`,
4297 * then the result will be `You should go to
4298 * &lt;a href="http://www.yahoo.com"&gt;http://www.yahoo.com&lt;/a&gt;`
4299 *
4300 * This method finds the text around any HTML elements in the input
4301 * `textOrHtml`, which will be the text that is processed. Any original HTML
4302 * elements will be left as-is, as well as the text that is already wrapped
4303 * in anchor (&lt;a&gt;) tags.
4304 *
4305 * @param {String} textOrHtml The HTML or text to autolink matches within
4306 * (depending on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #hashtag}, and {@link #mention} options are enabled).
4307 * @return {String} The HTML, with matches automatically linked.
4308 */
4309 Autolinker.prototype.link = function (textOrHtml) {
4310 if (!textOrHtml) {
4311 return '';
4312 } // handle `null` and `undefined` (for JavaScript users that don't have TypeScript support)
4313 /* We would want to sanitize the start and end characters of a tag
4314 * before processing the string in order to avoid an XSS scenario.
4315 * This behaviour can be changed by toggling the sanitizeHtml option.
4316 */
4317 if (this.sanitizeHtml) {
4318 textOrHtml = textOrHtml.replace(/</g, '&lt;').replace(/>/g, '&gt;');
4319 }
4320 var matches = this.parse(textOrHtml), newHtml = [], lastIndex = 0;
4321 for (var i = 0, len = matches.length; i < len; i++) {
4322 var match = matches[i];
4323 newHtml.push(textOrHtml.substring(lastIndex, match.getOffset()));
4324 newHtml.push(this.createMatchReturnVal(match));
4325 lastIndex = match.getOffset() + match.getMatchedText().length;
4326 }
4327 newHtml.push(textOrHtml.substring(lastIndex)); // handle the text after the last match
4328 return newHtml.join('');
4329 };
4330 /**
4331 * Creates the return string value for a given match in the input string.
4332 *
4333 * This method handles the {@link #replaceFn}, if one was provided.
4334 *
4335 * @private
4336 * @param {Autolinker.match.Match} match The Match object that represents
4337 * the match.
4338 * @return {String} The string that the `match` should be replaced with.
4339 * This is usually the anchor tag string, but may be the `matchStr` itself
4340 * if the match is not to be replaced.
4341 */
4342 Autolinker.prototype.createMatchReturnVal = function (match) {
4343 // Handle a custom `replaceFn` being provided
4344 var replaceFnResult;
4345 if (this.replaceFn) {
4346 replaceFnResult = this.replaceFn.call(this.context, match); // Autolinker instance is the context
4347 }
4348 if (typeof replaceFnResult === 'string') {
4349 return replaceFnResult; // `replaceFn` returned a string, use that
4350 }
4351 else if (replaceFnResult === false) {
4352 return match.getMatchedText(); // no replacement for the match
4353 }
4354 else if (replaceFnResult instanceof HtmlTag) {
4355 return replaceFnResult.toAnchorString();
4356 }
4357 else {
4358 // replaceFnResult === true, or no/unknown return value from function
4359 // Perform Autolinker's default anchor tag generation
4360 var anchorTag = match.buildTag(); // returns an Autolinker.HtmlTag instance
4361 return anchorTag.toAnchorString();
4362 }
4363 };
4364 /**
4365 * Lazily instantiates and returns the {@link Autolinker.matcher.Matcher}
4366 * instances for this Autolinker instance.
4367 *
4368 * @private
4369 * @return {Autolinker.matcher.Matcher[]}
4370 */
4371 Autolinker.prototype.getMatchers = function () {
4372 if (!this.matchers) {
4373 var tagBuilder = this.getTagBuilder();
4374 var matchers = [
4375 new HashtagMatcher({
4376 tagBuilder: tagBuilder,
4377 serviceName: this.hashtag,
4378 }),
4379 new EmailMatcher({ tagBuilder: tagBuilder }),
4380 new PhoneMatcher({ tagBuilder: tagBuilder }),
4381 new MentionMatcher({
4382 tagBuilder: tagBuilder,
4383 serviceName: this.mention,
4384 }),
4385 new UrlMatcher({
4386 tagBuilder: tagBuilder,
4387 stripPrefix: this.stripPrefix,
4388 stripTrailingSlash: this.stripTrailingSlash,
4389 decodePercentEncoding: this.decodePercentEncoding,
4390 }),
4391 ];
4392 return (this.matchers = matchers);
4393 }
4394 else {
4395 return this.matchers;
4396 }
4397 };
4398 /**
4399 * Returns the {@link #tagBuilder} instance for this Autolinker instance,
4400 * lazily instantiating it if it does not yet exist.
4401 *
4402 * @private
4403 * @return {Autolinker.AnchorTagBuilder}
4404 */
4405 Autolinker.prototype.getTagBuilder = function () {
4406 var tagBuilder = this.tagBuilder;
4407 if (!tagBuilder) {
4408 tagBuilder = this.tagBuilder = new AnchorTagBuilder({
4409 newWindow: this.newWindow,
4410 truncate: this.truncate,
4411 className: this.className,
4412 });
4413 }
4414 return tagBuilder;
4415 };
4416 // NOTE: must be 'export default' here for UMD module
4417 /**
4418 * @static
4419 * @property {String} version
4420 *
4421 * The Autolinker version number in the form major.minor.patch
4422 *
4423 * Ex: 3.15.0
4424 */
4425 Autolinker.version = version;
4426 /**
4427 * For backwards compatibility with Autolinker 1.x, the AnchorTagBuilder
4428 * class is provided as a static on the Autolinker class.
4429 */
4430 Autolinker.AnchorTagBuilder = AnchorTagBuilder;
4431 /**
4432 * For backwards compatibility with Autolinker 1.x, the HtmlTag class is
4433 * provided as a static on the Autolinker class.
4434 */
4435 Autolinker.HtmlTag = HtmlTag;
4436 /**
4437 * For backwards compatibility with Autolinker 1.x, the Matcher classes are
4438 * provided as statics on the Autolinker class.
4439 */
4440 Autolinker.matcher = {
4441 Email: EmailMatcher,
4442 Hashtag: HashtagMatcher,
4443 Matcher: Matcher,
4444 Mention: MentionMatcher,
4445 Phone: PhoneMatcher,
4446 Url: UrlMatcher,
4447 };
4448 /**
4449 * For backwards compatibility with Autolinker 1.x, the Match classes are
4450 * provided as statics on the Autolinker class.
4451 */
4452 Autolinker.match = {
4453 Email: EmailMatch,
4454 Hashtag: HashtagMatch,
4455 Match: Match,
4456 Mention: MentionMatch,
4457 Phone: PhoneMatch,
4458 Url: UrlMatch,
4459 };
4460 return Autolinker;
4461 }());
4462
4463 return Autolinker;
4464
4465}));
4466//# sourceMappingURL=autolinker.js.map
Note: See TracBrowser for help on using the repository browser.