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

127
node_modules/mathjax-full/ts/ui/safe/SafeHandler.ts generated vendored Normal file
View File

@@ -0,0 +1,127 @@
/*************************************************************
*
* Copyright (c) 2020-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 MathItem, MathDocument, and Handler for the safe extension
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {MathItem} from '../../core/MathItem.js';
import {MathDocument, MathDocumentConstructor} from '../../core/MathDocument.js';
import {Handler} from '../../core/Handler.js';
import {Safe} from './safe.js';
/*==========================================================================*/
/**
* Generic constructor for Mixins
*/
export type Constructor<T> = new(...args: any[]) => T;
/*==========================================================================*/
/**
* The properties needed in the MathDocument for sanitizing the internal MathML
*/
export interface SafeMathDocument<N, T, D> extends MathDocument<N, T, D> {
/**
* The Safe object for this document
*/
safe: Safe<N, T, D>;
}
/**
* The mixin for adding safe render action to MathDocuments
*
* @param {B} BaseDocument The MathDocument class to be extended
* @return {SafeMathDocument<N,T,D>} The extended MathDocument class
*/
export function SafeMathDocumentMixin<N, T, D, B extends MathDocumentConstructor<MathDocument<N, T, D>>>(
BaseDocument: B
): Constructor<SafeMathDocument<N, T, D>> & B {
return class extends BaseDocument {
/**
* @override
*/
public static OPTIONS = {
...BaseDocument.OPTIONS,
safeOptions: {
...Safe.OPTIONS,
},
SafeClass: Safe
};
/**
* An instance of the Safe object
*/
public safe: Safe<N, T, D>;
/**
* Extend the MathItem class used for this MathDocument
*
* @override
* @constructor
*/
constructor(...args: any[]) {
super(...args);
this.safe = new this.options.SafeClass(this, this.options.safeOptions);
const ProcessBits = (this.constructor as typeof BaseDocument).ProcessBits;
if (!ProcessBits.has('safe')) {
ProcessBits.allocate('safe');
}
for (const jax of this.inputJax) {
if (jax.name.match(/MathML/)) {
(jax as any).mathml.filterAttribute = this.safe.mmlAttribute.bind(this.safe);
(jax as any).mathml.filterClassList = this.safe.mmlClassList.bind(this.safe);
} else if (jax.name.match(/TeX/)) {
jax.postFilters.add(this.sanitize.bind(jax), -5.5);
}
}
}
/**
* @param {{document:SafeDocument<N,T,D>}} data The document to use for the filter
* (note: this has been bound to the input jax)
*/
protected sanitize(data: {math: MathItem<N, T, D>, document: SafeMathDocument<N, T, D>}) {
data.math.root = (this as any).parseOptions.root;
data.document.safe.sanitize(data.math, data.document);
}
};
}
/*==========================================================================*/
/**
* Add context-menu support to a Handler instance
*
* @param {Handler} handler The Handler instance to enhance
* @return {Handler} The handler that was modified (for purposes of chaining extensions)
*/
export function SafeHandler<N, T, D>(handler: Handler<N, T, D>): Handler<N, T, D> {
handler.documentClass = SafeMathDocumentMixin(handler.documentClass);
return handler;
}

288
node_modules/mathjax-full/ts/ui/safe/SafeMethods.ts generated vendored Normal file
View File

