1
0

add initial marp implementation with sample content and build configuration

This commit is contained in:
2025-09-13 18:13:22 +02:00
parent dcacc9b409
commit e5f219507f
10319 changed files with 1402023 additions and 0 deletions

563
node_modules/mathjax-full/ts/adaptors/HTMLAdaptor.ts generated vendored Normal file
View File

@@ -0,0 +1,563 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements the HTML DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {OptionList} from '../util/Options.js';
import {AttributeData, AbstractDOMAdaptor, DOMAdaptor, PageBBox} from '../core/DOMAdaptor.js';
/*****************************************************************/
/**
* The minimum fields needed for a Document
*
* @template N The HTMLElement node class
* @template T The Text node class
*/
export interface MinDocument<N, T> {
documentElement: N;
head: N;
body: N;
title: string;
doctype: {name: string};
/* tslint:disable:jsdoc-require */
createElement(kind: string): N;
createElementNS(ns: string, kind: string): N;
createTextNode(text: string): T;
querySelectorAll(selector: string): ArrayLike<N>;
/* tslint:enable */
}
/*****************************************************************/
/**
* The minimum fields needed for an HTML Element
*
* @template N The HTMLElement node class
* @template T The Text node class
*/
export interface MinHTMLElement<N, T> {
nodeType: number;
nodeName: string;
nodeValue: string;
textContent: string;
innerHTML: string;
outerHTML: string;
parentNode: N | Node;
nextSibling: N | T | Node;
previousSibling: N | T | Node;
offsetWidth: number;
offsetHeight: number;
attributes: AttributeData[] | NamedNodeMap;
className: string;
classList: DOMTokenList;
style: OptionList;
sheet?: {insertRule: (rule: string, index?: number) => void};
childNodes: (N | T)[] | NodeList;
firstChild: N | T | Node;
lastChild: N | T | Node;
/* tslint:disable:jsdoc-require */
getElementsByTagName(name: string): N[] | HTMLCollectionOf<Element>;
getElementsByTagNameNS(ns: string, name: string): N[] | HTMLCollectionOf<Element>;
contains(child: N | T): boolean;
appendChild(child: N | T): N | T | Node;
removeChild(child: N | T): N | T | Node;
replaceChild(nnode: N | T, onode: N | T): N | T | Node;
insertBefore(nchild: N | T, ochild: N | T): void;
cloneNode(deep: boolean): N | Node;
setAttribute(name: string, value: string): void;
setAttributeNS(ns: string, name: string, value: string): void;
getAttribute(name: string): string;
removeAttribute(name: string): void;
hasAttribute(name: string): boolean;
getBoundingClientRect(): Object;
getBBox?(): {x: number, y: number, width: number, height: number};
/* tslint:endable */
}
/*****************************************************************/
/**
* The minimum fields needed for a Text element
*
* @template N The HTMLElement node class
* @template T The Text node class
*/
export interface MinText<N, T> {
nodeType: number;
nodeName: string;
nodeValue: string;
parentNode: N | Node;
nextSibling: N | T | Node;
previousSibling: N | T | Node;
splitText(n: number): T;
}
/*****************************************************************/
/**
* The minimum fields needed for a DOMParser
*
* @template D The Document class
*/
export interface MinDOMParser<D> {
parseFromString(text: string, format?: string): D;
}
/*****************************************************************/
/**
* The minimum fields needed for a DOMParser
*
* @template N The HTMLElement node class
*/
export interface MinXMLSerializer<N> {
serializeToString(node: N): string;
}
/*****************************************************************/
/**
* The minimum fields needed for a Window
*
* @template N The HTMLElement node class
* @template D The Document class
*/
export interface MinWindow<N, D> {
document: D;
DOMParser: {
new(): MinDOMParser<D>
};
XMLSerializer: {
new(): MinXMLSerializer<N>;
};
NodeList: any;
HTMLCollection: any;
HTMLElement: any;
DocumentFragment: any;
Document: any;
getComputedStyle(node: N): any;
}
/*****************************************************************/
/**
* The minimum needed for an HTML Adaptor
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export interface MinHTMLAdaptor<N, T, D> extends DOMAdaptor<N, T, D> {
window: MinWindow<N, D>;
}
/*****************************************************************/
/**
* Abstract HTMLAdaptor class for manipulating HTML elements
* (subclass of AbstractDOMAdaptor)
*
* N = HTMLElement node class
* T = Text node class
* D = Document class
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export class HTMLAdaptor<N extends MinHTMLElement<N, T>, T extends MinText<N, T>, D extends MinDocument<N, T>> extends
AbstractDOMAdaptor<N, T, D> implements MinHTMLAdaptor<N, T, D> {
/**
* The window object for this adaptor
*/
public window: MinWindow<N, D>;
/**
* The DOMParser used to parse a string into a DOM tree
*/
public parser: MinDOMParser<D>;
/**
* @override
* @constructor
*/
constructor(window: MinWindow<N, D>) {
super(window.document);
this.window = window;
this.parser = new (window.DOMParser as any)();
}
/**
* @override
*/
public parse(text: string, format: string = 'text/html') {
return this.parser.parseFromString(text, format);
}
/**
* @override
*/
protected create(kind: string, ns?: string) {
return (ns ?
this.document.createElementNS(ns, kind) :
this.document.createElement(kind));
}
/**
* @override
*/
public text(text: string) {
return this.document.createTextNode(text);
}
/**
* @override
*/
public head(doc: D) {
return doc.head || (doc as any as N);
}
/**
* @override
*/
public body(doc: D) {
return doc.body || (doc as any as N);
}
/**
* @override
*/
public root(doc: D) {
return doc.documentElement || (doc as any as N);
}
/**
* @override
*/
public doctype(doc: D) {
return (doc.doctype ? `<!DOCTYPE ${doc.doctype.name}>` : '');
}
/**
* @override
*/
public tags(node: N, name: string, ns: string = null) {
let nodes = (ns ? node.getElementsByTagNameNS(ns, name) : node.getElementsByTagName(name));
return Array.from(nodes as N[]) as N[];
}
/**
* @override
*/
public getElements(nodes: (string | N | N[])[], _document: D) {
let containers: N[] = [];
for (const node of nodes) {
if (typeof(node) === 'string') {
containers = containers.concat(Array.from(this.document.querySelectorAll(node)));
} else if (Array.isArray(node)) {
containers = containers.concat(Array.from(node) as N[]);
} else if (node instanceof this.window.NodeList || node instanceof this.window.HTMLCollection) {
containers = containers.concat(Array.from(node as any as N[]));
} else {
containers.push(node);
}
}
return containers;
}
/**
* @override
*/
public contains(container: N, node: N | T) {
return container.contains(node);
}
/**
* @override
*/
public parent(node: N | T) {
return node.parentNode as N;
}
/**
* @override
*/
public append(node: N, child: N | T) {
return node.appendChild(child) as N | T;
}
/**
* @override
*/
public insert(nchild: N | T, ochild: N | T) {
return this.parent(ochild).insertBefore(nchild, ochild);
}
/**
* @override
*/
public remove(child: N | T) {
return this.parent(child).removeChild(child) as N | T;
}
/**
* @override
*/
public replace(nnode: N | T, onode: N | T) {
return this.parent(onode).replaceChild(nnode, onode) as N | T;
}
/**
* @override
*/
public clone(node: N) {
return node.cloneNode(true) as N;
}
/**
* @override
*/
public split(node: T, n: number) {
return node.splitText(n);
}
/**
* @override
*/
public next(node: N | T) {
return node.nextSibling as N | T;
}
/**
* @override
*/
public previous(node: N | T) {
return node.previousSibling as N | T;
}
/**
* @override
*/
public firstChild(node: N) {
return node.firstChild as N | T;
}
/**
* @override
*/
public lastChild(node: N) {
return node.lastChild as N | T;
}
/**
* @override
*/
public childNodes(node: N) {
return Array.from(node.childNodes as (N | T)[]);
}
/**
* @override
*/
public childNode(node: N, i: number) {
return node.childNodes[i] as N | T;
}
/**
* @override
*/
public kind(node: N | T) {
const n = node.nodeType;
return (n === 1 || n === 3 || n === 8 ? node.nodeName.toLowerCase() : '');
}
/**
* @override
*/
public value(node: N | T) {
return node.nodeValue || '';
}
/**
* @override
*/
public textContent(node: N) {
return node.textContent;
}
/**
* @override
*/
public innerHTML(node: N) {
return node.innerHTML;
}
/**
* @override
*/
public outerHTML(node: N) {
return node.outerHTML;
}
public serializeXML(node: N) {
const serializer = new this.window.XMLSerializer();
return serializer.serializeToString(node) as string;
}
/**
* @override
*/
public setAttribute(node: N, name: string, value: string, ns: string = null) {
if (!ns) {
return node.setAttribute(name, value);
}
name = ns.replace(/.*\//, '') + ':' + name.replace(/^.*:/, '');
return node.setAttributeNS(ns, name, value);
}
/**
* @override
*/
public getAttribute(node: N, name: string) {
return node.getAttribute(name);
}
/**
* @override
*/
public removeAttribute(node: N, name: string) {
return node.removeAttribute(name);
}
/**
* @override
*/
public hasAttribute(node: N, name: string) {
return node.hasAttribute(name);
}
/**
* @override
*/
public allAttributes(node: N) {
return Array.from(node.attributes).map(
(x: AttributeData) => {
return {name: x.name, value: x.value} as AttributeData;
}
);
}
/**
* @override
*/
public addClass(node: N, name: string) {
if (node.classList) {
node.classList.add(name);
} else {
node.className = (node.className + ' ' + name).trim();
}
}
/**
* @override
*/
public removeClass(node: N, name: string) {
if (node.classList) {
node.classList.remove(name);
} else {
node.className = node.className.split(/ /).filter((c) => c !== name).join(' ');
}
}
/**
* @override
*/
public hasClass(node: N, name: string) {
if (node.classList) {
return node.classList.contains(name);
}
return node.className.split(/ /).indexOf(name) >= 0;
}
/**
* @override
*/
public setStyle(node: N, name: string, value: string) {
(node.style as OptionList)[name] = value;
}
/**
* @override
*/
public getStyle(node: N, name: string) {
return (node.style as OptionList)[name];
}
/**
* @override
*/
public allStyles(node: N) {
return node.style.cssText;
}
/**
* @override
*/
public insertRules(node: N, rules: string[]) {
for (const rule of rules.reverse()) {
try {
node.sheet.insertRule(rule, 0);
} catch (e) {
console.warn(`MathJax: can't insert css rule '${rule}': ${e.message}`);
}
}
}
/**
* @override
*/
public fontSize(node: N) {
const style = this.window.getComputedStyle(node);
return parseFloat(style.fontSize);
}
/**
* @override
*/
public fontFamily(node: N) {
const style = this.window.getComputedStyle(node);
return style.fontFamily || '';
}
/**
* @override
*/
public nodeSize(node: N, em: number = 1, local: boolean = false) {
if (local && node.getBBox) {
let {width, height} = node.getBBox();
return [width / em , height / em] as [number, number];
}
return [node.offsetWidth / em, node.offsetHeight / em] as [number, number];
}
/**
* @override
*/
public nodeBBox(node: N) {
const {left, right, top, bottom} = node.getBoundingClientRect() as PageBBox;
return {left, right, top, bottom};
}
}

