mirror of
https://github.com/tutao/tutanota.git
synced 2025-10-19 16:03:43 +00:00

This version has a fix for breaking mails that contain HTML comments closed with +2 dashes. HTML comments closed with more than two dashes weren't handled correctly by linkify. Causing the comments' closing `>` to be escaped and replaced by `>` This resulted in actual markup being commented out. Example: `<div><!--comment--->Actual markup</div>` becomes `<div><!--comment--->Actual markup</div>-->`
1004 lines
34 KiB
JavaScript
Vendored
1004 lines
34 KiB
JavaScript
Vendored
import { Options, tokenize as tokenize$1 } from 'linkifyjs';
|
||
|
||
/**
|
||
* generated from https://raw.githubusercontent.com/w3c/html/26b5126f96f736f796b9e29718138919dd513744/entities.json
|
||
* do not edit
|
||
*/
|
||
var HTML5NamedCharRefs = {
|
||
// We don't need the complete named character reference because linkifyHtml
|
||
// does not modify the escape sequences. We do need so that
|
||
// whitespace is parsed properly. Other types of whitespace should already
|
||
// be accounted for. > < and " are also frequently relevant ones
|
||
amp: "&",
|
||
gt: ">",
|
||
lt: "<",
|
||
nbsp: " ",
|
||
quot: "\""
|
||
};
|
||
var HEXCHARCODE = /^#[xX]([A-Fa-f0-9]+)$/;
|
||
var CHARCODE = /^#([0-9]+)$/;
|
||
var NAMED = /^([A-Za-z0-9]+)$/;
|
||
var EntityParser = /** @class */function () {
|
||
function EntityParser(named) {
|
||
this.named = named;
|
||
}
|
||
EntityParser.prototype.parse = function (entity) {
|
||
if (!entity) {
|
||
return;
|
||
}
|
||
var matches = entity.match(HEXCHARCODE);
|
||
if (matches) {
|
||
return String.fromCharCode(parseInt(matches[1], 16));
|
||
}
|
||
matches = entity.match(CHARCODE);
|
||
if (matches) {
|
||
return String.fromCharCode(parseInt(matches[1], 10));
|
||
}
|
||
matches = entity.match(NAMED);
|
||
if (matches) {
|
||
return this.named[matches[1]] || "&" + matches[1] + ";";
|
||
}
|
||
};
|
||
return EntityParser;
|
||
}();
|
||
var WSP = /[\t\n\f ]/;
|
||
var ALPHA = /[A-Za-z]/;
|
||
var CRLF = /\r\n?/g;
|
||
function isSpace(char) {
|
||
return WSP.test(char);
|
||
}
|
||
function isAlpha(char) {
|
||
return ALPHA.test(char);
|
||
}
|
||
function preprocessInput(input) {
|
||
return input.replace(CRLF, '\n');
|
||
}
|
||
var EventedTokenizer = /** @class */function () {
|
||
function EventedTokenizer(delegate, entityParser, mode) {
|
||
if (mode === void 0) {
|
||
mode = 'precompile';
|
||
}
|
||
this.delegate = delegate;
|
||
this.entityParser = entityParser;
|
||
this.mode = mode;
|
||
this.state = "beforeData" /* beforeData */;
|
||
this.line = -1;
|
||
this.column = -1;
|
||
this.input = '';
|
||
this.index = -1;
|
||
this.tagNameBuffer = '';
|
||
this.states = {
|
||
beforeData: function () {
|
||
var char = this.peek();
|
||
if (char === '<' && !this.isIgnoredEndTag()) {
|
||
this.transitionTo("tagOpen" /* tagOpen */);
|
||
this.markTagStart();
|
||
this.consume();
|
||
} else {
|
||
if (this.mode === 'precompile' && char === '\n') {
|
||
var tag = this.tagNameBuffer.toLowerCase();
|
||
if (tag === 'pre' || tag === 'textarea') {
|
||
this.consume();
|
||
}
|
||
}
|
||
this.transitionTo("data" /* data */);
|
||
this.delegate.beginData();
|
||
}
|
||
},
|
||
data: function () {
|
||
var char = this.peek();
|
||
var tag = this.tagNameBuffer;
|
||
if (char === '<' && !this.isIgnoredEndTag()) {
|
||
this.delegate.finishData();
|
||
this.transitionTo("tagOpen" /* tagOpen */);
|
||
this.markTagStart();
|
||
this.consume();
|
||
} else if (char === '&' && tag !== 'script' && tag !== 'style') {
|
||
this.consume();
|
||
this.delegate.appendToData(this.consumeCharRef() || '&');
|
||
} else {
|
||
this.consume();
|
||
this.delegate.appendToData(char);
|
||
}
|
||
},
|
||
tagOpen: function () {
|
||
var char = this.consume();
|
||
if (char === '!') {
|
||
this.transitionTo("markupDeclarationOpen" /* markupDeclarationOpen */);
|
||
} else if (char === '/') {
|
||
this.transitionTo("endTagOpen" /* endTagOpen */);
|
||
} else if (char === '@' || char === ':' || isAlpha(char)) {
|
||
this.transitionTo("tagName" /* tagName */);
|
||
this.tagNameBuffer = '';
|
||
this.delegate.beginStartTag();
|
||
this.appendToTagName(char);
|
||
}
|
||
},
|
||
markupDeclarationOpen: function () {
|
||
var char = this.consume();
|
||
if (char === '-' && this.peek() === '-') {
|
||
this.consume();
|
||
this.transitionTo("commentStart" /* commentStart */);
|
||
this.delegate.beginComment();
|
||
} else {
|
||
var maybeDoctype = char.toUpperCase() + this.input.substring(this.index, this.index + 6).toUpperCase();
|
||
if (maybeDoctype === 'DOCTYPE') {
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.transitionTo("doctype" /* doctype */);
|
||
if (this.delegate.beginDoctype) this.delegate.beginDoctype();
|
||
}
|
||
}
|
||
},
|
||
doctype: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("beforeDoctypeName" /* beforeDoctypeName */);
|
||
}
|
||
},
|
||
beforeDoctypeName: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
return;
|
||
} else {
|
||
this.transitionTo("doctypeName" /* doctypeName */);
|
||
if (this.delegate.appendToDoctypeName) this.delegate.appendToDoctypeName(char.toLowerCase());
|
||
}
|
||
},
|
||
doctypeName: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("afterDoctypeName" /* afterDoctypeName */);
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
if (this.delegate.appendToDoctypeName) this.delegate.appendToDoctypeName(char.toLowerCase());
|
||
}
|
||
},
|
||
afterDoctypeName: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
return;
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
var nextSixChars = char.toUpperCase() + this.input.substring(this.index, this.index + 5).toUpperCase();
|
||
var isPublic = nextSixChars.toUpperCase() === 'PUBLIC';
|
||
var isSystem = nextSixChars.toUpperCase() === 'SYSTEM';
|
||
if (isPublic || isSystem) {
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
this.consume();
|
||
}
|
||
if (isPublic) {
|
||
this.transitionTo("afterDoctypePublicKeyword" /* afterDoctypePublicKeyword */);
|
||
} else if (isSystem) {
|
||
this.transitionTo("afterDoctypeSystemKeyword" /* afterDoctypeSystemKeyword */);
|
||
}
|
||
}
|
||
},
|
||
afterDoctypePublicKeyword: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("beforeDoctypePublicIdentifier" /* beforeDoctypePublicIdentifier */);
|
||
this.consume();
|
||
} else if (char === '"') {
|
||
this.transitionTo("doctypePublicIdentifierDoubleQuoted" /* doctypePublicIdentifierDoubleQuoted */);
|
||
this.consume();
|
||
} else if (char === "'") {
|
||
this.transitionTo("doctypePublicIdentifierSingleQuoted" /* doctypePublicIdentifierSingleQuoted */);
|
||
this.consume();
|
||
} else if (char === '>') {
|
||
this.consume();
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
}
|
||
},
|
||
doctypePublicIdentifierDoubleQuoted: function () {
|
||
var char = this.consume();
|
||
if (char === '"') {
|
||
this.transitionTo("afterDoctypePublicIdentifier" /* afterDoctypePublicIdentifier */);
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
if (this.delegate.appendToDoctypePublicIdentifier) this.delegate.appendToDoctypePublicIdentifier(char);
|
||
}
|
||
},
|
||
doctypePublicIdentifierSingleQuoted: function () {
|
||
var char = this.consume();
|
||
if (char === "'") {
|
||
this.transitionTo("afterDoctypePublicIdentifier" /* afterDoctypePublicIdentifier */);
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
if (this.delegate.appendToDoctypePublicIdentifier) this.delegate.appendToDoctypePublicIdentifier(char);
|
||
}
|
||
},
|
||
afterDoctypePublicIdentifier: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("betweenDoctypePublicAndSystemIdentifiers" /* betweenDoctypePublicAndSystemIdentifiers */);
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else if (char === '"') {
|
||
this.transitionTo("doctypeSystemIdentifierDoubleQuoted" /* doctypeSystemIdentifierDoubleQuoted */);
|
||
} else if (char === "'") {
|
||
this.transitionTo("doctypeSystemIdentifierSingleQuoted" /* doctypeSystemIdentifierSingleQuoted */);
|
||
}
|
||
},
|
||
betweenDoctypePublicAndSystemIdentifiers: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
return;
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else if (char === '"') {
|
||
this.transitionTo("doctypeSystemIdentifierDoubleQuoted" /* doctypeSystemIdentifierDoubleQuoted */);
|
||
} else if (char === "'") {
|
||
this.transitionTo("doctypeSystemIdentifierSingleQuoted" /* doctypeSystemIdentifierSingleQuoted */);
|
||
}
|
||
},
|
||
doctypeSystemIdentifierDoubleQuoted: function () {
|
||
var char = this.consume();
|
||
if (char === '"') {
|
||
this.transitionTo("afterDoctypeSystemIdentifier" /* afterDoctypeSystemIdentifier */);
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
if (this.delegate.appendToDoctypeSystemIdentifier) this.delegate.appendToDoctypeSystemIdentifier(char);
|
||
}
|
||
},
|
||
doctypeSystemIdentifierSingleQuoted: function () {
|
||
var char = this.consume();
|
||
if (char === "'") {
|
||
this.transitionTo("afterDoctypeSystemIdentifier" /* afterDoctypeSystemIdentifier */);
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
if (this.delegate.appendToDoctypeSystemIdentifier) this.delegate.appendToDoctypeSystemIdentifier(char);
|
||
}
|
||
},
|
||
afterDoctypeSystemIdentifier: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
return;
|
||
} else if (char === '>') {
|
||
if (this.delegate.endDoctype) this.delegate.endDoctype();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
}
|
||
},
|
||
commentStart: function () {
|
||
var char = this.consume();
|
||
if (char === '-' && this.peek() === '-') {
|
||
this.transitionTo("commentStartDash" /* commentStartDash */);
|
||
} else if (char === '>') {
|
||
this.delegate.finishComment();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.delegate.appendToCommentData(char);
|
||
this.transitionTo("comment" /* comment */);
|
||
}
|
||
},
|
||
commentStartDash: function () {
|
||
var char = this.consume();
|
||
if (char === '-') {
|
||
this.transitionTo("commentEnd" /* commentEnd */);
|
||
} else if (char === '>') {
|
||
this.delegate.finishComment();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.delegate.appendToCommentData('-');
|
||
this.transitionTo("comment" /* comment */);
|
||
}
|
||
},
|
||
comment: function () {
|
||
var char = this.consume();
|
||
if (char === '-') {
|
||
this.transitionTo("commentEndDash" /* commentEndDash */);
|
||
} else {
|
||
this.delegate.appendToCommentData(char);
|
||
}
|
||
},
|
||
commentEndDash: function () {
|
||
var char = this.consume();
|
||
if (char === '-' && this.peek() === '-') {
|
||
this.delegate.appendToCommentData(char);
|
||
} else if (char === '-') {
|
||
this.transitionTo("commentEnd" /* commentEnd */);
|
||
} else {
|
||
this.delegate.appendToCommentData('-' + char);
|
||
this.transitionTo("comment" /* comment */);
|
||
}
|
||
},
|
||
commentEnd: function () {
|
||
var char = this.consume();
|
||
if (char === '>') {
|
||
this.delegate.finishComment();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.delegate.appendToCommentData('--' + char);
|
||
this.transitionTo("comment" /* comment */);
|
||
}
|
||
},
|
||
tagName: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
|
||
} else if (char === '/') {
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
} else if (char === '>') {
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.appendToTagName(char);
|
||
}
|
||
},
|
||
endTagName: function () {
|
||
var char = this.consume();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
|
||
this.tagNameBuffer = '';
|
||
} else if (char === '/') {
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
this.tagNameBuffer = '';
|
||
} else if (char === '>') {
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
this.tagNameBuffer = '';
|
||
} else {
|
||
this.appendToTagName(char);
|
||
}
|
||
},
|
||
beforeAttributeName: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.consume();
|
||
return;
|
||
} else if (char === '/') {
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
this.consume();
|
||
} else if (char === '>') {
|
||
this.consume();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else if (char === '=') {
|
||
this.delegate.reportSyntaxError('attribute name cannot start with equals sign');
|
||
this.transitionTo("attributeName" /* attributeName */);
|
||
this.delegate.beginAttribute();
|
||
this.consume();
|
||
this.delegate.appendToAttributeName(char);
|
||
} else {
|
||
this.transitionTo("attributeName" /* attributeName */);
|
||
this.delegate.beginAttribute();
|
||
}
|
||
},
|
||
attributeName: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.transitionTo("afterAttributeName" /* afterAttributeName */);
|
||
this.consume();
|
||
} else if (char === '/') {
|
||
this.delegate.beginAttributeValue(false);
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
} else if (char === '=') {
|
||
this.transitionTo("beforeAttributeValue" /* beforeAttributeValue */);
|
||
this.consume();
|
||
} else if (char === '>') {
|
||
this.delegate.beginAttributeValue(false);
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else if (char === '"' || char === "'" || char === '<') {
|
||
this.delegate.reportSyntaxError(char + ' is not a valid character within attribute names');
|
||
this.consume();
|
||
this.delegate.appendToAttributeName(char);
|
||
} else {
|
||
this.consume();
|
||
this.delegate.appendToAttributeName(char);
|
||
}
|
||
},
|
||
afterAttributeName: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.consume();
|
||
return;
|
||
} else if (char === '/') {
|
||
this.delegate.beginAttributeValue(false);
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
} else if (char === '=') {
|
||
this.consume();
|
||
this.transitionTo("beforeAttributeValue" /* beforeAttributeValue */);
|
||
} else if (char === '>') {
|
||
this.delegate.beginAttributeValue(false);
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.delegate.beginAttributeValue(false);
|
||
this.delegate.finishAttributeValue();
|
||
this.transitionTo("attributeName" /* attributeName */);
|
||
this.delegate.beginAttribute();
|
||
this.consume();
|
||
this.delegate.appendToAttributeName(char);
|
||
}
|
||
},
|
||
beforeAttributeValue: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.consume();
|
||
} else if (char === '"') {
|
||
this.transitionTo("attributeValueDoubleQuoted" /* attributeValueDoubleQuoted */);
|
||
this.delegate.beginAttributeValue(true);
|
||
this.consume();
|
||
} else if (char === "'") {
|
||
this.transitionTo("attributeValueSingleQuoted" /* attributeValueSingleQuoted */);
|
||
this.delegate.beginAttributeValue(true);
|
||
this.consume();
|
||
} else if (char === '>') {
|
||
this.delegate.beginAttributeValue(false);
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.transitionTo("attributeValueUnquoted" /* attributeValueUnquoted */);
|
||
this.delegate.beginAttributeValue(false);
|
||
this.consume();
|
||
this.delegate.appendToAttributeValue(char);
|
||
}
|
||
},
|
||
attributeValueDoubleQuoted: function () {
|
||
var char = this.consume();
|
||
if (char === '"') {
|
||
this.delegate.finishAttributeValue();
|
||
this.transitionTo("afterAttributeValueQuoted" /* afterAttributeValueQuoted */);
|
||
} else if (char === '&') {
|
||
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
|
||
} else {
|
||
this.delegate.appendToAttributeValue(char);
|
||
}
|
||
},
|
||
attributeValueSingleQuoted: function () {
|
||
var char = this.consume();
|
||
if (char === "'") {
|
||
this.delegate.finishAttributeValue();
|
||
this.transitionTo("afterAttributeValueQuoted" /* afterAttributeValueQuoted */);
|
||
} else if (char === '&') {
|
||
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
|
||
} else {
|
||
this.delegate.appendToAttributeValue(char);
|
||
}
|
||
},
|
||
attributeValueUnquoted: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
|
||
} else if (char === '/') {
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
} else if (char === '&') {
|
||
this.consume();
|
||
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
|
||
} else if (char === '>') {
|
||
this.delegate.finishAttributeValue();
|
||
this.consume();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.consume();
|
||
this.delegate.appendToAttributeValue(char);
|
||
}
|
||
},
|
||
afterAttributeValueQuoted: function () {
|
||
var char = this.peek();
|
||
if (isSpace(char)) {
|
||
this.consume();
|
||
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
|
||
} else if (char === '/') {
|
||
this.consume();
|
||
this.transitionTo("selfClosingStartTag" /* selfClosingStartTag */);
|
||
} else if (char === '>') {
|
||
this.consume();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
|
||
}
|
||
},
|
||
selfClosingStartTag: function () {
|
||
var char = this.peek();
|
||
if (char === '>') {
|
||
this.consume();
|
||
this.delegate.markTagAsSelfClosing();
|
||
this.delegate.finishTag();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
} else {
|
||
this.transitionTo("beforeAttributeName" /* beforeAttributeName */);
|
||
}
|
||
},
|
||
endTagOpen: function () {
|
||
var char = this.consume();
|
||
if (char === '@' || char === ':' || isAlpha(char)) {
|
||
this.transitionTo("endTagName" /* endTagName */);
|
||
this.tagNameBuffer = '';
|
||
this.delegate.beginEndTag();
|
||
this.appendToTagName(char);
|
||
}
|
||
}
|
||
};
|
||
this.reset();
|
||
}
|
||
EventedTokenizer.prototype.reset = function () {
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
this.input = '';
|
||
this.tagNameBuffer = '';
|
||
this.index = 0;
|
||
this.line = 1;
|
||
this.column = 0;
|
||
this.delegate.reset();
|
||
};
|
||
EventedTokenizer.prototype.transitionTo = function (state) {
|
||
this.state = state;
|
||
};
|
||
EventedTokenizer.prototype.tokenize = function (input) {
|
||
this.reset();
|
||
this.tokenizePart(input);
|
||
this.tokenizeEOF();
|
||
};
|
||
EventedTokenizer.prototype.tokenizePart = function (input) {
|
||
this.input += preprocessInput(input);
|
||
while (this.index < this.input.length) {
|
||
var handler = this.states[this.state];
|
||
if (handler !== undefined) {
|
||
handler.call(this);
|
||
} else {
|
||
throw new Error("unhandled state " + this.state);
|
||
}
|
||
}
|
||
};
|
||
EventedTokenizer.prototype.tokenizeEOF = function () {
|
||
this.flushData();
|
||
};
|
||
EventedTokenizer.prototype.flushData = function () {
|
||
if (this.state === 'data') {
|
||
this.delegate.finishData();
|
||
this.transitionTo("beforeData" /* beforeData */);
|
||
}
|
||
};
|
||
EventedTokenizer.prototype.peek = function () {
|
||
return this.input.charAt(this.index);
|
||
};
|
||
EventedTokenizer.prototype.consume = function () {
|
||
var char = this.peek();
|
||
this.index++;
|
||
if (char === '\n') {
|
||
this.line++;
|
||
this.column = 0;
|
||
} else {
|
||
this.column++;
|
||
}
|
||
return char;
|
||
};
|
||
EventedTokenizer.prototype.consumeCharRef = function () {
|
||
var endIndex = this.input.indexOf(';', this.index);
|
||
if (endIndex === -1) {
|
||
return;
|
||
}
|
||
var entity = this.input.slice(this.index, endIndex);
|
||
var chars = this.entityParser.parse(entity);
|
||
if (chars) {
|
||
var count = entity.length;
|
||
// consume the entity chars
|
||
while (count) {
|
||
this.consume();
|
||
count--;
|
||
}
|
||
// consume the `;`
|
||
this.consume();
|
||
return chars;
|
||
}
|
||
};
|
||
EventedTokenizer.prototype.markTagStart = function () {
|
||
this.delegate.tagOpen();
|
||
};
|
||
EventedTokenizer.prototype.appendToTagName = function (char) {
|
||
this.tagNameBuffer += char;
|
||
this.delegate.appendToTagName(char);
|
||
};
|
||
EventedTokenizer.prototype.isIgnoredEndTag = function () {
|
||
var tag = this.tagNameBuffer;
|
||
return tag === 'title' && this.input.substring(this.index, this.index + 8) !== '</title>' || tag === 'style' && this.input.substring(this.index, this.index + 8) !== '</style>' || tag === 'script' && this.input.substring(this.index, this.index + 9) !== '</script>';
|
||
};
|
||
return EventedTokenizer;
|
||
}();
|
||
var Tokenizer = /** @class */function () {
|
||
function Tokenizer(entityParser, options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
this.options = options;
|
||
this.token = null;
|
||
this.startLine = 1;
|
||
this.startColumn = 0;
|
||
this.tokens = [];
|
||
this.tokenizer = new EventedTokenizer(this, entityParser, options.mode);
|
||
this._currentAttribute = undefined;
|
||
}
|
||
Tokenizer.prototype.tokenize = function (input) {
|
||
this.tokens = [];
|
||
this.tokenizer.tokenize(input);
|
||
return this.tokens;
|
||
};
|
||
Tokenizer.prototype.tokenizePart = function (input) {
|
||
this.tokens = [];
|
||
this.tokenizer.tokenizePart(input);
|
||
return this.tokens;
|
||
};
|
||
Tokenizer.prototype.tokenizeEOF = function () {
|
||
this.tokens = [];
|
||
this.tokenizer.tokenizeEOF();
|
||
return this.tokens[0];
|
||
};
|
||
Tokenizer.prototype.reset = function () {
|
||
this.token = null;
|
||
this.startLine = 1;
|
||
this.startColumn = 0;
|
||
};
|
||
Tokenizer.prototype.current = function () {
|
||
var token = this.token;
|
||
if (token === null) {
|
||
throw new Error('token was unexpectedly null');
|
||
}
|
||
if (arguments.length === 0) {
|
||
return token;
|
||
}
|
||
for (var i = 0; i < arguments.length; i++) {
|
||
if (token.type === arguments[i]) {
|
||
return token;
|
||
}
|
||
}
|
||
throw new Error("token type was unexpectedly " + token.type);
|
||
};
|
||
Tokenizer.prototype.push = function (token) {
|
||
this.token = token;
|
||
this.tokens.push(token);
|
||
};
|
||
Tokenizer.prototype.currentAttribute = function () {
|
||
return this._currentAttribute;
|
||
};
|
||
Tokenizer.prototype.addLocInfo = function () {
|
||
if (this.options.loc) {
|
||
this.current().loc = {
|
||
start: {
|
||
line: this.startLine,
|
||
column: this.startColumn
|
||
},
|
||
end: {
|
||
line: this.tokenizer.line,
|
||
column: this.tokenizer.column
|
||
}
|
||
};
|
||
}
|
||
this.startLine = this.tokenizer.line;
|
||
this.startColumn = this.tokenizer.column;
|
||
};
|
||
// Data
|
||
Tokenizer.prototype.beginDoctype = function () {
|
||
this.push({
|
||
type: "Doctype" /* Doctype */,
|
||
name: ''
|
||
});
|
||
};
|
||
Tokenizer.prototype.appendToDoctypeName = function (char) {
|
||
this.current("Doctype" /* Doctype */).name += char;
|
||
};
|
||
Tokenizer.prototype.appendToDoctypePublicIdentifier = function (char) {
|
||
var doctype = this.current("Doctype" /* Doctype */);
|
||
if (doctype.publicIdentifier === undefined) {
|
||
doctype.publicIdentifier = char;
|
||
} else {
|
||
doctype.publicIdentifier += char;
|
||
}
|
||
};
|
||
Tokenizer.prototype.appendToDoctypeSystemIdentifier = function (char) {
|
||
var doctype = this.current("Doctype" /* Doctype */);
|
||
if (doctype.systemIdentifier === undefined) {
|
||
doctype.systemIdentifier = char;
|
||
} else {
|
||
doctype.systemIdentifier += char;
|
||
}
|
||
};
|
||
Tokenizer.prototype.endDoctype = function () {
|
||
this.addLocInfo();
|
||
};
|
||
Tokenizer.prototype.beginData = function () {
|
||
this.push({
|
||
type: "Chars" /* Chars */,
|
||
chars: ''
|
||
});
|
||
};
|
||
Tokenizer.prototype.appendToData = function (char) {
|
||
this.current("Chars" /* Chars */).chars += char;
|
||
};
|
||
Tokenizer.prototype.finishData = function () {
|
||
this.addLocInfo();
|
||
};
|
||
// Comment
|
||
Tokenizer.prototype.beginComment = function () {
|
||
this.push({
|
||
type: "Comment" /* Comment */,
|
||
chars: ''
|
||
});
|
||
};
|
||
Tokenizer.prototype.appendToCommentData = function (char) {
|
||
this.current("Comment" /* Comment */).chars += char;
|
||
};
|
||
Tokenizer.prototype.finishComment = function () {
|
||
this.addLocInfo();
|
||
};
|
||
// Tags - basic
|
||
Tokenizer.prototype.tagOpen = function () {};
|
||
Tokenizer.prototype.beginStartTag = function () {
|
||
this.push({
|
||
type: "StartTag" /* StartTag */,
|
||
tagName: '',
|
||
attributes: [],
|
||
selfClosing: false
|
||
});
|
||
};
|
||
Tokenizer.prototype.beginEndTag = function () {
|
||
this.push({
|
||
type: "EndTag" /* EndTag */,
|
||
tagName: ''
|
||
});
|
||
};
|
||
Tokenizer.prototype.finishTag = function () {
|
||
this.addLocInfo();
|
||
};
|
||
Tokenizer.prototype.markTagAsSelfClosing = function () {
|
||
this.current("StartTag" /* StartTag */).selfClosing = true;
|
||
};
|
||
// Tags - name
|
||
Tokenizer.prototype.appendToTagName = function (char) {
|
||
this.current("StartTag" /* StartTag */, "EndTag" /* EndTag */).tagName += char;
|
||
};
|
||
// Tags - attributes
|
||
Tokenizer.prototype.beginAttribute = function () {
|
||
this._currentAttribute = ['', '', false];
|
||
};
|
||
Tokenizer.prototype.appendToAttributeName = function (char) {
|
||
this.currentAttribute()[0] += char;
|
||
};
|
||
Tokenizer.prototype.beginAttributeValue = function (isQuoted) {
|
||
this.currentAttribute()[2] = isQuoted;
|
||
};
|
||
Tokenizer.prototype.appendToAttributeValue = function (char) {
|
||
this.currentAttribute()[1] += char;
|
||
};
|
||
Tokenizer.prototype.finishAttributeValue = function () {
|
||
this.current("StartTag" /* StartTag */).attributes.push(this._currentAttribute);
|
||
};
|
||
Tokenizer.prototype.reportSyntaxError = function (message) {
|
||
this.current().syntaxError = message;
|
||
};
|
||
return Tokenizer;
|
||
}();
|
||
function tokenize(input, options) {
|
||
var tokenizer = new Tokenizer(new EntityParser(HTML5NamedCharRefs), options);
|
||
return tokenizer.tokenize(input);
|
||
}
|
||
|
||
const LinkifyResult = 'LinkifyResult';
|
||
const StartTag = 'StartTag';
|
||
const EndTag = 'EndTag';
|
||
const Chars = 'Chars';
|
||
const Comment = 'Comment';
|
||
const Doctype = 'Doctype';
|
||
|
||
/**
|
||
* @param {string} str html string to link
|
||
* @param {import('linkifyjs').Opts} [opts] linkify options
|
||
* @returns {string} resulting string
|
||
*/
|
||
function linkifyHtml(str, opts = {}) {
|
||
// `tokens` and `token` in this section refer to tokens generated by the
|
||
// HTML parser, not linkify's parser
|
||
const tokens = tokenize(str);
|
||
const linkifiedTokens = [];
|
||
const linkified = [];
|
||
const options = new Options(opts, defaultRender);
|
||
|
||
// Linkify the tokens given by the parser
|
||
for (let i = 0; i < tokens.length; i++) {
|
||
const token = tokens[i];
|
||
if (token.type === StartTag) {
|
||
linkifiedTokens.push(token);
|
||
|
||
// Ignore all the contents of ignored tags
|
||
const tagName = token.tagName.toUpperCase();
|
||
const isIgnored = tagName === 'A' || options.ignoreTags.indexOf(tagName) >= 0;
|
||
if (!isIgnored) {
|
||
continue;
|
||
}
|
||
let preskipLen = linkifiedTokens.length;
|
||
skipTagTokens(tagName, tokens, ++i, linkifiedTokens);
|
||
i += linkifiedTokens.length - preskipLen - 1;
|
||
} else if (token.type !== Chars) {
|
||
// Skip this token, it's not important
|
||
linkifiedTokens.push(token);
|
||
} else {
|
||
// Valid text token, linkify it!
|
||
const linkifedChars = linkifyChars(token.chars, options);
|
||
linkifiedTokens.push.apply(linkifiedTokens, linkifedChars);
|
||
}
|
||
}
|
||
|
||
// Convert the tokens back into a string
|
||
for (let i = 0; i < linkifiedTokens.length; i++) {
|
||
const token = linkifiedTokens[i];
|
||
switch (token.type) {
|
||
case LinkifyResult:
|
||
linkified.push(token.rendered);
|
||
break;
|
||
case StartTag:
|
||
{
|
||
let link = '<' + token.tagName;
|
||
if (token.attributes.length > 0) {
|
||
link += ' ' + attributeArrayToStrings(token.attributes).join(' ');
|
||
}
|
||
if (token.selfClosing) {
|
||
link += ' /';
|
||
}
|
||
link += '>';
|
||
linkified.push(link);
|
||
break;
|
||
}
|
||
case EndTag:
|
||
linkified.push(`</${token.tagName}>`);
|
||
break;
|
||
case Chars:
|
||
linkified.push(escapeText(token.chars));
|
||
break;
|
||
case Comment:
|
||
linkified.push(`<!--${escapeText(token.chars)}-->`);
|
||
break;
|
||
case Doctype:
|
||
{
|
||
let doctype = `<!DOCTYPE ${token.name}`;
|
||
if (token.publicIdentifier) {
|
||
doctype += ` PUBLIC "${token.publicIdentifier}"`;
|
||
}
|
||
if (token.systemIdentifier) {
|
||
doctype += ` "${token.systemIdentifier}"`;
|
||
}
|
||
doctype += '>';
|
||
linkified.push(doctype);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return linkified.join('');
|
||
}
|
||
|
||
/**
|
||
`tokens` and `token` in this section referes to tokens returned by
|
||
`linkify.tokenize`. `linkified` will contain HTML Parser-style tokens
|
||
@param {string}
|
||
@param {import('linkifyjs').Options}
|
||
*/
|
||
function linkifyChars(str, options) {
|
||
const tokens = tokenize$1(str);
|
||
const result = [];
|
||
for (let i = 0; i < tokens.length; i++) {
|
||
const token = tokens[i];
|
||
if (token.t === 'nl' && options.get('nl2br')) {
|
||
result.push({
|
||
type: StartTag,
|
||
tagName: 'br',
|
||
attributes: [],
|
||
selfClosing: true
|
||
});
|
||
} else if (!token.isLink || !options.check(token)) {
|
||
result.push({
|
||
type: Chars,
|
||
chars: token.toString()
|
||
});
|
||
} else {
|
||
result.push({
|
||
type: LinkifyResult,
|
||
rendered: options.render(token)
|
||
});
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
Returns a list of tokens skipped until the closing tag of tagName.
|
||
|
||
* `tagName` is the closing tag which will prompt us to stop skipping
|
||
* `tokens` is the array of tokens generated by HTML5Tokenizer which
|
||
* `i` is the index immediately after the opening tag to skip
|
||
* `skippedTokens` is an array which skipped tokens are being pushed into
|
||
|
||
Caveats
|
||
|
||
* Assumes that i is the first token after the given opening tagName
|
||
* The closing tag will be skipped, but nothing after it
|
||
* Will track whether there is a nested tag of the same type
|
||
*/
|
||
function skipTagTokens(tagName, tokens, i, skippedTokens) {
|
||
// number of tokens of this type on the [fictional] stack
|
||
let stackCount = 1;
|
||
while (i < tokens.length && stackCount > 0) {
|
||
let token = tokens[i];
|
||
if (token.type === StartTag && token.tagName.toUpperCase() === tagName) {
|
||
// Nested tag of the same type, "add to stack"
|
||
stackCount++;
|
||
} else if (token.type === EndTag && token.tagName.toUpperCase() === tagName) {
|
||
// Closing tag
|
||
stackCount--;
|
||
}
|
||
skippedTokens.push(token);
|
||
i++;
|
||
}
|
||
|
||
// Note that if stackCount > 0 here, the HTML is probably invalid
|
||
return skippedTokens;
|
||
}
|
||
function defaultRender({
|
||
tagName,
|
||
attributes,
|
||
content
|
||
}) {
|
||
return `<${tagName} ${attributesToString(attributes)}>${escapeText(content)}</${tagName}>`;
|
||
}
|
||
function escapeText(text) {
|
||
return text.replace(/</g, '<').replace(/>/g, '>');
|
||
}
|
||
function escapeAttr(attr) {
|
||
return attr.replace(/"/g, '"');
|
||
}
|
||
function attributesToString(attributes) {
|
||
const result = [];
|
||
for (const attr in attributes) {
|
||
const val = attributes[attr] + '';
|
||
result.push(`${attr}="${escapeAttr(val)}"`);
|
||
}
|
||
return result.join(' ');
|
||
}
|
||
function attributeArrayToStrings(attrs) {
|
||
const attrStrs = [];
|
||
for (let i = 0; i < attrs.length; i++) {
|
||
const name = attrs[i][0];
|
||
const value = attrs[i][1] + '';
|
||
attrStrs.push(`${name}="${escapeAttr(value)}"`);
|
||
}
|
||
return attrStrs;
|
||
}
|
||
|
||
export { linkifyHtml as default };
|