UNPKG

autolinker

Version:

Utility to automatically link the URLs, email addresses, phone numbers, hashtags, and mentions (Twitter, Instagram) in a given block of text/HTML

1,017 lines (1,001 loc) 224 kB
/*! * Autolinker.js * v4.1.0 * * Copyright(c) 2024 Gregory Jacobs <greg@greg-jacobs.com> * MIT License * * https://github.com/gregjacobs/Autolinker.js */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Autolinker = factory()); })(this, (function () { 'use strict'; // Important: this file is generated from the 'build' script and should not be // edited directly var version = '4.1.0'; /** * Simpler helper method to check for undefined simply for the benefit of * gaining better compression when minified by not needing to have multiple * comparisons to the `undefined` keyword in the codebase. */ function isUndefined(value) { return value === undefined; } /** * Simpler helper method to check for a boolean type simply for the benefit of * gaining better compression when minified by not needing to have multiple * `typeof` comparisons in the codebase. */ function isBoolean(value) { return typeof value === 'boolean'; } /** * Assigns (shallow copies) the properties of `src` onto `dest`, if the * corresponding property on `dest` === `undefined`. * * @param {Object} dest The destination object. * @param {Object} src The source object. * @return {Object} The destination object (`dest`) */ function defaults(dest, src) { for (var prop in src) { if (src.hasOwnProperty(prop) && isUndefined(dest[prop])) { dest[prop] = src[prop]; } } return dest; } /** * Truncates the `str` at `len - ellipsisChars.length`, and adds the `ellipsisChars` to the * end of the string (by default, two periods: '..'). If the `str` length does not exceed * `len`, the string will be returned unchanged. * * @param {String} str The string to truncate and add an ellipsis to. * @param {Number} truncateLen The length to truncate the string at. * @param {String} [ellipsisChars=...] The ellipsis character(s) to add to the end of `str` * when truncated. Defaults to '...' */ function ellipsis(str, truncateLen, ellipsisChars) { var ellipsisLength; if (str.length > truncateLen) { if (ellipsisChars == null) { ellipsisChars = '&hellip;'; ellipsisLength = 3; } else { ellipsisLength = ellipsisChars.length; } str = str.substring(0, truncateLen - ellipsisLength) + ellipsisChars; } return str; } /** * Removes array elements by value. Mutates the input array. * * Using this instead of the ES5 Array.prototype.filter() function to prevent * creating many new arrays in memory for removing an element. * * @param arr The array to remove elements from. This array is mutated. * @param fn The element to remove. */ function remove(arr, item) { for (var i = arr.length - 1; i >= 0; i--) { if (arr[i] === item) { arr.splice(i, 1); } } } /** * Removes array elements based on a filtering function. Mutates the input * array. * * Using this instead of the ES5 Array.prototype.filter() function to prevent * creating many new arrays in memory for filtering. * * @param arr The array to remove elements from. This array is mutated. * @param fn The predicate function which should return `true` to remove an * element. */ function removeWithPredicate(arr, fn) { for (var i = arr.length - 1; i >= 0; i--) { if (fn(arr[i]) === true) { arr.splice(i, 1); } } } /** * Function that should never be called but is used to check that every * enum value is handled using TypeScript's 'never' type. */ function assertNever(theValue) { throw new Error("Unhandled case for value: '".concat(theValue, "'")); } /* * This file builds and stores a library of the common regular expressions used * by the Autolinker utility. * * Other regular expressions may exist ad-hoc, but these are generally the * regular expressions that are shared between source files. */ /** * Regular expression to match upper and lowercase ASCII letters */ var letterRe = /[A-Za-z]/; /** * Regular expression to match ASCII digits */ var digitRe = /[\d]/; /** * Regular expression to match whitespace */ var whitespaceRe = /\s/; /** * Regular expression to match quote characters */ var quoteRe = /['"]/; /** * Regular expression to match the range of ASCII control characters (0-31), and * the backspace char (127) */ var controlCharsRe = /[\x00-\x1F\x7F]/; /** * The string form of a regular expression that would match all of the * alphabetic ("letter") chars in the unicode character set when placed in a * RegExp character class (`[]`). This includes all international alphabetic * characters. * * These would be the characters matched by unicode regex engines `\p{L}` * escape ("all letters"). * * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan) * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Letter' * regex's bmp * * VERY IMPORTANT: This set of characters is defined inside of a Regular * Expression literal rather than a string literal to prevent UglifyJS from * compressing the unicode escape sequences into their actual unicode * characters. If Uglify compresses these into the unicode characters * themselves, this results in the error "Range out of order in character * class" when these characters are used inside of a Regular Expression * character class (`[]`). See usages of this const. Alternatively, we can set * the UglifyJS option `ascii_only` to true for the build, but that doesn't * help others who are pulling in Autolinker into their own build and running * UglifyJS themselves. */ // prettier-ignore 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/ .source; // see note in above variable description /** * The string form of a regular expression that would match all emoji characters * Based on the emoji regex defined in this article: https://thekevinscott.com/emojis-in-javascript/ */ 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/ .source; // ^ high surrogate // ^ low surrogate /** * The string form of a regular expression that would match all of the * combining mark characters in the unicode character set when placed in a * RegExp character class (`[]`). * * These would be the characters matched by unicode regex engines `\p{M}` * escape ("all marks"). * * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan) * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Mark' * regex's bmp * * VERY IMPORTANT: This set of characters is defined inside of a Regular * Expression literal rather than a string literal to prevent UglifyJS from * compressing the unicode escape sequences into their actual unicode * characters. If Uglify compresses these into the unicode characters * themselves, this results in the error "Range out of order in character * class" when these characters are used inside of a Regular Expression * character class (`[]`). See usages of this const. Alternatively, we can set * the UglifyJS option `ascii_only` to true for the build, but that doesn't * help others who are pulling in Autolinker into their own build and running * UglifyJS themselves. */ // prettier-ignore 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/ .source; // see note in above variable description /** * The string form of a regular expression that would match all of the * alphabetic ("letter") chars, emoji, and combining marks in the unicode character set * when placed in a RegExp character class (`[]`). This includes all * international alphabetic characters. * * These would be the characters matched by unicode regex engines `\p{L}\p{M}` * escapes and emoji characters. */ var alphaCharsAndMarksStr = alphaCharsStr + emojiStr + marksStr; /** * The string form of a regular expression that would match all of the * decimal number chars in the unicode character set when placed in a RegExp * character class (`[]`). * * These would be the characters matched by unicode regex engines `\p{Nd}` * escape ("all decimal numbers") * * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan) * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Decimal_Number' * regex's bmp * * VERY IMPORTANT: This set of characters is defined inside of a Regular * Expression literal rather than a string literal to prevent UglifyJS from * compressing the unicode escape sequences into their actual unicode * characters. If Uglify compresses these into the unicode characters * themselves, this results in the error "Range out of order in character * class" when these characters are used inside of a Regular Expression * character class (`[]`). See usages of this const. Alternatively, we can set * the UglifyJS option `ascii_only` to true for the build, but that doesn't * help others who are pulling in Autolinker into their own build and running * UglifyJS themselves. */ // prettier-ignore 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/ .source; // see note in above variable description /** * The string form of a regular expression that would match all of the * letters, combining marks, and decimal number chars in the unicode character * set when placed in a RegExp character class (`[]`). * * These would be the characters matched by unicode regex engines * `[\p{L}\p{M}\p{Nd}]` escape ("all letters, combining marks, and decimal * numbers") */ var alphaNumericAndMarksCharsStr = alphaCharsAndMarksStr + decimalNumbersStr; /** * The regular expression that will match a single letter of the * {@link #alphaNumericAndMarksCharsStr}. */ var alphaNumericAndMarksRe = new RegExp("[".concat(alphaNumericAndMarksCharsStr, "]")); /** * @class Autolinker.HtmlTag * @extends Object * * Represents an HTML tag, which can be used to easily build/modify HTML tags programmatically. * * Autolinker uses this abstraction to create HTML tags, and then write them out as strings. You may also use * this class in your code, especially within a {@link Autolinker#replaceFn replaceFn}. * * ## Examples * * Example instantiation: * * var tag = new Autolinker.HtmlTag( { * tagName : 'a', * attrs : { 'href': 'http://google.com', 'class': 'external-link' }, * innerHtml : 'Google' * } ); * * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a> * * // Individual accessor methods * tag.getTagName(); // 'a' * tag.getAttr( 'href' ); // 'http://google.com' * tag.hasClass( 'external-link' ); // true * * * Using mutator methods (which may be used in combination with instantiation config properties): * * var tag = new Autolinker.HtmlTag(); * tag.setTagName( 'a' ); * tag.setAttr( 'href', 'http://google.com' ); * tag.addClass( 'external-link' ); * tag.setInnerHtml( 'Google' ); * * tag.getTagName(); // 'a' * tag.getAttr( 'href' ); // 'http://google.com' * tag.hasClass( 'external-link' ); // true * * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a> * * * ## Example use within a {@link Autolinker#replaceFn replaceFn} * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance, configured with the Match's href and anchor text * tag.setAttr( 'rel', 'nofollow' ); * * return tag; * } * } ); * * // generated html: * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a> * * * ## Example use with a new tag for the replacement * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = new Autolinker.HtmlTag( { * tagName : 'button', * attrs : { 'title': 'Load URL: ' + match.getAnchorHref() }, * innerHtml : 'Load URL: ' + match.getAnchorText() * } ); * * return tag; * } * } ); * * // generated html: * // Test <button title="Load URL: http://google.com">Load URL: google.com</button> */ var HtmlTag = /** @class */ (function () { /** * @method constructor * @param {Object} [cfg] The configuration properties for this class, in an Object (map) */ function HtmlTag(cfg) { if (cfg === void 0) { cfg = {}; } /** * @cfg {String} tagName * * The tag name. Ex: 'a', 'button', etc. * * Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toAnchorString} * is executed. */ this.tagName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Object.<String, String>} attrs * * An key/value Object (map) of attributes to create the tag with. The keys are the attribute names, and the * values are the attribute values. */ this.attrs = {}; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {String} innerHTML * * The inner HTML for the tag. */ this.innerHTML = ''; // default value just to get the above doc comment in the ES5 output and documentation generator this.tagName = cfg.tagName || ''; this.attrs = cfg.attrs || {}; this.innerHTML = cfg.innerHtml || cfg.innerHTML || ''; // accept either the camelCased form or the fully capitalized acronym as in the DOM } /** * Sets the tag name that will be used to generate the tag with. * * @param {String} tagName * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setTagName = function (tagName) { this.tagName = tagName; return this; }; /** * Retrieves the tag name. * * @return {String} */ HtmlTag.prototype.getTagName = function () { return this.tagName || ''; }; /** * Sets an attribute on the HtmlTag. * * @param {String} attrName The attribute name to set. * @param {String} attrValue The attribute value to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setAttr = function (attrName, attrValue) { var tagAttrs = this.getAttrs(); tagAttrs[attrName] = attrValue; return this; }; /** * Retrieves an attribute from the HtmlTag. If the attribute does not exist, returns `undefined`. * * @param {String} attrName The attribute name to retrieve. * @return {String} The attribute's value, or `undefined` if it does not exist on the HtmlTag. */ HtmlTag.prototype.getAttr = function (attrName) { return this.getAttrs()[attrName]; }; /** * Sets one or more attributes on the HtmlTag. * * @param {Object.<String, String>} attrs A key/value Object (map) of the attributes to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setAttrs = function (attrs) { Object.assign(this.getAttrs(), attrs); return this; }; /** * Retrieves the attributes Object (map) for the HtmlTag. * * @return {Object.<String, String>} A key/value object of the attributes for the HtmlTag. */ HtmlTag.prototype.getAttrs = function () { return this.attrs || (this.attrs = {}); }; /** * Sets the provided `cssClass`, overwriting any current CSS classes on the HtmlTag. * * @param {String} cssClass One or more space-separated CSS classes to set (overwrite). * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setClass = function (cssClass) { return this.setAttr('class', cssClass); }; /** * Convenience method to add one or more CSS classes to the HtmlTag. Will not add duplicate CSS classes. * * @param {String} cssClass One or more space-separated CSS classes to add. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.addClass = function (cssClass) { var classAttr = this.getClass(), classes = !classAttr ? [] : classAttr.split(whitespaceRe), newClasses = cssClass.split(whitespaceRe), newClass; while ((newClass = newClasses.shift())) { if (classes.indexOf(newClass) === -1) { classes.push(newClass); } } this.getAttrs()['class'] = classes.join(' '); return this; }; /** * Convenience method to remove one or more CSS classes from the HtmlTag. * * @param {String} cssClass One or more space-separated CSS classes to remove. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.removeClass = function (cssClass) { var classAttr = this.getClass(), classes = !classAttr ? [] : classAttr.split(whitespaceRe), removeClasses = cssClass.split(whitespaceRe), removeClass; while (classes.length && (removeClass = removeClasses.shift())) { var idx = classes.indexOf(removeClass); if (idx !== -1) { classes.splice(idx, 1); } } this.getAttrs()['class'] = classes.join(' '); return this; }; /** * Convenience method to retrieve the CSS class(es) for the HtmlTag, which will each be separated by spaces when * there are multiple. * * @return {String} */ HtmlTag.prototype.getClass = function () { return this.getAttrs()['class'] || ''; }; /** * Convenience method to check if the tag has a CSS class or not. * * @param {String} cssClass The CSS class to check for. * @return {Boolean} `true` if the HtmlTag has the CSS class, `false` otherwise. */ HtmlTag.prototype.hasClass = function (cssClass) { return (' ' + this.getClass() + ' ').indexOf(' ' + cssClass + ' ') !== -1; }; /** * Sets the inner HTML for the tag. * * @param {String} html The inner HTML to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setInnerHTML = function (html) { this.innerHTML = html; return this; }; /** * Backwards compatibility method name. * * @param {String} html The inner HTML to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setInnerHtml = function (html) { return this.setInnerHTML(html); }; /** * Retrieves the inner HTML for the tag. * * @return {String} */ HtmlTag.prototype.getInnerHTML = function () { return this.innerHTML || ''; }; /** * Backward compatibility method name. * * @return {String} */ HtmlTag.prototype.getInnerHtml = function () { return this.getInnerHTML(); }; /** * Generates the HTML string for the tag. * * @return {String} */ HtmlTag.prototype.toAnchorString = function () { var tagName = this.getTagName(), attrsStr = this.buildAttrsStr(); attrsStr = attrsStr ? ' ' + attrsStr : ''; // prepend a space if there are actually attributes return ['<', tagName, attrsStr, '>', this.getInnerHtml(), '</', tagName, '>'].join(''); }; /** * Support method for {@link #toAnchorString}, returns the string space-separated key="value" pairs, used to populate * the stringified HtmlTag. * * @protected * @return {String} Example return: `attr1="value1" attr2="value2"` */ HtmlTag.prototype.buildAttrsStr = function () { if (!this.attrs) return ''; // no `attrs` Object (map) has been set, return empty string var attrs = this.getAttrs(), attrsArr = []; for (var prop in attrs) { if (attrs.hasOwnProperty(prop)) { attrsArr.push(prop + '="' + attrs[prop] + '"'); } } return attrsArr.join(' '); }; return HtmlTag; }()); /** * Date: 2015-10-05 * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso) * * A truncation feature, where the ellipsis will be placed at a section within * the URL making it still somewhat human readable. * * @param {String} url A URL. * @param {Number} truncateLen The maximum length of the truncated output URL string. * @param {String} ellipsisChars The characters to place within the url, e.g. "...". * @return {String} The truncated URL. */ function truncateSmart(url, truncateLen, ellipsisChars) { var ellipsisLengthBeforeParsing; var ellipsisLength; if (ellipsisChars == null) { ellipsisChars = '&hellip;'; ellipsisLength = 3; ellipsisLengthBeforeParsing = 8; } else { ellipsisLength = ellipsisChars.length; ellipsisLengthBeforeParsing = ellipsisChars.length; } var parse_url = function (url) { // Functionality inspired by PHP function of same name var urlObj = {}; var urlSub = url; var match = urlSub.match(/^([a-z]+):\/\//i); if (match) { urlObj.scheme = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^(.*?)(?=(\?|#|\/|$))/i); if (match) { urlObj.host = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^\/(.*?)(?=(\?|#|$))/i); if (match) { urlObj.path = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^\?(.*?)(?=(#|$))/i); if (match) { urlObj.query = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^#(.*?)$/i); if (match) { urlObj.fragment = match[1]; //urlSub = urlSub.substr(match[0].length); -- not used. Uncomment if adding another block. } return urlObj; }; var buildUrl = function (urlObj) { var url = ''; if (urlObj.scheme && urlObj.host) { url += urlObj.scheme + '://'; } if (urlObj.host) { url += urlObj.host; } if (urlObj.path) { url += '/' + urlObj.path; } if (urlObj.query) { url += '?' + urlObj.query; } if (urlObj.fragment) { url += '#' + urlObj.fragment; } return url; }; var buildSegment = function (segment, remainingAvailableLength) { var remainingAvailableLengthHalf = remainingAvailableLength / 2, startOffset = Math.ceil(remainingAvailableLengthHalf), endOffset = -1 * Math.floor(remainingAvailableLengthHalf), end = ''; if (endOffset < 0) { end = segment.substr(endOffset); } return segment.substr(0, startOffset) + ellipsisChars + end; }; if (url.length <= truncateLen) { return url; } var availableLength = truncateLen - ellipsisLength; var urlObj = parse_url(url); // Clean up the URL if (urlObj.query) { var matchQuery = urlObj.query.match(/^(.*?)(?=(\?|\#))(.*?)$/i); if (matchQuery) { // Malformed URL; two or more "?". Removed any content behind the 2nd. urlObj.query = urlObj.query.substr(0, matchQuery[1].length); url = buildUrl(urlObj); } } if (url.length <= truncateLen) { return url; } if (urlObj.host) { urlObj.host = urlObj.host.replace(/^www\./, ''); url = buildUrl(urlObj); } if (url.length <= truncateLen) { return url; } // Process and build the URL var str = ''; if (urlObj.host) { str += urlObj.host; } if (str.length >= availableLength) { if (urlObj.host.length == truncateLen) { return (urlObj.host.substr(0, truncateLen - ellipsisLength) + ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing); } return buildSegment(str, availableLength).substr(0, availableLength + ellipsisLengthBeforeParsing); } var pathAndQuery = ''; if (urlObj.path) { pathAndQuery += '/' + urlObj.path; } if (urlObj.query) { pathAndQuery += '?' + urlObj.query; } if (pathAndQuery) { if ((str + pathAndQuery).length >= availableLength) { if ((str + pathAndQuery).length == truncateLen) { return (str + pathAndQuery).substr(0, truncateLen); } var remainingAvailableLength = availableLength - str.length; return (str + buildSegment(pathAndQuery, remainingAvailableLength)).substr(0, availableLength + ellipsisLengthBeforeParsing); } else { str += pathAndQuery; } } if (urlObj.fragment) { var fragment = '#' + urlObj.fragment; if ((str + fragment).length >= availableLength) { if ((str + fragment).length == truncateLen) { return (str + fragment).substr(0, truncateLen); } var remainingAvailableLength2 = availableLength - str.length; return (str + buildSegment(fragment, remainingAvailableLength2)).substr(0, availableLength + ellipsisLengthBeforeParsing); } else { str += fragment; } } if (urlObj.scheme && urlObj.host) { var scheme = urlObj.scheme + '://'; if ((str + scheme).length < availableLength) { return (scheme + str).substr(0, truncateLen); } } if (str.length <= truncateLen) { return str; } var end = ''; if (availableLength > 0) { end = str.substr(-1 * Math.floor(availableLength / 2)); } return (str.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing); } /** * Date: 2015-10-05 * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso) * * A truncation feature, where the ellipsis will be placed in the dead-center of the URL. * * @param {String} url A URL. * @param {Number} truncateLen The maximum length of the truncated output URL string. * @param {String} ellipsisChars The characters to place within the url, e.g. "..". * @return {String} The truncated URL. */ function truncateMiddle(url, truncateLen, ellipsisChars) { if (url.length <= truncateLen) { return url; } var ellipsisLengthBeforeParsing; var ellipsisLength; if (ellipsisChars == null) { ellipsisChars = '&hellip;'; ellipsisLengthBeforeParsing = 8; ellipsisLength = 3; } else { ellipsisLengthBeforeParsing = ellipsisChars.length; ellipsisLength = ellipsisChars.length; } var availableLength = truncateLen - ellipsisLength; var end = ''; if (availableLength > 0) { end = url.substr(-1 * Math.floor(availableLength / 2)); } return (url.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing); } /** * A truncation feature where the ellipsis will be placed at the end of the URL. * * @param {String} anchorText * @param {Number} truncateLen The maximum length of the truncated output URL string. * @param {String} ellipsisChars The characters to place within the url, e.g. "..". * @return {String} The truncated URL. */ function truncateEnd(anchorText, truncateLen, ellipsisChars) { return ellipsis(anchorText, truncateLen, ellipsisChars); } /** * @protected * @class Autolinker.AnchorTagBuilder * @extends Object * * Builds anchor (&lt;a&gt;) tags for the Autolinker utility when a match is * found. * * Normally this class is instantiated, configured, and used internally by an * {@link Autolinker} instance, but may actually be used indirectly in a * {@link Autolinker#replaceFn replaceFn} to create {@link Autolinker.HtmlTag HtmlTag} * instances which may be modified before returning from the * {@link Autolinker#replaceFn replaceFn}. For example: * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance * tag.setAttr( 'rel', 'nofollow' ); * * return tag; * } * } ); * * // generated html: * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a> */ var AnchorTagBuilder = /** @class */ (function () { /** * @method constructor * @param {Object} [cfg] The configuration options for the AnchorTagBuilder instance, specified in an Object (map). */ function AnchorTagBuilder(cfg) { if (cfg === void 0) { cfg = {}; } /** * @cfg {Boolean} newWindow * @inheritdoc Autolinker#newWindow */ this.newWindow = false; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Object} truncate * @inheritdoc Autolinker#truncate */ this.truncate = {}; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {String} className * @inheritdoc Autolinker#className */ this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator this.newWindow = cfg.newWindow || false; this.truncate = cfg.truncate || {}; this.className = cfg.className || ''; } /** * Generates the actual anchor (&lt;a&gt;) tag to use in place of the * matched text, via its `match` object. * * @param match The Match instance to generate an anchor tag from. * @return The HtmlTag instance for the anchor tag. */ AnchorTagBuilder.prototype.build = function (match) { return new HtmlTag({ tagName: 'a', attrs: this.createAttrs(match), innerHtml: this.processAnchorText(match.getAnchorText()), }); }; /** * Creates the Object (map) of the HTML attributes for the anchor (&lt;a&gt;) * tag being generated. * * @protected * @param match The Match instance to generate an anchor tag from. * @return A key/value Object (map) of the anchor tag's attributes. */ AnchorTagBuilder.prototype.createAttrs = function (match) { var attrs = { href: match.getAnchorHref(), // we'll always have the `href` attribute }; var cssClass = this.createCssClass(match); if (cssClass) { attrs['class'] = cssClass; } if (this.newWindow) { attrs['target'] = '_blank'; attrs['rel'] = 'noopener noreferrer'; // Issue #149. See https://mathiasbynens.github.io/rel-noopener/ } if (this.truncate) { if (this.truncate.length && this.truncate.length < match.getAnchorText().length) { attrs['title'] = match.getAnchorHref(); } } return attrs; }; /** * Creates the CSS class that will be used for a given anchor tag, based on * the `matchType` and the {@link #className} config. * * Example returns: * * - "" // no {@link #className} * - "myLink myLink-url" // url match * - "myLink myLink-email" // email match * - "myLink myLink-phone" // phone match * - "myLink myLink-hashtag" // hashtag match * - "myLink myLink-mention myLink-twitter" // mention match with Twitter service * * @protected * @param match The Match instance to generate an * anchor tag from. * @return The CSS class string for the link. Example return: * "myLink myLink-url". If no {@link #className} was configured, returns * an empty string. */ AnchorTagBuilder.prototype.createCssClass = function (match) { var className = this.className; if (!className) { return ''; } else { var returnClasses = [className], cssClassSuffixes = match.getCssClassSuffixes(); for (var i = 0, len = cssClassSuffixes.length; i < len; i++) { returnClasses.push(className + '-' + cssClassSuffixes[i]); } return returnClasses.join(' '); } }; /** * Processes the `anchorText` by truncating the text according to the * {@link #truncate} config. * * @private * @param anchorText The anchor tag's text (i.e. what will be * displayed). * @return The processed `anchorText`. */ AnchorTagBuilder.prototype.processAnchorText = function (anchorText) { anchorText = this.doTruncate(anchorText); return anchorText; }; /** * Performs the truncation of the `anchorText` based on the {@link #truncate} * option. If the `anchorText` is longer than the length specified by the * {@link #truncate} option, the truncation is performed based on the * `location` property. See {@link #truncate} for details. * * @private * @param anchorText The anchor tag's text (i.e. what will be * displayed). * @return The truncated anchor text. */ AnchorTagBuilder.prototype.doTruncate = function (anchorText) { var truncate = this.truncate; if (!truncate || !truncate.length) return anchorText; var truncateLength = truncate.length, truncateLocation = truncate.location; if (truncateLocation === 'smart') { return truncateSmart(anchorText, truncateLength); } else if (truncateLocation === 'middle') { return truncateMiddle(anchorText, truncateLength); } else { return truncateEnd(anchorText, truncateLength); } }; return AnchorTagBuilder; }()); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; /** * @abstract * @class Autolinker.match.AbstractMatch * * Represents a match found in an input string which should be Autolinked. A Match object is what is provided in a * {@link Autolinker#replaceFn replaceFn}, and may be used to query for details about the match. * * For example: * * var input = "..."; // string with URLs, Email Addresses, and Mentions (Twitter, Instagram, Soundcloud) * * var linkedText = Autolinker.link( input, { * replaceFn : function( match ) { * console.log( "href = ", match.getAnchorHref() ); * console.log( "text = ", match.getAnchorText() ); * * switch( match.getType() ) { * case 'url' : * console.log( "url: ", match.getUrl() ); * * case 'email' : * console.log( "email: ", match.getEmail() ); * * case 'mention' : * console.log( "mention: ", match.getMention() ); * } * } * } ); * * See the {@link Autolinker} class for more details on using the {@link Autolinker#replaceFn replaceFn}. */ var AbstractMatch = /** @class */ (function () { /** * @member Autolinker.match.Match * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function AbstractMatch(cfg) { /** * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required) * * Reference to the AnchorTagBuilder instance to use to generate an anchor * t