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,072 lines (1,054 loc) • 251 kB
JavaScript
/*!
* Autolinker.js
* v4.1.5
*
* Copyright(c) 2025 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';
/******************************************************************************
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);
};
function __read(o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
}
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
// Important: this file is generated from the 'build' script and should not be
// edited directly
var version = '4.1.5';
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* 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';
}
/**
* 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 = '…';
ellipsisLength = 3;
}
else {
ellipsisLength = ellipsisChars.length;
}
str = str.substring(0, truncateLen - ellipsisLength) + ellipsisChars;
}
return str;
}
/**
* 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.
*/
/* istanbul ignore next */
function assertNever(theValue) {
throw new Error("Unhandled case for value: '".concat(theValue, "'"));
}
// Regular expression to match whitespace
var whitespaceRe = /\s+/;
/**
* @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;
};
/**
* 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();
var classes = !classAttr ? [] : classAttr.split(whitespaceRe);
var newClasses = cssClass.split(whitespaceRe);
var 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();
var classes = !classAttr ? [] : classAttr.split(whitespaceRe);
var removeClasses = cssClass.split(whitespaceRe);
var 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();
var 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 () {
var attrs = this.getAttrs(), attrsArr = [];
for (var prop in attrs) {
if (hasOwnProperty.call(attrs, 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 = '…';
ellipsisLength = 3;
ellipsisLengthBeforeParsing = 8;
}
else {
ellipsisLength = ellipsisChars.length;
ellipsisLengthBeforeParsing = ellipsisChars.length;
}
// If the URL is shorter than the truncate length, return it as is
if (url.length <= truncateLen) {
return url;
}
var availableLength = truncateLen - ellipsisLength;
var urlObj = parseUrl(url);
// Clean up the URL by removing any malformed query string
// (e.g. "?foo=bar?ignorethis")
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; // removing a malformed query string brought the URL under the truncateLength
}
// Clean up the URL by removing 'www.' from the host if it exists
if (urlObj.host) {
urlObj.host = urlObj.host.replace(/^www\./, '');
url = buildUrl(urlObj);
}
if (url.length <= truncateLen) {
return url; // removing 'www.' brought the URL under the truncateLength
}
// Process and build the truncated URL, starting with the hostname
var truncatedUrl = '';
if (urlObj.host) {
truncatedUrl += urlObj.host;
}
if (truncatedUrl.length >= availableLength) {
if (urlObj.host.length === truncateLen) {
return (urlObj.host.substr(0, truncateLen - ellipsisLength) + ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing);
}
return buildSegment(truncatedUrl, availableLength, ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing);
}
// If we still have available chars left, add the path and query string
var pathAndQuery = '';
if (urlObj.path) {
pathAndQuery += '/' + urlObj.path;
}
if (urlObj.query) {
pathAndQuery += '?' + urlObj.query;
}
if (pathAndQuery) {
if ((truncatedUrl + pathAndQuery).length >= availableLength) {
if ((truncatedUrl + pathAndQuery).length == truncateLen) {
return (truncatedUrl + pathAndQuery).substr(0, truncateLen);
}
var remainingAvailableLength = availableLength - truncatedUrl.length;
return (truncatedUrl + buildSegment(pathAndQuery, remainingAvailableLength, ellipsisChars)).substr(0, availableLength + ellipsisLengthBeforeParsing);
}
else {
truncatedUrl += pathAndQuery;
}
}
// If we still have available chars left, add the fragment
if (urlObj.fragment) {
var fragment = '#' + urlObj.fragment;
if ((truncatedUrl + fragment).length >= availableLength) {
if ((truncatedUrl + fragment).length == truncateLen) {
return (truncatedUrl + fragment).substr(0, truncateLen);
}
var remainingAvailableLength2 = availableLength - truncatedUrl.length;
return (truncatedUrl + buildSegment(fragment, remainingAvailableLength2, ellipsisChars)).substr(0, availableLength + ellipsisLengthBeforeParsing);
}
else {
truncatedUrl += fragment;
}
}
// If we still have available chars left, add the scheme
if (urlObj.scheme && urlObj.host) {
var scheme = urlObj.scheme + '://';
if ((truncatedUrl + scheme).length < availableLength) {
return (scheme + truncatedUrl).substr(0, truncateLen);
}
}
if (truncatedUrl.length <= truncateLen) {
return truncatedUrl;
}
var end = '';
if (availableLength > 0) {
end = truncatedUrl.substr(-1 * Math.floor(availableLength / 2));
}
return (truncatedUrl.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
}
/**
* Parses a URL into its components: scheme, host, path, query, and fragment.
*/
function parseUrl(url) {
// Functionality inspired by PHP function of same name
var urlObj = {};
var urlSub = url;
// Parse scheme
var match = urlSub.match(/^([a-z]+):\/\//i);
if (match) {
urlObj.scheme = match[1];
urlSub = urlSub.slice(match[0].length);
}
// Parse host
match = urlSub.match(/^(.*?)(?=(\?|#|\/|$))/i);
if (match) {
urlObj.host = match[1];
urlSub = urlSub.slice(match[0].length);
}
// Parse path
match = urlSub.match(/^\/(.*?)(?=(\?|#|$))/i);
if (match) {
urlObj.path = match[1];
urlSub = urlSub.slice(match[0].length);
}
// Parse query
match = urlSub.match(/^\?(.*?)(?=(#|$))/i);
if (match) {
urlObj.query = match[1];
urlSub = urlSub.slice(match[0].length);
}
// Parse fragment
match = urlSub.match(/^#(.*?)$/i);
if (match) {
urlObj.fragment = match[1];
//urlSub = urlSub.slice(match[0].length); -- not used. Uncomment if adding another block.
}
return urlObj;
}
function buildUrl(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;
}
function buildSegment(segment, remainingAvailableLength, ellipsisChars) {
var remainingAvailableLengthHalf = remainingAvailableLength / 2;
var startOffset = Math.ceil(remainingAvailableLengthHalf);
var endOffset = -1 * Math.floor(remainingAvailableLengthHalf);
var end = '';
if (endOffset < 0) {
end = segment.substr(endOffset);
}
return segment.substr(0, startOffset) + ellipsisChars + end;
}
/**
* 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 = '…';
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 (<a>) 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 (<a>) 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 (<a>)
* 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.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.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;
}());
/**
* @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
* tag for the Match.
*/
// @ts-expect-error Property used just to get the above doc comment into the ES5 output and documentation generator
this._ = null;
/**
* @cfg {String} matchedText (required)
*
* The original text that was matched by the {@link Autolinker.matcher.Matcher}.
*/
this.matchedText = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
/**
* @cfg {Number} offset (required)
*
* The offset of where the match was made in the input string.
*/
this.offset = 0; // default value just to get the above doc comment in the ES5 output and documentation generator
this.tagBuilder = cfg.tagBuilder;
this.matchedText = cfg.matchedText;
this.offset = cfg.offset;
}
/**
* Returns the original text that was matched.
*
* @return {String}
*/
AbstractMatch.prototype.getMatchedText = function () {
return this.matchedText;
};
/**
* Sets the {@link #offset} of where the match was made in the input string.
*
* A {@link Autolinker.matcher.Matcher} will be fed only HTML text nodes,
* and will therefore set an original offset that is relative to the HTML
* text node itself. However, we want this offset to be relative to the full
* HTML input string, and thus if using {@link Autolinker#parse} (rather
* than calling a {@link Autolinker.matcher.Matcher} directly), then this
* offset is corrected after the Matcher itself has done its job.
*
* @private
* @param {Number} offset
*/
AbstractMatch.prototype.setOffset = function (offset) {
this.offset = offset;
};
/**
* Returns the offset of where the match was made in the input string. This
* is the 0-based index of the match.
*
* @return {Number}
*/
AbstractMatch.prototype.getOffset = function () {
return this.offset;
};
/**
* Returns the CSS class suffix(es) for this match.
*
* A CSS class suffix is appended to the {@link Autolinker#className} in
* the {@link Autolinker.AnchorTagBuilder} when a match is translated into
* an anchor tag.
*
* For example, if {@link Autolinker#className} was configured as 'myLink',
* and this method returns `[ 'url' ]`, the final class name of the element
* will become: 'myLink myLink-url'.
*
* The match may provide multiple CSS class suffixes to be appended to the
* {@link Autolinker#className} in order to facilitate better styling
* options for different match criteria. See {@link Autolinker.match.Mention}
* for an example.
*
* By default, this method returns a single array with the match's
* {@link #getType type} name, but may be overridden by subclasses.
*
* @return {String[]}
*/
AbstractMatch.prototype.getCssClassSuffixes = function () {
return [this.type];
};
/**
* Builds and returns an {@link Autolinker.HtmlTag} instance based on the
* Match.
*
* This can be used to easily generate anchor tags from matches, and either
* return their HTML string, or modify them before doing so.
*
* Example Usage:
*
* var tag = match.buildTag();
* tag.addClass( 'cordova-link' );
* tag.setAttr( 'target', '_system' );
*
* tag.toAnchorString(); // <a href="http://google.com" class="cordova-link" target="_system">Google</a>
*
* Example Usage in {@link Autolinker#replaceFn}:
*
* 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>
*/
AbstractMatch.prototype.buildTag = function () {
return this.tagBuilder.build(this);
};
return AbstractMatch;
}());
// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
// INSTEAD, RUN: npm run generate-char-utils
/**
* Determines if the given character `c` matches the regular expression /[\x00-\x1F\x7F]/
* by checking it via character code in a binary search fashion.
*
* This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
* on the character itself.
*
* NOTE: This function is generated. Do not edit manually. To regenerate, run:
*
* npm run generate-char-utils
*/
function isControlChar(c) {
return ((c >= 0 && c <= 31) || c == 127);
}
/**
* Determines if the given character `c` matches the regular expression /[A-Za-z]/
* by checking it via character code in a binary search fashion.
*
* This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
* on the character itself.
*
* NOTE: This function is generated. Do not edit manually. To regenerate, run:
*
* npm run generate-char-utils
*/
function isAsciiLetterChar(c) {
return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122));
}
/**
* Determines if the given character `c` matches the regular expression /\d/
* by checking it via character code in a binary search fashion.
*
* This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
* on the character itself.
*
* NOTE: This function is generated. Do not edit manually. To regenerate, run:
*
* npm run generate-char-utils
*/
function isDigitChar(c) {
return (c >= 48 && c <= 57);
}
/**
* Determines if the given character `c` matches the regular expression /['"]/
* by checking it via character code in a binary search fashion.
*
* This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
* on the character itself.
*
* NOTE: This function is generated. Do not edit manually. To regenerate, run:
*
* npm run generate-char-utils
*/
function isQuoteChar(c) {
return (c == 34 || c == 39);
}
/**
* Determines if the given character `c` matches the regular expression /\s/
* by checking it via character code in a binary search fashion.
*
* This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
* on the character itself.
*
* NOTE: This function is generated. Do not edit manually. To regenerate, run:
*
* npm run generate-char-utils
*/
function isWhitespaceChar(c) {
return (c < 8232 ? (c < 160 ? ((c >= 9 && c <= 13) || c == 32) : (c < 5760 ? c == 160 : (c == 5760 || (c >= 8192 && c <= 8202)))) : (c < 8287 ? ((c >= 8232 && c <= 8233) || c == 8239) : (c < 12288 ? c == 8287 : (c == 12288 || c == 65279))));
}
/**
* Determines if the given character `c` matches the regular expression /[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\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\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\