@@ -0,0 +1,288 @@
/*************************************************************
*
* Copyright (c) 2020-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 Support functions for the safe extension
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {length2em} from '../../util/lengths.js';
import {Safe, FilterFunction} from './safe.js';
/**
* The default attribute-filtering functions
*/
export const SafeMethods: {[name: string]: FilterFunction<any, any, any>} = {
/**
* Filter HREF URL's
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} url The URL being tested
* @return {string|null} The URL if OK and null if not
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterURL<N, T, D>(safe: Safe<N, T, D>, url: string): string | null {
const protocol = (url.match(/^\s*([a-z]+):/i) || [null, ''])[1].toLowerCase();
const allow = safe.allow.URLs;
return (allow === 'all' || (allow === 'safe' &&
(safe.options.safeProtocols[protocol] || !protocol))) ? url : null;
},
/**
* Filter a class list
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} list The class list being tested
* @return {string|null} The class list if OK and null if not
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterClassList<N, T, D>(safe: Safe<N, T, D>, list: string): string | null {
const classes = list.trim().replace(/\s\s+/g, ' ').split(/ /);
return classes.map((name) => this.filterClass(safe, name) || '').join(' ').trim().replace(/\s\s+/g, '');
},
/**
* Filter a class name
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} CLASS The class being tested
* @return {string|null} The class if OK and null if not
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterClass<N, T, D>(safe: Safe<N, T, D>, CLASS: string): string | null {
const allow = safe.allow.classes;
return (allow === 'all' || (allow === 'safe' && CLASS.match(safe.options.classPattern))) ? CLASS : null;
},
/**
* Filter ids
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} id The id being tested
* @return {string|null} The id if OK and null if not
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterID<N, T, D>(safe: Safe<N, T, D>, id: string): string | null {
const allow = safe.allow.cssIDs;
return (allow === 'all' || (allow === 'safe' && id.match(safe.options.idPattern))) ? id : null;
},
/**
* Filter style strings
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} styles The style string being tested
* @return {string} The sanitized style string
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterStyles<N, T, D>(safe: Safe<N, T, D>, styles: string): string {
if (safe.allow.styles === 'all') return styles;
if (safe.allow.styles !== 'safe') return null;
const adaptor = safe.adaptor;
const options = safe.options;
try {
//
// Create div1 with styles set to the given styles, and div2 with blank styles
//
const div1 = adaptor.node('div', {style: styles});
const div2 = adaptor.node('div');
//
// Check each allowed style and transfer OK ones to div2
// If the style has Top/Right/Bottom/Left, look at all four separately
//
for (const style of Object.keys(options.safeStyles)) {
if (options.styleParts[style]) {
for (const sufix of ['Top', 'Right', 'Bottom', 'Left']) {
const name = style + sufix;
const value = this.filterStyle(safe, name, div1);
if (value) {
adaptor.setStyle(div2, name, value);
}
}
} else {
const value = this.filterStyle(safe, style, div1);
if (value) {
adaptor.setStyle(div2, style, value);
}
}
}
//
// Return the div2 style string
//
styles = adaptor.allStyles(div2);
} catch (err) {
styles = '';
}
return styles;
},
/**
* Filter an individual name:value style pair
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} style The style name being tested
* @param {N} div The temp DIV node containing the style object to be tested
* @return {string|null} The sanitized style string or null if invalid
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterStyle<N, T, D>(safe: Safe<N, T, D>, style: string, div: N): string | null {
const value = safe.adaptor.getStyle(div, style);
if (typeof value !== 'string' || value === '' || value.match(/^\s*calc/) ||
(value.match(/javascript:/) && !safe.options.safeProtocols.javascript) ||
(value.match(/data:/) && !safe.options.safeProtocols.data)) {
return null;
}
const name = style.replace(/Top|Right|Left|Bottom/, '');
if (!safe.options.safeStyles[style] && !safe.options.safeStyles[name]) {
return null;
}
return this.filterStyleValue(safe, style, value, div);
},
/**
* Filter a style's value, handling compound values (e.g., borders that have widths as well as styles and colors)
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} style The style name being tested
* @param {string} value The value of the style to test
* @param {N} div The temp DIV node containing the style object to be tested
* @return {string|null} The sanitized style string or null if invalid
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterStyleValue<N, T, D>(safe: Safe<N, T, D>, style: string, value: string, div: N): string | null {
const name = safe.options.styleLengths[style];
if (!name) {
return value;
}
if (typeof name !== 'string') {
return this.filterStyleLength(safe, style, value);
}
const length = this.filterStyleLength(safe, name, safe.adaptor.getStyle(div, name));
if (!length) {
return null;
}
safe.adaptor.setStyle(div, name, length);
return safe.adaptor.getStyle(div, style);
},
/**
* Filter a length value
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} style The style name being tested
* @param {string} value The value of the style to test
* @return {string|null} The sanitized length value
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterStyleLength<N, T, D>(safe: Safe<N, T, D>, style: string, value: string): string | null {
if (!value.match(/^(.+)(em|ex|ch|rem|px|mm|cm|in|pt|pc|%)$/)) return null;
const em = length2em(value, 1);
const lengths = safe.options.styleLengths[style];
const [m, M] = (Array.isArray(lengths) ? lengths : [-safe.options.lengthMax, safe.options.lengthMax]);
return (m <= em && em <= M ? value : (em < m ? m : M).toFixed(3).replace(/\.?0+$/, '') + 'em');
},
/**
* Filter a font size
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} size The font size to test
* @return {string|null} The sanitized style string or null if invalid
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterFontSize<N, T, D>(safe: Safe<N, T, D>, size: string): string | null {
return this.filterStyleLength(safe, 'fontSize', size);
},
/**
* Filter scriptsizemultiplier
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} size The script size multiplier to test
* @return {string} The sanitized size
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterSizeMultiplier<N, T, D>(safe: Safe<N, T, D>, size: string): string {
const [m, M] = safe.options.scriptsizemultiplierRange || [-Infinity, Infinity];
return Math.min(M, Math.max(m, parseFloat(size))).toString();
},
/**
* Filter scriptLevel
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} size The scriptlevel to test
* @return {string|null} The sanitized scriptlevel or null
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterScriptLevel<N, T, D>(safe: Safe<N, T, D>, level: string): string | null {
const [m, M] = safe.options.scriptlevelRange || [-Infinity, Infinity];
return Math.min(M, Math.max(m, parseInt(level))).toString();
},
/**
* Filter a data-* attribute
*
* @param {Safe<N,T,D>} safe The Safe object being used
* @param {string} value The attribute's value
* @param {string} id The attribute's id (e.g., data-mjx-variant)
* @return {number|null} The sanitized value or null
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
filterData<N, T, D>(safe: Safe<N, T, D>, value: string, id: string): string | null {
return (id.match(safe.options.dataPattern) ? value : null);
}
};

270
node_modules/mathjax-full/ts/ui/safe/safe.ts generated vendored Normal file
View File

@@ -0,0 +1,270 @@
/*************************************************************
*
* Copyright (c) 2020-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 Support for the safe extension
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {Property} from '../../core/Tree/Node.js';
import {MmlNode} from '../../core/MmlTree/MmlNode.js';
import {MathItem} from '../../core/MathItem.js';
import {MathDocument} from '../../core/MathDocument.js';
import {OptionList, expandable} from '../../util/Options.js';
import {DOMAdaptor} from '../../core/DOMAdaptor.js';
import {SafeMethods} from './SafeMethods.js';
/**
* Function type for filtering attributes
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export type FilterFunction<N, T, D> = (safe: Safe<N, T, D>, value: Property, ...args: any[]) => Property;
/**
* The Safe object for sanitizing the internal MathML representation of an expression
*
* @template N The HTMLElement node class
* @template T The Text node class
* @template D The Document class
*/
export class Safe<N, T, D> {
/**
* The options controlling the handling of the safe extension
*/
public static OPTIONS: OptionList = {
allow: {
//
// Values can be "all", "safe", or "none"
//
URLs: 'safe', // safe are in safeProtocols below
classes: 'safe', // safe start with mjx- (can be set by pattern below)
cssIDs: 'safe', // safe start with mjx- (can be set by pattern below)
styles: 'safe' // safe are in safeStyles below
},
//
// Largest padding/border/margin, etc. in em's
//
lengthMax: 3,
//
// Valid range for scriptsizemultiplier
//
scriptsizemultiplierRange: [.6, 1],
//
// Valid range for scriptlevel
//
scriptlevelRange: [-2, 2],
//
// Pattern for allowed class names
//
classPattern: /^mjx-[-a-zA-Z0-9_.]+$/,
//
// Pattern for allowed ids
//
idPattern: /^mjx-[-a-zA-Z0-9_.]+$/,
//
// Pattern for data attributes
//
dataPattern: /^data-mjx-/,
//
// Which URL protocols are allowed
//
safeProtocols: expandable({
http: true,
https: true,
file: true,
javascript: false,
data: false
}),
//
// Which styles are allowed
//
safeStyles: expandable({
color: true,
backgroundColor: true,
border: true,
cursor: true,
margin: true,
padding: true,
textShadow: true,
fontFamily: true,
fontSize: true,
fontStyle: true,
fontWeight: true,
opacity: true,
outline: true
}),
//
// CSS styles that have Top/Right/Bottom/Left versions
//
styleParts: expandable({
border: true,
padding: true,
margin: true,
outline: true
}),
//
// CSS styles that are lengths needing max/min testing
// A string value means test that style value;
// An array gives [min,max] in em's
// Otherwise use [-lengthMax,lengthMax] from above
//
styleLengths: expandable({
borderTop: 'borderTopWidth',
borderRight: 'borderRightWidth',
borderBottom: 'borderBottomWidth',
borderLeft: 'borderLeftWidth',
paddingTop: true,
paddingRight: true,
paddingBottom: true,
paddingLeft: true,
marginTop: true,
marginRight: true,
marginBottom: true,
marginLeft: true,
outlineTop: true,
outlineRight: true,
outlineBottom: true,
outlineLeft: true,
fontSize: [.707, 1.44]
})
};
/**
* The attribute-to-filter-method mapping
*/
public filterAttributes: Map<string, string> = new Map([
//
// Methods called for MathML attribute processing
//
['href', 'filterURL'],
['src', 'filterURL'],
['altimg', 'filterURL'],
['class', 'filterClassList'],
['style', 'filterStyles'],
['id', 'filterID'],
['fontsize', 'filterFontSize'],
['mathsize', 'filterFontSize'],
['scriptminsize', 'filterFontSize'],
['scriptsizemultiplier', 'filterSizeMultiplier'],
['scriptlevel', 'filterScriptLevel'],
['data-', 'filterData']
]);
/**
* The safe options from the document option list
*/
public options: OptionList;
/**
* Shorthand for options.allow
*/
public allow: OptionList;
/**
* The DOM adaptor from the document
*/
public adaptor: DOMAdaptor<N, T, D>;
/**
* The methods for filtering the MathML attributes
*/
public filterMethods: {[name: string]: FilterFunction<N, T, D>} = {
...SafeMethods
};
/**
* @param {MathDocument<N,T,D>} document The MathDocument we are sanitizing
* @param {OptionList} options The safeOptions from the document
*/
constructor(document: MathDocument<N, T, D>, options: OptionList) {
this.adaptor = document.adaptor;
this.options = options;
this.allow = this.options.allow;
}
/**
* Sanitize a MathItem's root MathML tree
*
* @param {MathItem<N,T,D>} math The MathItem to sanitize
* @param {MathDocument<N,T,D>} document The MathDocument in which it lives
*/
public sanitize(math: MathItem<N, T, D>, document: MathDocument<N, T, D>) {
try {
math.root.walkTree(this.sanitizeNode.bind(this));
} catch (err) {
document.options.compileError(document, math, err);
}
}
/**
* Sanitize a node's attributes
*
* @param {MmlNode} node The node to sanitize
*/
protected sanitizeNode(node: MmlNode) {
const attributes = node.attributes.getAllAttributes();
for (const id of Object.keys(attributes)) {
const method = this.filterAttributes.get(id);
if (method) {
const value = this.filterMethods[method](this, attributes[id]);
if (value) {
if (value !== (typeof value === 'number' ? parseFloat(attributes[id] as string) : attributes[id])) {
attributes[id] = value;
}
} else {
delete attributes[id];
}
}
}
}
/**
* Sanitize a MathML input attribute
*
* @param {string} id The name of the attribute
* @param {string} value The value of the attribute
* @return {string|null} The sanitized value
*/
public mmlAttribute(id: string, value: string): string | null {
if (id === 'class') return null;
const method = this.filterAttributes.get(id);
const filter = (method || (id.substr(0, 5) === 'data-' ? this.filterAttributes.get('data-') : null));
if (!filter) {
return value;
}
const result = this.filterMethods[filter](this, value, id);
return (typeof result === 'number' || typeof result === 'boolean' ? String(result) : result);
}
/**
* Sanitize a list of class names
*
* @param {string[]} list The list of class names
* @return {string[]} The sanitized list
*/
public mmlClassList(list: string[]): string[] {
return list.map((name) => this.filterMethods.filterClass(this, name) as string)
.filter((value) => value !== null);
}
}