mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
Remove codes that require expensive polyfill (#1469)
* Revert "Cleanup domTree.js to re-use code (#1305)"
This reverts commit 9bb48b83f1
.
* Cleanup domTree.js to re-use code
* Extract common constructor/methods into functions
* Remove for...of
* Added eslint rule to disallow for...of
* Remove array destructuring
* Added eslint rule to disallow array destructuring
* Add eslint rule to disallow class inheritance
* Remove Object.keys and Object.assign
* Do not polyfill Object.freeze
* Babel: enable loose mode
* Undo disabling `linebreak-style`
* Move `children` initialization out of `initNode`
* Blacklist files for `no-restricted-syntax`
* Revert "Remove array destructuring"
This reverts commit c9d52c2db31c68cca77fea6ad774ee58b0632ff3.
This commit is contained in:
committed by
Kevin Barabash
parent
8eed4e2795
commit
c9947220b6
3
.babelrc
3
.babelrc
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"presets": [
|
||||
["es2015", {
|
||||
"modules": false
|
||||
"modules": false,
|
||||
"loose": true
|
||||
}],
|
||||
"flow"
|
||||
],
|
||||
|
@@ -70,6 +70,13 @@
|
||||
"valid-jsdoc": 0,
|
||||
"require-jsdoc": 0
|
||||
},
|
||||
"overrides": [{
|
||||
"files": ["katex.js", "src/**/*.js"],
|
||||
"excludedFiles": "unicodeMake.js",
|
||||
"rules": {
|
||||
"no-restricted-syntax": [2, "ForOfStatement", "ClassDeclaration[superClass]", "ClassExpression[superClass]"]
|
||||
}
|
||||
}],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true,
|
||||
|
@@ -46,11 +46,13 @@ export default class Namespace<Value> {
|
||||
"to pop global namespace; please report this as a bug");
|
||||
}
|
||||
const undefs = this.undefStack.pop();
|
||||
for (const undef of Object.getOwnPropertyNames(undefs)) {
|
||||
if (undefs[undef] === undefined) {
|
||||
delete this.current[undef];
|
||||
} else {
|
||||
this.current[undef] = undefs[undef];
|
||||
for (const undef in undefs) {
|
||||
if (undefs.hasOwnProperty(undef)) {
|
||||
if (undefs[undef] === undefined) {
|
||||
delete this.current[undef];
|
||||
} else {
|
||||
this.current[undef] = undefs[undef];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,8 @@ export default class SourceLocation {
|
||||
this.lexer = lexer;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
Object.freeze(this); // Immutable to allow sharing in range().
|
||||
// $FlowFixMe, do not polyfill
|
||||
Object["freeze"](this); // Immutable to allow sharing in range().
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,4 +41,3 @@ export default class SourceLocation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -315,7 +315,8 @@ const sizeElementFromChildren = function(
|
||||
let depth = 0;
|
||||
let maxFontSize = 0;
|
||||
|
||||
for (const child of elem.children) {
|
||||
for (let i = 0; i < elem.children.length; i++) {
|
||||
const child = elem.children[i];
|
||||
if (child.height > height) {
|
||||
height = child.height;
|
||||
}
|
||||
@@ -490,7 +491,8 @@ const getVListChildrenAndDepth = function(params: VListParam): {
|
||||
// We always start at the bottom, so calculate the bottom by adding up
|
||||
// all the sizes
|
||||
let bottom = params.positionData;
|
||||
for (const child of params.children) {
|
||||
for (let i = 0; i < params.children.length; i++) {
|
||||
const child = params.children[i];
|
||||
bottom -= child.type === "kern"
|
||||
? child.size
|
||||
: child.elem.height + child.elem.depth;
|
||||
@@ -531,7 +533,8 @@ const makeVList = function(params: VListParam, options: Options): DomSpan {
|
||||
// be positioned precisely without worrying about font ascent and
|
||||
// line-height.
|
||||
let pstrutSize = 0;
|
||||
for (const child of children) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (child.type === "elem") {
|
||||
const elem = child.elem;
|
||||
pstrutSize = Math.max(pstrutSize, elem.maxFontSize, elem.height);
|
||||
@@ -546,7 +549,8 @@ const makeVList = function(params: VListParam, options: Options): DomSpan {
|
||||
let minPos = depth;
|
||||
let maxPos = depth;
|
||||
let currPos = depth;
|
||||
for (const child of children) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (child.type === "kern") {
|
||||
currPos += child.size;
|
||||
} else {
|
||||
|
279
src/domTree.js
279
src/domTree.js
@@ -7,6 +7,9 @@
|
||||
* work with the DOM.
|
||||
*
|
||||
* Similar functions for working with MathML nodes exist in mathMLTree.js.
|
||||
*
|
||||
* TODO: refactor `span` and `anchor` into common superclass when
|
||||
* target environments support class inheritance
|
||||
*/
|
||||
import {scriptFromCodepoint} from "./unicodeScripts";
|
||||
import utils from "./utils";
|
||||
@@ -25,6 +28,103 @@ const createClass = function(classes: string[]): string {
|
||||
return classes.filter(cls => cls).join(" ");
|
||||
};
|
||||
|
||||
const initNode = function(
|
||||
classes?: string[],
|
||||
options?: Options,
|
||||
style?: CssStyle,
|
||||
) {
|
||||
this.classes = classes || [];
|
||||
this.attributes = {};
|
||||
this.height = 0;
|
||||
this.depth = 0;
|
||||
this.maxFontSize = 0;
|
||||
this.style = style || {};
|
||||
if (options) {
|
||||
if (options.style.isTight()) {
|
||||
this.classes.push("mtight");
|
||||
}
|
||||
const color = options.getColor();
|
||||
if (color) {
|
||||
this.style.color = color;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert into an HTML node
|
||||
*/
|
||||
const toNode = function(tagName: string): HTMLElement {
|
||||
const node = document.createElement(tagName);
|
||||
|
||||
// Apply the class
|
||||
node.className = createClass(this.classes);
|
||||
|
||||
// Apply inline styles
|
||||
for (const style in this.style) {
|
||||
if (this.style.hasOwnProperty(style)) {
|
||||
// $FlowFixMe Flow doesn't seem to understand span.style's type.
|
||||
node.style[style] = this.style[style];
|
||||
}
|
||||
}
|
||||
|
||||
// Apply attributes
|
||||
for (const attr in this.attributes) {
|
||||
if (this.attributes.hasOwnProperty(attr)) {
|
||||
node.setAttribute(attr, this.attributes[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
// Append the children, also as HTML nodes
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
node.appendChild(this.children[i].toNode());
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert into an HTML markup string
|
||||
*/
|
||||
const toMarkup = function(tagName: string): string {
|
||||
let markup = `<${tagName}`;
|
||||
|
||||
// Add the class
|
||||
if (this.classes.length) {
|
||||
markup += ` class="${utils.escape(createClass(this.classes))}"`;
|
||||
}
|
||||
|
||||
let styles = "";
|
||||
|
||||
// Add the styles, after hyphenation
|
||||
for (const style in this.style) {
|
||||
if (this.style.hasOwnProperty(style)) {
|
||||
styles += `${utils.hyphenate(style)}:${this.style[style]};`;
|
||||
}
|
||||
}
|
||||
|
||||
if (styles) {
|
||||
markup += ` style="${utils.escape(styles)}"`;
|
||||
}
|
||||
|
||||
// Add the attributes
|
||||
for (const attr in this.attributes) {
|
||||
if (this.attributes.hasOwnProperty(attr)) {
|
||||
markup += ` ${attr}="${utils.escape(this.attributes[attr])}"`;
|
||||
}
|
||||
}
|
||||
|
||||
markup += ">";
|
||||
|
||||
// Add the markup of the children, also as markup
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
markup += this.children[i].toMarkup();
|
||||
}
|
||||
|
||||
markup += `</${tagName}>`;
|
||||
|
||||
return markup;
|
||||
};
|
||||
|
||||
export type CssStyle = {[name: string]: string};
|
||||
|
||||
export interface HtmlDomNode extends VirtualNode {
|
||||
@@ -47,8 +147,16 @@ export type SvgChildNode = pathNode | lineNode;
|
||||
export type documentFragment = tree.documentFragment<HtmlDomNode>;
|
||||
|
||||
|
||||
export class HtmlDomContainer<ChildType: VirtualNode>
|
||||
implements HtmlDomNode {
|
||||
/**
|
||||
* This node represents a span node, with a className, a list of children, and
|
||||
* an inline style. It also contains information about its height, depth, and
|
||||
* maxFontSize.
|
||||
*
|
||||
* Represents two types with different uses: SvgSpan to wrap an SVG and DomSpan
|
||||
* otherwise. This typesafety is important when HTML builders access a span's
|
||||
* children.
|
||||
*/
|
||||
class span<ChildType: VirtualNode> implements HtmlDomNode {
|
||||
children: ChildType[];
|
||||
attributes: {[string]: string};
|
||||
classes: string[];
|
||||
@@ -64,26 +172,12 @@ export class HtmlDomContainer<ChildType: VirtualNode>
|
||||
options?: Options,
|
||||
style?: CssStyle,
|
||||
) {
|
||||
this.classes = classes || [];
|
||||
initNode.call(this, classes, options, style);
|
||||
this.children = children || [];
|
||||
this.attributes = {};
|
||||
this.height = 0;
|
||||
this.depth = 0;
|
||||
this.maxFontSize = 0;
|
||||
this.style = Object.assign({}, style);
|
||||
if (options) {
|
||||
if (options.style.isTight()) {
|
||||
this.classes.push("mtight");
|
||||
}
|
||||
const color = options.getColor();
|
||||
if (color) {
|
||||
this.style.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an arbitrary attribute on the node. Warning: use this wisely. Not
|
||||
* Sets an arbitrary attribute on the span. Warning: use this wisely. Not
|
||||
* all browsers support attributes the same, and having too many custom
|
||||
* attributes is probably bad.
|
||||
*/
|
||||
@@ -104,119 +198,27 @@ export class HtmlDomContainer<ChildType: VirtualNode>
|
||||
return false;
|
||||
}
|
||||
|
||||
tagName(): string {
|
||||
throw new Error("use of generic HtmlDomContainer tagName");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert into an HTML node
|
||||
*/
|
||||
toNode(): HTMLElement {
|
||||
const node = document.createElement(this.tagName());
|
||||
|
||||
// Apply the class
|
||||
node.className = createClass(this.classes);
|
||||
|
||||
// Apply inline styles
|
||||
for (const style in this.style) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.style, style)) {
|
||||
// $FlowFixMe Flow doesn't seem to understand node.style's type.
|
||||
node.style[style] = this.style[style];
|
||||
}
|
||||
}
|
||||
|
||||
// Apply attributes
|
||||
for (const attr in this.attributes) {
|
||||
if (this.attributes.hasOwnProperty(attr)) {
|
||||
node.setAttribute(attr, this.attributes[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
// Append the children, also as HTML nodes
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
node.appendChild(this.children[i].toNode());
|
||||
}
|
||||
|
||||
return node;
|
||||
return toNode.call(this, "span");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert into an HTML markup string
|
||||
*/
|
||||
toMarkup(): string {
|
||||
let markup = "<" + this.tagName();
|
||||
|
||||
// Add the class
|
||||
if (this.classes.length) {
|
||||
markup += ` class="${utils.escape(createClass(this.classes))}"`;
|
||||
}
|
||||
|
||||
let styles = "";
|
||||
|
||||
// Add the styles, after hyphenation
|
||||
for (const style in this.style) {
|
||||
if (this.style.hasOwnProperty(style)) {
|
||||
styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
|
||||
}
|
||||
}
|
||||
|
||||
if (styles) {
|
||||
markup += ` style="${utils.escape(styles)}"`;
|
||||
}
|
||||
|
||||
// Add the attributes
|
||||
for (const attr in this.attributes) {
|
||||
if (this.attributes.hasOwnProperty(attr)) {
|
||||
markup += " " + attr + "=\"";
|
||||
markup += utils.escape(this.attributes[attr]);
|
||||
markup += "\"";
|
||||
}
|
||||
}
|
||||
|
||||
markup += ">";
|
||||
|
||||
// Add the markup of the children, also as markup
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
markup += this.children[i].toMarkup();
|
||||
}
|
||||
|
||||
markup += `</${this.tagName()}>`;
|
||||
|
||||
return markup;
|
||||
return toMarkup.call(this, "span");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This node represents a span node, with a className, a list of children, and
|
||||
* an inline style. It also contains information about its height, depth, and
|
||||
* maxFontSize.
|
||||
*
|
||||
* Represents two types with different uses: SvgSpan to wrap an SVG and DomSpan
|
||||
* otherwise. This typesafety is important when HTML builders access a span's
|
||||
* children.
|
||||
* This node represents an anchor (<a>) element with a hyperlink. See `span`
|
||||
* for further details.
|
||||
*/
|
||||
class span<ChildType: VirtualNode> extends HtmlDomContainer<ChildType> {
|
||||
constructor(
|
||||
classes?: string[],
|
||||
children?: ChildType[],
|
||||
options?: Options,
|
||||
style?: CssStyle,
|
||||
) {
|
||||
super(classes, children, options, style);
|
||||
}
|
||||
|
||||
tagName() {
|
||||
return "span";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This node represents an anchor (<a>) element with a hyperlink, a list of classes,
|
||||
* a list of children, and an inline style. It also contains information about its
|
||||
* height, depth, and maxFontSize.
|
||||
*/
|
||||
class anchor extends HtmlDomContainer<HtmlDomNode> {
|
||||
href: string;
|
||||
class anchor implements HtmlDomNode {
|
||||
children: HtmlDomNode[];
|
||||
attributes: {[string]: string};
|
||||
classes: string[];
|
||||
height: number;
|
||||
depth: number;
|
||||
maxFontSize: number;
|
||||
style: CssStyle;
|
||||
|
||||
constructor(
|
||||
href: string,
|
||||
@@ -224,12 +226,29 @@ class anchor extends HtmlDomContainer<HtmlDomNode> {
|
||||
children: HtmlDomNode[],
|
||||
options: Options,
|
||||
) {
|
||||
super(classes, children, options);
|
||||
initNode.call(this, classes, options);
|
||||
this.children = children || [];
|
||||
this.setAttribute('href', href);
|
||||
}
|
||||
|
||||
tagName() {
|
||||
return "a";
|
||||
setAttribute(attribute: string, value: string) {
|
||||
this.attributes[attribute] = value;
|
||||
}
|
||||
|
||||
hasClass(className: string): boolean {
|
||||
return utils.contains(this.classes, className);
|
||||
}
|
||||
|
||||
tryCombine(sibling: HtmlDomNode): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
toNode(): HTMLElement {
|
||||
return toNode.call(this, "a");
|
||||
}
|
||||
|
||||
toMarkup(): string {
|
||||
return toMarkup.call(this, "a");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +293,7 @@ class symbolNode implements HtmlDomNode {
|
||||
this.skew = skew || 0;
|
||||
this.width = width || 0;
|
||||
this.classes = classes || [];
|
||||
this.style = Object.assign({}, style);
|
||||
this.style = style || {};
|
||||
this.maxFontSize = 0;
|
||||
|
||||
// Mark text from non-Latin scripts with specific classes so that we
|
||||
@@ -534,13 +553,13 @@ export function assertSymbolDomNode(
|
||||
}
|
||||
}
|
||||
|
||||
export function assertDomContainer(
|
||||
export function assertSpan(
|
||||
group: HtmlDomNode,
|
||||
): HtmlDomContainer<HtmlDomNode> {
|
||||
if (group instanceof HtmlDomContainer) {
|
||||
): span<HtmlDomNode> {
|
||||
if (group instanceof span) {
|
||||
return group;
|
||||
} else {
|
||||
throw new Error(`Expected HtmlDomContainer but got ${String(group)}.`);
|
||||
throw new Error(`Expected span<HtmlDomNode> but got ${String(group)}.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import mathMLTree from "../mathMLTree";
|
||||
import utils from "../utils";
|
||||
import stretchy from "../stretchy";
|
||||
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
|
||||
import {assertDomContainer, assertSymbolDomNode} from "../domTree";
|
||||
import {assertSpan, assertSymbolDomNode} from "../domTree";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
@@ -40,7 +40,7 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
|
||||
|
||||
// Rerender the supsub group with its new base, and store that
|
||||
// result.
|
||||
supSubGroup = assertDomContainer(html.buildGroup(supSub, options));
|
||||
supSubGroup = assertSpan(html.buildGroup(supSub, options));
|
||||
|
||||
// reset original base
|
||||
supSub.value.base = group;
|
||||
|
@@ -86,18 +86,10 @@ defineFunction({
|
||||
},
|
||||
});
|
||||
|
||||
const oldFontFuncsMap = {
|
||||
"\\rm": "mathrm",
|
||||
"\\sf": "mathsf",
|
||||
"\\tt": "mathtt",
|
||||
"\\bf": "mathbf",
|
||||
"\\it": "mathit",
|
||||
};
|
||||
|
||||
// Old font changing functions
|
||||
defineFunction({
|
||||
type: "font",
|
||||
names: Object.keys(oldFontFuncsMap),
|
||||
names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it"],
|
||||
props: {
|
||||
numArgs: 0,
|
||||
allowedInText: true,
|
||||
@@ -106,7 +98,7 @@ defineFunction({
|
||||
const {mode} = parser;
|
||||
parser.consumeSpaces();
|
||||
const body = parser.parseExpression(true, breakOnTokenText);
|
||||
const style = oldFontFuncsMap[funcName];
|
||||
const style = `math${funcName.slice(1)}`;
|
||||
|
||||
return new ParseNode("font", {
|
||||
type: "font",
|
||||
|
@@ -39,7 +39,8 @@ defineFunction({
|
||||
const expression = html.buildExpression(
|
||||
groupValue, options.withFont("mathrm"), true);
|
||||
|
||||
for (const child of expression) {
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
const child = expression[i];
|
||||
if (child instanceof domTree.symbolNode) {
|
||||
// Per amsopn package,
|
||||
// change minus to hyphen and \ast to asterisk
|
||||
@@ -60,7 +61,8 @@ defineFunction({
|
||||
|
||||
// Is expression a string or has it something like a fraction?
|
||||
let isAllString = true; // default
|
||||
for (const node of expression) {
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
const node = expression[i];
|
||||
if (node instanceof mathMLTree.SpaceNode) {
|
||||
// Do nothing
|
||||
} else if (node instanceof mathMLTree.MathNode) {
|
||||
|
@@ -80,8 +80,8 @@ export class MathNode implements MathDomNode {
|
||||
}
|
||||
}
|
||||
|
||||
for (const child of this.children) {
|
||||
node.appendChild(child.toNode());
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
node.appendChild(this.children[i].toNode());
|
||||
}
|
||||
|
||||
return node;
|
||||
|
@@ -84,8 +84,10 @@ const scriptData: Array<Script> = [
|
||||
* it is from, or null if it is not part of a known block
|
||||
*/
|
||||
export function scriptFromCodepoint(codepoint: number): ?string {
|
||||
for (const script of scriptData) {
|
||||
for (const block of script.blocks) {
|
||||
for (let i = 0; i < scriptData.length; i++) {
|
||||
const script = scriptData[i];
|
||||
for (let i = 0; i < script.blocks.length; i++) {
|
||||
const block = script.blocks[i];
|
||||
if (codepoint >= block[0] && codepoint <= block[1]) {
|
||||
return script.name;
|
||||
}
|
||||
|
Reference in New Issue
Block a user