160
node_modules/mathjax-full/ts/adaptors/NodeMixin.ts generated vendored Normal file
View File

@@ -0,0 +1,160 @@
/*************************************************************
*
* Copyright (c) 2022-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a mixin for node-based adaptors that overrides
* the methods that obtain DOM node sizes, when those aren't
* available from the DOM itself.
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {DOMAdaptor} from '../core/DOMAdaptor.js';
import {userOptions, defaultOptions, OptionList} from '../util/Options.js';
/**
* A constructor for a given class
*
* @template T The class to construct
*/
export type Constructor<T> = (new(...args: any[]) => T);
/**
* The type of an Adaptor class
*/
export type AdaptorConstructor<N, T, D> = Constructor<DOMAdaptor<N, T, D>>;
/**
* The options to the NodeMixin
*/
export const NodeMixinOptions: OptionList = {
badCSS: true, // getComputedStyles() is not implemented in the DOM
badSizes: true, // element sizes (e.g., ClientWidth, etc.) are not implemented in the DOM
};
/**
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export function NodeMixin<N, T, D, A extends AdaptorConstructor<N, T, D>>(
Base: A,
options: typeof NodeMixinOptions = {}
): A {
options = userOptions(defaultOptions({}, NodeMixinOptions), options);
return class NodeAdaptor extends Base {
/**
* The default options
*/
public static OPTIONS: OptionList = {
...(options.badCSS ? {
fontSize: 16, // We can't compute the font size, so always use this
fontFamily: 'Times', // We can't compute the font family, so always use this
} : {}),
...(options.badSizes ? {
cjkCharWidth: 1, // Width (in em units) of full width characters
unknownCharWidth: .6, // Width (in em units) of unknown (non-full-width) characters
unknownCharHeight: .8, // Height (in em units) of unknown characters
} : {})
};
/**
* Pattern to identify CJK (i.e., full-width) characters
*/
public static cjkPattern = new RegExp([
'[',
'\u1100-\u115F', // Hangul Jamo
'\u2329\u232A', // LEFT-POINTING ANGLE BRACKET, RIGHT-POINTING ANGLE BRACKET
'\u2E80-\u303E', // CJK Radicals Supplement ... CJK Symbols and Punctuation
'\u3040-\u3247', // Hiragana ... Enclosed CJK Letters and Months
'\u3250-\u4DBF', // Enclosed CJK Letters and Months ... CJK Unified Ideographs Extension A
'\u4E00-\uA4C6', // CJK Unified Ideographs ... Yi Radicals
'\uA960-\uA97C', // Hangul Jamo Extended-A
'\uAC00-\uD7A3', // Hangul Syllables
'\uF900-\uFAFF', // CJK Compatibility Ideographs
'\uFE10-\uFE19', // Vertical Forms
'\uFE30-\uFE6B', // CJK Compatibility Forms ... Small Form Variants
'\uFF01-\uFF60\uFFE0-\uFFE6', // Halfwidth and Fullwidth Forms
'\u{1B000}-\u{1B001}', // Kana Supplement
'\u{1F200}-\u{1F251}', // Enclosed Ideographic Supplement
'\u{20000}-\u{3FFFD}', // CJK Unified Ideographs Extension B ... Tertiary Ideographic Plane
']'
].join(''), 'gu');
/**
* The options for the instance
*/
public options: OptionList;
/**
* @param {any} window The window to work with
* @param {OptionList} options The options for the adaptor
* @constructor
*/
constructor(...args: any[]) {
super(args[0]);
let CLASS = this.constructor as typeof NodeAdaptor;
this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), args[1]);
}
/**
* For DOMs that don't handle CSS well, use the font size from the options
*
* @override
*/
public fontSize(node: N) {
return (options.badCSS ? this.options.fontSize : super.fontSize(node));
}
/**
* For DOMs that don't handle CSS well, use the font family from the options
*
* @override
*/
public fontFamily(node: N) {
return (options.badCSS ? this.options.fontFamily : super.fontFamily(node));
}
/**
* @override
*/
public nodeSize(node: N, em: number = 1, local: boolean = null) {
if (!options.badSizes) {
return super.nodeSize(node, em, local);
}
const text = this.textContent(node);
const non = Array.from(text.replace(NodeAdaptor.cjkPattern, '')).length; // # of non-CJK chars
const CJK = Array.from(text).length - non; // # of cjk chars
return [
CJK * this.options.cjkCharWidth + non * this.options.unknownCharWidth,
this.options.unknownCharHeight
] as [number, number];
}
/**
* @override
*/
public nodeBBox(node: N) {
return (options.badSizes ? {left: 0, right: 0, top: 0, bottom: 0} : super.nodeBBox(node));
}
};
}

