mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-06 19:58:40 +00:00
Refactor documentFragment and implement both HtmlDomNode and MathDomNode interfaces (stepping stone to port buildMathML to flow) (#1478)
* Make MathNodeClass include documentFragment for ergonomics. * Separate out the HTML and MathML documentFragments. These two documentFragments have different additional properties/methodsi and limitations. This separation is needed for porting buildMathML to flow. * Coalesce the documentFragment subclasses to avoid subclassing polyfill. * Make DomSpan and SvgSpan type aliases again instead of subclasses. * Remove type MathNodeClass in favor of MathDomNode. * Resolve $FlowFixMes by reordering variants of a union type.
This commit is contained in:
@@ -11,9 +11,11 @@ import symbols, {ligatures} from "./symbols";
|
|||||||
import utils from "./utils";
|
import utils from "./utils";
|
||||||
import {wideCharacterFont} from "./wide-character";
|
import {wideCharacterFont} from "./wide-character";
|
||||||
import {calculateSize} from "./units";
|
import {calculateSize} from "./units";
|
||||||
|
import * as tree from "./tree";
|
||||||
|
|
||||||
import type Options from "./Options";
|
import type Options from "./Options";
|
||||||
import type ParseNode from "./ParseNode";
|
import type ParseNode from "./ParseNode";
|
||||||
|
import type {documentFragment as HtmlDocumentFragment} from "./domTree";
|
||||||
import type {NodeType} from "./ParseNode";
|
import type {NodeType} from "./ParseNode";
|
||||||
import type {CharacterMetrics} from "./fontMetrics";
|
import type {CharacterMetrics} from "./fontMetrics";
|
||||||
import type {Mode} from "./types";
|
import type {Mode} from "./types";
|
||||||
@@ -233,7 +235,7 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
|
|||||||
group: ParseNode<NODETYPE>,
|
group: ParseNode<NODETYPE>,
|
||||||
options: Options,
|
options: Options,
|
||||||
type: "mathord" | "textord",
|
type: "mathord" | "textord",
|
||||||
): domTree.symbolNode | domTree.documentFragment {
|
): HtmlDocumentFragment | domTree.symbolNode {
|
||||||
const mode = group.mode;
|
const mode = group.mode;
|
||||||
const value = group.value;
|
const value = group.value;
|
||||||
|
|
||||||
@@ -307,7 +309,7 @@ const tryCombineChars = function(chars: HtmlDomNode[]): HtmlDomNode[] {
|
|||||||
* children.
|
* children.
|
||||||
*/
|
*/
|
||||||
const sizeElementFromChildren = function(
|
const sizeElementFromChildren = function(
|
||||||
elem: DomSpan | domTree.anchor | domTree.documentFragment,
|
elem: DomSpan | domTree.anchor | HtmlDocumentFragment,
|
||||||
) {
|
) {
|
||||||
let height = 0;
|
let height = 0;
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
@@ -394,8 +396,8 @@ const makeAnchor = function(
|
|||||||
*/
|
*/
|
||||||
const makeFragment = function(
|
const makeFragment = function(
|
||||||
children: HtmlDomNode[],
|
children: HtmlDomNode[],
|
||||||
): domTree.documentFragment {
|
): HtmlDocumentFragment {
|
||||||
const fragment = new domTree.documentFragment(children);
|
const fragment = new tree.documentFragment(children);
|
||||||
|
|
||||||
sizeElementFromChildren(fragment);
|
sizeElementFromChildren(fragment);
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ import utils, {assert} from "./utils";
|
|||||||
import {checkNodeType} from "./ParseNode";
|
import {checkNodeType} from "./ParseNode";
|
||||||
import {spacings, tightSpacings} from "./spacingData";
|
import {spacings, tightSpacings} from "./spacingData";
|
||||||
import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
|
import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
|
||||||
|
import * as tree from "./tree";
|
||||||
|
|
||||||
import type Options from "./Options";
|
import type Options from "./Options";
|
||||||
import type {AnyParseNode} from "./ParseNode";
|
import type {AnyParseNode} from "./ParseNode";
|
||||||
@@ -90,7 +91,7 @@ export const buildExpression = function(
|
|||||||
const rawGroups: HtmlDomNode[] = [];
|
const rawGroups: HtmlDomNode[] = [];
|
||||||
for (let i = 0; i < expression.length; i++) {
|
for (let i = 0; i < expression.length; i++) {
|
||||||
const output = buildGroup(expression[i], options);
|
const output = buildGroup(expression[i], options);
|
||||||
if (output instanceof domTree.documentFragment) {
|
if (output instanceof tree.documentFragment) {
|
||||||
const children: HtmlDomNode[] = output.children;
|
const children: HtmlDomNode[] = output.children;
|
||||||
rawGroups.push(...children);
|
rawGroups.push(...children);
|
||||||
} else {
|
} else {
|
||||||
@@ -203,7 +204,7 @@ const getOutermostNode = function(
|
|||||||
node: HtmlDomNode,
|
node: HtmlDomNode,
|
||||||
side: Side,
|
side: Side,
|
||||||
): HtmlDomNode {
|
): HtmlDomNode {
|
||||||
if (node instanceof domTree.documentFragment ||
|
if (node instanceof tree.documentFragment ||
|
||||||
node instanceof domTree.anchor) {
|
node instanceof domTree.anchor) {
|
||||||
const children = node.children;
|
const children = node.children;
|
||||||
if (children.length) {
|
if (children.length) {
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import {checkNodeType} from "./ParseNode";
|
import {checkNodeType} from "./ParseNode";
|
||||||
import domTree from "./domTree";
|
|
||||||
|
|
||||||
import type Parser from "./Parser";
|
import type Parser from "./Parser";
|
||||||
import type ParseNode, {AnyParseNode, NodeType} from "./ParseNode";
|
import type ParseNode, {AnyParseNode, NodeType} from "./ParseNode";
|
||||||
@@ -8,7 +7,7 @@ import type Options from "./Options";
|
|||||||
import type {ArgType, BreakToken, Mode} from "./types";
|
import type {ArgType, BreakToken, Mode} from "./types";
|
||||||
import type {HtmlDomNode} from "./domTree";
|
import type {HtmlDomNode} from "./domTree";
|
||||||
import type {Token} from "./Token";
|
import type {Token} from "./Token";
|
||||||
import type {MathNodeClass} from "./mathMLTree";
|
import type {MathDomNode} from "./mathMLTree";
|
||||||
|
|
||||||
/** Context provided to function handlers for error messages. */
|
/** Context provided to function handlers for error messages. */
|
||||||
export type FunctionContext = {|
|
export type FunctionContext = {|
|
||||||
@@ -28,7 +27,7 @@ export type HtmlBuilder<NODETYPE> = (ParseNode<NODETYPE>, Options) => HtmlDomNod
|
|||||||
export type MathMLBuilder<NODETYPE> = (
|
export type MathMLBuilder<NODETYPE> = (
|
||||||
group: ParseNode<NODETYPE>,
|
group: ParseNode<NODETYPE>,
|
||||||
options: Options,
|
options: Options,
|
||||||
) => MathNodeClass | domTree.documentFragment;
|
) => MathDomNode;
|
||||||
|
|
||||||
// More general version of `HtmlBuilder` for nodes (e.g. \sum, accent types)
|
// More general version of `HtmlBuilder` for nodes (e.g. \sum, accent types)
|
||||||
// whose presence impacts super/subscripting. In this case, ParseNode<"supsub">
|
// whose presence impacts super/subscripting. In this case, ParseNode<"supsub">
|
||||||
@@ -119,8 +118,6 @@ type FunctionDefSpec<NODETYPE: NodeType> = {|
|
|||||||
// This should not modify the `ParseNode`.
|
// This should not modify the `ParseNode`.
|
||||||
htmlBuilder?: HtmlBuilder<NODETYPE>,
|
htmlBuilder?: HtmlBuilder<NODETYPE>,
|
||||||
|
|
||||||
// TODO: Currently functions/op.js returns documentFragment. Refactor it
|
|
||||||
// and update the return type of this function.
|
|
||||||
// This function returns an object representing the MathML structure to be
|
// This function returns an object representing the MathML structure to be
|
||||||
// created when rendering the defined LaTeX function.
|
// created when rendering the defined LaTeX function.
|
||||||
// This should not modify the `ParseNode`.
|
// This should not modify the `ParseNode`.
|
||||||
|
@@ -12,6 +12,10 @@ import {scriptFromCodepoint} from "./unicodeScripts";
|
|||||||
import utils from "./utils";
|
import utils from "./utils";
|
||||||
import svgGeometry from "./svgGeometry";
|
import svgGeometry from "./svgGeometry";
|
||||||
import type Options from "./Options";
|
import type Options from "./Options";
|
||||||
|
import * as tree from "./tree";
|
||||||
|
|
||||||
|
import type {VirtualNode} from "./tree";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an HTML className based on a list of classes. In addition to joining
|
* Create an HTML className based on a list of classes. In addition to joining
|
||||||
@@ -23,13 +27,7 @@ const createClass = function(classes: string[]): string {
|
|||||||
|
|
||||||
export type CssStyle = {[name: string]: string};
|
export type CssStyle = {[name: string]: string};
|
||||||
|
|
||||||
// To ensure that all nodes have compatible signatures for these methods.
|
export interface HtmlDomNode extends VirtualNode {
|
||||||
interface VirtualNodeInterface {
|
|
||||||
toNode(): Node;
|
|
||||||
toMarkup(): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HtmlDomNode extends VirtualNodeInterface {
|
|
||||||
classes: string[];
|
classes: string[];
|
||||||
height: number;
|
height: number;
|
||||||
depth: number;
|
depth: number;
|
||||||
@@ -46,9 +44,10 @@ export type DomSpan = span<HtmlDomNode>;
|
|||||||
export type SvgSpan = span<svgNode>;
|
export type SvgSpan = span<svgNode>;
|
||||||
|
|
||||||
export type SvgChildNode = pathNode | lineNode;
|
export type SvgChildNode = pathNode | lineNode;
|
||||||
|
export type documentFragment = tree.documentFragment<HtmlDomNode>;
|
||||||
|
|
||||||
|
|
||||||
export class HtmlDomContainer<ChildType: VirtualNodeInterface>
|
export class HtmlDomContainer<ChildType: VirtualNode>
|
||||||
implements HtmlDomNode {
|
implements HtmlDomNode {
|
||||||
children: ChildType[];
|
children: ChildType[];
|
||||||
attributes: {[string]: string};
|
attributes: {[string]: string};
|
||||||
@@ -196,7 +195,7 @@ export class HtmlDomContainer<ChildType: VirtualNodeInterface>
|
|||||||
* otherwise. This typesafety is important when HTML builders access a span's
|
* otherwise. This typesafety is important when HTML builders access a span's
|
||||||
* children.
|
* children.
|
||||||
*/
|
*/
|
||||||
class span<ChildType: VirtualNodeInterface> extends HtmlDomContainer<ChildType> {
|
class span<ChildType: VirtualNode> extends HtmlDomContainer<ChildType> {
|
||||||
constructor(
|
constructor(
|
||||||
classes?: string[],
|
classes?: string[],
|
||||||
children?: ChildType[],
|
children?: ChildType[],
|
||||||
@@ -234,67 +233,6 @@ class anchor extends HtmlDomContainer<HtmlDomNode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This node represents a document fragment, which contains elements, but when
|
|
||||||
* placed into the DOM doesn't have any representation itself. Thus, it only
|
|
||||||
* contains children and doesn't have any HTML properties. It also keeps track
|
|
||||||
* of a height, depth, and maxFontSize.
|
|
||||||
*/
|
|
||||||
class documentFragment implements HtmlDomNode {
|
|
||||||
children: HtmlDomNode[];
|
|
||||||
classes: string[]; // Never used; needed for satisfying interface.
|
|
||||||
height: number;
|
|
||||||
depth: number;
|
|
||||||
maxFontSize: number;
|
|
||||||
style: CssStyle; // Never used; needed for satisfying interface.
|
|
||||||
|
|
||||||
constructor(children?: HtmlDomNode[]) {
|
|
||||||
this.children = children || [];
|
|
||||||
this.classes = [];
|
|
||||||
this.height = 0;
|
|
||||||
this.depth = 0;
|
|
||||||
this.maxFontSize = 0;
|
|
||||||
this.style = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
hasClass(className: string): boolean {
|
|
||||||
return utils.contains(this.classes, className);
|
|
||||||
}
|
|
||||||
|
|
||||||
tryCombine(sibling: HtmlDomNode): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the fragment into a node
|
|
||||||
*/
|
|
||||||
toNode(): Node {
|
|
||||||
// Create a fragment
|
|
||||||
const frag = document.createDocumentFragment();
|
|
||||||
|
|
||||||
// Append the children
|
|
||||||
for (let i = 0; i < this.children.length; i++) {
|
|
||||||
frag.appendChild(this.children[i].toNode());
|
|
||||||
}
|
|
||||||
|
|
||||||
return frag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the fragment into HTML markup
|
|
||||||
*/
|
|
||||||
toMarkup(): string {
|
|
||||||
let markup = "";
|
|
||||||
|
|
||||||
// Simply concatenate the markup for the children together
|
|
||||||
for (let i = 0; i < this.children.length; i++) {
|
|
||||||
markup += this.children[i].toMarkup();
|
|
||||||
}
|
|
||||||
|
|
||||||
return markup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const iCombinations = {
|
const iCombinations = {
|
||||||
'î': '\u0131\u0302',
|
'î': '\u0131\u0302',
|
||||||
'ï': '\u0131\u0308',
|
'ï': '\u0131\u0308',
|
||||||
@@ -470,7 +408,7 @@ class symbolNode implements HtmlDomNode {
|
|||||||
/**
|
/**
|
||||||
* SVG nodes are used to render stretchy wide elements.
|
* SVG nodes are used to render stretchy wide elements.
|
||||||
*/
|
*/
|
||||||
class svgNode implements VirtualNodeInterface {
|
class svgNode implements VirtualNode {
|
||||||
children: SvgChildNode[];
|
children: SvgChildNode[];
|
||||||
attributes: {[string]: string};
|
attributes: {[string]: string};
|
||||||
|
|
||||||
@@ -519,7 +457,7 @@ class svgNode implements VirtualNodeInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class pathNode implements VirtualNodeInterface {
|
class pathNode implements VirtualNode {
|
||||||
pathName: string;
|
pathName: string;
|
||||||
alternate: ?string;
|
alternate: ?string;
|
||||||
|
|
||||||
@@ -550,7 +488,7 @@ class pathNode implements VirtualNodeInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class lineNode implements VirtualNodeInterface {
|
class lineNode implements VirtualNode {
|
||||||
attributes: {[string]: string};
|
attributes: {[string]: string};
|
||||||
|
|
||||||
constructor(attributes?: {[string]: string}) {
|
constructor(attributes?: {[string]: string}) {
|
||||||
@@ -609,7 +547,6 @@ export function assertDomContainer(
|
|||||||
export default {
|
export default {
|
||||||
span,
|
span,
|
||||||
anchor,
|
anchor,
|
||||||
documentFragment,
|
|
||||||
symbolNode,
|
symbolNode,
|
||||||
svgNode,
|
svgNode,
|
||||||
pathNode,
|
pathNode,
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import defineFunction, {ordargument} from "../defineFunction";
|
import defineFunction, {ordargument} from "../defineFunction";
|
||||||
import buildCommon from "../buildCommon";
|
import buildCommon from "../buildCommon";
|
||||||
import domTree from "../domTree";
|
import mathMLTree from "../mathMLTree";
|
||||||
import ParseNode from "../ParseNode";
|
import ParseNode from "../ParseNode";
|
||||||
|
|
||||||
import * as html from "../buildHTML";
|
import * as html from "../buildHTML";
|
||||||
@@ -16,7 +16,7 @@ function htmlBuilder(group, options) {
|
|||||||
|
|
||||||
function mathmlBuilder(group, options) {
|
function mathmlBuilder(group, options) {
|
||||||
const inner = mml.buildExpression(group.value.value, options);
|
const inner = mml.buildExpression(group.value.value, options);
|
||||||
return new domTree.documentFragment(inner);
|
return mathMLTree.newDocumentFragment(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Math class commands except \mathop
|
// Math class commands except \mathop
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
import defineFunction, {ordargument} from "../defineFunction";
|
import defineFunction, {ordargument} from "../defineFunction";
|
||||||
import buildCommon from "../buildCommon";
|
import buildCommon from "../buildCommon";
|
||||||
import domTree from "../domTree";
|
import domTree from "../domTree";
|
||||||
import mathMLTree from "../mathMLTree";
|
import * as mathMLTree from "../mathMLTree";
|
||||||
import utils from "../utils";
|
import utils from "../utils";
|
||||||
import Style from "../Style";
|
import Style from "../Style";
|
||||||
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
|
import ParseNode, {assertNodeType, checkNodeType} from "../ParseNode";
|
||||||
@@ -258,11 +258,7 @@ const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
|
|||||||
const operator = new mathMLTree.MathNode("mo",
|
const operator = new mathMLTree.MathNode("mo",
|
||||||
[mml.makeText("\u2061", "text")]);
|
[mml.makeText("\u2061", "text")]);
|
||||||
|
|
||||||
// TODO: Refactor to not return an HTML DOM object from MathML builder
|
return mathMLTree.newDocumentFragment([node, operator]);
|
||||||
// or refactor documentFragment to be standalone and explicitly reusable
|
|
||||||
// for both HTML and MathML DOM operations. In either case, update the
|
|
||||||
// return type of `mathBuilder` in `defineFunction` to accommodate.
|
|
||||||
return new domTree.documentFragment([node, operator]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
@@ -90,6 +90,6 @@ defineFunction({
|
|||||||
const operator = new mathMLTree.MathNode("mo",
|
const operator = new mathMLTree.MathNode("mo",
|
||||||
[mml.makeText("\u2061", "text")]);
|
[mml.makeText("\u2061", "text")]);
|
||||||
|
|
||||||
return new domTree.documentFragment([identifier, operator]);
|
return mathMLTree.newDocumentFragment([identifier, operator]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import defineFunction from "../defineFunction";
|
import defineFunction from "../defineFunction";
|
||||||
import buildCommon from "../buildCommon";
|
import buildCommon from "../buildCommon";
|
||||||
import domTree from "../domTree";
|
|
||||||
import mathMLTree from "../mathMLTree";
|
import mathMLTree from "../mathMLTree";
|
||||||
import delimiter from "../delimiter";
|
import delimiter from "../delimiter";
|
||||||
import Style from "../Style";
|
import Style from "../Style";
|
||||||
import ParseNode from "../ParseNode";
|
import ParseNode from "../ParseNode";
|
||||||
|
|
||||||
|
import * as tree from "../tree";
|
||||||
import * as html from "../buildHTML";
|
import * as html from "../buildHTML";
|
||||||
import * as mml from "../buildMathML";
|
import * as mml from "../buildMathML";
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ defineFunction({
|
|||||||
|
|
||||||
// Some groups can return document fragments. Handle those by wrapping
|
// Some groups can return document fragments. Handle those by wrapping
|
||||||
// them in a span.
|
// them in a span.
|
||||||
if (inner instanceof domTree.documentFragment) {
|
if (inner instanceof tree.documentFragment) {
|
||||||
inner = buildCommon.makeSpan([], [inner], options);
|
inner = buildCommon.makeSpan([], [inner], options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ import * as mml from "../buildMathML";
|
|||||||
// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
|
// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
|
||||||
// src/symbols.js.
|
// src/symbols.js.
|
||||||
|
|
||||||
const defaultVariant = {
|
const defaultVariant: {[string]: string} = {
|
||||||
"mi": "italic",
|
"mi": "italic",
|
||||||
"mn": "normal",
|
"mn": "normal",
|
||||||
"mtext": "normal",
|
"mtext": "normal",
|
||||||
|
@@ -10,6 +10,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import utils from "./utils";
|
import utils from "./utils";
|
||||||
|
import * as tree from "./tree";
|
||||||
|
|
||||||
|
import type {VirtualNode} from "./tree";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MathML node types used in KaTeX. For a complete list of MathML nodes, see
|
* MathML node types used in KaTeX. For a complete list of MathML nodes, see
|
||||||
@@ -24,19 +27,26 @@ export type MathNodeType =
|
|||||||
"mrow" | "menclose" |
|
"mrow" | "menclose" |
|
||||||
"mstyle" | "mpadded" | "mphantom";
|
"mstyle" | "mpadded" | "mphantom";
|
||||||
|
|
||||||
export type MathNodeClass = MathNode | TextNode | SpaceNode;
|
export interface MathDomNode extends VirtualNode {
|
||||||
|
toText(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type documentFragment = tree.documentFragment<MathDomNode>;
|
||||||
|
export function newDocumentFragment(children: MathDomNode[]): documentFragment {
|
||||||
|
return new tree.documentFragment(children);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This node represents a general purpose MathML node of any type. The
|
* This node represents a general purpose MathML node of any type. The
|
||||||
* constructor requires the type of node to create (for example, `"mo"` or
|
* constructor requires the type of node to create (for example, `"mo"` or
|
||||||
* `"mspace"`, corresponding to `<mo>` and `<mspace>` tags).
|
* `"mspace"`, corresponding to `<mo>` and `<mspace>` tags).
|
||||||
*/
|
*/
|
||||||
export class MathNode {
|
export class MathNode implements MathDomNode {
|
||||||
type: MathNodeType;
|
type: MathNodeType;
|
||||||
attributes: {[string]: string};
|
attributes: {[string]: string};
|
||||||
children: (MathNode | TextNode)[];
|
children: MathDomNode[];
|
||||||
|
|
||||||
constructor(type: MathNodeType, children?: (MathNode | TextNode)[]) {
|
constructor(type: MathNodeType, children?: MathDomNode[]) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.attributes = {};
|
this.attributes = {};
|
||||||
this.children = children || [];
|
this.children = children || [];
|
||||||
@@ -114,7 +124,7 @@ export class MathNode {
|
|||||||
/**
|
/**
|
||||||
* This node represents a piece of text.
|
* This node represents a piece of text.
|
||||||
*/
|
*/
|
||||||
export class TextNode {
|
export class TextNode implements MathDomNode {
|
||||||
text: string;
|
text: string;
|
||||||
needsEscape: boolean;
|
needsEscape: boolean;
|
||||||
|
|
||||||
@@ -151,7 +161,7 @@ export class TextNode {
|
|||||||
* This node represents a space, but may render as <mspace.../> or as text,
|
* This node represents a space, but may render as <mspace.../> or as text,
|
||||||
* depending on the width.
|
* depending on the width.
|
||||||
*/
|
*/
|
||||||
class SpaceNode {
|
class SpaceNode implements MathDomNode {
|
||||||
width: number;
|
width: number;
|
||||||
character: ?string;
|
character: ?string;
|
||||||
|
|
||||||
@@ -226,4 +236,5 @@ export default {
|
|||||||
MathNode,
|
MathNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
SpaceNode,
|
SpaceNode,
|
||||||
|
newDocumentFragment,
|
||||||
};
|
};
|
||||||
|
82
src/tree.js
Normal file
82
src/tree.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import utils from "./utils";
|
||||||
|
|
||||||
|
import type {CssStyle, HtmlDomNode} from "./domTree";
|
||||||
|
import type {MathDomNode} from "./mathMLTree";
|
||||||
|
|
||||||
|
|
||||||
|
// To ensure that all nodes have compatible signatures for these methods.
|
||||||
|
export interface VirtualNode {
|
||||||
|
toNode(): Node;
|
||||||
|
toMarkup(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a document fragment, which contains elements, but when
|
||||||
|
* placed into the DOM doesn't have any representation itself. It only contains
|
||||||
|
* children and doesn't have any DOM node properties.
|
||||||
|
*/
|
||||||
|
export class documentFragment<ChildType: VirtualNode>
|
||||||
|
implements HtmlDomNode, MathDomNode {
|
||||||
|
children: ChildType[];
|
||||||
|
// HtmlDomNode
|
||||||
|
classes: string[];
|
||||||
|
height: number;
|
||||||
|
depth: number;
|
||||||
|
maxFontSize: number;
|
||||||
|
style: CssStyle; // Never used; needed for satisfying interface.
|
||||||
|
|
||||||
|
constructor(children: ChildType[]) {
|
||||||
|
this.children = children;
|
||||||
|
this.classes = [];
|
||||||
|
this.height = 0;
|
||||||
|
this.depth = 0;
|
||||||
|
this.maxFontSize = 0;
|
||||||
|
this.style = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
hasClass(className: string): boolean {
|
||||||
|
return utils.contains(this.classes, className);
|
||||||
|
}
|
||||||
|
|
||||||
|
tryCombine(sibling: HtmlDomNode): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert the fragment into a node. */
|
||||||
|
toNode(): Node {
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
|
||||||
|
for (let i = 0; i < this.children.length; i++) {
|
||||||
|
frag.appendChild(this.children[i].toNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert the fragment into HTML markup. */
|
||||||
|
toMarkup(): string {
|
||||||
|
let markup = "";
|
||||||
|
|
||||||
|
// Simply concatenate the markup for the children together.
|
||||||
|
for (let i = 0; i < this.children.length; i++) {
|
||||||
|
markup += this.children[i].toMarkup();
|
||||||
|
}
|
||||||
|
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the math node into a string, similar to innerText. Applies to
|
||||||
|
* MathDomNode's only.
|
||||||
|
*/
|
||||||
|
toText(): string {
|
||||||
|
// To avoid this, we would subclass documentFragment separately for
|
||||||
|
// MathML, but polyfills for subclassing is expensive per PR 1469.
|
||||||
|
// $FlowFixMe: Only works for ChildType = MathDomNode.
|
||||||
|
const toText = (child: ChildType): string => child.toText();
|
||||||
|
return this.children.map(toText).join("");
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user