[d24f17c] | 1 | import { version } from './version';
|
---|
| 2 | import { defaults, remove, splitAndCapture } from './utils';
|
---|
| 3 | import { AnchorTagBuilder } from './anchor-tag-builder';
|
---|
| 4 | import { Match } from './match/match';
|
---|
| 5 | import { EmailMatch } from './match/email-match';
|
---|
| 6 | import { HashtagMatch } from './match/hashtag-match';
|
---|
| 7 | import { MentionMatch } from './match/mention-match';
|
---|
| 8 | import { PhoneMatch } from './match/phone-match';
|
---|
| 9 | import { UrlMatch } from './match/url-match';
|
---|
| 10 | import { Matcher } from './matcher/matcher';
|
---|
| 11 | import { HtmlTag } from './html-tag';
|
---|
| 12 | import { EmailMatcher } from './matcher/email-matcher';
|
---|
| 13 | import { UrlMatcher } from './matcher/url-matcher';
|
---|
| 14 | import { HashtagMatcher, hashtagServices } from './matcher/hashtag-matcher';
|
---|
| 15 | import { PhoneMatcher } from './matcher/phone-matcher';
|
---|
| 16 | import { MentionMatcher } from './matcher/mention-matcher';
|
---|
| 17 | import { parseHtml } from './htmlParser/parse-html';
|
---|
| 18 | /**
|
---|
| 19 | * @class Autolinker
|
---|
| 20 | * @extends Object
|
---|
| 21 | *
|
---|
| 22 | * Utility class used to process a given string of text, and wrap the matches in
|
---|
| 23 | * the appropriate anchor (<a>) tags to turn them into links.
|
---|
| 24 | *
|
---|
| 25 | * Any of the configuration options may be provided in an Object provided
|
---|
| 26 | * to the Autolinker constructor, which will configure how the {@link #link link()}
|
---|
| 27 | * method will process the links.
|
---|
| 28 | *
|
---|
| 29 | * For example:
|
---|
| 30 | *
|
---|
| 31 | * var autolinker = new Autolinker( {
|
---|
| 32 | * newWindow : false,
|
---|
| 33 | * truncate : 30
|
---|
| 34 | * } );
|
---|
| 35 | *
|
---|
| 36 | * var html = autolinker.link( "Joe went to www.yahoo.com" );
|
---|
| 37 | * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
|
---|
| 38 | *
|
---|
| 39 | *
|
---|
| 40 | * The {@link #static-link static link()} method may also be used to inline
|
---|
| 41 | * options into a single call, which may be more convenient for one-off uses.
|
---|
| 42 | * For example:
|
---|
| 43 | *
|
---|
| 44 | * var html = Autolinker.link( "Joe went to www.yahoo.com", {
|
---|
| 45 | * newWindow : false,
|
---|
| 46 | * truncate : 30
|
---|
| 47 | * } );
|
---|
| 48 | * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
|
---|
| 49 | *
|
---|
| 50 | *
|
---|
| 51 | * ## Custom Replacements of Links
|
---|
| 52 | *
|
---|
| 53 | * If the configuration options do not provide enough flexibility, a {@link #replaceFn}
|
---|
| 54 | * may be provided to fully customize the output of Autolinker. This function is
|
---|
| 55 | * called once for each URL/Email/Phone#/Hashtag/Mention (Twitter, Instagram, Soundcloud)
|
---|
| 56 | * match that is encountered.
|
---|
| 57 | *
|
---|
| 58 | * For example:
|
---|
| 59 | *
|
---|
| 60 | * var input = "..."; // string with URLs, Email Addresses, Phone #s, Hashtags, and Mentions (Twitter, Instagram, Soundcloud)
|
---|
| 61 | *
|
---|
| 62 | * var linkedText = Autolinker.link( input, {
|
---|
| 63 | * replaceFn : function( match ) {
|
---|
| 64 | * console.log( "href = ", match.getAnchorHref() );
|
---|
| 65 | * console.log( "text = ", match.getAnchorText() );
|
---|
| 66 | *
|
---|
| 67 | * switch( match.getType() ) {
|
---|
| 68 | * case 'url' :
|
---|
| 69 | * console.log( "url: ", match.getUrl() );
|
---|
| 70 | *
|
---|
| 71 | * if( match.getUrl().indexOf( 'mysite.com' ) === -1 ) {
|
---|
| 72 | * var tag = match.buildTag(); // returns an `Autolinker.HtmlTag` instance, which provides mutator methods for easy changes
|
---|
| 73 | * tag.setAttr( 'rel', 'nofollow' );
|
---|
| 74 | * tag.addClass( 'external-link' );
|
---|
| 75 | *
|
---|
| 76 | * return tag;
|
---|
| 77 | *
|
---|
| 78 | * } else {
|
---|
| 79 | * return true; // let Autolinker perform its normal anchor tag replacement
|
---|
| 80 | * }
|
---|
| 81 | *
|
---|
| 82 | * case 'email' :
|
---|
| 83 | * var email = match.getEmail();
|
---|
| 84 | * console.log( "email: ", email );
|
---|
| 85 | *
|
---|
| 86 | * if( email === "my@own.address" ) {
|
---|
| 87 | * return false; // don't auto-link this particular email address; leave as-is
|
---|
| 88 | * } else {
|
---|
| 89 | * return; // no return value will have Autolinker perform its normal anchor tag replacement (same as returning `true`)
|
---|
| 90 | * }
|
---|
| 91 | *
|
---|
| 92 | * case 'phone' :
|
---|
| 93 | * var phoneNumber = match.getPhoneNumber();
|
---|
| 94 | * console.log( phoneNumber );
|
---|
| 95 | *
|
---|
| 96 | * return '<a href="http://newplace.to.link.phone.numbers.to/">' + phoneNumber + '</a>';
|
---|
| 97 | *
|
---|
| 98 | * case 'hashtag' :
|
---|
| 99 | * var hashtag = match.getHashtag();
|
---|
| 100 | * console.log( hashtag );
|
---|
| 101 | *
|
---|
| 102 | * return '<a href="http://newplace.to.link.hashtag.handles.to/">' + hashtag + '</a>';
|
---|
| 103 | *
|
---|
| 104 | * case 'mention' :
|
---|
| 105 | * var mention = match.getMention();
|
---|
| 106 | * console.log( mention );
|
---|
| 107 | *
|
---|
| 108 | * return '<a href="http://newplace.to.link.mention.to/">' + mention + '</a>';
|
---|
| 109 | * }
|
---|
| 110 | * }
|
---|
| 111 | * } );
|
---|
| 112 | *
|
---|
| 113 | *
|
---|
| 114 | * The function may return the following values:
|
---|
| 115 | *
|
---|
| 116 | * - `true` (Boolean): Allow Autolinker to replace the match as it normally
|
---|
| 117 | * would.
|
---|
| 118 | * - `false` (Boolean): Do not replace the current match at all - leave as-is.
|
---|
| 119 | * - Any String: If a string is returned from the function, the string will be
|
---|
| 120 | * used directly as the replacement HTML for the match.
|
---|
| 121 | * - An {@link Autolinker.HtmlTag} instance, which can be used to build/modify
|
---|
| 122 | * an HTML tag before writing out its HTML text.
|
---|
| 123 | */
|
---|
| 124 | var Autolinker = /** @class */ (function () {
|
---|
| 125 | /**
|
---|
| 126 | * @method constructor
|
---|
| 127 | * @param {Object} [cfg] The configuration options for the Autolinker instance,
|
---|
| 128 | * specified in an Object (map).
|
---|
| 129 | */
|
---|
| 130 | function Autolinker(cfg) {
|
---|
| 131 | if (cfg === void 0) { cfg = {}; }
|
---|
| 132 | /**
|
---|
| 133 | * The Autolinker version number exposed on the instance itself.
|
---|
| 134 | *
|
---|
| 135 | * Ex: 0.25.1
|
---|
| 136 | */
|
---|
| 137 | this.version = Autolinker.version;
|
---|
| 138 | /**
|
---|
| 139 | * @cfg {Boolean/Object} [urls]
|
---|
| 140 | *
|
---|
| 141 | * `true` if URLs should be automatically linked, `false` if they should not
|
---|
| 142 | * be. Defaults to `true`.
|
---|
| 143 | *
|
---|
| 144 | * Examples:
|
---|
| 145 | *
|
---|
| 146 | * urls: true
|
---|
| 147 | *
|
---|
| 148 | * // or
|
---|
| 149 | *
|
---|
| 150 | * urls: {
|
---|
| 151 | * schemeMatches : true,
|
---|
| 152 | * wwwMatches : true,
|
---|
| 153 | * tldMatches : true
|
---|
| 154 | * }
|
---|
| 155 | *
|
---|
| 156 | * As shown above, this option also accepts an Object form with 3 properties
|
---|
| 157 | * to allow for more customization of what exactly gets linked. All default
|
---|
| 158 | * to `true`:
|
---|
| 159 | *
|
---|
| 160 | * @cfg {Boolean} [urls.schemeMatches] `true` to match URLs found prefixed
|
---|
| 161 | * with a scheme, i.e. `http://google.com`, or `other+scheme://google.com`,
|
---|
| 162 | * `false` to prevent these types of matches.
|
---|
| 163 | * @cfg {Boolean} [urls.wwwMatches] `true` to match urls found prefixed with
|
---|
| 164 | * `'www.'`, i.e. `www.google.com`. `false` to prevent these types of
|
---|
| 165 | * matches. Note that if the URL had a prefixed scheme, and
|
---|
| 166 | * `schemeMatches` is true, it will still be linked.
|
---|
| 167 | * @cfg {Boolean} [urls.tldMatches] `true` to match URLs with known top
|
---|
| 168 | * level domains (.com, .net, etc.) that are not prefixed with a scheme or
|
---|
| 169 | * `'www.'`. This option attempts to match anything that looks like a URL
|
---|
| 170 | * in the given text. Ex: `google.com`, `asdf.org/?page=1`, etc. `false`
|
---|
| 171 | * to prevent these types of matches.
|
---|
| 172 | */
|
---|
| 173 | this.urls = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 174 | /**
|
---|
| 175 | * @cfg {Boolean} [email=true]
|
---|
| 176 | *
|
---|
| 177 | * `true` if email addresses should be automatically linked, `false` if they
|
---|
| 178 | * should not be.
|
---|
| 179 | */
|
---|
| 180 | this.email = true; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 181 | /**
|
---|
| 182 | * @cfg {Boolean} [phone=true]
|
---|
| 183 | *
|
---|
| 184 | * `true` if Phone numbers ("(555)555-5555") should be automatically linked,
|
---|
| 185 | * `false` if they should not be.
|
---|
| 186 | */
|
---|
| 187 | this.phone = true; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 188 | /**
|
---|
| 189 | * @cfg {Boolean/String} [hashtag=false]
|
---|
| 190 | *
|
---|
| 191 | * A string for the service name to have hashtags (ex: "#myHashtag")
|
---|
| 192 | * auto-linked to. The currently-supported values are:
|
---|
| 193 | *
|
---|
| 194 | * - 'twitter'
|
---|
| 195 | * - 'facebook'
|
---|
| 196 | * - 'instagram'
|
---|
| 197 | *
|
---|
| 198 | * Pass `false` to skip auto-linking of hashtags.
|
---|
| 199 | */
|
---|
| 200 | this.hashtag = false; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 201 | /**
|
---|
| 202 | * @cfg {String/Boolean} [mention=false]
|
---|
| 203 | *
|
---|
| 204 | * A string for the service name to have mentions (ex: "@myuser")
|
---|
| 205 | * auto-linked to. The currently supported values are:
|
---|
| 206 | *
|
---|
| 207 | * - 'twitter'
|
---|
| 208 | * - 'instagram'
|
---|
| 209 | * - 'soundcloud'
|
---|
| 210 | *
|
---|
| 211 | * Defaults to `false` to skip auto-linking of mentions.
|
---|
| 212 | */
|
---|
| 213 | this.mention = false; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 214 | /**
|
---|
| 215 | * @cfg {Boolean} [newWindow=true]
|
---|
| 216 | *
|
---|
| 217 | * `true` if the links should open in a new window, `false` otherwise.
|
---|
| 218 | */
|
---|
| 219 | this.newWindow = true; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 220 | /**
|
---|
| 221 | * @cfg {Boolean/Object} [stripPrefix=true]
|
---|
| 222 | *
|
---|
| 223 | * `true` if 'http://' (or 'https://') and/or the 'www.' should be stripped
|
---|
| 224 | * from the beginning of URL links' text, `false` otherwise. Defaults to
|
---|
| 225 | * `true`.
|
---|
| 226 | *
|
---|
| 227 | * Examples:
|
---|
| 228 | *
|
---|
| 229 | * stripPrefix: true
|
---|
| 230 | *
|
---|
| 231 | * // or
|
---|
| 232 | *
|
---|
| 233 | * stripPrefix: {
|
---|
| 234 | * scheme : true,
|
---|
| 235 | * www : true
|
---|
| 236 | * }
|
---|
| 237 | *
|
---|
| 238 | * As shown above, this option also accepts an Object form with 2 properties
|
---|
| 239 | * to allow for more customization of what exactly is prevented from being
|
---|
| 240 | * displayed. Both default to `true`:
|
---|
| 241 | *
|
---|
| 242 | * @cfg {Boolean} [stripPrefix.scheme] `true` to prevent the scheme part of
|
---|
| 243 | * a URL match from being displayed to the user. Example:
|
---|
| 244 | * `'http://google.com'` will be displayed as `'google.com'`. `false` to
|
---|
| 245 | * not strip the scheme. NOTE: Only an `'http://'` or `'https://'` scheme
|
---|
| 246 | * will be removed, so as not to remove a potentially dangerous scheme
|
---|
| 247 | * (such as `'file://'` or `'javascript:'`)
|
---|
| 248 | * @cfg {Boolean} [stripPrefix.www] www (Boolean): `true` to prevent the
|
---|
| 249 | * `'www.'` part of a URL match from being displayed to the user. Ex:
|
---|
| 250 | * `'www.google.com'` will be displayed as `'google.com'`. `false` to not
|
---|
| 251 | * strip the `'www'`.
|
---|
| 252 | */
|
---|
| 253 | this.stripPrefix = {
|
---|
| 254 | scheme: true,
|
---|
| 255 | www: true,
|
---|
| 256 | }; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 257 | /**
|
---|
| 258 | * @cfg {Boolean} [stripTrailingSlash=true]
|
---|
| 259 | *
|
---|
| 260 | * `true` to remove the trailing slash from URL matches, `false` to keep
|
---|
| 261 | * the trailing slash.
|
---|
| 262 | *
|
---|
| 263 | * Example when `true`: `http://google.com/` will be displayed as
|
---|
| 264 | * `http://google.com`.
|
---|
| 265 | */
|
---|
| 266 | this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 267 | /**
|
---|
| 268 | * @cfg {Boolean} [decodePercentEncoding=true]
|
---|
| 269 | *
|
---|
| 270 | * `true` to decode percent-encoded characters in URL matches, `false` to keep
|
---|
| 271 | * the percent-encoded characters.
|
---|
| 272 | *
|
---|
| 273 | * Example when `true`: `https://en.wikipedia.org/wiki/San_Jos%C3%A9` will
|
---|
| 274 | * be displayed as `https://en.wikipedia.org/wiki/San_José`.
|
---|
| 275 | */
|
---|
| 276 | this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 277 | /**
|
---|
| 278 | * @cfg {Number/Object} [truncate=0]
|
---|
| 279 | *
|
---|
| 280 | * ## Number Form
|
---|
| 281 | *
|
---|
| 282 | * A number for how many characters matched text should be truncated to
|
---|
| 283 | * inside the text of a link. If the matched text is over this number of
|
---|
| 284 | * characters, it will be truncated to this length by adding a two period
|
---|
| 285 | * ellipsis ('..') to the end of the string.
|
---|
| 286 | *
|
---|
| 287 | * For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file'
|
---|
| 288 | * truncated to 25 characters might look something like this:
|
---|
| 289 | * 'yahoo.com/some/long/pat..'
|
---|
| 290 | *
|
---|
| 291 | * Example Usage:
|
---|
| 292 | *
|
---|
| 293 | * truncate: 25
|
---|
| 294 | *
|
---|
| 295 | *
|
---|
| 296 | * Defaults to `0` for "no truncation."
|
---|
| 297 | *
|
---|
| 298 | *
|
---|
| 299 | * ## Object Form
|
---|
| 300 | *
|
---|
| 301 | * An Object may also be provided with two properties: `length` (Number) and
|
---|
| 302 | * `location` (String). `location` may be one of the following: 'end'
|
---|
| 303 | * (default), 'middle', or 'smart'.
|
---|
| 304 | *
|
---|
| 305 | * Example Usage:
|
---|
| 306 | *
|
---|
| 307 | * truncate: { length: 25, location: 'middle' }
|
---|
| 308 | *
|
---|
| 309 | * @cfg {Number} [truncate.length=0] How many characters to allow before
|
---|
| 310 | * truncation will occur. Defaults to `0` for "no truncation."
|
---|
| 311 | * @cfg {"end"/"middle"/"smart"} [truncate.location="end"]
|
---|
| 312 | *
|
---|
| 313 | * - 'end' (default): will truncate up to the number of characters, and then
|
---|
| 314 | * add an ellipsis at the end. Ex: 'yahoo.com/some/long/pat..'
|
---|
| 315 | * - 'middle': will truncate and add the ellipsis in the middle. Ex:
|
---|
| 316 | * 'yahoo.com/s..th/to/a/file'
|
---|
| 317 | * - 'smart': for URLs where the algorithm attempts to strip out unnecessary
|
---|
| 318 | * parts first (such as the 'www.', then URL scheme, hash, etc.),
|
---|
| 319 | * attempting to make the URL human-readable before looking for a good
|
---|
| 320 | * point to insert the ellipsis if it is still too long. Ex:
|
---|
| 321 | * 'yahoo.com/some..to/a/file'. For more details, see
|
---|
| 322 | * {@link Autolinker.truncate.TruncateSmart}.
|
---|
| 323 | */
|
---|
| 324 | this.truncate = {
|
---|
| 325 | length: 0,
|
---|
| 326 | location: 'end',
|
---|
| 327 | }; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 328 | /**
|
---|
| 329 | * @cfg {String} className
|
---|
| 330 | *
|
---|
| 331 | * A CSS class name to add to the generated links. This class will be added
|
---|
| 332 | * to all links, as well as this class plus match suffixes for styling
|
---|
| 333 | * url/email/phone/hashtag/mention links differently.
|
---|
| 334 | *
|
---|
| 335 | * For example, if this config is provided as "myLink", then:
|
---|
| 336 | *
|
---|
| 337 | * - URL links will have the CSS classes: "myLink myLink-url"
|
---|
| 338 | * - Email links will have the CSS classes: "myLink myLink-email", and
|
---|
| 339 | * - Phone links will have the CSS classes: "myLink myLink-phone"
|
---|
| 340 | * - Hashtag links will have the CSS classes: "myLink myLink-hashtag"
|
---|
| 341 | * - Mention links will have the CSS classes: "myLink myLink-mention myLink-[type]"
|
---|
| 342 | * where [type] is either "instagram", "twitter" or "soundcloud"
|
---|
| 343 | */
|
---|
| 344 | this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 345 | /**
|
---|
| 346 | * @cfg {Function} replaceFn
|
---|
| 347 | *
|
---|
| 348 | * A function to individually process each match found in the input string.
|
---|
| 349 | *
|
---|
| 350 | * See the class's description for usage.
|
---|
| 351 | *
|
---|
| 352 | * The `replaceFn` can be called with a different context object (`this`
|
---|
| 353 | * reference) using the {@link #context} cfg.
|
---|
| 354 | *
|
---|
| 355 | * This function is called with the following parameter:
|
---|
| 356 | *
|
---|
| 357 | * @cfg {Autolinker.match.Match} replaceFn.match The Match instance which
|
---|
| 358 | * can be used to retrieve information about the match that the `replaceFn`
|
---|
| 359 | * is currently processing. See {@link Autolinker.match.Match} subclasses
|
---|
| 360 | * for details.
|
---|
| 361 | */
|
---|
| 362 | this.replaceFn = null; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 363 | /**
|
---|
| 364 | * @cfg {Object} context
|
---|
| 365 | *
|
---|
| 366 | * The context object (`this` reference) to call the `replaceFn` with.
|
---|
| 367 | *
|
---|
| 368 | * Defaults to this Autolinker instance.
|
---|
| 369 | */
|
---|
| 370 | this.context = undefined; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 371 | /**
|
---|
| 372 | * @cfg {Boolean} [sanitizeHtml=false]
|
---|
| 373 | *
|
---|
| 374 | * `true` to HTML-encode the start and end brackets of existing HTML tags found
|
---|
| 375 | * in the input string. This will escape `<` and `>` characters to `<` and
|
---|
| 376 | * `>`, respectively.
|
---|
| 377 | *
|
---|
| 378 | * Setting this to `true` will prevent XSS (Cross-site Scripting) attacks,
|
---|
| 379 | * but will remove the significance of existing HTML tags in the input string. If
|
---|
| 380 | * you would like to maintain the significance of existing HTML tags while also
|
---|
| 381 | * making the output HTML string safe, leave this option as `false` and use a
|
---|
| 382 | * tool like https://github.com/cure53/DOMPurify (or others) on the input string
|
---|
| 383 | * before running Autolinker.
|
---|
| 384 | */
|
---|
| 385 | this.sanitizeHtml = false; // default value just to get the above doc comment in the ES5 output and documentation generator
|
---|
| 386 | /**
|
---|
| 387 | * @private
|
---|
| 388 | * @property {Autolinker.matcher.Matcher[]} matchers
|
---|
| 389 | *
|
---|
| 390 | * The {@link Autolinker.matcher.Matcher} instances for this Autolinker
|
---|
| 391 | * instance.
|
---|
| 392 | *
|
---|
| 393 | * This is lazily created in {@link #getMatchers}.
|
---|
| 394 | */
|
---|
| 395 | this.matchers = null;
|
---|
| 396 | /**
|
---|
| 397 | * @private
|
---|
| 398 | * @property {Autolinker.AnchorTagBuilder} tagBuilder
|
---|
| 399 | *
|
---|
| 400 | * The AnchorTagBuilder instance used to build match replacement anchor tags.
|
---|
| 401 | * Note: this is lazily instantiated in the {@link #getTagBuilder} method.
|
---|
| 402 | */
|
---|
| 403 | this.tagBuilder = null;
|
---|
| 404 | // Note: when `this.something` is used in the rhs of these assignments,
|
---|
| 405 | // it refers to the default values set above the constructor
|
---|
| 406 | this.urls = this.normalizeUrlsCfg(cfg.urls);
|
---|
| 407 | this.email = typeof cfg.email === 'boolean' ? cfg.email : this.email;
|
---|
| 408 | this.phone = typeof cfg.phone === 'boolean' ? cfg.phone : this.phone;
|
---|
| 409 | this.hashtag = cfg.hashtag || this.hashtag;
|
---|
| 410 | this.mention = cfg.mention || this.mention;
|
---|
| 411 | this.newWindow = typeof cfg.newWindow === 'boolean' ? cfg.newWindow : this.newWindow;
|
---|
| 412 | this.stripPrefix = this.normalizeStripPrefixCfg(cfg.stripPrefix);
|
---|
| 413 | this.stripTrailingSlash =
|
---|
| 414 | typeof cfg.stripTrailingSlash === 'boolean'
|
---|
| 415 | ? cfg.stripTrailingSlash
|
---|
| 416 | : this.stripTrailingSlash;
|
---|
| 417 | this.decodePercentEncoding =
|
---|
| 418 | typeof cfg.decodePercentEncoding === 'boolean'
|
---|
| 419 | ? cfg.decodePercentEncoding
|
---|
| 420 | : this.decodePercentEncoding;
|
---|
| 421 | this.sanitizeHtml = cfg.sanitizeHtml || false;
|
---|
| 422 | // Validate the value of the `mention` cfg
|
---|
| 423 | var mention = this.mention;
|
---|
| 424 | if (mention !== false &&
|
---|
| 425 | ['twitter', 'instagram', 'soundcloud', 'tiktok'].indexOf(mention) === -1) {
|
---|
| 426 | throw new Error("invalid `mention` cfg '".concat(mention, "' - see docs"));
|
---|
| 427 | }
|
---|
| 428 | // Validate the value of the `hashtag` cfg
|
---|
| 429 | var hashtag = this.hashtag;
|
---|
| 430 | if (hashtag !== false && hashtagServices.indexOf(hashtag) === -1) {
|
---|
| 431 | throw new Error("invalid `hashtag` cfg '".concat(hashtag, "' - see docs"));
|
---|
| 432 | }
|
---|
| 433 | this.truncate = this.normalizeTruncateCfg(cfg.truncate);
|
---|
| 434 | this.className = cfg.className || this.className;
|
---|
| 435 | this.replaceFn = cfg.replaceFn || this.replaceFn;
|
---|
| 436 | this.context = cfg.context || this;
|
---|
| 437 | }
|
---|
| 438 | /**
|
---|
| 439 | * Automatically links URLs, Email addresses, Phone Numbers, Twitter handles,
|
---|
| 440 | * Hashtags, and Mentions found in the given chunk of HTML. Does not link URLs
|
---|
| 441 | * found within HTML tags.
|
---|
| 442 | *
|
---|
| 443 | * For instance, if given the text: `You should go to http://www.yahoo.com`,
|
---|
| 444 | * then the result will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>`
|
---|
| 445 | *
|
---|
| 446 | * Example:
|
---|
| 447 | *
|
---|
| 448 | * var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } );
|
---|
| 449 | * // Produces: "Go to <a href="http://google.com">google.com</a>"
|
---|
| 450 | *
|
---|
| 451 | * @static
|
---|
| 452 | * @param {String} textOrHtml The HTML or text to find matches within (depending
|
---|
| 453 | * on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #mention},
|
---|
| 454 | * {@link #hashtag}, and {@link #mention} options are enabled).
|
---|
| 455 | * @param {Object} [options] Any of the configuration options for the Autolinker
|
---|
| 456 | * class, specified in an Object (map). See the class description for an
|
---|
| 457 | * example call.
|
---|
| 458 | * @return {String} The HTML text, with matches automatically linked.
|
---|
| 459 | */
|
---|
| 460 | Autolinker.link = function (textOrHtml, options) {
|
---|
| 461 | var autolinker = new Autolinker(options);
|
---|
| 462 | return autolinker.link(textOrHtml);
|
---|
| 463 | };
|
---|
| 464 | /**
|
---|
| 465 | * Parses the input `textOrHtml` looking for URLs, email addresses, phone
|
---|
| 466 | * numbers, username handles, and hashtags (depending on the configuration
|
---|
| 467 | * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
|
---|
| 468 | * objects describing those matches (without making any replacements).
|
---|
| 469 | *
|
---|
| 470 | * Note that if parsing multiple pieces of text, it is slightly more efficient
|
---|
| 471 | * to create an Autolinker instance, and use the instance-level {@link #parse}
|
---|
| 472 | * method.
|
---|
| 473 | *
|
---|
| 474 | * Example:
|
---|
| 475 | *
|
---|
| 476 | * var matches = Autolinker.parse( "Hello google.com, I am asdf@asdf.com", {
|
---|
| 477 | * urls: true,
|
---|
| 478 | * email: true
|
---|
| 479 | * } );
|
---|
| 480 | *
|
---|
| 481 | * console.log( matches.length ); // 2
|
---|
| 482 | * console.log( matches[ 0 ].getType() ); // 'url'
|
---|
| 483 | * console.log( matches[ 0 ].getUrl() ); // 'google.com'
|
---|
| 484 | * console.log( matches[ 1 ].getType() ); // 'email'
|
---|
| 485 | * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
|
---|
| 486 | *
|
---|
| 487 | * @static
|
---|
| 488 | * @param {String} textOrHtml The HTML or text to find matches within
|
---|
| 489 | * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
|
---|
| 490 | * {@link #hashtag}, and {@link #mention} options are enabled).
|
---|
| 491 | * @param {Object} [options] Any of the configuration options for the Autolinker
|
---|
| 492 | * class, specified in an Object (map). See the class description for an
|
---|
| 493 | * example call.
|
---|
| 494 | * @return {Autolinker.match.Match[]} The array of Matches found in the
|
---|
| 495 | * given input `textOrHtml`.
|
---|
| 496 | */
|
---|
| 497 | Autolinker.parse = function (textOrHtml, options) {
|
---|
| 498 | var autolinker = new Autolinker(options);
|
---|
| 499 | return autolinker.parse(textOrHtml);
|
---|
| 500 | };
|
---|
| 501 | /**
|
---|
| 502 | * Normalizes the {@link #urls} config into an Object with 3 properties:
|
---|
| 503 | * `schemeMatches`, `wwwMatches`, and `tldMatches`, all Booleans.
|
---|
| 504 | *
|
---|
| 505 | * See {@link #urls} config for details.
|
---|
| 506 | *
|
---|
| 507 | * @private
|
---|
| 508 | * @param {Boolean/Object} urls
|
---|
| 509 | * @return {Object}
|
---|
| 510 | */
|
---|
| 511 | Autolinker.prototype.normalizeUrlsCfg = function (urls) {
|
---|
| 512 | if (urls == null)
|
---|
| 513 | urls = true; // default to `true`
|
---|
| 514 | if (typeof urls === 'boolean') {
|
---|
| 515 | return { schemeMatches: urls, wwwMatches: urls, tldMatches: urls };
|
---|
| 516 | }
|
---|
| 517 | else {
|
---|
| 518 | // object form
|
---|
| 519 | return {
|
---|
| 520 | schemeMatches: typeof urls.schemeMatches === 'boolean' ? urls.schemeMatches : true,
|
---|
| 521 | wwwMatches: typeof urls.wwwMatches === 'boolean' ? urls.wwwMatches : true,
|
---|
| 522 | tldMatches: typeof urls.tldMatches === 'boolean' ? urls.tldMatches : true,
|
---|
| 523 | };
|
---|
| 524 | }
|
---|
| 525 | };
|
---|
| 526 | /**
|
---|
| 527 | * Normalizes the {@link #stripPrefix} config into an Object with 2
|
---|
| 528 | * properties: `scheme`, and `www` - both Booleans.
|
---|
| 529 | *
|
---|
| 530 | * See {@link #stripPrefix} config for details.
|
---|
| 531 | *
|
---|
| 532 | * @private
|
---|
| 533 | * @param {Boolean/Object} stripPrefix
|
---|
| 534 | * @return {Object}
|
---|
| 535 | */
|
---|
| 536 | Autolinker.prototype.normalizeStripPrefixCfg = function (stripPrefix) {
|
---|
| 537 | if (stripPrefix == null)
|
---|
| 538 | stripPrefix = true; // default to `true`
|
---|
| 539 | if (typeof stripPrefix === 'boolean') {
|
---|
| 540 | return { scheme: stripPrefix, www: stripPrefix };
|
---|
| 541 | }
|
---|
| 542 | else {
|
---|
| 543 | // object form
|
---|
| 544 | return {
|
---|
| 545 | scheme: typeof stripPrefix.scheme === 'boolean' ? stripPrefix.scheme : true,
|
---|
| 546 | www: typeof stripPrefix.www === 'boolean' ? stripPrefix.www : true,
|
---|
| 547 | };
|
---|
| 548 | }
|
---|
| 549 | };
|
---|
| 550 | /**
|
---|
| 551 | * Normalizes the {@link #truncate} config into an Object with 2 properties:
|
---|
| 552 | * `length` (Number), and `location` (String).
|
---|
| 553 | *
|
---|
| 554 | * See {@link #truncate} config for details.
|
---|
| 555 | *
|
---|
| 556 | * @private
|
---|
| 557 | * @param {Number/Object} truncate
|
---|
| 558 | * @return {Object}
|
---|
| 559 | */
|
---|
| 560 | Autolinker.prototype.normalizeTruncateCfg = function (truncate) {
|
---|
| 561 | if (typeof truncate === 'number') {
|
---|
| 562 | return { length: truncate, location: 'end' };
|
---|
| 563 | }
|
---|
| 564 | else {
|
---|
| 565 | // object, or undefined/null
|
---|
| 566 | return defaults(truncate || {}, {
|
---|
| 567 | length: Number.POSITIVE_INFINITY,
|
---|
| 568 | location: 'end',
|
---|
| 569 | });
|
---|
| 570 | }
|
---|
| 571 | };
|
---|
| 572 | /**
|
---|
| 573 | * Parses the input `textOrHtml` looking for URLs, email addresses, phone
|
---|
| 574 | * numbers, username handles, and hashtags (depending on the configuration
|
---|
| 575 | * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
|
---|
| 576 | * objects describing those matches (without making any replacements).
|
---|
| 577 | *
|
---|
| 578 | * This method is used by the {@link #link} method, but can also be used to
|
---|
| 579 | * simply do parsing of the input in order to discover what kinds of links
|
---|
| 580 | * there are and how many.
|
---|
| 581 | *
|
---|
| 582 | * Example usage:
|
---|
| 583 | *
|
---|
| 584 | * var autolinker = new Autolinker( {
|
---|
| 585 | * urls: true,
|
---|
| 586 | * email: true
|
---|
| 587 | * } );
|
---|
| 588 | *
|
---|
| 589 | * var matches = autolinker.parse( "Hello google.com, I am asdf@asdf.com" );
|
---|
| 590 | *
|
---|
| 591 | * console.log( matches.length ); // 2
|
---|
| 592 | * console.log( matches[ 0 ].getType() ); // 'url'
|
---|
| 593 | * console.log( matches[ 0 ].getUrl() ); // 'google.com'
|
---|
| 594 | * console.log( matches[ 1 ].getType() ); // 'email'
|
---|
| 595 | * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
|
---|
| 596 | *
|
---|
| 597 | * @param {String} textOrHtml The HTML or text to find matches within
|
---|
| 598 | * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
|
---|
| 599 | * {@link #hashtag}, and {@link #mention} options are enabled).
|
---|
| 600 | * @return {Autolinker.match.Match[]} The array of Matches found in the
|
---|
| 601 | * given input `textOrHtml`.
|
---|
| 602 | */
|
---|
| 603 | Autolinker.prototype.parse = function (textOrHtml) {
|
---|
| 604 | var _this = this;
|
---|
| 605 | 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
|
---|
| 606 | matches = [];
|
---|
| 607 | // Find all matches within the `textOrHtml` (but not matches that are
|
---|
| 608 | // already nested within <a>, <style> and <script> tags)
|
---|
| 609 | parseHtml(textOrHtml, {
|
---|
| 610 | onOpenTag: function (tagName) {
|
---|
| 611 | if (skipTagNames.indexOf(tagName) >= 0) {
|
---|
| 612 | skipTagsStackCount++;
|
---|
| 613 | }
|
---|
| 614 | },
|
---|
| 615 | onText: function (text, offset) {
|
---|
| 616 | // Only process text nodes that are not within an <a>, <style> or <script> tag
|
---|
| 617 | if (skipTagsStackCount === 0) {
|
---|
| 618 | // "Walk around" common HTML entities. An ' ' (for example)
|
---|
| 619 | // could be at the end of a URL, but we don't want to
|
---|
| 620 | // include the trailing '&' in the URL. See issue #76
|
---|
| 621 | // TODO: Handle HTML entities separately in parseHtml() and
|
---|
| 622 | // don't emit them as "text" except for & entities
|
---|
| 623 | var htmlCharacterEntitiesRegex = /( | |<|<|>|>|"|"|')/gi;
|
---|
| 624 | var textSplit = splitAndCapture(text, htmlCharacterEntitiesRegex);
|
---|
| 625 | var currentOffset_1 = offset;
|
---|
| 626 | textSplit.forEach(function (splitText, i) {
|
---|
| 627 | // even number matches are text, odd numbers are html entities
|
---|
| 628 | if (i % 2 === 0) {
|
---|
| 629 | var textNodeMatches = _this.parseText(splitText, currentOffset_1);
|
---|
| 630 | matches.push.apply(matches, textNodeMatches);
|
---|
| 631 | }
|
---|
| 632 | currentOffset_1 += splitText.length;
|
---|
| 633 | });
|
---|
| 634 | }
|
---|
| 635 | },
|
---|
| 636 | onCloseTag: function (tagName) {
|
---|
| 637 | if (skipTagNames.indexOf(tagName) >= 0) {
|
---|
| 638 | skipTagsStackCount = Math.max(skipTagsStackCount - 1, 0); // attempt to handle extraneous </a> tags by making sure the stack count never goes below 0
|
---|
| 639 | }
|
---|
| 640 | },
|
---|
| 641 | onComment: function (offset) { },
|
---|
| 642 | onDoctype: function (offset) { }, // no need to process doctype nodes
|
---|
| 643 | });
|
---|
| 644 | // After we have found all matches, remove subsequent matches that
|
---|
| 645 | // overlap with a previous match. This can happen for instance with URLs,
|
---|
| 646 | // where the url 'google.com/#link' would match '#link' as a hashtag.
|
---|
| 647 | matches = this.compactMatches(matches);
|
---|
| 648 | // And finally, remove matches for match types that have been turned
|
---|
| 649 | // off. We needed to have all match types turned on initially so that
|
---|
| 650 | // things like hashtags could be filtered out if they were really just
|
---|
| 651 | // part of a URL match (for instance, as a named anchor).
|
---|
| 652 | matches = this.removeUnwantedMatches(matches);
|
---|
| 653 | return matches;
|
---|
| 654 | };
|
---|
| 655 | /**
|
---|
| 656 | * After we have found all matches, we need to remove matches that overlap
|
---|
| 657 | * with a previous match. This can happen for instance with URLs, where the
|
---|
| 658 | * url 'google.com/#link' would match '#link' as a hashtag. Because the
|
---|
| 659 | * '#link' part is contained in a larger match that comes before the HashTag
|
---|
| 660 | * match, we'll remove the HashTag match.
|
---|
| 661 | *
|
---|
| 662 | * @private
|
---|
| 663 | * @param {Autolinker.match.Match[]} matches
|
---|
| 664 | * @return {Autolinker.match.Match[]}
|
---|
| 665 | */
|
---|
| 666 | Autolinker.prototype.compactMatches = function (matches) {
|
---|
| 667 | // First, the matches need to be sorted in order of offset
|
---|
| 668 | matches.sort(function (a, b) {
|
---|
| 669 | return a.getOffset() - b.getOffset();
|
---|
| 670 | });
|
---|
| 671 | var i = 0;
|
---|
| 672 | while (i < matches.length - 1) {
|
---|
| 673 | var match = matches[i], offset = match.getOffset(), matchedTextLength = match.getMatchedText().length, endIdx = offset + matchedTextLength;
|
---|
| 674 | if (i + 1 < matches.length) {
|
---|
| 675 | // Remove subsequent matches that equal offset with current match
|
---|
| 676 | if (matches[i + 1].getOffset() === offset) {
|
---|
| 677 | var removeIdx = matches[i + 1].getMatchedText().length > matchedTextLength ? i : i + 1;
|
---|
| 678 | matches.splice(removeIdx, 1);
|
---|
| 679 | continue;
|
---|
| 680 | }
|
---|
| 681 | // Remove subsequent matches that overlap with the current match
|
---|
| 682 | if (matches[i + 1].getOffset() < endIdx) {
|
---|
| 683 | matches.splice(i + 1, 1);
|
---|
| 684 | continue;
|
---|
| 685 | }
|
---|
| 686 | }
|
---|
| 687 | i++;
|
---|
| 688 | }
|
---|
| 689 | return matches;
|
---|
| 690 | };
|
---|
| 691 | /**
|
---|
| 692 | * Removes matches for matchers that were turned off in the options. For
|
---|
| 693 | * example, if {@link #hashtag hashtags} were not to be matched, we'll
|
---|
| 694 | * remove them from the `matches` array here.
|
---|
| 695 | *
|
---|
| 696 | * Note: we *must* use all Matchers on the input string, and then filter
|
---|
| 697 | * them out later. For example, if the options were `{ url: false, hashtag: true }`,
|
---|
| 698 | * we wouldn't want to match the text '#link' as a HashTag inside of the text
|
---|
| 699 | * 'google.com/#link'. The way the algorithm works is that we match the full
|
---|
| 700 | * URL first (which prevents the accidental HashTag match), and then we'll
|
---|
| 701 | * simply throw away the URL match.
|
---|
| 702 | *
|
---|
| 703 | * @private
|
---|
| 704 | * @param {Autolinker.match.Match[]} matches The array of matches to remove
|
---|
| 705 | * the unwanted matches from. Note: this array is mutated for the
|
---|
| 706 | * removals.
|
---|
| 707 | * @return {Autolinker.match.Match[]} The mutated input `matches` array.
|
---|
| 708 | */
|
---|
| 709 | Autolinker.prototype.removeUnwantedMatches = function (matches) {
|
---|
| 710 | if (!this.hashtag)
|
---|
| 711 | remove(matches, function (match) {
|
---|
| 712 | return match.getType() === 'hashtag';
|
---|
| 713 | });
|
---|
| 714 | if (!this.email)
|
---|
| 715 | remove(matches, function (match) {
|
---|
| 716 | return match.getType() === 'email';
|
---|
| 717 | });
|
---|
| 718 | if (!this.phone)
|
---|
| 719 | remove(matches, function (match) {
|
---|
| 720 | return match.getType() === 'phone';
|
---|
| 721 | });
|
---|
| 722 | if (!this.mention)
|
---|
| 723 | remove(matches, function (match) {
|
---|
| 724 | return match.getType() === 'mention';
|
---|
| 725 | });
|
---|
| 726 | if (!this.urls.schemeMatches) {
|
---|
| 727 | remove(matches, function (m) {
|
---|
| 728 | return m.getType() === 'url' && m.getUrlMatchType() === 'scheme';
|
---|
| 729 | });
|
---|
| 730 | }
|
---|
| 731 | if (!this.urls.wwwMatches) {
|
---|
| 732 | remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'www'; });
|
---|
| 733 | }
|
---|
| 734 | if (!this.urls.tldMatches) {
|
---|
| 735 | remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'tld'; });
|
---|
| 736 | }
|
---|
| 737 | return matches;
|
---|
| 738 | };
|
---|
| 739 | /**
|
---|
| 740 | * Parses the input `text` looking for URLs, email addresses, phone
|
---|
| 741 | * numbers, username handles, and hashtags (depending on the configuration
|
---|
| 742 | * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
|
---|
| 743 | * objects describing those matches.
|
---|
| 744 | *
|
---|
| 745 | * This method processes a **non-HTML string**, and is used to parse and
|
---|
| 746 | * match within the text nodes of an HTML string. This method is used
|
---|
| 747 | * internally by {@link #parse}.
|
---|
| 748 | *
|
---|
| 749 | * @private
|
---|
| 750 | * @param {String} text The text to find matches within (depending on if the
|
---|
| 751 | * {@link #urls}, {@link #email}, {@link #phone},
|
---|
| 752 | * {@link #hashtag}, and {@link #mention} options are enabled). This must be a non-HTML string.
|
---|
| 753 | * @param {Number} [offset=0] The offset of the text node within the
|
---|
| 754 | * original string. This is used when parsing with the {@link #parse}
|
---|
| 755 | * method to generate correct offsets within the {@link Autolinker.match.Match}
|
---|
| 756 | * instances, but may be omitted if calling this method publicly.
|
---|
| 757 | * @return {Autolinker.match.Match[]} The array of Matches found in the
|
---|
| 758 | * given input `text`.
|
---|
| 759 | */
|
---|
| 760 | Autolinker.prototype.parseText = function (text, offset) {
|
---|
| 761 | if (offset === void 0) { offset = 0; }
|
---|
| 762 | offset = offset || 0;
|
---|
| 763 | var matchers = this.getMatchers(), matches = [];
|
---|
| 764 | for (var i = 0, numMatchers = matchers.length; i < numMatchers; i++) {
|
---|
| 765 | var textMatches = matchers[i].parseMatches(text);
|
---|
| 766 | // Correct the offset of each of the matches. They are originally
|
---|
| 767 | // the offset of the match within the provided text node, but we
|
---|
| 768 | // need to correct them to be relative to the original HTML input
|
---|
| 769 | // string (i.e. the one provided to #parse).
|
---|
| 770 | for (var j = 0, numTextMatches = textMatches.length; j < numTextMatches; j++) {
|
---|
| 771 | textMatches[j].setOffset(offset + textMatches[j].getOffset());
|
---|
| 772 | }
|
---|
| 773 | matches.push.apply(matches, textMatches);
|
---|
| 774 | }
|
---|
| 775 | return matches;
|
---|
| 776 | };
|
---|
| 777 | /**
|
---|
| 778 | * Automatically links URLs, Email addresses, Phone numbers, Hashtags,
|
---|
| 779 | * and Mentions (Twitter, Instagram, Soundcloud) found in the given chunk of HTML. Does not link
|
---|
| 780 | * URLs found within HTML tags.
|
---|
| 781 | *
|
---|
| 782 | * For instance, if given the text: `You should go to http://www.yahoo.com`,
|
---|
| 783 | * then the result will be `You should go to
|
---|
| 784 | * <a href="http://www.yahoo.com">http://www.yahoo.com</a>`
|
---|
| 785 | *
|
---|
| 786 | * This method finds the text around any HTML elements in the input
|
---|
| 787 | * `textOrHtml`, which will be the text that is processed. Any original HTML
|
---|
| 788 | * elements will be left as-is, as well as the text that is already wrapped
|
---|
| 789 | * in anchor (<a>) tags.
|
---|
| 790 | *
|
---|
| 791 | * @param {String} textOrHtml The HTML or text to autolink matches within
|
---|
| 792 | * (depending on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #hashtag}, and {@link #mention} options are enabled).
|
---|
| 793 | * @return {String} The HTML, with matches automatically linked.
|
---|
| 794 | */
|
---|
| 795 | Autolinker.prototype.link = function (textOrHtml) {
|
---|
| 796 | if (!textOrHtml) {
|
---|
| 797 | return '';
|
---|
| 798 | } // handle `null` and `undefined` (for JavaScript users that don't have TypeScript support)
|
---|
| 799 | /* We would want to sanitize the start and end characters of a tag
|
---|
| 800 | * before processing the string in order to avoid an XSS scenario.
|
---|
| 801 | * This behaviour can be changed by toggling the sanitizeHtml option.
|
---|
| 802 | */
|
---|
| 803 | if (this.sanitizeHtml) {
|
---|
| 804 | textOrHtml = textOrHtml.replace(/</g, '<').replace(/>/g, '>');
|
---|
| 805 | }
|
---|
| 806 | var matches = this.parse(textOrHtml), newHtml = [], lastIndex = 0;
|
---|
| 807 | for (var i = 0, len = matches.length; i < len; i++) {
|
---|
| 808 | var match = matches[i];
|
---|
| 809 | newHtml.push(textOrHtml.substring(lastIndex, match.getOffset()));
|
---|
| 810 | newHtml.push(this.createMatchReturnVal(match));
|
---|
| 811 | lastIndex = match.getOffset() + match.getMatchedText().length;
|
---|
| 812 | }
|
---|
| 813 | newHtml.push(textOrHtml.substring(lastIndex)); // handle the text after the last match
|
---|
| 814 | return newHtml.join('');
|
---|
| 815 | };
|
---|
| 816 | /**
|
---|
| 817 | * Creates the return string value for a given match in the input string.
|
---|
| 818 | *
|
---|
| 819 | * This method handles the {@link #replaceFn}, if one was provided.
|
---|
| 820 | *
|
---|
| 821 | * @private
|
---|
| 822 | * @param {Autolinker.match.Match} match The Match object that represents
|
---|
| 823 | * the match.
|
---|
| 824 | * @return {String} The string that the `match` should be replaced with.
|
---|
| 825 | * This is usually the anchor tag string, but may be the `matchStr` itself
|
---|
| 826 | * if the match is not to be replaced.
|
---|
| 827 | */
|
---|
| 828 | Autolinker.prototype.createMatchReturnVal = function (match) {
|
---|
| 829 | // Handle a custom `replaceFn` being provided
|
---|
| 830 | var replaceFnResult;
|
---|
| 831 | if (this.replaceFn) {
|
---|
| 832 | replaceFnResult = this.replaceFn.call(this.context, match); // Autolinker instance is the context
|
---|
| 833 | }
|
---|
| 834 | if (typeof replaceFnResult === 'string') {
|
---|
| 835 | return replaceFnResult; // `replaceFn` returned a string, use that
|
---|
| 836 | }
|
---|
| 837 | else if (replaceFnResult === false) {
|
---|
| 838 | return match.getMatchedText(); // no replacement for the match
|
---|
| 839 | }
|
---|
| 840 | else if (replaceFnResult instanceof HtmlTag) {
|
---|
| 841 | return replaceFnResult.toAnchorString();
|
---|
| 842 | }
|
---|
| 843 | else {
|
---|
| 844 | // replaceFnResult === true, or no/unknown return value from function
|
---|
| 845 | // Perform Autolinker's default anchor tag generation
|
---|
| 846 | var anchorTag = match.buildTag(); // returns an Autolinker.HtmlTag instance
|
---|
| 847 | return anchorTag.toAnchorString();
|
---|
| 848 | }
|
---|
| 849 | };
|
---|
| 850 | /**
|
---|
| 851 | * Lazily instantiates and returns the {@link Autolinker.matcher.Matcher}
|
---|
| 852 | * instances for this Autolinker instance.
|
---|
| 853 | *
|
---|
| 854 | * @private
|
---|
| 855 | * @return {Autolinker.matcher.Matcher[]}
|
---|
| 856 | */
|
---|
| 857 | Autolinker.prototype.getMatchers = function () {
|
---|
| 858 | if (!this.matchers) {
|
---|
| 859 | var tagBuilder = this.getTagBuilder();
|
---|
| 860 | var matchers = [
|
---|
| 861 | new HashtagMatcher({
|
---|
| 862 | tagBuilder: tagBuilder,
|
---|
| 863 | serviceName: this.hashtag,
|
---|
| 864 | }),
|
---|
| 865 | new EmailMatcher({ tagBuilder: tagBuilder }),
|
---|
| 866 | new PhoneMatcher({ tagBuilder: tagBuilder }),
|
---|
| 867 | new MentionMatcher({
|
---|
| 868 | tagBuilder: tagBuilder,
|
---|
| 869 | serviceName: this.mention,
|
---|
| 870 | }),
|
---|
| 871 | new UrlMatcher({
|
---|
| 872 | tagBuilder: tagBuilder,
|
---|
| 873 | stripPrefix: this.stripPrefix,
|
---|
| 874 | stripTrailingSlash: this.stripTrailingSlash,
|
---|
| 875 | decodePercentEncoding: this.decodePercentEncoding,
|
---|
| 876 | }),
|
---|
| 877 | ];
|
---|
| 878 | return (this.matchers = matchers);
|
---|
| 879 | }
|
---|
| 880 | else {
|
---|
| 881 | return this.matchers;
|
---|
| 882 | }
|
---|
| 883 | };
|
---|
| 884 | /**
|
---|
| 885 | * Returns the {@link #tagBuilder} instance for this Autolinker instance,
|
---|
| 886 | * lazily instantiating it if it does not yet exist.
|
---|
| 887 | *
|
---|
| 888 | * @private
|
---|
| 889 | * @return {Autolinker.AnchorTagBuilder}
|
---|
| 890 | */
|
---|
| 891 | Autolinker.prototype.getTagBuilder = function () {
|
---|
| 892 | var tagBuilder = this.tagBuilder;
|
---|
| 893 | if (!tagBuilder) {
|
---|
| 894 | tagBuilder = this.tagBuilder = new AnchorTagBuilder({
|
---|
| 895 | newWindow: this.newWindow,
|
---|
| 896 | truncate: this.truncate,
|
---|
| 897 | className: this.className,
|
---|
| 898 | });
|
---|
| 899 | }
|
---|
| 900 | return tagBuilder;
|
---|
| 901 | };
|
---|
| 902 | // NOTE: must be 'export default' here for UMD module
|
---|
| 903 | /**
|
---|
| 904 | * @static
|
---|
| 905 | * @property {String} version
|
---|
| 906 | *
|
---|
| 907 | * The Autolinker version number in the form major.minor.patch
|
---|
| 908 | *
|
---|
| 909 | * Ex: 3.15.0
|
---|
| 910 | */
|
---|
| 911 | Autolinker.version = version;
|
---|
| 912 | /**
|
---|
| 913 | * For backwards compatibility with Autolinker 1.x, the AnchorTagBuilder
|
---|
| 914 | * class is provided as a static on the Autolinker class.
|
---|
| 915 | */
|
---|
| 916 | Autolinker.AnchorTagBuilder = AnchorTagBuilder;
|
---|
| 917 | /**
|
---|
| 918 | * For backwards compatibility with Autolinker 1.x, the HtmlTag class is
|
---|
| 919 | * provided as a static on the Autolinker class.
|
---|
| 920 | */
|
---|
| 921 | Autolinker.HtmlTag = HtmlTag;
|
---|
| 922 | /**
|
---|
| 923 | * For backwards compatibility with Autolinker 1.x, the Matcher classes are
|
---|
| 924 | * provided as statics on the Autolinker class.
|
---|
| 925 | */
|
---|
| 926 | Autolinker.matcher = {
|
---|
| 927 | Email: EmailMatcher,
|
---|
| 928 | Hashtag: HashtagMatcher,
|
---|
| 929 | Matcher: Matcher,
|
---|
| 930 | Mention: MentionMatcher,
|
---|
| 931 | Phone: PhoneMatcher,
|
---|
| 932 | Url: UrlMatcher,
|
---|
| 933 | };
|
---|
| 934 | /**
|
---|
| 935 | * For backwards compatibility with Autolinker 1.x, the Match classes are
|
---|
| 936 | * provided as statics on the Autolinker class.
|
---|
| 937 | */
|
---|
| 938 | Autolinker.match = {
|
---|
| 939 | Email: EmailMatch,
|
---|
| 940 | Hashtag: HashtagMatch,
|
---|
| 941 | Match: Match,
|
---|
| 942 | Mention: MentionMatch,
|
---|
| 943 | Phone: PhoneMatch,
|
---|
| 944 | Url: UrlMatch,
|
---|
| 945 | };
|
---|
| 946 | return Autolinker;
|
---|
| 947 | }());
|
---|
| 948 | export default Autolinker;
|
---|
| 949 | //# sourceMappingURL=autolinker.js.map |
---|