View File

@@ -0,0 +1,48 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements the browser DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {HTMLAdaptor} from './HTMLAdaptor.js';
//
// Let Typescript know about these
//
declare global {
interface Window {
Document: typeof Document;
DOMParser: typeof DOMParser;
XMLSerializer: typeof XMLSerializer;
HTMLElement: typeof HTMLElement;
HTMLCollection: typeof HTMLCollection;
NodeList: typeof NodeList;
DocumentFragment: typeof DocumentFragment;
}
}
/**
* Function to create an HTML adpator for browsers
*
* @return {HTMLAdaptor} The newly created adaptor
*/
export function browserAdaptor(): HTMLAdaptor<HTMLElement, Text, Document> {
return new HTMLAdaptor<HTMLElement, Text, Document>(window);
}

39
node_modules/mathjax-full/ts/adaptors/chooseAdaptor.ts generated vendored Normal file
View File

@@ -0,0 +1,39 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Chooses between jdsom and browser DOM adaptors
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {liteAdaptor} from './liteAdaptor.js';
import {browserAdaptor} from './browserAdaptor.js';
let choose;
try {
document; // errors if not in browser
choose = browserAdaptor;
} catch (e) {
choose = liteAdaptor;
}
/**
* Function to select which adaptor to use (depending on whether we are in a browser or node.js)
*/
export const chooseAdaptor = choose;

46
node_modules/mathjax-full/ts/adaptors/jsdomAdaptor.ts generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements the jdsom DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {HTMLAdaptor} from './HTMLAdaptor.js';
import {NodeMixin, Constructor} from './NodeMixin.js';
import {OptionList} from '../util/Options.js';
/**
* The constructor for an HTMLAdaptor
*/
export type HTMLAdaptorConstructor = Constructor<HTMLAdaptor<HTMLElement, Text, Document>>;
/**
* The JsdomAdaptor class
*/
export class JsdomAdaptor extends NodeMixin<HTMLElement, Text, Document, HTMLAdaptorConstructor>(HTMLAdaptor) {}
/**
* Function for creating an HTML adaptor using jsdom
*
* @param {any} JSDOM The jsdom object to use for this adaptor
* @return {HTMLAdaptor} The newly created adaptor
*/
export function jsdomAdaptor(JSDOM: any, options: OptionList = null): JsdomAdaptor {
return new JsdomAdaptor(new JSDOM().window, options);
}

View File

