yoastseo
Version:
Yoast client-side content analysis
152 lines (144 loc) • 5.77 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.inlineElements = exports.getBlocks = exports.default = exports.blockElements = void 0;
exports.isBlockElement = isBlockElement;
exports.isInlineElement = isInlineElement;
var _lodash = require("lodash");
var _core = _interopRequireDefault(require("tokenizer2/core"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const blockElements = exports.blockElements = ["address", "article", "aside", "blockquote", "canvas", "dd", "div", "dl", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "nav", "noscript", "ol", "output", "p", "pre", "section", "table", "tfoot", "ul", "video"];
const inlineElements = exports.inlineElements = ["b", "big", "i", "small", "tt", "abbr", "acronym", "cite", "code", "dfn", "em", "kbd", "strong", "samp", "time", "var", "a", "bdo", "br", "img", "map", "object", "q", "script", "span", "sub", "sup", "button", "input", "label", "select", "textarea"];
const blockElementsRegex = new RegExp("^(" + blockElements.join("|") + ")$", "i");
const inlineElementsRegex = new RegExp("^(" + inlineElements.join("|") + ")$", "i");
const blockElementStartRegex = new RegExp("^<(" + blockElements.join("|") + ")[^>]*?>$", "i");
const blockElementEndRegex = new RegExp("^</(" + blockElements.join("|") + ")[^>]*?>$", "i");
const inlineElementStartRegex = new RegExp("^<(" + inlineElements.join("|") + ")[^>]*>$", "i");
const inlineElementEndRegex = new RegExp("^</(" + inlineElements.join("|") + ")[^>]*>$", "i");
const otherElementStartRegex = /^<([^>\s/]+)[^>]*>$/;
const otherElementEndRegex = /^<\/([^>\s]+)[^>]*>$/;
const contentRegex = /^[^<]+$/;
const greaterThanContentRegex = /^<[^><]*$/;
const commentRegex = /<!--(.|[\r\n])*?-->/g;
let tokens = [];
let htmlBlockTokenizer;
/**
* Creates a tokenizer to tokenize HTML into blocks.
*
* @returns {void}
*/
function createTokenizer() {
tokens = [];
htmlBlockTokenizer = (0, _core.default)(function (token) {
tokens.push(token);
});
htmlBlockTokenizer.addRule(contentRegex, "content");
htmlBlockTokenizer.addRule(greaterThanContentRegex, "greater-than-sign-content");
htmlBlockTokenizer.addRule(blockElementStartRegex, "block-start");
htmlBlockTokenizer.addRule(blockElementEndRegex, "block-end");
htmlBlockTokenizer.addRule(inlineElementStartRegex, "inline-start");
htmlBlockTokenizer.addRule(inlineElementEndRegex, "inline-end");
htmlBlockTokenizer.addRule(otherElementStartRegex, "other-element-start");
htmlBlockTokenizer.addRule(otherElementEndRegex, "other-element-end");
}
/**
* Returns whether or not the given element name is a block element.
*
* @param {string} htmlElementName The name of the HTML element.
* @returns {boolean} Whether or not it is a block element.
*/
function isBlockElement(htmlElementName) {
return blockElementsRegex.test(htmlElementName);
}
/**
* Returns whether or not the given element name is an inline element.
*
* @param {string} htmlElementName The name of the HTML element.
* @returns {boolean} Whether or not it is an inline element.
*/
function isInlineElement(htmlElementName) {
return inlineElementsRegex.test(htmlElementName);
}
/**
* Splits a text into blocks based on HTML block elements.
*
* @param {string} text The text to split.
* @returns {Array} A list of blocks based on HTML block elements.
*/
function getBlocks(text) {
const blocks = [];
let depth = 0,
blockStartTag = "",
currentBlock = "",
blockEndTag = "";
// Remove all comments because it is very hard to tokenize them.
text = text.replace(commentRegex, "");
createTokenizer();
htmlBlockTokenizer.onText(text);
htmlBlockTokenizer.end();
(0, _lodash.forEach)(tokens, function (token, i) {
const nextToken = tokens[i + 1];
switch (token.type) {
case "content":
case "greater-than-sign-content":
case "inline-start":
case "inline-end":
case "other-tag":
case "other-element-start":
case "other-element-end":
case "greater than sign":
if (!nextToken || depth === 0 && (nextToken.type === "block-start" || nextToken.type === "block-end")) {
currentBlock += token.src;
blocks.push(currentBlock);
blockStartTag = "";
currentBlock = "";
blockEndTag = "";
} else {
currentBlock += token.src;
}
break;
case "block-start":
if (depth !== 0) {
if (currentBlock.trim() !== "") {
blocks.push(currentBlock);
}
currentBlock = "";
blockEndTag = "";
}
depth++;
blockStartTag = token.src;
break;
case "block-end":
depth--;
blockEndTag = token.src;
/*
* We try to match the most deep blocks so discard any other blocks that have been started but not
* finished.
*/
if ("" !== blockStartTag && "" !== blockEndTag) {
blocks.push(blockStartTag + currentBlock + blockEndTag);
} else if ("" !== currentBlock.trim()) {
blocks.push(currentBlock);
}
blockStartTag = "";
currentBlock = "";
blockEndTag = "";
break;
}
// Handles HTML with too many closing tags.
if (depth < 0) {
depth = 0;
}
});
return blocks;
}
const memoizedGetBlocks = exports.getBlocks = (0, _lodash.memoize)(getBlocks);
var _default = exports.default = {
blockElements: blockElements,
inlineElements: inlineElements,
isBlockElement: isBlockElement,
isInlineElement: isInlineElement,
getBlocks: memoizedGetBlocks
};
//# sourceMappingURL=html.js.map