@@ -0,0 +1,71 @@
/*************************************************************
*
* Copyright (c) 2022-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements the linkedom DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {HTMLAdaptor} from './HTMLAdaptor.js';
import {NodeMixin, Constructor} from './NodeMixin.js';
import {OptionList} from '../util/Options.js';
/**
* The constructor for an HTMLAdaptor
*/
export type HTMLAdaptorConstructor = Constructor<HTMLAdaptor<HTMLElement, Text, Document>>;
/**
* The LinkedomAdaptor class
*/
export class LinkedomAdaptor extends NodeMixin<HTMLElement, Text, Document, HTMLAdaptorConstructor>(HTMLAdaptor) {
/**
* @override
*/
public parse(text: string, format: string = 'text/html') {
//
// Make sure the text string has nodes (in particular, it can't be empty)
//
if (!text.match(/^(?:\s|\n)*</)) text = '<html>' + text + '</html>';
return this.parser.parseFromString(text, format);
}
/**
* @override
*
* This will do an HTML serialization, which may be good enough, but
* won't necessarily close some tags properly.
*/
public serializeXML(node: HTMLElement) {
return this.outerHTML(node);
}
}
/**
* Function for creating an HTML adaptor using linkedom
*
* @param {any} parseHTML The linkedom HTML parser to use for this adaptor
* @return {LinkeddomAdaptor} The newly created adaptor
*/
export function linkedomAdaptor(parseHTML: any, options: OptionList = null): LinkedomAdaptor {
const window = parseHTML('<html></html>');
window.constructor.prototype.HTMLCollection = class {}; // add fake class for missing HTMLCollecton
return new LinkedomAdaptor(window, options);
}

66
node_modules/mathjax-full/ts/adaptors/lite/Document.ts generated vendored Normal file
View File

@@ -0,0 +1,66 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {LiteElement} from './Element.js';
/************************************************************/
/**
* Implements a lightweight Document replacement
*/
export class LiteDocument {
/**
* The document's <html> element
*/
public root: LiteElement;
/**
* The document's <head> element
*/
public head: LiteElement;
/**
* The document's <body> element
*/
public body: LiteElement;
/**
* the DOCTYPE comment
*/
public type: string;
/**
* The kind is always #document
*/
public get kind() {
return '#document';
}
/**
* @constructor
*/
constructor() {
this.root = new LiteElement('html', {}, [
this.head = new LiteElement('head'),
this.body = new LiteElement('body')
]);
this.type = '';
}
}

84
node_modules/mathjax-full/ts/adaptors/lite/Element.ts generated vendored Normal file
View File

@@ -0,0 +1,84 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight HTML Element replacement
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {OptionList} from '../../util/Options.js';
import {Styles} from '../../util/Styles.js';
import {LiteText} from './Text.js';
/**
* Type for attribute lists
*/
export type LiteAttributeList = OptionList;
/**
* Type for generic nodes in LiteAdaptor
*/
export type LiteNode = LiteElement | LiteText;
/************************************************************/
/**
* Implements a lightweight HTML element replacement
*/
export class LiteElement {
/**
* The type of element (tag name)
*/
public kind: string;
/**
* The element's attribute list
*/
public attributes: LiteAttributeList;
/**
* The element's children
*/
public children: LiteNode[];
/**
* The element's parent
*/
public parent: LiteElement;
/**
* The styles for the element
*/
public styles: Styles;
/**
* @param {string} kind The type of node to create
* @param {LiteAttributeList} attributes The list of attributes to set (if any)
* @param {LiteNode[]} children The children for the node (if any)
* @constructor
*/
constructor(kind: string, attributes: LiteAttributeList = {}, children: LiteNode[] = []) {
this.kind = kind;
this.attributes = {...attributes};
this.children = [...children];
for (const child of this.children) {
child.parent = this;
}
this.styles = null;
}
}

70
node_modules/mathjax-full/ts/adaptors/lite/List.ts generated vendored Normal file
View File

@@ -0,0 +1,70 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {LiteNode} from './Element.js';
/************************************************************/
/**
* Implements a lightweight DocumentFragment or NodeList replacement
*
* @template N The HTMLElement node class
*/
export class LiteList<N> {
/**
* The nodes held in the fragment
*/
public nodes: N[] = [];
/**
* @param {N[]} children The children for the fragment
* @constructor
*/
constructor(children: N[]) {
this.nodes = [...children];
}
/**
* @param {N} node A node to append to the fragment
*/
public append(node: N) {
this.nodes.push(node);
}
/**
* Make this class iterable (so it can be used with Array.from())
*/
public [Symbol.iterator](): Iterator<LiteNode> {
let i = 0;
return {
/**
* @return {IteratorResult<LiteNode>}
*/
next(): IteratorResult<LiteNode> {
return (i === this.nodes.length ?
{value: null, done: true} :
{value: this.nodes[i++], done: false});
}
};
}
}

405
node_modules/mathjax-full/ts/adaptors/lite/Parser.ts generated vendored Normal file
View File

@@ -0,0 +1,405 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {AttributeData} from '../../core/DOMAdaptor.js';
import {MinDOMParser} from '../HTMLAdaptor.js';
import * as Entities from '../../util/Entities.js';
import {LiteDocument} from './Document.js';
import {LiteElement} from './Element.js';
import {LiteText, LiteComment} from './Text.js';
import {LiteAdaptor} from '../liteAdaptor.js';
/**
* Patterns used in parsing serialized HTML
*/
export namespace PATTERNS {
export const TAGNAME = '[a-z][^\\s\\n>]*';
export const ATTNAME = '[a-z][^\\s\\n>=]*';
export const VALUE = `(?:'[^']*'|"[^"]*"|[^\\s\\n]+)`;
export const VALUESPLIT = `(?:'([^']*)'|"([^"]*)"|([^\\s\\n]+))`;
export const SPACE = '(?:\\s|\\n)+';
export const OPTIONALSPACE = '(?:\\s|\\n)*';
export const ATTRIBUTE = ATTNAME + '(?:' + OPTIONALSPACE + '=' + OPTIONALSPACE + VALUE + ')?';
export const ATTRIBUTESPLIT = '(' + ATTNAME + ')(?:' + OPTIONALSPACE + '=' + OPTIONALSPACE + VALUESPLIT + ')?';
export const TAG = '(<(?:' + TAGNAME + '(?:' + SPACE + ATTRIBUTE + ')*'
+ OPTIONALSPACE + '/?|/' + TAGNAME + '|!--[^]*?--|![^]*?)(?:>|$))';
export const tag = new RegExp(TAG, 'i');
export const attr = new RegExp(ATTRIBUTE, 'i');
export const attrsplit = new RegExp(ATTRIBUTESPLIT, 'i');
}
/************************************************************/
/**
* Implements a lightweight DOMParser replacement
* (Not perfect, but handles most well-formed HTML)
*/
export class LiteParser implements MinDOMParser<LiteDocument> {
/**
* The list of self-closing tags
*/
public static SELF_CLOSING: {[name: string]: boolean} = {
area: true,
base: true,
br: true,
col: true,
command: true,
embed: true,
hr: true,
img: true,
input: true,
keygen: true,
link: true,
menuitem: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true
};
/**
* The list of tags chose content is not parsed (PCDATA)
*/
public static PCDATA: {[name: string]: boolean} = {
option: true,
textarea: true,
fieldset: true,
title: true,
style: true,
script: true
};
/**
* The list of attributes that don't get entity translation
*/
public static CDATA_ATTR: {[name: string]: boolean} = {
style: true,
datafld: true,
datasrc: true,
href: true,
src: true,
longdesc: true,
usemap: true,
cite: true,
datetime: true,
action: true,
axis: true,
profile: true,
content: true,
scheme: true
};
/**
* @override
*/
public parseFromString(text: string, _format: string = 'text/html', adaptor: LiteAdaptor = null) {
const root = adaptor.createDocument();
let node = adaptor.body(root);
//
// Split the HTML into an array of text, tag, text, tag, ...
// Then loop through them and add text nodes and process tags.
//
let parts = text.replace(/<\?.*?\?>/g, '').split(PATTERNS.tag);
while (parts.length) {
const text = parts.shift();
const tag = parts.shift();
if (text) {
this.addText(adaptor, node, text);
}
if (tag && tag.charAt(tag.length - 1) === '>') {
if (tag.charAt(1) === '!') {
this.addComment(adaptor, node, tag);
} else if (tag.charAt(1) === '/') {
node = this.closeTag(adaptor, node, tag);
} else {
node = this.openTag(adaptor, node, tag, parts);
}
}
}
this.checkDocument(adaptor, root);
return root;
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node to add a text element to
* @param {string} text The text for the text node
* @return {LiteText} The text element
*/
protected addText(adaptor: LiteAdaptor, node: LiteElement, text: string): LiteText {
text = Entities.translate(text);
return adaptor.append(node, adaptor.text(text)) as LiteText;
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node to add a comment to
* @param {string} comment The text for the comment node
* @return {LiteComment} The comment element
*/
protected addComment(adaptor: LiteAdaptor, node: LiteElement, comment: string): LiteComment {
return adaptor.append(node, new LiteComment(comment)) as LiteComment;
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node to close
* @param {string} tag The close tag being processed
* @return {LiteElement} The first unclosed parent node
*/
protected closeTag(adaptor: LiteAdaptor, node: LiteElement, tag: string): LiteElement {
const kind = tag.slice(2, tag.length - 1).toLowerCase();
while (adaptor.parent(node) && adaptor.kind(node) !== kind) {
node = adaptor.parent(node);
}
return adaptor.parent(node);
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The parent node for the tag
* @param {string} tag The tag being processed
* @param {string[]} parts The rest of the text/tag array
* @return {LiteElement} The node to which the next tag will be added
*/
protected openTag(adaptor: LiteAdaptor, node: LiteElement, tag: string, parts: string[]): LiteElement {
const PCDATA = (this.constructor as typeof LiteParser).PCDATA;
const SELF_CLOSING = (this.constructor as typeof LiteParser).SELF_CLOSING;
//
// Get the child to be added to the node
//
const kind = tag.match(/<(.*?)[\s\n>\/]/)[1].toLowerCase();
const child = adaptor.node(kind) as LiteElement;
//
// Split out the tag attributes as an array of space, name, value1, value3, value3,
// where value1, value2, and value3 are the value of the node (only one is defined)
// that come from matching quoted strings with ' (value1), " (value2) or no quotes (value3).
//
const attributes = tag.replace(/^<.*?[\s\n>]/, '').split(PATTERNS.attrsplit);
//
// If the tag was complete (it ends with > or has no attributes)
//
if (attributes.pop().match(/>$/) || attributes.length < 5) {
this.addAttributes(adaptor, child, attributes);
adaptor.append(node, child);
//
// For non-self-closing tags,
// For tags whose contents is PCDATA (like <script>), collect the
// content up until the end tag, and continue adding nee tags
// to the current parent node.
// Otherwise, the child tag becames the parent node to which
// new tags are added
//
if (!SELF_CLOSING[kind] && !tag.match(/\/>$/)) {
if (PCDATA[kind]) {
this.handlePCDATA(adaptor, child, kind, parts);
} else {
node = child;
}
}
}
return node;
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node getting the attributes
* @param {string[]} attributes The array of space, name, value1, value2, value3
* as described above.
*/
protected addAttributes(adaptor: LiteAdaptor, node: LiteElement, attributes: string[]) {
const CDATA_ATTR = (this.constructor as typeof LiteParser).CDATA_ATTR;
while (attributes.length) {
let [ , name, v1, v2, v3] = attributes.splice(0, 5);
let value = v1 || v2 || v3 || '';
if (!CDATA_ATTR[name]) {
value = Entities.translate(value);
}
adaptor.setAttribute(node, name, value);
}
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node whose PCDATA content is being collected
* @param {string} kind The tag name being handled
* @param {string[]} parts The array of text/tag data for the document
*/
protected handlePCDATA(adaptor: LiteAdaptor, node: LiteElement, kind: string, parts: string[]) {
const pcdata = [] as string[];
const etag = '</' + kind + '>';
let ptag = '';
//
// Look through the parts until the end tag is found
// Add the unmatched tag and the following text
// and try the next tag until we find the end tag.
//
while (parts.length && ptag !== etag) {
pcdata.push(ptag);
pcdata.push(parts.shift());
ptag = parts.shift();
}
//
// Add the collected contents as a text node
//
adaptor.append(node, adaptor.text(pcdata.join('')));
}
/**
* Check the contents of the parsed document and move html, head, and body
* tags into the document structure. That way, you can parse fragments or
* full documents and still get a valid document.
*
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteDocument} root The document being checked
*/
protected checkDocument(adaptor: LiteAdaptor, root: LiteDocument) {
let node = this.getOnlyChild(adaptor, adaptor.body(root));
if (!node) return;
for (const child of adaptor.childNodes(adaptor.body(root))) {
if (child === node) {
break;
}
if (child instanceof LiteComment && child.value.match(/^<!DOCTYPE/)) {
root.type = child.value;
}
}
switch (adaptor.kind(node)) {
case 'html':
//
// Look through the children for the head and body
//
for (const child of node.children) {
switch (adaptor.kind(child)) {
case 'head':
root.head = child as LiteElement;
break;
case 'body':
root.body = child as LiteElement;
break;
}
}
//
// Make sure the elements are linked in properly
//
root.root = node;
adaptor.remove(node);
if (adaptor.parent(root.body) !== node) {
adaptor.append(node, root.body);
}
if (adaptor.parent(root.head) !== node) {
adaptor.insert(root.head, root.body);
}
break;
case 'head':
root.head = adaptor.replace(node, root.head) as LiteElement;
break;
case 'body':
root.body = adaptor.replace(node, root.body) as LiteElement;
break;
}
}
/**
* Checks if the body has only one element child (as opposed to comments or text nodes)
* and returns that sole element (or null if none or more than one)
*
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} body The body element being checked
* @return {LiteElement} The sole LiteElement child of the body, or null if none or more than one
*/
protected getOnlyChild(adaptor: LiteAdaptor, body: LiteElement): LiteElement {
let node: LiteElement = null;
for (const child of adaptor.childNodes(body)) {
if (child instanceof LiteElement) {
if (node) return null;
node = child;
}
}
return node;
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node to serialize
* @param {boolean} xml True when producing XML, false for HTML
* @return {string} The serialized element (like outerHTML)
*/
public serialize(adaptor: LiteAdaptor, node: LiteElement, xml: boolean = false): string {
const SELF_CLOSING = (this.constructor as typeof LiteParser).SELF_CLOSING;
const CDATA = (this.constructor as typeof LiteParser).CDATA_ATTR;
const tag = adaptor.kind(node);
const attributes = adaptor.allAttributes(node).map(
(x: AttributeData) => x.name + '="' + (CDATA[x.name] ? x.value : this.protectAttribute(x.value)) + '"'
).join(' ');
const content = this.serializeInner(adaptor, node, xml);
const html =
'<' + tag + (attributes ? ' ' + attributes : '')
+ ((!xml || content) && !SELF_CLOSING[tag] ? `>${content}</${tag}>` : xml ? '/>' : '>');
return html;
}
/**
* @param {LiteAdaptor} adaptor The adaptor for managing nodes
* @param {LiteElement} node The node whose innerHTML is needed
* @return {string} The serialized element (like innerHTML)
*/
public serializeInner(adaptor: LiteAdaptor, node: LiteElement, xml: boolean = false): string {
const PCDATA = (this.constructor as typeof LiteParser).PCDATA;
if (PCDATA.hasOwnProperty(node.kind)) {
return adaptor.childNodes(node).map(x => adaptor.value(x)).join('');
}
return adaptor.childNodes(node).map(x => {
const kind = adaptor.kind(x);
return (kind === '#text' ? this.protectHTML(adaptor.value(x)) :
kind === '#comment' ? (x as LiteComment).value :
this.serialize(adaptor, x as LiteElement, xml));
}).join('');
}
/**
* @param {string} text The attribute value to be HTML escaped
* @return {string} The string with " replaced by entities
*/
public protectAttribute(text: string): string {
if (typeof text !== 'string') {
text = String(text);
}
return text.replace(/"/g, '&quot;');
}
/**
* @param {string} text The text to be HTML escaped
* @return {string} The string with &, <, and > replaced by entities
*/
public protectHTML(text: string): string {
return text.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
}

65
node_modules/mathjax-full/ts/adaptors/lite/Text.ts generated vendored Normal file
View File

@@ -0,0 +1,65 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight Text element replacement
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {LiteElement} from './Element.js';
/************************************************************/
/**
* Implements a lightweight Text node replacement
*/
export class LiteText {
/**
* The text stored in the node
*/
public value: string;
/**
* The parent holding this text
*/
public parent: LiteElement;
/**
* The kind of node is #text
*/
public get kind() {
return '#text';
}
/**
* @param {string} text The text for the node
* @constructor
*/
constructor(text: string = '') {
this.value = text;
}
}
/************************************************************/
/**
* Implements a lightweight Comment node replacement
*/
export class LiteComment extends LiteText {
public get kind() {
return '#comment';
}
}

69
node_modules/mathjax-full/ts/adaptors/lite/Window.ts generated vendored Normal file
View File

@@ -0,0 +1,69 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {LiteElement} from './Element.js';
import {LiteDocument} from './Document.js';
import {LiteList} from './List.js';
import {LiteParser} from './Parser.js';
/************************************************************/
/**
* Implements a lightweight Window replacement
*/
export class LiteWindow {
/**
* The window's document instance
*/
public document: LiteDocument;
/**
* The DOMParser class
*/
public DOMParser: typeof LiteParser = LiteParser;
/**
* The NodeList class
*/
public NodeList: typeof LiteList = LiteList;
/**
* The HTMLCollection class
*/
public HTMLCollection: typeof LiteList = LiteList;
/**
* The HTMLElement class
*/
public HTMLElement: typeof LiteElement = LiteElement;
/**
* The DocumentFragment class
*/
public DocumentFragment: typeof LiteList = LiteList;
/**
* The Document class
*/
public Document: typeof LiteDocument = LiteDocument;
/**
* Create the LiteWindow and its LiteDocument
*/
constructor() {
this.document = new LiteDocument();
}
}

603
node_modules/mathjax-full/ts/adaptors/liteAdaptor.ts generated vendored Normal file
View File

@@ -0,0 +1,603 @@
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Implements a lightweight DOM adaptor
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {AbstractDOMAdaptor} from '../core/DOMAdaptor.js';
import {NodeMixin, Constructor} from './NodeMixin.js';
import {LiteDocument} from './lite/Document.js';
import {LiteElement, LiteNode} from './lite/Element.js';
import {LiteText, LiteComment} from './lite/Text.js';
import {LiteList} from './lite/List.js';
import {LiteWindow} from './lite/Window.js';
import {LiteParser} from './lite/Parser.js';
import {Styles} from '../util/Styles.js';
import {OptionList} from '../util/Options.js';
/************************************************************/
/**
* Implements a lightweight DOMAdaptor on liteweight HTML elements
*/
export class LiteBase extends AbstractDOMAdaptor<LiteElement, LiteText, LiteDocument> {
/**
* The document in which the HTML nodes will be created
*/
public document: LiteDocument;
/**
* The window for the document
*/
public window: LiteWindow;
/**
* The parser for serialized HTML
*/
public parser: LiteParser;
/**
* @param {OptionList} options The options for the lite adaptor (e.g., fontSize)
* @constructor
*/
constructor() {
super();
this.parser = new LiteParser();
this.window = new LiteWindow();
}
/**
* @override
*/
public parse(text: string, format?: string): LiteDocument {
return this.parser.parseFromString(text, format, this);
}
/**
* @override
*/
protected create(kind: string, _ns: string = null) {
return new LiteElement(kind);
}
/**
* @override
*/
public text(text: string) {
return new LiteText(text);
}
/**
* @param {string} text The text of the comment
* @return {LiteComment} The comment node
*/
public comment(text: string): LiteComment {
return new LiteComment(text);
}
/**
* @return {LiteDocument} A new document element
*/
public createDocument(): LiteDocument {
return new LiteDocument();
}
/**
* @override
*/
public head(doc: LiteDocument) {
return doc.head;
}
/**
* @override
*/
public body(doc: LiteDocument) {
return doc.body;
}
/**
* @override
*/
public root(doc: LiteDocument) {
return doc.root;
}
/**
* @override
*/
public doctype(doc: LiteDocument) {
return doc.type;
}
/**
* @override
*/
public tags(node: LiteElement, name: string, ns: string = null) {
let stack = [] as LiteNode[];
let tags = [] as LiteElement[];
if (ns) {
return tags; // we don't have namespaces
}
let n: LiteNode = node;
while (n) {
let kind = n.kind;
if (kind !== '#text' && kind !== '#comment') {
n = n as LiteElement;
if (kind === name) {
tags.push(n);
}
if (n.children.length) {
stack = n.children.concat(stack);
}
}
n = stack.shift();
}
return tags;
}
/**
* @param {LiteElement} node The node to be searched
* @param {string} id The id of the node to look for
* @return {LiteElement} The child node having the given id
*/
public elementById(node: LiteElement, id: string): LiteElement {
let stack = [] as LiteNode[];
let n = node as LiteNode;
while (n) {
if (n.kind !== '#text' && n.kind !== '#comment') {
n = n as LiteElement;
if (n.attributes['id'] === id) {
return n;
}
if (n.children.length) {
stack = n.children.concat(stack);
}
}
n = stack.shift();
}
return null;
}
/**
* @param {LiteElement} node The node to be searched
* @param {string} name The name of the class to find
* @return {LiteElement[]} The nodes with the given class
*/
public elementsByClass(node: LiteElement, name: string): LiteElement[] {
let stack = [] as LiteNode[];
let tags = [] as LiteElement[];
let n: LiteNode = node;
while (n) {
if (n.kind !== '#text' && n.kind !== '#comment') {
n = n as LiteElement;
const classes = (n.attributes['class'] || '').trim().split(/ +/);
if (classes.includes(name)) {
tags.push(n);
}
if (n.children.length) {
stack = n.children.concat(stack);
}
}
n = stack.shift();
}
return tags;
}
/**
* @override
*/
public getElements(nodes: (string | LiteElement | LiteElement[])[], document: LiteDocument) {
let containers = [] as LiteElement[];
const body = this.body(document);
for (const node of nodes) {
if (typeof(node) === 'string') {
if (node.charAt(0) === '#') {
const n = this.elementById(body, node.slice(1));
if (n) {
containers.push(n);
}
} else if (node.charAt(0) === '.') {
containers = containers.concat(this.elementsByClass(body, node.slice(1)));
} else if (node.match(/^[-a-z][-a-z0-9]*$/i)) {
containers = containers.concat(this.tags(body, node));
}
} else if (Array.isArray(node)) {
containers = containers.concat(node);
} else if (node instanceof this.window.NodeList || node instanceof this.window.HTMLCollection) {
containers = containers.concat((node as LiteList<LiteElement>).nodes);
} else {
containers.push(node);
}
}
return containers;
}
/**
* @override
*/
public contains(container: LiteNode, node: LiteNode | LiteText) {
while (node && node !== container) {
node = this.parent(node);
}
return !!node;
}
/**
* @override
*/
public parent(node: LiteNode) {
return node.parent;
}
/**
* @param {LiteNode} node The node whose index is needed
* @return {number} The index of the node it its parent's children array
*/
public childIndex(node: LiteNode): number {
return (node.parent ? node.parent.children.findIndex(n => n === node) : -1);
}
/**
* @override
*/
public append(node: LiteElement, child: LiteNode) {
if (child.parent) {
this.remove(child);
}
node.children.push(child);
child.parent = node;
return child;
}
/**
* @override
*/
public insert(nchild: LiteNode, ochild: LiteNode) {
if (nchild.parent) {
this.remove(nchild);
}
if (ochild && ochild.parent) {
const i = this.childIndex(ochild);
ochild.parent.children.splice(i, 0, nchild);
nchild.parent = ochild.parent;
}
}
/**
* @override
*/
public remove(child: LiteNode) {
const i = this.childIndex(child);
if (i >= 0) {
child.parent.children.splice(i, 1);
}
child.parent = null;
return child;
}
/**
* @override
*/
public replace(nnode: LiteNode, onode: LiteNode) {
const i = this.childIndex(onode);
if (i >= 0) {
onode.parent.children[i] = nnode;
nnode.parent = onode.parent;
onode.parent = null;
}
return onode;
}
/**
* @override
*/
public clone(node: LiteElement) {
const nnode = new LiteElement(node.kind);
nnode.attributes = {...node.attributes};
nnode.children = node.children.map(n => {
if (n.kind === '#text') {
return new LiteText((n as LiteText).value);
} else if (n.kind === '#comment') {
return new LiteComment((n as LiteComment).value);
} else {
const m = this.clone(n as LiteElement);
m.parent = nnode;
return m;
}
});
return nnode;
}
/**
* @override
*/
public split(node: LiteText, n: number) {
const text = new LiteText(node.value.slice(n));
node.value = node.value.slice(0, n);
node.parent.children.splice(this.childIndex(node) + 1, 0, text);
text.parent = node.parent;
return text;
}
/**
* @override
*/
public next(node: LiteNode) {
const parent = node.parent;
if (!parent) return null;
const i = this.childIndex(node) + 1;
return (i >= 0 && i < parent.children.length ? parent.children[i] : null);
}
/**
* @override
*/
public previous(node: LiteNode) {
const parent = node.parent;
if (!parent) return null;
const i = this.childIndex(node) - 1;
return (i >= 0 ? parent.children[i] : null);
}
/**
* @override
*/
public firstChild(node: LiteElement) {
return node.children[0];
}
/**
* @override
*/
public lastChild(node: LiteElement) {
return node.children[node.children.length - 1];
}
/**
* @override
*/
public childNodes(node: LiteElement) {
return [...node.children];
}
/**
* @override
*/
public childNode(node: LiteElement, i: number) {
return node.children[i];
}
/**
* @override
*/
public kind(node: LiteNode) {
return node.kind;
}
/**
* @override
*/
public value(node: LiteNode | LiteText) {
return (node.kind === '#text' ? (node as LiteText).value :
node.kind === '#comment' ? (node as LiteComment).value.replace(/^<!(--)?((?:.|\n)*)\1>$/, '$2') : '');
}
/**
* @override
*/
public textContent(node: LiteElement): string {
return node.children.reduce((s: string, n: LiteNode) => {
return s + (n.kind === '#text' ? (n as LiteText).value :
n.kind === '#comment' ? '' : this.textContent(n as LiteElement));
}, '');
}
/**
* @override
*/
public innerHTML(node: LiteElement): string {
return this.parser.serializeInner(this, node);
}
/**
* @override
*/
public outerHTML(node: LiteElement): string {
return this.parser.serialize(this, node);
}
/**
* @override
*/
public serializeXML(node: LiteElement): string {
return this.parser.serialize(this, node, true);
}
/**
* @override
*/
public setAttribute(node: LiteElement, name: string, value: string | number, ns: string = null) {
if (typeof value !== 'string') {
value = String(value);
}
if (ns) {
name = ns.replace(/.*\//, '') + ':' + name.replace(/^.*:/, '');
}
node.attributes[name] = value;
if (name === 'style') {
node.styles = null;
}
}
/**
* @override
*/
public getAttribute(node: LiteElement, name: string) {
return node.attributes[name];
}
/**
* @override
*/
public removeAttribute(node: LiteElement, name: string) {
delete node.attributes[name];
}
/**
* @override
*/
public hasAttribute(node: LiteElement, name: string) {
return node.attributes.hasOwnProperty(name);
}
/**
* @override
*/
public allAttributes(node: LiteElement) {
const attributes = node.attributes;
const list = [];
for (const name of Object.keys(attributes)) {
list.push({name: name, value: attributes[name] as string});
}
return list;
}
/**
* @override
*/
public addClass(node: LiteElement, name: string) {
const classes = (node.attributes['class'] as string || '').split(/ /);
if (!classes.find(n => n === name)) {
classes.push(name);
node.attributes['class'] = classes.join(' ');
}
}
/**
* @override
*/
public removeClass(node: LiteElement, name: string) {
const classes = (node.attributes['class'] as string || '').split(/ /);
const i = classes.findIndex(n => n === name);
if (i >= 0) {
classes.splice(i, 1);
node.attributes['class'] = classes.join(' ');
}
}
/**
* @override
*/
public hasClass(node: LiteElement, name: string) {
const classes = (node.attributes['class'] as string || '').split(/ /);
return !!classes.find(n => n === name);
}
/**
* @override
*/
public setStyle(node: LiteElement, name: string, value: string) {
if (!node.styles) {
node.styles = new Styles(this.getAttribute(node, 'style'));
}
node.styles.set(name, value);
node.attributes['style'] = node.styles.cssText;
}
/**
* @override
*/
public getStyle(node: LiteElement, name: string) {
if (!node.styles) {
const style = this.getAttribute(node, 'style');
if (!style) {
return '';
}
node.styles = new Styles(style);
}
return node.styles.get(name);
}
/**
* @override
*/
public allStyles(node: LiteElement) {
return this.getAttribute(node, 'style');
}
/**
* @override
*/
public insertRules(node: LiteElement, rules: string[]) {
node.children = [this.text(rules.join('\n\n') + '\n\n' + this.textContent(node))];
}
/*******************************************************************/
/*
* The next four methods get overridden by the NodeMixin below
*/
/**
* @override
*/
public fontSize(_node: LiteElement) {
return 0;
}
/**
* @override
*/
public fontFamily(_node: LiteElement) {
return '';
}
/**
* @override
*/
public nodeSize(_node: LiteElement, _em: number = 1, _local: boolean = null) {
return [0, 0] as [number, number];
}
/**
* @override
*/
public nodeBBox(_node: LiteElement) {
return {left: 0, right: 0, top: 0, bottom: 0};
}
}
/**
* The LiteAdaptor class (add in the NodeMixin methods and options)
*/
export class LiteAdaptor extends NodeMixin<LiteElement, LiteText, LiteDocument, Constructor<LiteBase>>(LiteBase) {}
/************************************************************/
/**
* The function to call to obtain a LiteAdaptor
*
* @param {OptionList} options The options for the adaptor
* @return {LiteAdaptor} The newly created adaptor
*/
export function liteAdaptor(options: OptionList = null): LiteAdaptor {
return new LiteAdaptor(null, options);
}