完成世界书、骰子、apiconfig页面处理

This commit is contained in:
2026-04-30 01:35:10 +08:00
parent a3e3711b2b
commit ba9b925c32
4602 changed files with 785225 additions and 23 deletions

114
frontend/node_modules/katex/cli.js generated vendored Normal file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env node
// Simple CLI for KaTeX.
// Reads TeX from stdin, outputs HTML to stdout.
// To run this from the repository, you must first build KaTeX by running
// `yarn` and `yarn build`.
/* eslint no-console:0 */
let katex;
try {
katex = require("./");
} catch (e) {
console.error(
"KaTeX could not import, likely because dist/katex.js is missing.");
console.error("Please run 'yarn' and 'yarn build' before running");
console.error("cli.js from the KaTeX repository.");
console.error();
throw e;
}
const {version} = require("./package.json");
const fs = require("fs");
const {program} = require("commander");
program.version(version);
for (const prop in katex.SETTINGS_SCHEMA) {
if (katex.SETTINGS_SCHEMA.hasOwnProperty(prop)) {
const opt = katex.SETTINGS_SCHEMA[prop];
if (opt.cli !== false) {
program.option(opt.cli || "--" + prop, opt.cliDescription ||
opt.description, opt.cliProcessor, opt.cliDefault);
}
}
}
program.option("-f, --macro-file <path>",
"Read macro definitions, one per line, from the given file.")
.option("-i, --input <path>", "Read LaTeX input from the given file.")
.option("-o, --output <path>", "Write html output to the given file.");
let options;
function readMacros() {
if (options.macroFile) {
fs.readFile(options.macroFile, "utf-8", function(err, data) {
if (err) {throw err;}
splitMacros(data.toString().split('\n'));
});
} else {
splitMacros([]);
}
}
function splitMacros(macroStrings) {
// Override macros from macro file (if any)
// with macros from command line (if any)
macroStrings = macroStrings.concat(options.macro);
const macros = {};
for (const m of macroStrings) {
const i = m.search(":");
if (i !== -1) {
macros[m.substring(0, i).trim()] = m.substring(i + 1).trim();
}
}
options.macros = macros;
readInput();
}
function readInput() {
let input = "";
if (options.input) {
fs.readFile(options.input, "utf-8", function(err, data) {
if (err) {throw err;}
input = data.toString();
writeOutput(input);
});
} else {
process.stdin.on("data", function(chunk) {
input += chunk.toString();
});
process.stdin.on("end", function() {
writeOutput(input);
});
}
}
function writeOutput(input) {
// --format specifies the KaTeX output
const outputFile = options.output;
options.output = options.format;
const output = katex.renderToString(input, options) + "\n";
if (outputFile) {
fs.writeFile(outputFile, output, function(err) {
if (err) {
return console.log(err);
}
});
} else {
console.log(output);
}
}
if (require.main !== module) {
module.exports = program;
} else {
options = program.parse(process.argv).opts();
readMacros();
}

View File

@@ -0,0 +1,8 @@
# Auto-render extension
This is an extension to automatically render all of the math inside of text. It
searches all of the text nodes in a given element for the given delimiters, and
renders the math in place.
See [Auto-render extension documentation](https://katex.org/docs/autorender.html)
for more information.

View File

@@ -0,0 +1,38 @@
# `math/tex` Custom Script Type Extension
This is an extension to automatically display code inside `script` tags with `type=math/tex` using KaTeX.
This script type is commonly used by MathJax, so this can be used to support compatibility with MathJax.
### Usage
This extension isn't part of KaTeX proper, so the script should be separately
included in the page, in addition to KaTeX.
Load the extension by adding the following line to your HTML file.
```html
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.38/dist/contrib/mathtex-script-type.min.js" integrity="sha384-Va76RKpsqLRTaW8meIebMfcIo7cxNDc0uKaZNSuZzckwzNtDa3Xf77LciJ0CAjIC" crossorigin="anonymous"></script>
```
You can download the script and use it locally, or from a local KaTeX installation instead.
For example, in the following simple page, we first load KaTeX as usual.
Then, in the body, we use a `math/tex` script to typeset the equation `x+\sqrt{1-x^2}`.
```html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.38/dist/katex.min.css" integrity="sha384-/L6i+LN3dyoaK2jYG5ZLh5u13cjdsPDcFOSNJeFBFa/KgVXR5kOfTdiN3ft1uMAq" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.38/dist/katex.min.js" integrity="sha384-H6s1ZrH2CKpFpqR680poRdStIRJGXty7fSkxAcIfxwl9iu6A4BOPtTk7vQ58Ovio" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.38/dist/contrib/mathtex-script-type.min.js" integrity="sha384-Va76RKpsqLRTaW8meIebMfcIo7cxNDc0uKaZNSuZzckwzNtDa3Xf77LciJ0CAjIC" crossorigin="anonymous"></script>
</head>
<body>
<script type="math/tex">x+\sqrt{1-x^2}</script>
</body>
</html>
```
ECMAScript module is also available:
```html
<script type="module" src="https://cdn.jsdelivr.net/npm/katex@0.16.38/dist/contrib/mathtex-script-type.mjs" integrity="sha384-NG25TLm9zCgNw3KKNK+Ks1i5zmDB4v17CYL2TVA06JcpsALdnFs/0TabnkhyjbQb" crossorigin="anonymous"></script>

23
frontend/node_modules/katex/contrib/mhchem/README.md generated vendored Normal file
View File

@@ -0,0 +1,23 @@
# mhchem extension
This extension adds to KaTeX the `\ce` and `\pu` functions from the [mhchem](https://mhchem.github.io/MathJax-mhchem/) package.
### Usage
This extension isn't part of core KaTeX, so the script should be separately included. Write the following line into the HTML page's `<head>`. Place it *after* the line that calls `katex.js`, and if you make use of the [auto-render](https://katex.org/docs/autorender.html) extension, place it *before* the line that calls `auto-render.js`.
```html
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.38/dist/contrib/mhchem.min.js" integrity="sha384-fB8BH//9nBzROkMUsu/Dr35jWHIbnKesUo9rW0hfEgw8mZGnkAyBAjKX9F98OVuo" crossorigin="anonymous"></script>
```
If you remove the `defer` attribute from this tag, then you must also remove the `defer` attribute from the `<script src="https://../katex.min.js">` tag.
### Syntax
See the [mhchem Manual](https://mhchem.github.io/MathJax-mhchem/) for a full explanation of the input syntax, with working examples. The manual also includes a demonstration box.
Note that old versions of `mhchem.sty` used `\cf` for chemical formula and `\ce` for chemical equations, but `\cf` has been deprecated in place of `\ce`. This extension supports only `\ce`. You can define a macro mapping `\cf` to `\ce` if needed.
### Browser Support
This extension has been tested on Chrome, Firefox, Opera, and Edge.

305
frontend/node_modules/katex/dist/contrib/auto-render.js generated vendored Normal file
View File

@@ -0,0 +1,305 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("katex"));
else if(typeof define === 'function' && define.amd)
define(["katex"], factory);
else if(typeof exports === 'object')
exports["renderMathInElement"] = factory(require("katex"));
else
root["renderMathInElement"] = factory(root["katex"]);
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__757__) {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 757:
/***/ (function(module) {
module.exports = __WEBPACK_EXTERNAL_MODULE__757__;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": function() { return /* binding */ auto_render; }
});
// EXTERNAL MODULE: external "katex"
var external_katex_ = __webpack_require__(757);
var external_katex_default = /*#__PURE__*/__webpack_require__.n(external_katex_);
;// ./contrib/auto-render/splitAtDelimiters.ts
/* eslint no-constant-condition:0 */
const findEndOfMath = function (delimiter, text, startIndex) {
// Adapted from
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
let index = startIndex;
let braceLevel = 0;
const delimLength = delimiter.length;
while (index < text.length) {
const character = text[index];
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
return index;
} else if (character === "\\") {
index++;
} else if (character === "{") {
braceLevel++;
} else if (character === "}") {
braceLevel--;
}
index++;
}
return -1;
};
const escapeRegex = function (string) {
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
};
const amsRegex = /^\\begin{/;
const splitAtDelimiters = function (text, delimiters) {
let index;
const data = [];
const regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
while (true) {
index = text.search(regexLeft);
if (index === -1) {
break;
}
if (index > 0) {
data.push({
type: "text",
data: text.slice(0, index)
});
text = text.slice(index); // now text starts with delimiter
}
// ... so this always succeeds:
const i = delimiters.findIndex(delim => text.startsWith(delim.left));
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
if (index === -1) {
break;
}
const rawData = text.slice(0, index + delimiters[i].right.length);
const math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
data.push({
type: "math",
data: math,
rawData,
display: delimiters[i].display
});
text = text.slice(index + delimiters[i].right.length);
}
if (text !== "") {
data.push({
type: "text",
data: text
});
}
return data;
};
/* harmony default export */ var auto_render_splitAtDelimiters = (splitAtDelimiters);
;// ./contrib/auto-render/auto-render.ts
/* eslint no-console:0 */
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
* API, we should copy it before mutating.
*/
const renderMathInText = function (text, optionsCopy) {
const data = auto_render_splitAtDelimiters(text, optionsCopy.delimiters);
if (data.length === 1 && data[0].type === 'text') {
// There is no formula in the text.
// Let's return null which means there is no need to replace
// the current text node with a new one.
return null;
}
const fragment = document.createDocumentFragment();
for (let i = 0; i < data.length; i++) {
if (data[i].type === "text") {
fragment.appendChild(document.createTextNode(data[i].data));
} else {
const span = document.createElement("span");
let math = data[i].data;
// Override any display mode defined in the settings with that
// defined by the text itself
optionsCopy.displayMode = data[i].display;
try {
if (optionsCopy.preProcess) {
math = optionsCopy.preProcess(math);
}
external_katex_default().render(math, span, optionsCopy);
} catch (e) {
if (!(e instanceof (external_katex_default()).ParseError)) {
throw e;
}
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
fragment.appendChild(document.createTextNode(data[i].rawData));
continue;
}
fragment.appendChild(span);
}
}
return fragment;
};
const renderElem = function (elem, optionsCopy) {
for (let i = 0; i < elem.childNodes.length; i++) {
const childNode = elem.childNodes[i];
if (childNode.nodeType === 3) {
var _childNode$textConten;
// Text node
// Concatenate all sibling text nodes.
// Webkit browsers split very large text nodes into smaller ones,
// so the delimiters may be split across different nodes.
let textContentConcat = (_childNode$textConten = childNode.textContent) != null ? _childNode$textConten : "";
let sibling = childNode.nextSibling;
let nSiblings = 0;
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
var _sibling$textContent;
textContentConcat += (_sibling$textContent = sibling.textContent) != null ? _sibling$textContent : "";
sibling = sibling.nextSibling;
nSiblings++;
}
const frag = renderMathInText(textContentConcat, optionsCopy);
if (frag) {
// Remove extra text nodes
for (let j = 0; j < nSiblings; j++) {
childNode.nextSibling.remove();
}
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
} else {
// If the concatenated text does not contain math
// the siblings will not either
i += nSiblings;
}
} else if (childNode.nodeType === 1) {
// Element node
const className = ' ' + childNode.className + ' ';
const shouldRender = !optionsCopy.ignoredTags.has(childNode.nodeName.toLowerCase()) && optionsCopy.ignoredClasses.every(x => !className.includes(' ' + x + ' '));
if (shouldRender) {
renderElem(childNode, optionsCopy);
}
}
// Otherwise, it's something else, and ignore it.
}
};
const renderMathInElement = function (elem, options) {
if (!elem) {
throw new Error("No element provided to render");
}
const optionsCopy = {};
Object.assign(optionsCopy, options);
// default options
optionsCopy.delimiters = optionsCopy.delimiters || [{
left: "$$",
right: "$$",
display: true
}, {
left: "\\(",
right: "\\)",
display: false
},
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
// {left: "$", right: "$", display: false},
// $ must come after $$
// Render AMS environments even if outside $$…$$ delimiters.
{
left: "\\begin{equation}",
right: "\\end{equation}",
display: true
}, {
left: "\\begin{align}",
right: "\\end{align}",
display: true
}, {
left: "\\begin{alignat}",
right: "\\end{alignat}",
display: true
}, {
left: "\\begin{gather}",
right: "\\end{gather}",
display: true
}, {
left: "\\begin{CD}",
right: "\\end{CD}",
display: true
}, {
left: "\\[",
right: "\\]",
display: true
}];
optionsCopy.ignoredTags = new Set((options == null ? void 0 : options.ignoredTags) || ["script", "noscript", "style", "textarea", "pre", "code", "option"]);
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
// Enable sharing of global macros defined via `\gdef` between different
// math elements within a single call to `renderMathInElement`.
optionsCopy.macros = optionsCopy.macros || {};
renderElem(elem, optionsCopy);
};
/* harmony default export */ var auto_render = (renderMathInElement);
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});

113
frontend/node_modules/katex/dist/contrib/copy-tex.js generated vendored Normal file
View File

@@ -0,0 +1,113 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})((typeof self !== 'undefined' ? self : this), function() {
return /******/ (function() { // webpackBootstrap
/******/ "use strict";
var __webpack_exports__ = {};
;// ./contrib/copy-tex/katex2tex.ts
// Set these to how you want inline and display math to be delimited.
const defaultCopyDelimiters = {
inline: ['$', '$'],
// alternative: ['\(', '\)']
display: ['$$', '$$'] // alternative: ['\[', '\]']
};
// Replace .katex elements with their TeX source (<annotation> element).
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
// as in copy-tex.js.
function katexReplaceWithTex(fragment, copyDelimiters) {
if (copyDelimiters === void 0) {
copyDelimiters = defaultCopyDelimiters;
}
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
// (which will get replaced below).
const katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
for (let i = 0; i < katexHtml.length; i++) {
const element = katexHtml[i];
if (element.remove) {
element.remove();
} else if (element.parentNode) {
element.parentNode.removeChild(element);
}
}
// Replace .katex-mathml elements with their annotation (TeX source)
// descendant, with inline delimiters.
const katexMathml = fragment.querySelectorAll('.katex-mathml');
for (let i = 0; i < katexMathml.length; i++) {
const element = katexMathml[i];
const texSource = element.querySelector('annotation');
if (texSource) {
if (element.replaceWith) {
element.replaceWith(texSource);
} else if (element.parentNode) {
element.parentNode.replaceChild(texSource, element);
}
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
}
}
// Switch display math to display delimiters.
const displays = fragment.querySelectorAll('.katex-display annotation');
for (let i = 0; i < displays.length; i++) {
const element = displays[i];
element.innerHTML = copyDelimiters.display[0] + element.innerHTML.substr(copyDelimiters.inline[0].length, element.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
}
return fragment;
}
/* harmony default export */ var katex2tex = (katexReplaceWithTex);
;// ./contrib/copy-tex/copy-tex.ts
// Return <div class="katex"> element containing node, or null if not found.
function closestKatex(node) {
// If node is a Text Node, for example, go up to containing Element,
// where we can apply the `closest` method.
const element = node instanceof Element ? node : node.parentElement;
return element && element.closest('.katex');
}
// Global copy handler to modify behavior on/within .katex elements.
document.addEventListener('copy', function (event) {
const selection = window.getSelection();
if (!selection || selection.isCollapsed || !event.clipboardData) {
return; // default action OK if selection is empty or unchangeable
}
const clipboardData = event.clipboardData;
const range = selection.getRangeAt(0);
// When start point is within a formula, expand to entire formula.
const startKatex = closestKatex(range.startContainer);
if (startKatex) {
range.setStartBefore(startKatex);
}
// Similarly, when end point is within a formula, expand to entire formula.
const endKatex = closestKatex(range.endContainer);
if (endKatex) {
range.setEndAfter(endKatex);
}
const fragment = range.cloneContents();
if (!fragment.querySelector('.katex-mathml')) {
return; // default action OK if no .katex-mathml elements
}
const htmlContents = Array.prototype.map.call(fragment.childNodes, el => el instanceof Text ? el.textContent : el.outerHTML).join('');
// Preserve usual HTML copy/paste behavior.
clipboardData.setData('text/html', htmlContents);
// Rewrite plain-text version.
clipboardData.setData('text/plain', katex2tex(fragment).textContent);
// Prevent normal copy handling.
event.preventDefault();
});
__webpack_exports__ = __webpack_exports__["default"];
/******/ return __webpack_exports__;
/******/ })()
;
});

85
frontend/node_modules/katex/dist/contrib/copy-tex.mjs generated vendored Normal file
View File

@@ -0,0 +1,85 @@
// Set these to how you want inline and display math to be delimited.
var defaultCopyDelimiters = {
inline: ['$', '$'],
// alternative: ['\(', '\)']
display: ['$$', '$$'] // alternative: ['\[', '\]']
};
// Replace .katex elements with their TeX source (<annotation> element).
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
// as in copy-tex.js.
function katexReplaceWithTex(fragment, copyDelimiters) {
if (copyDelimiters === void 0) {
copyDelimiters = defaultCopyDelimiters;
}
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
// (which will get replaced below).
var katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
for (var i = 0; i < katexHtml.length; i++) {
var element = katexHtml[i];
if (element.remove) {
element.remove();
} else if (element.parentNode) {
element.parentNode.removeChild(element);
}
}
// Replace .katex-mathml elements with their annotation (TeX source)
// descendant, with inline delimiters.
var katexMathml = fragment.querySelectorAll('.katex-mathml');
for (var _i = 0; _i < katexMathml.length; _i++) {
var _element = katexMathml[_i];
var texSource = _element.querySelector('annotation');
if (texSource) {
if (_element.replaceWith) {
_element.replaceWith(texSource);
} else if (_element.parentNode) {
_element.parentNode.replaceChild(texSource, _element);
}
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
}
}
// Switch display math to display delimiters.
var displays = fragment.querySelectorAll('.katex-display annotation');
for (var _i2 = 0; _i2 < displays.length; _i2++) {
var _element2 = displays[_i2];
_element2.innerHTML = copyDelimiters.display[0] + _element2.innerHTML.substr(copyDelimiters.inline[0].length, _element2.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
}
return fragment;
}
// Return <div class="katex"> element containing node, or null if not found.
function closestKatex(node) {
// If node is a Text Node, for example, go up to containing Element,
// where we can apply the `closest` method.
var element = node instanceof Element ? node : node.parentElement;
return element && element.closest('.katex');
}
// Global copy handler to modify behavior on/within .katex elements.
document.addEventListener('copy', function (event) {
var selection = window.getSelection();
if (!selection || selection.isCollapsed || !event.clipboardData) {
return; // default action OK if selection is empty or unchangeable
}
var clipboardData = event.clipboardData;
var range = selection.getRangeAt(0);
// When start point is within a formula, expand to entire formula.
var startKatex = closestKatex(range.startContainer);
if (startKatex) {
range.setStartBefore(startKatex);
}
// Similarly, when end point is within a formula, expand to entire formula.
var endKatex = closestKatex(range.endContainer);
if (endKatex) {
range.setEndAfter(endKatex);
}
var fragment = range.cloneContents();
if (!fragment.querySelector('.katex-mathml')) {
return; // default action OK if no .katex-mathml elements
}
var htmlContents = Array.prototype.map.call(fragment.childNodes, el => el instanceof Text ? el.textContent : el.outerHTML).join('');
// Preserve usual HTML copy/paste behavior.
clipboardData.setData('text/html', htmlContents);
// Rewrite plain-text version.
clipboardData.setData('text/plain', katexReplaceWithTex(fragment).textContent);
// Prevent normal copy handling.
event.preventDefault();
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
frontend/node_modules/katex/dist/katex-swap.min.css generated vendored Normal file

File diff suppressed because one or more lines are too long

1237
frontend/node_modules/katex/dist/katex.css generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
frontend/node_modules/katex/dist/katex.min.css generated vendored Normal file

File diff suppressed because one or more lines are too long

1
frontend/node_modules/katex/dist/katex.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

16461
frontend/node_modules/katex/dist/katex.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

314
frontend/node_modules/katex/src/Options.ts generated vendored Normal file
View File

@@ -0,0 +1,314 @@
/**
* This file contains information about the options that the Parser carries
* around with it while parsing. Data is held in an `Options` object, and when
* recursing, a new `Options` object can be created with the `.with*` and
* `.reset` functions.
*/
import {getGlobalMetrics} from "./fontMetrics";
import type {FontMetrics} from "./fontMetrics";
import type {StyleInterface} from "./Style";
const sizeStyleMap = [
// Each element contains [textsize, scriptsize, scriptscriptsize].
// The size mappings are taken from TeX with \normalsize=10pt.
[1, 1, 1], // size1: [5, 5, 5] \tiny
[2, 1, 1], // size2: [6, 5, 5]
[3, 1, 1], // size3: [7, 5, 5] \scriptsize
[4, 2, 1], // size4: [8, 6, 5] \footnotesize
[5, 2, 1], // size5: [9, 6, 5] \small
[6, 3, 1], // size6: [10, 7, 5] \normalsize
[7, 4, 2], // size7: [12, 8, 6] \large
[8, 6, 3], // size8: [14.4, 10, 7] \Large
[9, 7, 6], // size9: [17.28, 12, 10] \LARGE
[10, 8, 7], // size10: [20.74, 14.4, 12] \huge
[11, 10, 9], // size11: [24.88, 20.74, 17.28] \HUGE
];
const sizeMultipliers = [
// fontMetrics.js:getGlobalMetrics also uses size indexes, so if
// you change size indexes, change that function.
0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488,
];
const sizeAtStyle = function(size: number, style: StyleInterface): number {
return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1];
};
// In these types, "" (empty string) means "no change".
export type FontWeight = "textbf" | "textmd" | "";
export type FontShape = "textit" | "textup" | "";
export type OptionsData = {
style: StyleInterface;
color?: string | undefined;
size?: number;
textSize?: number;
phantom?: boolean;
font?: string;
fontFamily?: string;
fontWeight?: FontWeight;
fontShape?: FontShape;
sizeMultiplier?: number;
maxSize: number;
minRuleThickness: number;
};
/**
* This is the main options class. It contains the current style, size, color,
* and font.
*
* Options objects should not be modified. To create a new Options with
* different properties, call a `.having*` method.
*/
class Options {
style: StyleInterface;
color: string | undefined;
size: number;
textSize: number;
phantom: boolean;
// A font family applies to a group of fonts (i.e. SansSerif), while a font
// represents a specific font (i.e. SansSerif Bold).
// See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm
font: string;
fontFamily: string;
fontWeight: FontWeight;
fontShape: FontShape;
sizeMultiplier: number;
maxSize: number;
minRuleThickness: number;
_fontMetrics: FontMetrics | undefined;
/**
* The base size index.
*/
static BASESIZE: number = 6;
constructor(data: OptionsData) {
this.style = data.style;
this.color = data.color;
this.size = data.size || Options.BASESIZE;
this.textSize = data.textSize || this.size;
this.phantom = !!data.phantom;
this.font = data.font || "";
this.fontFamily = data.fontFamily || "";
this.fontWeight = data.fontWeight || '';
this.fontShape = data.fontShape || '';
this.sizeMultiplier = sizeMultipliers[this.size - 1];
this.maxSize = data.maxSize;
this.minRuleThickness = data.minRuleThickness;
this._fontMetrics = undefined;
}
/**
* Returns a new options object with the same properties as "this". Properties
* from "extension" will be copied to the new options object.
*/
extend(extension: Partial<OptionsData>): Options {
const data: OptionsData = {
style: this.style,
size: this.size,
textSize: this.textSize,
color: this.color,
phantom: this.phantom,
font: this.font,
fontFamily: this.fontFamily,
fontWeight: this.fontWeight,
fontShape: this.fontShape,
maxSize: this.maxSize,
minRuleThickness: this.minRuleThickness,
};
Object.assign(data, extension);
return new Options(data);
}
/**
* Return an options object with the given style. If `this.style === style`,
* returns `this`.
*/
havingStyle(style: StyleInterface): Options {
if (this.style === style) {
return this;
} else {
return this.extend({
style: style,
size: sizeAtStyle(this.textSize, style),
});
}
}
/**
* Return an options object with a cramped version of the current style. If
* the current style is cramped, returns `this`.
*/
havingCrampedStyle(): Options {
return this.havingStyle(this.style.cramp());
}
/**
* Return an options object with the given size and in at least `\textstyle`.
* Returns `this` if appropriate.
*/
havingSize(size: number): Options {
if (this.size === size && this.textSize === size) {
return this;
} else {
return this.extend({
style: this.style.text(),
size: size,
textSize: size,
sizeMultiplier: sizeMultipliers[size - 1],
});
}
}
/**
* Like `this.havingSize(BASESIZE).havingStyle(style)`. If `style` is omitted,
* changes to at least `\textstyle`.
*/
havingBaseStyle(style: StyleInterface): Options {
style = style || this.style.text();
const wantSize = sizeAtStyle(Options.BASESIZE, style);
if (this.size === wantSize && this.textSize === Options.BASESIZE
&& this.style === style) {
return this;
} else {
return this.extend({
style: style,
size: wantSize,
});
}
}
/**
* Remove the effect of sizing changes such as \Huge.
* Keep the effect of the current style, such as \scriptstyle.
*/
havingBaseSizing(): Options {
let size;
switch (this.style.id) {
case 4:
case 5:
size = 3; // normalsize in scriptstyle
break;
case 6:
case 7:
size = 1; // normalsize in scriptscriptstyle
break;
default:
size = 6; // normalsize in textstyle or displaystyle
}
return this.extend({
style: this.style.text(),
size: size,
});
}
/**
* Create a new options object with the given color.
*/
withColor(color: string): Options {
return this.extend({
color: color,
});
}
/**
* Create a new options object with "phantom" set to true.
*/
withPhantom(): Options {
return this.extend({
phantom: true,
});
}
/**
* Creates a new options object with the given math font or old text font.
* @type {[type]}
*/
withFont(font: string): Options {
return this.extend({
font,
});
}
/**
* Create a new options objects with the given fontFamily.
*/
withTextFontFamily(fontFamily: string): Options {
return this.extend({
fontFamily,
font: "",
});
}
/**
* Creates a new options object with the given font weight
*/
withTextFontWeight(fontWeight: FontWeight): Options {
return this.extend({
fontWeight,
font: "",
});
}
/**
* Creates a new options object with the given font weight
*/
withTextFontShape(fontShape: FontShape): Options {
return this.extend({
fontShape,
font: "",
});
}
/**
* Return the CSS sizing classes required to switch from enclosing options
* `oldOptions` to `this`. Returns an array of classes.
*/
sizingClasses(oldOptions: Options): Array<string> {
if (oldOptions.size !== this.size) {
return ["sizing", "reset-size" + oldOptions.size, "size" + this.size];
} else {
return [];
}
}
/**
* Return the CSS sizing classes required to switch to the base size. Like
* `this.havingSize(BASESIZE).sizingClasses(this)`.
*/
baseSizingClasses(): Array<string> {
if (this.size !== Options.BASESIZE) {
return ["sizing", "reset-size" + this.size, "size" + Options.BASESIZE];
} else {
return [];
}
}
/**
* Return the font metrics for this size.
*/
fontMetrics(): FontMetrics {
if (!this._fontMetrics) {
this._fontMetrics = getGlobalMetrics(this.size);
}
return this._fontMetrics;
}
/**
* Gets the CSS color of the current options object
*/
getColor(): string | undefined {
if (this.phantom) {
return "transparent";
} else {
return this.color;
}
}
}
export default Options;

775
frontend/node_modules/katex/src/buildCommon.ts generated vendored Normal file
View File

@@ -0,0 +1,775 @@
/* eslint no-console:0 */
/**
* This module contains general functions that can be used for building
* different kinds of domTree nodes in a consistent manner.
*/
import {SymbolNode, Anchor, Span, PathNode, SvgNode, createClass} from "./domTree";
import {getCharacterMetrics} from "./fontMetrics";
import symbols, {ligatures} from "./symbols";
import {wideCharacterFont} from "./wide-character";
import {calculateSize, makeEm} from "./units";
import {DocumentFragment} from "./tree";
import type Options from "./Options";
import type {ParseNode} from "./parseNode";
import type {CharacterMetrics} from "./fontMetrics";
import type {FontVariant, Mode} from "./types";
import type {documentFragment as HtmlDocumentFragment} from "./domTree";
import type {HtmlDomNode, DomSpan, SvgSpan, CssStyle} from "./domTree";
import type {Measurement} from "./units";
/**
* Looks up the given symbol in fontMetrics, after applying any symbol
* replacements defined in symbol.js
*/
const lookupSymbol = function(
value: string,
// TODO(#963): Use a union type for this.
fontName: string,
mode: Mode,
): {
value: string;
metrics: CharacterMetrics | null | undefined;
} {
// Replace the value with its replaced value from symbol.js
if (symbols[mode][value]) {
const replacement = symbols[mode][value].replace;
if (replacement) {
value = replacement;
}
}
return {
value,
metrics: getCharacterMetrics(value, fontName, mode),
};
};
/**
* Makes a symbolNode after translation via the list of symbols in symbols.js.
* Correctly pulls out metrics for the character, and optionally takes a list of
* classes to be attached to the node.
*
* TODO: make argument order closer to makeSpan
* TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which
* should if present come first in `classes`.
* TODO(#953): Make `options` mandatory and always pass it in.
*/
export const makeSymbol = function(
value: string,
fontName: string,
mode: Mode,
options?: Options,
classes?: string[],
): SymbolNode {
const lookup = lookupSymbol(value, fontName, mode);
const metrics = lookup.metrics;
value = lookup.value;
let symbolNode;
if (metrics) {
let italic = metrics.italic;
if (mode === "text" || (options && options.font === "mathit")) {
italic = 0;
}
symbolNode = new SymbolNode(
value, metrics.height, metrics.depth, italic, metrics.skew,
metrics.width, classes);
} else {
// TODO(emily): Figure out a good way to only print this in development
typeof console !== "undefined" && console.warn("No character metrics " +
`for '${value}' in style '${fontName}' and mode '${mode}'`);
symbolNode = new SymbolNode(value, 0, 0, 0, 0, 0, classes);
}
if (options) {
symbolNode.maxFontSize = options.sizeMultiplier;
if (options.style.isTight()) {
symbolNode.classes.push("mtight");
}
const color = options.getColor();
if (color) {
symbolNode.style.color = color;
}
}
return symbolNode;
};
/**
* Makes a symbol in Main-Regular or AMS-Regular.
* Used for rel, bin, open, close, inner, and punct.
*/
export const mathsym = function(
value: string,
mode: Mode,
options: Options,
classes: string[] = [],
): SymbolNode {
// Decide what font to render the symbol in by its entry in the symbols
// table.
// Have a special case for when the value = \ because the \ is used as a
// textord in unsupported command errors but cannot be parsed as a regular
// text ordinal and is therefore not present as a symbol in the symbols
// table for text, as well as a special case for boldsymbol because it
// can be used for bold + and -
if (options.font === "boldsymbol" &&
lookupSymbol(value, "Main-Bold", mode).metrics) {
return makeSymbol(value, "Main-Bold", mode, options,
classes.concat(["mathbf"]));
} else if (value === "\\" || symbols[mode][value].font === "main") {
return makeSymbol(value, "Main-Regular", mode, options, classes);
} else {
return makeSymbol(
value, "AMS-Regular", mode, options, classes.concat(["amsrm"]));
}
};
/**
* Determines which of the two font names (Main-Bold and Math-BoldItalic) and
* corresponding style tags (mathbf or boldsymbol) to use for font "boldsymbol",
* depending on the symbol. Use this function instead of fontMap for font
* "boldsymbol".
*/
const boldsymbol = function(
value: string,
mode: Mode,
options: Options,
classes: string[],
type: "mathord" | "textord",
): {
fontName: string;
fontClass: string;
} {
if (type !== "textord" &&
lookupSymbol(value, "Math-BoldItalic", mode).metrics) {
return {
fontName: "Math-BoldItalic",
fontClass: "boldsymbol",
};
} else {
// Some glyphs do not exist in Math-BoldItalic so we need to use
// Main-Bold instead.
return {
fontName: "Main-Bold",
fontClass: "mathbf",
};
}
};
/**
* Makes either a mathord or textord in the correct font and color.
*/
export const makeOrd = function<NODETYPE extends "spacing" | "mathord" | "textord">(
group: ParseNode<NODETYPE>,
options: Options,
type: "mathord" | "textord",
): HtmlDocumentFragment | SymbolNode {
const mode = group.mode;
const text = group.text;
const classes = ["mord"];
// Math mode or Old font (i.e. \rm)
const isFont = mode === "math" || (mode === "text" && options.font);
const fontOrFamily = isFont ? options.font : options.fontFamily;
let wideFontName = "";
let wideFontClass = "";
if (text.charCodeAt(0) === 0xD835) {
[wideFontName, wideFontClass] = wideCharacterFont(text, mode);
}
if (wideFontName.length > 0) {
// surrogate pairs get special treatment
return makeSymbol(text, wideFontName, mode, options,
classes.concat(wideFontClass));
} else if (fontOrFamily) {
let fontName;
let fontClasses;
if (fontOrFamily === "boldsymbol") {
const fontData = boldsymbol(text, mode, options, classes, type);
fontName = fontData.fontName;
fontClasses = [fontData.fontClass];
} else if (isFont) {
fontName = fontMap[fontOrFamily].fontName;
fontClasses = [fontOrFamily];
} else {
fontName = retrieveTextFontName(fontOrFamily, options.fontWeight,
options.fontShape);
fontClasses = [fontOrFamily, options.fontWeight, options.fontShape];
}
if (lookupSymbol(text, fontName, mode).metrics) {
return makeSymbol(text, fontName, mode, options,
classes.concat(fontClasses));
} else if (ligatures.hasOwnProperty(text) &&
fontName.slice(0, 10) === "Typewriter") {
// Deconstruct ligatures in monospace fonts (\texttt, \tt).
const parts = [];
for (let i = 0; i < text.length; i++) {
parts.push(makeSymbol(text[i], fontName, mode, options,
classes.concat(fontClasses)));
}
return makeFragment(parts);
}
}
// Makes a symbol in the default font for mathords and textords.
if (type === "mathord") {
return makeSymbol(text, "Math-Italic", mode, options,
classes.concat(["mathnormal"]));
} else if (type === "textord") {
const font = symbols[mode][text] && symbols[mode][text].font;
if (font === "ams") {
const fontName = retrieveTextFontName("amsrm", options.fontWeight,
options.fontShape);
return makeSymbol(
text, fontName, mode, options,
classes.concat("amsrm", options.fontWeight, options.fontShape));
} else if (font === "main" || !font) {
const fontName = retrieveTextFontName("textrm", options.fontWeight,
options.fontShape);
return makeSymbol(
text, fontName, mode, options,
classes.concat(options.fontWeight, options.fontShape));
} else { // fonts added by plugins
const fontName = retrieveTextFontName(font, options.fontWeight,
options.fontShape);
// We add font name as a css class
return makeSymbol(
text, fontName, mode, options,
classes.concat(fontName, options.fontWeight, options.fontShape));
}
} else {
throw new Error("unexpected type: " + type + " in makeOrd");
}
};
/**
* Returns true if subsequent symbolNodes have the same classes, skew, maxFont,
* and styles. For mathnormal text, the left node must also have zero italic
* correction so we don't lose spacing between combined glyphs.
*/
const canCombine = (prev: SymbolNode, next: SymbolNode) => {
if (createClass(prev.classes) !== createClass(next.classes)
|| prev.skew !== next.skew
|| prev.maxFontSize !== next.maxFontSize
|| (prev.italic !== 0 && prev.hasClass("mathnormal"))) {
return false;
}
// If prev and next both are just "mbin"s or "mord"s we don't combine them
// so that the proper spacing can be preserved.
if (prev.classes.length === 1) {
const cls = prev.classes[0];
if (cls === "mbin" || cls === "mord") {
return false;
}
}
for (const key of Object.keys(prev.style) as Array<keyof CssStyle>) {
if (prev.style[key] !== next.style[key]) {
return false;
}
}
for (const key of Object.keys(next.style) as Array<keyof CssStyle>) {
if (prev.style[key] !== next.style[key]) {
return false;
}
}
return true;
};
/**
* Combine consecutive domTree.symbolNodes into a single symbolNode.
* Note: this function mutates the argument.
*/
export const tryCombineChars = (chars: HtmlDomNode[]): HtmlDomNode[] => {
for (let i = 0; i < chars.length - 1; i++) {
const prev = chars[i];
const next = chars[i + 1];
if (prev instanceof SymbolNode
&& next instanceof SymbolNode
&& canCombine(prev, next)) {
prev.text += next.text;
prev.height = Math.max(prev.height, next.height);
prev.depth = Math.max(prev.depth, next.depth);
// Use the last character's italic correction since we use
// it to add padding to the right of the span created from
// the combined characters.
prev.italic = next.italic;
chars.splice(i + 1, 1);
i--;
}
}
return chars;
};
/**
* Calculate the height, depth, and maxFontSize of an element based on its
* children.
*/
const sizeElementFromChildren = function(
elem: DomSpan | Anchor | HtmlDocumentFragment,
) {
let height = 0;
let depth = 0;
let maxFontSize = 0;
for (let i = 0; i < elem.children.length; i++) {
const child = elem.children[i];
if (child.height > height) {
height = child.height;
}
if (child.depth > depth) {
depth = child.depth;
}
if (child.maxFontSize > maxFontSize) {
maxFontSize = child.maxFontSize;
}
}
elem.height = height;
elem.depth = depth;
elem.maxFontSize = maxFontSize;
};
/**
* Makes a span with the given list of classes, list of children, and options.
*
* TODO(#953): Ensure that `options` is always provided (currently some call
* sites don't pass it) and make the type below mandatory.
* TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which
* should if present come first in `classes`.
*/
export const makeSpan = function(
classes?: string[],
children?: HtmlDomNode[],
options?: Options,
style?: CssStyle,
): DomSpan {
const span = new Span(classes, children, options, style);
sizeElementFromChildren(span);
return span;
};
// SVG one is simpler -- doesn't require height, depth, max-font setting.
// This is also a separate method for typesafety.
export const makeSvgSpan = (
classes?: string[],
children?: SvgNode[],
options?: Options,
style?: CssStyle,
): SvgSpan => new Span(classes, children, options, style);
export const makeLineSpan = function(
className: string,
options: Options,
thickness?: number,
): DomSpan {
const line = makeSpan([className], [], options);
line.height = Math.max(
thickness || options.fontMetrics().defaultRuleThickness,
options.minRuleThickness,
);
line.style.borderBottomWidth = makeEm(line.height);
line.maxFontSize = 1.0;
return line;
};
/**
* Makes an anchor with the given href, list of classes, list of children,
* and options.
*/
export const makeAnchor = function(
href: string,
classes: string[],
children: HtmlDomNode[],
options: Options,
): Anchor {
const anchor = new Anchor(href, classes, children, options);
sizeElementFromChildren(anchor);
return anchor;
};
/**
* Makes a document fragment with the given list of children.
*/
export const makeFragment = function(
children: HtmlDomNode[],
): HtmlDocumentFragment {
const fragment = new DocumentFragment(children);
sizeElementFromChildren(fragment);
return fragment;
};
/**
* Wraps group in a span if it's a document fragment, allowing to apply classes
* and styles
*/
export const wrapFragment = function(
group: HtmlDomNode,
options: Options,
): HtmlDomNode {
if (group instanceof DocumentFragment) {
return makeSpan([], [group], options);
}
return group;
};
export type VListElem = {
type: "elem";
elem: HtmlDomNode;
marginLeft?: string | null | undefined;
marginRight?: string;
wrapperClasses?: string[];
wrapperStyle?: CssStyle;
};
type VListElemAndShift = {
type: "elem";
elem: HtmlDomNode;
shift: number;
marginLeft?: string | null | undefined;
marginRight?: string;
wrapperClasses?: string[];
wrapperStyle?: CssStyle;
};
type VListKern = {
type: "kern";
size: number;
};
// A list of child or kern nodes to be stacked on top of each other (i.e. the
// first element will be at the bottom, and the last at the top).
type VListChild = VListElem | VListKern;
type VListParam = {
// Each child contains how much it should be shifted downward.
positionType: "individualShift";
children: VListElemAndShift[];
} | {
// "top": The positionData specifies the topmost point of the vlist (note this
// is expected to be a height, so positive values move up).
// "bottom": The positionData specifies the bottommost point of the vlist (note
// this is expected to be a depth, so positive values move down).
// "shift": The vlist will be positioned such that its baseline is positionData
// away from the baseline of the first child which MUST be an
// "elem". Positive values move downwards.
positionType: "top" | "bottom" | "shift";
positionData: number;
children: VListChild[];
} | {
// The vlist is positioned so that its baseline is aligned with the baseline
// of the first child which MUST be an "elem". This is equivalent to "shift"
// with positionData=0.
positionType: "firstBaseline";
children: VListChild[];
};
// Computes the updated `children` list and the overall depth.
//
// This helper function for makeVList makes it easier to enforce type safety by
// allowing early exits (returns) in the logic.
const getVListChildrenAndDepth = function(params: VListParam): {
children: (VListChild | VListElemAndShift)[] | VListChild[];
depth: number;
} {
if (params.positionType === "individualShift") {
const oldChildren = params.children;
const children: (VListChild | VListElemAndShift)[] = [oldChildren[0]];
// Add in kerns to the list of params.children to get each element to be
// shifted to the correct specified shift
const depth = -oldChildren[0].shift - oldChildren[0].elem.depth;
let currPos = depth;
for (let i = 1; i < oldChildren.length; i++) {
const diff = -oldChildren[i].shift - currPos -
oldChildren[i].elem.depth;
const size = diff -
(oldChildren[i - 1].elem.height +
oldChildren[i - 1].elem.depth);
currPos = currPos + diff;
children.push({type: "kern", size});
children.push(oldChildren[i]);
}
return {children, depth};
}
let depth;
if (params.positionType === "top") {
// We always start at the bottom, so calculate the bottom by adding up
// all the sizes
let bottom = params.positionData;
for (let i = 0; i < params.children.length; i++) {
const child = params.children[i];
bottom -= child.type === "kern"
? child.size
: child.elem.height + child.elem.depth;
}
depth = bottom;
} else if (params.positionType === "bottom") {
depth = -params.positionData;
} else {
const firstChild = params.children[0];
if (firstChild.type !== "elem") {
throw new Error('First child must have type "elem".');
}
if (params.positionType === "shift") {
depth = -firstChild.elem.depth - params.positionData;
} else if (params.positionType === "firstBaseline") {
depth = -firstChild.elem.depth;
} else {
throw new Error(`Invalid positionType ${params.positionType}.`);
}
}
return {children: params.children, depth};
};
/**
* Makes a vertical list by stacking elements and kerns on top of each other.
* Allows for many different ways of specifying the positioning method.
*
* See VListParam documentation above.
*/
export const makeVList = function(params: VListParam, options: Options): DomSpan {
const {children, depth} = getVListChildrenAndDepth(params);
// Create a strut that is taller than any list item. The strut is added to
// each item, where it will determine the item's baseline. Since it has
// `overflow:hidden`, the strut's top edge will sit on the item's line box's
// top edge and the strut's bottom edge will sit on the item's baseline,
// with no additional line-height spacing. This allows the item baseline to
// be positioned precisely without worrying about font ascent and
// line-height.
let pstrutSize = 0;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.type === "elem") {
const elem = child.elem;
pstrutSize = Math.max(pstrutSize, elem.maxFontSize, elem.height);
}
}
pstrutSize += 2;
const pstrut = makeSpan(["pstrut"], []);
pstrut.style.height = makeEm(pstrutSize);
// Create a new list of actual children at the correct offsets
const realChildren = [];
let minPos = depth;
let maxPos = depth;
let currPos = depth;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.type === "kern") {
currPos += child.size;
} else {
const elem = child.elem;
const classes = child.wrapperClasses || [];
const style = child.wrapperStyle || {};
const childWrap = makeSpan(classes, [pstrut, elem], undefined, style);
childWrap.style.top = makeEm(-pstrutSize - currPos - elem.depth);
if (child.marginLeft) {
childWrap.style.marginLeft = child.marginLeft;
}
if (child.marginRight) {
childWrap.style.marginRight = child.marginRight;
}
realChildren.push(childWrap);
currPos += elem.height + elem.depth;
}
minPos = Math.min(minPos, currPos);
maxPos = Math.max(maxPos, currPos);
}
// The vlist contents go in a table-cell with `vertical-align:bottom`.
// This cell's bottom edge will determine the containing table's baseline
// without overly expanding the containing line-box.
const vlist = makeSpan(["vlist"], realChildren);
vlist.style.height = makeEm(maxPos);
// A second row is used if necessary to represent the vlist's depth.
let rows;
if (minPos < 0) {
// We will define depth in an empty span with display: table-cell.
// It should render with the height that we define. But Chrome, in
// contenteditable mode only, treats that span as if it contains some
// text content. And that min-height over-rides our desired height.
// So we put another empty span inside the depth strut span.
const emptySpan = makeSpan([], []);
const depthStrut = makeSpan(["vlist"], [emptySpan]);
depthStrut.style.height = makeEm(-minPos);
// Safari wants the first row to have inline content; otherwise it
// puts the bottom of the *second* row on the baseline.
const topStrut = makeSpan(["vlist-s"], [new SymbolNode("\u200b")]);
rows = [makeSpan(["vlist-r"], [vlist, topStrut]),
makeSpan(["vlist-r"], [depthStrut])];
} else {
rows = [makeSpan(["vlist-r"], [vlist])];
}
const vtable = makeSpan(["vlist-t"], rows);
if (rows.length === 2) {
vtable.classes.push("vlist-t2");
}
vtable.height = maxPos;
vtable.depth = -minPos;
return vtable;
};
// Glue is a concept from TeX which is a flexible space between elements in
// either a vertical or horizontal list. In KaTeX, at least for now, it's
// static space between elements in a horizontal layout.
export const makeGlue = (measurement: Measurement, options: Options): DomSpan => {
// Make an empty span for the space
const rule = makeSpan(["mspace"], [], options);
const size = calculateSize(measurement, options);
rule.style.marginRight = makeEm(size);
return rule;
};
// Takes font options, and returns the appropriate fontLookup name
const retrieveTextFontName = function(
fontFamily: string,
fontWeight: string,
fontShape: string,
): string {
let baseFontName = "";
switch (fontFamily) {
case "amsrm":
baseFontName = "AMS";
break;
case "textrm":
baseFontName = "Main";
break;
case "textsf":
baseFontName = "SansSerif";
break;
case "texttt":
baseFontName = "Typewriter";
break;
default:
baseFontName = fontFamily; // use fonts added by a plugin
}
let fontStylesName;
if (fontWeight === "textbf" && fontShape === "textit") {
fontStylesName = "BoldItalic";
} else if (fontWeight === "textbf") {
fontStylesName = "Bold";
} else if (fontWeight === "textit") {
fontStylesName = "Italic";
} else {
fontStylesName = "Regular";
}
return `${baseFontName}-${fontStylesName}`;
};
/**
* Maps TeX font commands to objects containing:
* - variant: string used for "mathvariant" attribute in buildMathML.js
* - fontName: the "style" parameter to fontMetrics.getCharacterMetrics
*/
// A map between tex font commands an MathML mathvariant attribute values
export const fontMap: Record<string, {
variant: FontVariant;
fontName: string;
}> = {
// styles
"mathbf": {
variant: "bold",
fontName: "Main-Bold",
},
"mathrm": {
variant: "normal",
fontName: "Main-Regular",
},
"textit": {
variant: "italic",
fontName: "Main-Italic",
},
"mathit": {
variant: "italic",
fontName: "Main-Italic",
},
"mathnormal": {
variant: "italic",
fontName: "Math-Italic",
},
"mathsfit": {
variant: "sans-serif-italic",
fontName: "SansSerif-Italic",
},
// "boldsymbol" is missing because they require the use of multiple fonts:
// Math-BoldItalic and Main-Bold. This is handled by a special case in
// makeOrd which ends up calling boldsymbol.
// families
"mathbb": {
variant: "double-struck",
fontName: "AMS-Regular",
},
"mathcal": {
variant: "script",
fontName: "Caligraphic-Regular",
},
"mathfrak": {
variant: "fraktur",
fontName: "Fraktur-Regular",
},
"mathscr": {
variant: "script",
fontName: "Script-Regular",
},
"mathsf": {
variant: "sans-serif",
fontName: "SansSerif-Regular",
},
"mathtt": {
variant: "monospace",
fontName: "Typewriter-Regular",
},
};
export const svgData: Record<string, [string, number, number]> = {
// path, width, height
vec: ["vec", 0.471, 0.714], // values from the font glyph
oiintSize1: ["oiintSize1", 0.957, 0.499], // oval to overlay the integrand
oiintSize2: ["oiintSize2", 1.472, 0.659],
oiiintSize1: ["oiiintSize1", 1.304, 0.499],
oiiintSize2: ["oiiintSize2", 1.98, 0.659],
};
export const staticSvg = function(value: string, options: Options): SvgSpan {
// Create a span with inline SVG for the element.
const [pathName, width, height] = svgData[value];
const path = new PathNode(pathName);
const svgNode = new SvgNode([path], {
"width": makeEm(width),
"height": makeEm(height),
// Override CSS rule `.katex svg { width: 100% }`
"style": "width:" + makeEm(width),
"viewBox": "0 0 " + 1000 * width + " " + 1000 * height,
"preserveAspectRatio": "xMinYMin",
});
const span = makeSvgSpan(["overlay"], [svgNode], options);
span.height = height;
span.style.height = makeEm(height);
span.style.width = makeEm(width);
return span;
};

124
frontend/node_modules/katex/src/defineMacro.ts generated vendored Normal file
View File

@@ -0,0 +1,124 @@
import {Token} from "./Token";
import type Namespace from "./Namespace";
import type {Mode} from "./types";
/**
* Provides context to macros defined by functions. Implemented by
* MacroExpander.
*/
export interface MacroContextInterface {
mode: Mode;
/**
* Object mapping macros to their expansions.
*/
macros: Namespace<MacroDefinition>;
/**
* Returns the topmost token on the stack, without expanding it.
* Similar in behavior to TeX's `\futurelet`.
*/
future(): Token;
/**
* Remove and return the next unexpanded token.
*/
popToken(): Token;
/**
* Consume all following space tokens, without expansion.
*/
consumeSpaces(): void;
/**
* Expand the next token only once if possible.
*/
expandOnce(expandableOnly?: boolean): number | boolean;
/**
* Expand the next token only once (if possible), and return the resulting
* top token on the stack (without removing anything from the stack).
* Similar in behavior to TeX's `\expandafter\futurelet`.
*/
expandAfterFuture(): Token;
/**
* Recursively expand first token, then return first non-expandable token.
*/
expandNextToken(): Token;
/**
* Fully expand the given macro name and return the resulting list of
* tokens, or return `undefined` if no such macro is defined.
*/
expandMacro(name: string): Token[] | undefined;
/**
* Fully expand the given macro name and return the result as a string,
* or return `undefined` if no such macro is defined.
*/
expandMacroAsText(name: string): string | undefined;
/**
* Fully expand the given token stream and return the resulting list of
* tokens. Note that the input tokens are in reverse order, but the
* output tokens are in forward order.
*/
expandTokens(tokens: Token[]): Token[];
/**
* Consume an argument from the token stream, and return the resulting array
* of tokens and start/end token.
*/
consumeArg(delims?: string[] | null | undefined): MacroArg;
/**
* Consume the specified number of arguments from the token stream,
* and return the resulting array of arguments.
*/
consumeArgs(numArgs: number): Token[][];
/**
* Determine whether a command is currently "defined" (has some
* functionality), meaning that it's a macro (in the current group),
* a function, a symbol, or one of the special commands listed in
* `implicitCommands`.
*/
isDefined(name: string): boolean;
/**
* Determine whether a command is expandable.
*/
isExpandable(name: string): boolean;
}
export type MacroArg = {
tokens: Token[];
start: Token;
end: Token;
};
/** Macro tokens (in reverse order). */
export type MacroExpansion = {
tokens: Token[];
numArgs: number;
delimiters?: string[][];
unexpandable?: boolean; // used in \let
};
export type MacroDefinition = string | MacroExpansion |
((arg0: MacroContextInterface) => string | MacroExpansion);
export type MacroMap = Record<string, MacroDefinition>;
/**
* All registered global/built-in macros.
* `macros.js` exports this same dictionary again and makes it public.
* `Parser.js` requires this dictionary via `macros.js`.
*/
export const _macros: MacroMap = {};
// This function might one day accept an additional argument and do more things.
export default function defineMacro(name: string, body: MacroDefinition) {
_macros[name] = body;
}

280
frontend/node_modules/katex/src/fontMetrics.ts generated vendored Normal file
View File

@@ -0,0 +1,280 @@
import {supportedCodepoint} from "./unicodeScripts";
import type {Mode} from "./types";
/**
* This file contains metrics regarding fonts and individual symbols. The sigma
* and xi variables, as well as the metricMap map contain data extracted from
* TeX, TeX font metrics, and the TTF files. These data are then exposed via the
* `metrics` variable and the getCharacterMetrics function.
*/
// In TeX, there are actually three sets of dimensions, one for each of
// textstyle (size index 5 and higher: >=9pt), scriptstyle (size index 3 and 4:
// 7-8pt), and scriptscriptstyle (size index 1 and 2: 5-6pt). These are
// provided in the arrays below, in that order.
//
// The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respectively.
// This was determined by running the following script:
//
// latex -interaction=nonstopmode \
// '\documentclass{article}\usepackage{amsmath}\begin{document}' \
// '$a$ \expandafter\show\the\textfont2' \
// '\expandafter\show\the\scriptfont2' \
// '\expandafter\show\the\scriptscriptfont2' \
// '\stop'
//
// The metrics themselves were retrieved using the following commands:
//
// tftopl cmsy10
// tftopl cmsy7
// tftopl cmsy5
//
// The output of each of these commands is quite lengthy. The only part we
// care about is the FONTDIMEN section. Each value is measured in EMs.
const sigmasAndXis: Record<string, [number, number, number]> = {
slant: [0.250, 0.250, 0.250], // sigma1
space: [0.000, 0.000, 0.000], // sigma2
stretch: [0.000, 0.000, 0.000], // sigma3
shrink: [0.000, 0.000, 0.000], // sigma4
xHeight: [0.431, 0.431, 0.431], // sigma5
quad: [1.000, 1.171, 1.472], // sigma6
extraSpace: [0.000, 0.000, 0.000], // sigma7
num1: [0.677, 0.732, 0.925], // sigma8
num2: [0.394, 0.384, 0.387], // sigma9
num3: [0.444, 0.471, 0.504], // sigma10
denom1: [0.686, 0.752, 1.025], // sigma11
denom2: [0.345, 0.344, 0.532], // sigma12
sup1: [0.413, 0.503, 0.504], // sigma13
sup2: [0.363, 0.431, 0.404], // sigma14
sup3: [0.289, 0.286, 0.294], // sigma15
sub1: [0.150, 0.143, 0.200], // sigma16
sub2: [0.247, 0.286, 0.400], // sigma17
supDrop: [0.386, 0.353, 0.494], // sigma18
subDrop: [0.050, 0.071, 0.100], // sigma19
delim1: [2.390, 1.700, 1.980], // sigma20
delim2: [1.010, 1.157, 1.420], // sigma21
axisHeight: [0.250, 0.250, 0.250], // sigma22
// These font metrics are extracted from TeX by using tftopl on cmex10.tfm;
// they correspond to the font parameters of the extension fonts (family 3).
// See the TeXbook, page 441. In AMSTeX, the extension fonts scale; to
// match cmex7, we'd use cmex7.tfm values for script and scriptscript
// values.
defaultRuleThickness: [0.04, 0.049, 0.049], // xi8; cmex7: 0.049
bigOpSpacing1: [0.111, 0.111, 0.111], // xi9
bigOpSpacing2: [0.166, 0.166, 0.166], // xi10
bigOpSpacing3: [0.2, 0.2, 0.2], // xi11
bigOpSpacing4: [0.6, 0.611, 0.611], // xi12; cmex7: 0.611
bigOpSpacing5: [0.1, 0.143, 0.143], // xi13; cmex7: 0.143
// The \sqrt rule width is taken from the height of the surd character.
// Since we use the same font at all sizes, this thickness doesn't scale.
sqrtRuleThickness: [0.04, 0.04, 0.04],
// This value determines how large a pt is, for metrics which are defined
// in terms of pts.
// This value is also used in katex.scss; if you change it make sure the
// values match.
ptPerEm: [10.0, 10.0, 10.0],
// The space between adjacent `|` columns in an array definition. From
// `\showthe\doublerulesep` in LaTeX. Equals 2.0 / ptPerEm.
doubleRuleSep: [0.2, 0.2, 0.2],
// The width of separator lines in {array} environments. From
// `\showthe\arrayrulewidth` in LaTeX. Equals 0.4 / ptPerEm.
arrayRuleWidth: [0.04, 0.04, 0.04],
// Two values from LaTeX source2e:
fboxsep: [0.3, 0.3, 0.3], // 3 pt / ptPerEm
fboxrule: [0.04, 0.04, 0.04], // 0.4 pt / ptPerEm
};
// This map contains a mapping from font name and character code to character
// metrics, including height, depth, italic correction, and skew (kern from the
// character to the corresponding \skewchar)
// This map is generated via `make metrics`. It should not be changed manually.
import metricMap from "./fontMetricsData";
// These are very rough approximations. We default to Times New Roman which
// should have Latin-1 and Cyrillic characters, but may not depending on the
// operating system. The metrics do not account for extra height from the
// accents. In the case of Cyrillic characters which have both ascenders and
// descenders we prefer approximations with ascenders, primarily to prevent
// the fraction bar or root line from intersecting the glyph.
// TODO(kevinb) allow union of multiple glyph metrics for better accuracy.
const extraCharacterMap: Record<string, string> = {
// Latin-1
'Å': 'A',
'Ð': 'D',
'Þ': 'o',
'å': 'a',
'ð': 'd',
'þ': 'o',
// Cyrillic
'А': 'A',
'Б': 'B',
'В': 'B',
'Г': 'F',
'Д': 'A',
'Е': 'E',
'Ж': 'K',
'З': '3',
'И': 'N',
'Й': 'N',
'К': 'K',
'Л': 'N',
'М': 'M',
'Н': 'H',
'О': 'O',
'П': 'N',
'Р': 'P',
'С': 'C',
'Т': 'T',
'У': 'y',
'Ф': 'O',
'Х': 'X',
'Ц': 'U',
'Ч': 'h',
'Ш': 'W',
'Щ': 'W',
'Ъ': 'B',
'Ы': 'X',
'Ь': 'B',
'Э': '3',
'Ю': 'X',
'Я': 'R',
'а': 'a',
'б': 'b',
'в': 'a',
'г': 'r',
'д': 'y',
'е': 'e',
'ж': 'm',
'з': 'e',
'и': 'n',
'й': 'n',
'к': 'n',
'л': 'n',
'м': 'm',
'н': 'n',
'о': 'o',
'п': 'n',
'р': 'p',
'с': 'c',
'т': 'o',
'у': 'y',
'ф': 'b',
'х': 'x',
'ц': 'n',
'ч': 'n',
'ш': 'w',
'щ': 'w',
'ъ': 'a',
'ы': 'm',
'ь': 'a',
'э': 'e',
'ю': 'm',
'я': 'r',
};
export type CharacterMetrics = {
depth: number;
height: number;
italic: number;
skew: number;
width: number;
};
export type MetricMap = {
[key: string]: [number, number, number, number, number];
};
/**
* This function adds new font metrics to default metricMap
* It can also override existing metrics
*/
export function setFontMetrics(fontName: string, metrics: MetricMap) {
metricMap[fontName] = metrics;
}
/**
* This function is a convenience function for looking up information in the
* metricMap table. It takes a character as a string, and a font.
*
* Note: the `width` property may be undefined if fontMetricsData.js wasn't
* built using `Make extended_metrics`.
*/
export function getCharacterMetrics(
character: string,
font: string,
mode: Mode,
): CharacterMetrics | null | undefined {
if (!metricMap[font]) {
throw new Error(`Font metrics not found for font: ${font}.`);
}
let ch = character.charCodeAt(0);
let metrics = metricMap[font][ch];
if (!metrics && character[0] in extraCharacterMap) {
ch = extraCharacterMap[character[0]].charCodeAt(0);
metrics = metricMap[font][ch];
}
if (!metrics && mode === 'text') {
// We don't typically have font metrics for Asian scripts.
// But since we support them in text mode, we need to return
// some sort of metrics.
// So if the character is in a script we support but we
// don't have metrics for it, just use the metrics for
// the Latin capital letter M. This is close enough because
// we (currently) only care about the height of the glyph
// not its width.
if (supportedCodepoint(ch)) {
metrics = metricMap[font][77]; // 77 is the charcode for 'M'
}
}
if (metrics) {
return {
depth: metrics[0],
height: metrics[1],
italic: metrics[2],
skew: metrics[3],
width: metrics[4],
};
}
}
type FontSizeIndex = 0 | 1 | 2;
export type FontMetrics = {
cssEmPerMu: number;
[key: string]: number;
};
const fontMetricsBySizeIndex: Partial<Record<FontSizeIndex, FontMetrics>> = {};
/**
* Get the font metrics for a given size.
*/
export function getGlobalMetrics(size: number): FontMetrics {
let sizeIndex: FontSizeIndex;
if (size >= 5) {
sizeIndex = 0;
} else if (size >= 3) {
sizeIndex = 1;
} else {
sizeIndex = 2;
}
if (!fontMetricsBySizeIndex[sizeIndex]) {
const metrics: FontMetrics = fontMetricsBySizeIndex[sizeIndex] = {
cssEmPerMu: sigmasAndXis.quad[sizeIndex] / 18,
};
for (const key in sigmasAndXis) {
if (sigmasAndXis.hasOwnProperty(key)) {
metrics[key] = sigmasAndXis[key][sizeIndex];
}
}
}
return fontMetricsBySizeIndex[sizeIndex]!;
}

2077
frontend/node_modules/katex/src/fontMetricsData.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

139
frontend/node_modules/katex/src/fonts/Makefile generated vendored Normal file
View File

@@ -0,0 +1,139 @@
#!gmake
#
# Version: Apache License 2.0
#
# Copyright (c) 2013 MathJax Project
# Copyright (c) 2013 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.
#
CUSTOM=custom.cfg
-include $(CUSTOM)
MFTRACE_MODIFIED=lib/mftrace-modified
all: config fonts
$(CUSTOM):
@cp default.cfg $(CUSTOM);
$(CUSTOM).pl: $(CUSTOM)
@echo "Creating Perl config file..."
@cp $(CUSTOM) $(CUSTOM).pl
@echo >> $(CUSTOM).pl # ensure that the config file ends by a new line
@echo "MFTRACE_PATH=`$(WHICH) $(MFTRACE)`" >> $(CUSTOM).pl
@$(SED) -i "s|^\([A-Z_0-9]*\)=\(.*\)|$$\1='\2';|" $(CUSTOM).pl
@echo "1;" >> $(CUSTOM).pl
.PHONY: config
config: $(CUSTOM).pl
blacker: $(MFTRACE_MODIFIED)
$(MFTRACE_MODIFIED):
$(PERL) -I. makeBlacker 15 # values between 10 and 30 seem best
pfa: $(MFTRACE_MODIFIED)
@echo "cmr10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmr10
@echo "cmmi10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/aae443f0.enc --simplify cmmi10
@echo "cmsy10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/10037936.enc --simplify cmsy10
@echo "cmex10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmex10
@echo "cmbx10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmbx10
@echo "cmbxti10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmbxti10
@echo "cmti10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmti10
@echo "msam10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify --encoding $(TETEXENCODING)/10037936.enc msam10
@echo "msbm10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify --encoding $(TETEXENCODING)/10037936.enc msbm10
@echo "cmmib10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/aae443f0.enc --simplify cmmib10
@echo "cmbsy10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/10037936.enc --simplify cmbsy10
@echo "cmtt10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmtt10
@echo "cmss10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmss10
@echo "cmssi10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmssi10
@echo "cmssbx10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmssbx10
@echo "eufm10"
cp "`$(KPSEWHICH) eufm10.pfb`" eufm10.pfb
@echo "eufb10"
cp "`$(KPSEWHICH) eufb10.pfb`" eufb10.pfb
# echo "eusm10"
# $(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify eusm10
# echo "eusb10"
# $(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify eusb10
@echo "rsfs10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify --encoding $(BASEENCODING)/tex256.enc rsfs10
mkdir -p pfa
rm -f pfa/*
mv *.pfa pfa
mv *.pfb pfa
ff: pfa
mkdir -p ff otf
rm -f ff/* otf/*
$(PERL) -I. makeFF
.PHONY: fonts
fonts: ff
mkdir -p ttf woff woff2
rm -f ttf/* woff/* woff2/*
@for file in `ls ff/*.ff | $(SED) 's|ff/\(.*\)\.ff|\1|'`; do \
echo ""; \
echo $$file; \
$(FONTFORGE) -lang=ff -script ff/$$file.ff; \
\
echo "Hinting $$file"; \
if echo "$$file" | $(GREP) -q -e "Size[1-4]" -e "Typewriter"; then \
$(TTFAUTOHINT) -f none -S --windows-compatibility --symbol ttf/$$file.ttf ttf/$$file.ttf.hinted; \
else \
$(TTFAUTOHINT) -f none -S --windows-compatibility ttf/$$file.ttf ttf/$$file.ttf.hinted; \
fi; \
mv ttf/$$file.ttf.hinted ttf/$$file.ttf; \
\
echo "Generating $$file..."; \
$(PYTHON) generate_fonts.py ttf/$$file.ttf; \
done
clean:
rm -f $(CUSTOM).pl
rm -f $(MFTRACE_MODIFIED) lib/blacker.mf
rm -rf pfa ff otf ttf woff woff2

20
frontend/node_modules/katex/src/fonts/default.cfg generated vendored Normal file
View File

@@ -0,0 +1,20 @@
# Note: paths should be absolute, unless they point to programs in your $PATH.
##### Standard programs #####
GREP=grep
PERL=perl
PYTHON=python3
SED=sed
WHICH=which
KPSEWHICH=kpsewhich
##### Font tools #####
# Most of the tools below are standard and should be available from your package manager.
FONTFORGE=fontforge
MFTRACE=mftrace
TTX=ttx
TTFAUTOHINT=ttfautohint
##### TeXLive Encoding
TETEXENCODING=/usr/share/texlive/texmf-texlive/fonts/enc/dvips/tetex/
BASEENCODING=/usr/share/texlive/texmf-texlive/fonts/enc/dvips/base/

234
frontend/node_modules/katex/src/fonts/lib/Space.ttx generated vendored Normal file
View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="3.28">
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="space"/>
<GlyphID id="2" name="uni00A0"/>
</GlyphOrder>
<head>
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0xa98c8795"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1000"/>
<created value="Sun Jan 24 23:04:46 2010"/>
<modified value="Sat May 14 12:26:22 2011"/>
<xMin value="50"/>
<yMin value="0"/>
<xMax value="200"/>
<yMax value="533"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="8"/>
<fontDirectionHint value="2"/>
<indexToLocFormat value="0"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="0x00010000"/>
<ascent value="800"/>
<descent value="-200"/>
<lineGap value="90"/>
<advanceWidthMax value="250"/>
<minLeftSideBearing value="50"/>
<minRightSideBearing value="50"/>
<xMaxExtent value="200"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
<numberOfHMetrics value="1"/>
</hhea>
<maxp>
<tableVersion value="0x5000"/>
<numGlyphs value="3"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="2"/>
<xAvgCharWidth value="225"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00000000"/>
<ySubscriptXSize value="650"/>
<ySubscriptYSize value="700"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="140"/>
<ySuperscriptXSize value="650"/>
<ySuperscriptYSize value="700"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="480"/>
<yStrikeoutSize value="49"/>
<yStrikeoutPosition value="258"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="0"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="10000000 00000000 00000000 11101111"/>
<ulUnicodeRange2 value="00010000 00000000 11101100 11101101"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="PfEd"/>
<fsSelection value="00000000 01000000"/>
<usFirstCharIndex value="32"/>
<usLastCharIndex value="160"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="90"/>
<usWinAscent value="533"/>
<usWinDescent value="0"/>
<ulCodePageRange1 value="00100000 00000000 00000000 10001111"/>
<ulCodePageRange2 value="01011110 00000011 00000000 00000000"/>
<sxHeight value="0"/>
<sCapHeight value="0"/>
<usDefaultChar value="32"/>
<usBreakChar value="32"/>
<usMaxContext value="1"/>
</OS_2>
<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright (c) 2009-2010 Design Science, Inc.
Copyright (c) 2014-2018 Khan Academy
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
*NAME*
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
*WEIGHT_S*
</namerecord>
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
FontForge 2.0 : *NAME*-*WEIGHT*
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
*NAME*-*WEIGHT*
</namerecord>
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
Version 1.1
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
*NAME*-*WEIGHT*
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Copyright (c) 2009-2010, Design Science, Inc. (&lt;www.mathjax.org&gt;)
Copyright (c) 2014-2018 Khan Academy (&lt;www.khanacademy.org&gt;),
with Reserved Font Name *NAME*.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license available with a FAQ at:
http://scripts.sil.org/OFL
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://scripts.sil.org/OFL
</namerecord>
</name>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="0" platEncID="3" language="0">
<map code="0x20" name="space"/><!-- SPACE -->
<map code="0xa0" name="uni00A0"/><!-- NO-BREAK SPACE -->
</cmap_format_4>
<cmap_format_4 platformID="3" platEncID="1" language="0">
<map code="0x20" name="space"/><!-- SPACE -->
<map code="0xa0" name="uni00A0"/><!-- NO-BREAK SPACE -->
</cmap_format_4>
</cmap>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="-125"/>
<underlineThickness value="50"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
<CFF>
<major value="1"/>
<minor value="0"/>
<CFFFont name="*NAME*-*WEIGHT*">
<version value="001.001"/>
<Notice value="Copyright (c) 2009-2010 Design Science, Inc., Copyright (c) 2014-2018 Khan Academy"/>
<FullName value="*NAME*-*WEIGHT*"/>
<FamilyName value="*NAME*"/>
<Weight value="*NORMAL*"/>
<isFixedPitch value="0"/>
<ItalicAngle value="0"/>
<UnderlinePosition value="-150"/>
<UnderlineThickness value="50"/>
<PaintType value="0"/>
<CharstringType value="2"/>
<FontMatrix value="0.001 0 0 0.001 0 0"/>
<FontBBox value="50 0 200 533"/>
<StrokeWidth value="0"/>
<!-- charset is dumped separately as the 'GlyphOrder' element -->
<Encoding name="StandardEncoding"/>
<Private>
<BlueScale value="0.03963"/>
<BlueShift value="0"/>
<BlueFuzz value="1"/>
<StdHW value="50"/>
<StdVW value="50"/>
<ForceBold value="0"/>
<LanguageGroup value="0"/>
<ExpansionFactor value="0.06"/>
<initialRandomSeed value="0"/>
<defaultWidthX value="250"/>
<nominalWidthX value="193"/>
</Private>
<CharStrings>
<CharString name=".notdef">
0 50 433 50 hstem
50 50 50 50 vstem
50 hmoveto
150 533 -150 hlineto
50 -483 rmoveto
433 50 -433 vlineto
endchar
</CharString>
<CharString name="space">
endchar
</CharString>
<CharString name="uni00A0">
endchar
</CharString>
</CharStrings>
</CFFFont>
<GlobalSubrs>
<!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
</GlobalSubrs>
</CFF>
<hmtx>
<mtx name=".notdef" width="250" lsb="50"/>
<mtx name="space" width="250" lsb="0"/>
<mtx name="uni00A0" width="250" lsb="0"/>
</hmtx>
</ttFont>

54
frontend/node_modules/katex/src/functions.ts generated vendored Normal file
View File

@@ -0,0 +1,54 @@
/** Include this to ensure that all functions are defined. */
import {_functions} from "./defineFunction";
const functions = _functions;
export default functions;
// TODO(kevinb): have functions return an object and call defineFunction with
// that object in this file instead of relying on side-effects.
import "./functions/accent";
import "./functions/accentunder";
import "./functions/arrow";
import "./functions/pmb";
import "./environments/cd";
import "./functions/char";
import "./functions/color";
import "./functions/cr";
import "./functions/def";
import "./functions/delimsizing";
import "./functions/enclose";
import "./functions/environment";
import "./functions/font";
import "./functions/genfrac";
import "./functions/horizBrace";
import "./functions/href";
import "./functions/hbox";
import "./functions/html";
import "./functions/htmlmathml";
import "./functions/includegraphics";
import "./functions/kern";
import "./functions/lap";
import "./functions/math";
import "./functions/mathchoice";
import "./functions/mclass";
import "./functions/op";
import "./functions/operatorname";
import "./functions/ordgroup";
import "./functions/overline";
import "./functions/phantom";
import "./functions/raisebox";
import "./functions/relax";
import "./functions/rule";
import "./functions/sizing";
import "./functions/smash";
import "./functions/sqrt";
import "./functions/styling";
import "./functions/supsub";
import "./functions/symbolsOp";
import "./functions/symbolsOrd";
import "./functions/symbolsSpacing";
import "./functions/tag";
import "./functions/text";
import "./functions/underline";
import "./functions/vcenter";
import "./functions/verb";

119
frontend/node_modules/katex/src/functions/font.ts generated vendored Normal file
View File

@@ -0,0 +1,119 @@
// TODO(kevinb): implement \\sl and \\sc
import {binrelClass} from "./mclass";
import defineFunction, {normalizeArgument} from "../defineFunction";
import {isCharacterBox} from "../utils";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type Options from "../Options";
import type {ParseNode} from "../parseNode";
const htmlBuilder = (group: ParseNode<"font">, options: Options) => {
const font = group.font;
const newOptions = options.withFont(font);
return html.buildGroup(group.body, newOptions);
};
const mathmlBuilder = (group: ParseNode<"font">, options: Options) => {
const font = group.font;
const newOptions = options.withFont(font);
return mml.buildGroup(group.body, newOptions);
};
const fontAliases: Record<string, string> = {
"\\Bbb": "\\mathbb",
"\\bold": "\\mathbf",
"\\frak": "\\mathfrak",
"\\bm": "\\boldsymbol",
};
defineFunction({
type: "font",
names: [
// styles, except \boldsymbol defined below
"\\mathrm", "\\mathit", "\\mathbf", "\\mathnormal", "\\mathsfit",
// families
"\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
"\\mathtt",
// aliases, except \bm defined below
"\\Bbb", "\\bold", "\\frak",
],
props: {
numArgs: 1,
allowedInArgument: true,
},
handler: ({parser, funcName}, args) => {
const body = normalizeArgument(args[0]);
let func = funcName;
if (func in fontAliases) {
func = fontAliases[func];
}
return {
type: "font",
mode: parser.mode,
font: func.slice(1),
body,
};
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "mclass",
names: ["\\boldsymbol", "\\bm"],
props: {
numArgs: 1,
},
handler: ({parser}, args) => {
const body = args[0];
// amsbsy.sty's \boldsymbol uses \binrel spacing to inherit the
// argument's bin|rel|ord status
return {
type: "mclass",
mode: parser.mode,
mclass: binrelClass(body),
body: [
{
type: "font",
mode: parser.mode,
font: "boldsymbol",
body,
},
],
isCharacterBox: isCharacterBox(body),
};
},
});
// Old font changing functions
defineFunction({
type: "font",
names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
props: {
numArgs: 0,
allowedInText: true,
},
handler: ({parser, funcName, breakOnTokenText}, args) => {
const {mode} = parser;
const body = parser.parseExpression(true, breakOnTokenText);
const style = `math${funcName.slice(1)}`;
return {
type: "font",
mode: mode,
font: style,
body: {
type: "ordgroup",
mode: parser.mode,
body,
},
};
},
htmlBuilder,
mathmlBuilder,
});

105
frontend/node_modules/katex/src/functions/html.ts generated vendored Normal file
View File

@@ -0,0 +1,105 @@
import defineFunction, {ordargument} from "../defineFunction";
import {makeSpan} from "../buildCommon";
import {assertNodeType} from "../parseNode";
import ParseError from "../ParseError";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {AnyTrustContext} from "../Settings";
defineFunction({
type: "html",
names: ["\\htmlClass", "\\htmlId", "\\htmlStyle", "\\htmlData"],
props: {
numArgs: 2,
argTypes: ["raw", "original"],
allowedInText: true,
},
handler: ({parser, funcName, token}, args) => {
const value = assertNodeType(args[0], "raw").string;
const body = args[1];
if (parser.settings.strict) {
parser.settings.reportNonstrict("htmlExtension",
"HTML extension is disabled on strict mode");
}
let trustContext: AnyTrustContext;
const attributes: Record<string, string> = {};
switch (funcName) {
case "\\htmlClass":
attributes.class = value;
trustContext = {
command: "\\htmlClass",
class: value,
};
break;
case "\\htmlId":
attributes.id = value;
trustContext = {
command: "\\htmlId",
id: value,
};
break;
case "\\htmlStyle":
attributes.style = value;
trustContext = {
command: "\\htmlStyle",
style: value,
};
break;
case "\\htmlData": {
const data = value.split(",");
for (let i = 0; i < data.length; i++) {
const item = data[i];
const firstEquals = item.indexOf("=");
if (firstEquals < 0) {
throw new ParseError(`\\htmlData key/value '${item}'` +
` missing equals sign`);
}
const key = item.slice(0, firstEquals);
const value = item.slice(firstEquals + 1);
attributes["data-" + key.trim()] = value;
}
trustContext = {
command: "\\htmlData",
attributes,
};
break;
}
default:
throw new Error("Unrecognized html command");
}
if (!parser.settings.isTrusted(trustContext)) {
return parser.formatUnsupportedCmd(funcName);
}
return {
type: "html",
mode: parser.mode,
attributes,
body: ordargument(body),
};
},
htmlBuilder: (group, options) => {
const elements = html.buildExpression(group.body, options, false);
const classes = ["enclosing"];
if (group.attributes.class) {
classes.push(...group.attributes.class.trim().split(/\s+/));
}
const span = makeSpan(classes, elements, options);
for (const attr in group.attributes) {
if (attr !== "class" && group.attributes.hasOwnProperty(attr)) {
span.setAttribute(attr, group.attributes[attr]);
}
}
return span;
},
mathmlBuilder: (group, options) => {
return mml.buildExpressionRow(group.body, options);
},
});

55
frontend/node_modules/katex/src/functions/kern.ts generated vendored Normal file
View File

@@ -0,0 +1,55 @@
// Horizontal spacing commands
import defineFunction from "../defineFunction";
import {makeGlue} from "../buildCommon";
import {SpaceNode} from "../mathMLTree";
import {calculateSize} from "../units";
import {assertNodeType} from "../parseNode";
// TODO: \hskip and \mskip should support plus and minus in lengths
defineFunction({
type: "kern",
names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"],
props: {
numArgs: 1,
argTypes: ["size"],
primitive: true,
allowedInText: true,
},
handler({parser, funcName}, args) {
const size = assertNodeType(args[0], "size");
if (parser.settings.strict) {
const mathFunction = (funcName[1] === 'm'); // \mkern, \mskip
const muUnit = (size.value.unit === 'mu');
if (mathFunction) {
if (!muUnit) {
parser.settings.reportNonstrict("mathVsTextUnits",
`LaTeX's ${funcName} supports only mu units, ` +
`not ${size.value.unit} units`);
}
if (parser.mode !== "math") {
parser.settings.reportNonstrict("mathVsTextUnits",
`LaTeX's ${funcName} works only in math mode`);
}
} else { // !mathFunction
if (muUnit) {
parser.settings.reportNonstrict("mathVsTextUnits",
`LaTeX's ${funcName} doesn't support mu units`);
}
}
}
return {
type: "kern",
mode: parser.mode,
dimension: size.value,
};
},
htmlBuilder(group, options) {
return makeGlue(group.dimension, options);
},
mathmlBuilder(group, options) {
const dimension = calculateSize(group.dimension, options);
return new SpaceNode(dimension);
},
});

41
frontend/node_modules/katex/src/functions/math.ts generated vendored Normal file
View File

@@ -0,0 +1,41 @@
import defineFunction from "../defineFunction";
import ParseError from "../ParseError";
// Switching from text mode back to math mode
defineFunction({
type: "styling",
names: ["\\(", "$"],
props: {
numArgs: 0,
allowedInText: true,
allowedInMath: false,
},
handler({funcName, parser}, args) {
const outerMode = parser.mode;
parser.switchMode("math");
const close = (funcName === "\\(" ? "\\)" : "$");
const body = parser.parseExpression(false, close);
parser.expect(close);
parser.switchMode(outerMode);
return {
type: "styling",
mode: parser.mode,
style: "text",
body,
};
},
});
// Check for extra closing math delimiters
defineFunction({
type: "text", // Doesn't matter what this is.
names: ["\\)", "\\]"],
props: {
numArgs: 0,
allowedInText: true,
allowedInMath: false,
},
handler(context, args) {
throw new ParseError(`Mismatched ${context.funcName}`);
},
});

165
frontend/node_modules/katex/src/functions/mclass.ts generated vendored Normal file
View File

@@ -0,0 +1,165 @@
import defineFunction, {ordargument} from "../defineFunction";
import {makeSpan} from "../buildCommon";
import {isCharacterBox} from "../utils";
import {MathNode} from "../mathMLTree";
import type {AnyParseNode} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type Options from "../Options";
import type {ParseNode} from "../parseNode";
function htmlBuilder(group: ParseNode<"mclass">, options: Options) {
const elements = html.buildExpression(group.body, options, true);
return makeSpan([group.mclass], elements, options);
}
function mathmlBuilder(group: ParseNode<"mclass">, options: Options) {
let node: MathNode;
const inner = mml.buildExpression(group.body, options);
if (group.mclass === "minner") {
node = new MathNode("mpadded", inner);
} else if (group.mclass === "mord") {
if (group.isCharacterBox) {
node = inner[0];
node.type = "mi";
} else {
node = new MathNode("mi", inner);
}
} else {
if (group.isCharacterBox) {
node = inner[0];
node.type = "mo";
} else {
node = new MathNode("mo", inner);
}
// Set spacing based on what is the most likely adjacent atom type.
// See TeXbook p170.
if (group.mclass === "mbin") {
node.attributes.lspace = "0.22em"; // medium space
node.attributes.rspace = "0.22em";
} else if (group.mclass === "mpunct") {
node.attributes.lspace = "0em";
node.attributes.rspace = "0.17em"; // thinspace
} else if (group.mclass === "mopen" || group.mclass === "mclose") {
node.attributes.lspace = "0em";
node.attributes.rspace = "0em";
} else if (group.mclass === "minner") {
node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option
node.attributes.width = "+0.1111em";
}
// MathML <mo> default space is 5/18 em, so <mrel> needs no action.
// Ref: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
}
return node;
}
// Math class commands except \mathop
defineFunction({
type: "mclass",
names: [
"\\mathord", "\\mathbin", "\\mathrel", "\\mathopen",
"\\mathclose", "\\mathpunct", "\\mathinner",
],
props: {
numArgs: 1,
primitive: true,
},
handler({parser, funcName}, args) {
const body = args[0];
return {
type: "mclass",
mode: parser.mode,
mclass: "m" + funcName.slice(5), // TODO(kevinb): don't prefix with 'm'
body: ordargument(body),
isCharacterBox: isCharacterBox(body),
};
},
htmlBuilder,
mathmlBuilder,
});
export const binrelClass = (arg: AnyParseNode): string => {
// \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument.
// (by rendering separately and with {}s before and after, and measuring
// the change in spacing). We'll do roughly the same by detecting the
// atom type directly.
const atom = (arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg);
if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) {
return "m" + atom.family;
} else {
return "mord";
}
};
// \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord.
// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX.
defineFunction({
type: "mclass",
names: ["\\@binrel"],
props: {
numArgs: 2,
},
handler({parser}, args) {
return {
type: "mclass",
mode: parser.mode,
mclass: binrelClass(args[0]),
body: ordargument(args[1]),
isCharacterBox: isCharacterBox(args[1]),
};
},
});
// Build a relation or stacked op by placing one symbol on top of another
defineFunction({
type: "mclass",
names: ["\\stackrel", "\\overset", "\\underset"],
props: {
numArgs: 2,
},
handler({parser, funcName}, args) {
const baseArg = args[1];
const shiftedArg = args[0];
let mclass;
if (funcName !== "\\stackrel") {
// LaTeX applies \binrel spacing to \overset and \underset.
mclass = binrelClass(baseArg);
} else {
mclass = "mrel"; // for \stackrel
}
const baseOp: ParseNode<"op"> = {
type: "op",
mode: baseArg.mode,
limits: true,
alwaysHandleSupSub: true,
parentIsSupSub: false,
symbol: false,
suppressBaseShift: funcName !== "\\stackrel",
body: ordargument(baseArg),
};
const supsub: ParseNode<"supsub"> = {
type: "supsub",
mode: shiftedArg.mode,
base: baseOp,
sup: funcName === "\\underset" ? null : shiftedArg,
sub: funcName === "\\underset" ? shiftedArg : null,
};
return {
type: "mclass",
mode: parser.mode,
mclass,
body: [supsub],
isCharacterBox: isCharacterBox(supsub),
};
},
htmlBuilder,
mathmlBuilder,
});

View File

@@ -0,0 +1,161 @@
import defineFunction, {ordargument} from "../defineFunction";
import defineMacro from "../defineMacro";
import {makeSpan} from "../buildCommon";
import {MathNode, newDocumentFragment, SpaceNode, TextNode} from "../mathMLTree";
import {SymbolNode} from "../domTree";
import {assembleSupSub} from "./utils/assembleSupSub";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
import type {AnyParseNode, ParseNode} from "../parseNode";
// NOTE: Unlike most `htmlBuilder`s, this one handles not only
// "operatorname", but also "supsub" since \operatorname* can
// affect super/subscripting.
export const htmlBuilder: HtmlBuilderSupSub<"operatorname"> = (grp, options) => {
// Operators are handled in the TeXbook pg. 443-444, rule 13(a).
let supGroup;
let subGroup;
let hasLimits = false;
let group: ParseNode<"operatorname">;
if (grp.type === "supsub") {
// If we have limits, supsub will pass us its group to handle. Pull
// out the superscript and subscript and set the group to the op in
// its base.
supGroup = grp.sup;
subGroup = grp.sub;
group = assertNodeType(grp.base, "operatorname");
hasLimits = true;
} else {
group = assertNodeType(grp, "operatorname");
}
let base;
if (group.body.length > 0) {
const body = group.body.map((child): AnyParseNode => {
const childText = "text" in child ? child.text : undefined;
if (typeof childText === "string") {
return {
type: "textord",
mode: child.mode,
text: childText,
};
} else {
return child;
}
});
// Consolidate function names into symbol characters.
const expression = html.buildExpression(
body, options.withFont("mathrm"), true);
for (let i = 0; i < expression.length; i++) {
const child = expression[i];
if (child instanceof SymbolNode) {
// Per amsopn package,
// change minus to hyphen and \ast to asterisk
child.text = child.text.replace(/\u2212/, "-")
.replace(/\u2217/, "*");
}
}
base = makeSpan(["mop"], expression, options);
} else {
base = makeSpan(["mop"], [], options);
}
if (hasLimits) {
return assembleSupSub(base, supGroup, subGroup, options,
options.style, 0, 0);
} else {
return base;
}
};
const mathmlBuilder: MathMLBuilder<"operatorname"> = (group, options) => {
// The steps taken here are similar to the html version.
let expression: Array<MathNode | TextNode> = mml.buildExpression(
group.body, options.withFont("mathrm"));
// Is expression a string or has it something like a fraction?
let isAllString = true; // default
for (let i = 0; i < expression.length; i++) {
const node = expression[i];
if (node instanceof SpaceNode) {
// Do nothing
} else if (node instanceof MathNode) {
switch (node.type) {
case "mi":
case "mn":
case "mspace":
case "mtext":
break; // Do nothing yet.
case "mo": {
const child = node.children[0];
if (node.children.length === 1 &&
child instanceof TextNode) {
child.text =
child.text.replace(/\u2212/, "-")
.replace(/\u2217/, "*");
} else {
isAllString = false;
}
break;
}
default:
isAllString = false;
}
} else {
isAllString = false;
}
}
if (isAllString) {
// Write a single TextNode instead of multiple nested tags.
const word = expression.map(node => node.toText()).join("");
expression = [new TextNode(word)];
}
const identifier = new MathNode("mi", expression);
identifier.setAttribute("mathvariant", "normal");
// \u2061 is the same as &ApplyFunction;
// ref: https://www.w3schools.com/charsets/ref_html_entities_a.asp
const operator = new MathNode("mo",
[mml.makeText("\u2061", "text")]);
if (group.parentIsSupSub) {
return new MathNode("mrow", [identifier, operator]);
} else {
return newDocumentFragment([identifier, operator]);
}
};
// \operatorname
// amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@
defineFunction({
type: "operatorname",
names: ["\\operatorname@", "\\operatornamewithlimits"],
props: {
numArgs: 1,
},
handler: ({parser, funcName}, args) => {
const body = args[0];
return {
type: "operatorname",
mode: parser.mode,
body: ordargument(body),
alwaysHandleSupSub: (funcName === "\\operatornamewithlimits"),
limits: false,
parentIsSupSub: false,
};
},
htmlBuilder,
mathmlBuilder,
});
defineMacro("\\operatorname",
"\\@ifstar\\operatornamewithlimits\\operatorname@");

124
frontend/node_modules/katex/src/functions/sqrt.ts generated vendored Normal file
View File

@@ -0,0 +1,124 @@
import defineFunction from "../defineFunction";
import {makeSpan, makeVList, wrapFragment} from "../buildCommon";
import {MathNode} from "../mathMLTree";
import {makeSqrtImage} from "../delimiter";
import Style from "../Style";
import {makeEm} from "../units";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
defineFunction({
type: "sqrt",
names: ["\\sqrt"],
props: {
numArgs: 1,
numOptionalArgs: 1,
},
handler({parser}, args, optArgs) {
const index = optArgs[0];
const body = args[0];
return {
type: "sqrt",
mode: parser.mode,
body,
index,
};
},
htmlBuilder(group, options) {
// Square roots are handled in the TeXbook pg. 443, Rule 11.
// First, we do the same steps as in overline to build the inner group
// and line
let inner = html.buildGroup(group.body, options.havingCrampedStyle());
if (inner.height === 0) {
// Render a small surd.
inner.height = options.fontMetrics().xHeight;
}
// Some groups can return document fragments. Handle those by wrapping
// them in a span.
inner = wrapFragment(inner, options);
// Calculate the minimum size for the \surd delimiter
const metrics = options.fontMetrics();
const theta = metrics.defaultRuleThickness;
let phi = theta;
if (options.style.id < Style.TEXT.id) {
phi = options.fontMetrics().xHeight;
}
// Calculate the clearance between the body and line
let lineClearance = theta + phi / 4;
const minDelimiterHeight = (inner.height + inner.depth +
lineClearance + theta);
// Create a sqrt SVG of the required minimum size
const {span: img, ruleWidth, advanceWidth} =
makeSqrtImage(minDelimiterHeight, options);
const delimDepth = img.height - ruleWidth;
// Adjust the clearance based on the delimiter size
if (delimDepth > inner.height + inner.depth + lineClearance) {
lineClearance =
(lineClearance + delimDepth - inner.height - inner.depth) / 2;
}
// Shift the sqrt image
const imgShift = img.height - inner.height - lineClearance - ruleWidth;
inner.style.paddingLeft = makeEm(advanceWidth);
// Overlay the image and the argument.
const body = makeVList({
positionType: "firstBaseline",
children: [
{type: "elem", elem: inner, wrapperClasses: ["svg-align"]},
{type: "kern", size: -(inner.height + imgShift)},
{type: "elem", elem: img},
{type: "kern", size: ruleWidth},
],
}, options);
if (!group.index) {
return makeSpan(["mord", "sqrt"], [body], options);
} else {
// Handle the optional root index
// The index is always in scriptscript style
const newOptions = options.havingStyle(Style.SCRIPTSCRIPT);
const rootm = html.buildGroup(group.index, newOptions, options);
// The amount the index is shifted by. This is taken from the TeX
// source, in the definition of `\r@@t`.
const toShift = 0.6 * (body.height - body.depth);
// Build a VList with the superscript shifted up correctly
const rootVList = makeVList({
positionType: "shift",
positionData: -toShift,
children: [{type: "elem", elem: rootm}],
}, options);
// Add a class surrounding it so we can add on the appropriate
// kerning
const rootVListWrap = makeSpan(["root"], [rootVList]);
return makeSpan(["mord", "sqrt"],
[rootVListWrap, body], options);
}
},
mathmlBuilder(group, options) {
const {body, index} = group;
return index ?
new MathNode(
"mroot", [
mml.buildGroup(body, options),
mml.buildGroup(index, options),
]) :
new MathNode(
"msqrt", [mml.buildGroup(body, options)]);
},
});

View File

@@ -0,0 +1,72 @@
import {defineFunctionBuilders} from "../defineFunction";
import {mathsym, makeOrd, makeSpan} from "../buildCommon";
import {MathNode, TextNode} from "../mathMLTree";
import ParseError from "../ParseError";
// A map of CSS-based spacing functions to their CSS class.
const cssSpace: Record<string, string> = {
"\\nobreak": "nobreak",
"\\allowbreak": "allowbreak",
};
// A lookup table to determine whether a spacing function/symbol should be
// treated like a regular space character. If a symbol or command is a key
// in this table, then it should be a regular space character. Furthermore,
// the associated value may have a `className` specifying an extra CSS class
// to add to the created `span`.
const regularSpace: Record<string, {className?: string}> = {
" ": {},
"\\ ": {},
"~": {
className: "nobreak",
},
"\\space": {},
"\\nobreakspace": {
className: "nobreak",
},
};
// ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in
// src/symbols.js.
defineFunctionBuilders({
type: "spacing",
htmlBuilder(group, options) {
if (regularSpace.hasOwnProperty(group.text)) {
const className = regularSpace[group.text].className || "";
// Spaces are generated by adding an actual space. Each of these
// things has an entry in the symbols table, so these will be turned
// into appropriate outputs.
if (group.mode === "text") {
const ord = makeOrd(group, options, "textord");
ord.classes.push(className);
return ord;
} else {
return makeSpan(["mspace", className],
[mathsym(group.text, group.mode, options)],
options);
}
} else if (cssSpace.hasOwnProperty(group.text)) {
// Spaces based on just a CSS class.
return makeSpan(
["mspace", cssSpace[group.text]],
[], options);
} else {
throw new ParseError(`Unknown type of space "${group.text}"`);
}
},
mathmlBuilder(group, options) {
let node;
if (regularSpace.hasOwnProperty(group.text)) {
node = new MathNode(
"mtext", [new TextNode("\u00a0")]);
} else if (cssSpace.hasOwnProperty(group.text)) {
// CSS-based MathML spaces (\nobreak, \allowbreak) are ignored
return new MathNode("mspace");
} else {
throw new ParseError(`Unknown type of space "${group.text}"`);
}
return node;
},
});

77
frontend/node_modules/katex/src/functions/text.ts generated vendored Normal file
View File

@@ -0,0 +1,77 @@
import defineFunction, {ordargument} from "../defineFunction";
import {makeSpan} from "../buildCommon";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type Options from "../Options";
import type {ParseNode} from "../parseNode";
// Non-mathy text, possibly in a font
const textFontFamilies: Record<string, string | undefined> = {
"\\text": undefined, "\\textrm": "textrm", "\\textsf": "textsf",
"\\texttt": "texttt", "\\textnormal": "textrm",
};
const textFontWeights: Record<string, "textbf" | "textmd"> = {
"\\textbf": "textbf",
"\\textmd": "textmd",
};
const textFontShapes: Record<string, "textit" | "textup"> = {
"\\textit": "textit",
"\\textup": "textup",
};
const optionsWithFont = (group: ParseNode<"text">, options: Options): Options => {
const font = group.font;
// Checks if the argument is a font family or a font style.
if (!font) {
return options;
} else if (textFontFamilies[font]) {
return options.withTextFontFamily(textFontFamilies[font]);
} else if (textFontWeights[font]) {
return options.withTextFontWeight(textFontWeights[font]);
} else if (font === "\\emph") {
return options.fontShape === "textit" ?
options.withTextFontShape("textup") :
options.withTextFontShape("textit");
}
return options.withTextFontShape(textFontShapes[font] as "textit" | "textup");
};
defineFunction({
type: "text",
names: [
// Font families
"\\text", "\\textrm", "\\textsf", "\\texttt", "\\textnormal",
// Font weights
"\\textbf", "\\textmd",
// Font Shapes
"\\textit", "\\textup", "\\emph",
],
props: {
numArgs: 1,
argTypes: ["text"],
allowedInArgument: true,
allowedInText: true,
},
handler({parser, funcName}, args) {
const body = args[0];
return {
type: "text",
mode: parser.mode,
body: ordargument(body),
font: funcName,
};
},
htmlBuilder(group, options) {
const newOptions = optionsWithFont(group, options);
const inner = html.buildExpression(group.body, newOptions, true);
return makeSpan(["mord", "text"], inner, newOptions);
},
mathmlBuilder(group, options) {
const newOptions = optionsWithFont(group, options);
return mml.buildExpressionRow(group.body, newOptions);
},
});

259
frontend/node_modules/katex/src/mathMLTree.ts generated vendored Normal file
View File

@@ -0,0 +1,259 @@
/**
* These objects store data about MathML nodes. This is the MathML equivalent
* of the types in domTree.js. Since MathML handles its own rendering, and
* since we're mainly using MathML to improve accessibility, we don't manage
* any of the styling state that the plain DOM nodes do.
*
* The `toNode` and `toMarkup` functions work similarly to how they do in
* domTree.js, creating namespaced DOM nodes and HTML text markup respectively.
*/
import {escape} from "./utils";
import {DocumentFragment} from "./tree";
import {createClass} from "./domTree";
import {makeEm} from "./units";
import type {VirtualNode} from "./tree";
/**
* MathML node types used in KaTeX. For a complete list of MathML nodes, see
* https://developer.mozilla.org/en-US/docs/Web/MathML/Element.
*/
export type MathNodeType =
"math" | "annotation" | "semantics" |
"mtext" | "mn" | "mo" | "mi" | "mspace" |
"mover" | "munder" | "munderover" | "msup" | "msub" | "msubsup" |
"mfrac" | "mroot" | "msqrt" |
"mtable" | "mtr" | "mtd" | "mlabeledtr" |
"mrow" | "menclose" |
"mstyle" | "mpadded" | "mphantom" | "mglyph";
export interface MathDomNode extends VirtualNode {
toText(): string;
}
export type documentFragment = DocumentFragment<MathDomNode>;
export function newDocumentFragment(
children: ReadonlyArray<MathDomNode>
): documentFragment {
return new DocumentFragment(children);
}
/**
* This node represents a general purpose MathML node of any type. The
* constructor requires the type of node to create (for example, `"mo"` or
* `"mspace"`, corresponding to `<mo>` and `<mspace>` tags).
*/
export class MathNode implements MathDomNode {
type: MathNodeType;
attributes: Record<string, string>;
children: MathDomNode[];
classes: string[];
constructor(
type: MathNodeType,
children?: MathDomNode[],
classes?: string[]
) {
this.type = type;
this.attributes = {};
this.children = children || [];
this.classes = classes || [];
}
/**
* Sets an attribute on a MathML node. MathML depends on attributes to convey a
* semantic content, so this is used heavily.
*/
setAttribute(name: string, value: string) {
this.attributes[name] = value;
}
/**
* Gets an attribute on a MathML node.
*/
getAttribute(name: string): string {
return this.attributes[name];
}
/**
* Converts the math node into a MathML-namespaced DOM element.
*/
toNode(): Node {
const node = document.createElementNS(
"http://www.w3.org/1998/Math/MathML", this.type);
for (const attr in this.attributes) {
if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
node.setAttribute(attr, this.attributes[attr]);
}
}
if (this.classes.length > 0) {
node.className = createClass(this.classes);
}
for (let i = 0; i < this.children.length; i++) {
// Combine multiple TextNodes into one TextNode, to prevent
// screen readers from reading each as a separate word [#3995]
if (this.children[i] instanceof TextNode &&
this.children[i + 1] instanceof TextNode) {
let text = this.children[i].toText() + this.children[++i].toText();
while (this.children[i + 1] instanceof TextNode) {
text += this.children[++i].toText();
}
node.appendChild(new TextNode(text).toNode());
} else {
node.appendChild(this.children[i].toNode());
}
}
return node;
}
/**
* Converts the math node into an HTML markup string.
*/
toMarkup(): string {
let markup = "<" + this.type;
// Add the attributes
for (const attr in this.attributes) {
if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
markup += " " + attr + "=\"";
markup += escape(this.attributes[attr]);
markup += "\"";
}
}
if (this.classes.length > 0) {
markup += ` class ="${escape(createClass(this.classes))}"`;
}
markup += ">";
for (let i = 0; i < this.children.length; i++) {
markup += this.children[i].toMarkup();
}
markup += "</" + this.type + ">";
return markup;
}
/**
* Converts the math node into a string, similar to innerText, but escaped.
*/
toText(): string {
return this.children.map(child => child.toText()).join("");
}
}
/**
* This node represents a piece of text.
*/
export class TextNode implements MathDomNode {
text: string;
constructor(text: string) {
this.text = text;
}
/**
* Converts the text node into a DOM text node.
*/
toNode(): Node {
return document.createTextNode(this.text);
}
/**
* Converts the text node into escaped HTML markup
* (representing the text itself).
*/
toMarkup(): string {
return escape(this.toText());
}
/**
* Converts the text node into a string
* (representing the text itself).
*/
toText(): string {
return this.text;
}
}
/**
* This node represents a space, but may render as <mspace.../> or as text,
* depending on the width.
*/
export class SpaceNode implements MathDomNode {
width: number;
character: string | null | undefined;
/**
* Create a Space node with width given in CSS ems.
*/
constructor(width: number) {
this.width = width;
// See https://www.w3.org/TR/2000/WD-MathML2-20000328/chapter6.html
// for a table of space-like characters. We use Unicode
// representations instead of &LongNames; as it's not clear how to
// make the latter via document.createTextNode.
if (width >= 0.05555 && width <= 0.05556) {
this.character = "\u200a"; // &VeryThinSpace;
} else if (width >= 0.1666 && width <= 0.1667) {
this.character = "\u2009"; // &ThinSpace;
} else if (width >= 0.2222 && width <= 0.2223) {
this.character = "\u2005"; // &MediumSpace;
} else if (width >= 0.2777 && width <= 0.2778) {
this.character = "\u2005\u200a"; // &ThickSpace;
} else if (width >= -0.05556 && width <= -0.05555) {
this.character = "\u200a\u2063"; // &NegativeVeryThinSpace;
} else if (width >= -0.1667 && width <= -0.1666) {
this.character = "\u2009\u2063"; // &NegativeThinSpace;
} else if (width >= -0.2223 && width <= -0.2222) {
this.character = "\u205f\u2063"; // &NegativeMediumSpace;
} else if (width >= -0.2778 && width <= -0.2777) {
this.character = "\u2005\u2063"; // &NegativeThickSpace;
} else {
this.character = null;
}
}
/**
* Converts the math node into a MathML-namespaced DOM element.
*/
toNode(): Node {
if (this.character) {
return document.createTextNode(this.character);
} else {
const node = document.createElementNS(
"http://www.w3.org/1998/Math/MathML", "mspace");
node.setAttribute("width", makeEm(this.width));
return node;
}
}
/**
* Converts the math node into an HTML markup string.
*/
toMarkup(): string {
if (this.character) {
return `<mtext>${this.character}</mtext>`;
} else {
return `<mspace width="${makeEm(this.width)}"/>`;
}
}
/**
* Converts the math node into a string, similar to innerText.
*/
toText(): string {
if (this.character) {
return this.character;
} else {
return " ";
}
}
}

28
frontend/node_modules/katex/src/metrics/format_json.py generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import sys
import json
props = ['depth', 'height', 'italic', 'skew']
if len(sys.argv) > 1:
if sys.argv[1] == '--width':
props.append('width')
data = json.load(sys.stdin)
sys.stdout.write(
"// This file is GENERATED by buildMetrics.sh. DO NOT MODIFY.\n")
sep = "export default {\n "
for font in sorted(data):
sys.stdout.write(sep + json.dumps(font))
sep = ": {\n "
for glyph in sorted(data[font], key=int):
sys.stdout.write(sep + json.dumps(glyph) + ": ")
values = [value if value != 0.0 else 0 for value in
[data[font][glyph][key] for key in props]]
sys.stdout.write(json.dumps(values))
sep = ",\n "
sep = ",\n },\n "
sys.stdout.write(",\n },\n};\n")

108
frontend/node_modules/katex/src/unicodeSupOrSub.ts generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Helpers for Parser.js handling of Unicode (sub|super)script characters.
export const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/;
export const uSubsAndSups: Readonly<Record<string, string>> = Object.freeze({
'₊': '+',
'₋': '-',
'₌': '=',
'₍': '(',
'₎': ')',
'₀': '0',
'₁': '1',
'₂': '2',
'₃': '3',
'₄': '4',
'₅': '5',
'₆': '6',
'₇': '7',
'₈': '8',
'₉': '9',
'\u2090': 'a',
'\u2091': 'e',
'\u2095': 'h',
'\u1D62': 'i',
'\u2C7C': 'j',
'\u2096': 'k',
'\u2097': 'l',
'\u2098': 'm',
'\u2099': 'n',
'\u2092': 'o',
'\u209A': 'p',
'\u1D63': 'r',
'\u209B': 's',
'\u209C': 't',
'\u1D64': 'u',
'\u1D65': 'v',
'\u2093': 'x',
'\u1D66': 'β',
'\u1D67': 'γ',
'\u1D68': 'ρ',
'\u1D69': '\u03d5',
'\u1D6A': 'χ',
'⁺': '+',
'⁻': '-',
'⁼': '=',
'⁽': '(',
'⁾': ')',
'⁰': '0',
'¹': '1',
'²': '2',
'³': '3',
'⁴': '4',
'⁵': '5',
'⁶': '6',
'⁷': '7',
'⁸': '8',
'⁹': '9',
'\u1D2C': 'A',
'\u1D2E': 'B',
'\u1D30': 'D',
'\u1D31': 'E',
'\u1D33': 'G',
'\u1D34': 'H',
'\u1D35': 'I',
'\u1D36': 'J',
'\u1D37': 'K',
'\u1D38': 'L',
'\u1D39': 'M',
'\u1D3A': 'N',
'\u1D3C': 'O',
'\u1D3E': 'P',
'\u1D3F': 'R',
'\u1D40': 'T',
'\u1D41': 'U',
'\u2C7D': 'V',
'\u1D42': 'W',
'\u1D43': 'a',
'\u1D47': 'b',
'\u1D9C': 'c',
'\u1D48': 'd',
'\u1D49': 'e',
'\u1DA0': 'f',
'\u1D4D': 'g',
'\u02B0': 'h',
'\u2071': 'i',
'\u02B2': 'j',
'\u1D4F': 'k',
'\u02E1': 'l',
'\u1D50': 'm',
'\u207F': 'n',
'\u1D52': 'o',
'\u1D56': 'p',
'\u02B3': 'r',
'\u02E2': 's',
'\u1D57': 't',
'\u1D58': 'u',
'\u1D5B': 'v',
'\u02B7': 'w',
'\u02E3': 'x',
'\u02B8': 'y',
'\u1DBB': 'z',
'\u1D5D': 'β',
'\u1D5E': 'γ',
'\u1D5F': 'δ',
'\u1D60': '\u03d5',
'\u1D61': 'χ',
'\u1DBF': 'θ',
});

259
frontend/node_modules/katex/types/katex.d.ts generated vendored Normal file
View File

@@ -0,0 +1,259 @@
// Adapted from
// - https://katex.org/docs/options
// - https://katex.org/docs/api
// - https://katex.org/docs/error
// for v0.16.11 on 2024/12/01
// with some references from https://www.npmjs.com/package/@types/katex
/**
* For the `trust` option in `KatexOptions`, a custom function
* `handler(context)` can be provided to customize behavior depending on the
* context (command, arguments e.g. a URL, etc.)
* @see https://katex.org/docs/options
*/
export type TrustContext =
| { command: "\\url", url: string, protocol?: string }
| { command: "\\href", url: string, protocol?: string }
| { command: "\\includegraphics", url: string, protocol?: string }
| { command: "\\htmlClass", class: string }
| { command: "\\htmlId", id: string }
| { command: "\\htmlStyle", style: string }
| { command: "\\htmlData", attributes: Record<string, string> }
export type Catcodes = Record<string, number>;
export interface Lexer {
input: string;
tokenRegex: RegExp;
settings: Required<KatexOptions>;
catcodes: Catcodes;
}
export interface SourceLocation {
start: number;
end: number;
lexer: Lexer;
}
export interface Token {
text: string;
loc: SourceLocation | undefined;
noexpand?: boolean;
treatAsRelax?: boolean;
}
export type StrictFunction = (
errorCode:
| "unknownSymbol"
| "unicodeTextInMathMode"
| "mathVsTextUnits"
| "commentAtEnd"
| "htmlExtension"
| "newLineInDisplayMode",
errorMsg: string,
token: Token,
) => boolean | "error" | "warn" | "ignore" | undefined;
/**
* Options for `katex.render` and `katex.renderToString`.
* @see https://katex.org/docs/options
*/
export interface KatexOptions {
/**
* If `true` the math will be rendered in display mode.
* If `false` the math will be rendered in inline mode.
* @see https://katex.org/docs/options
*
* @default false
*/
displayMode?: boolean;
/**
* Determines the markup language of the output. The valid choices are:
* - `html`: Outputs KaTeX in HTML only.
* - `mathml`: Outputs KaTeX in MathML only.
* - `htmlAndMathml`: Outputs HTML for visual rendering and includes MathML
* for accessibility.
*
* @default "htmlAndMathml"
*/
output?: "html" | "mathml" | "htmlAndMathml";
/**
* If `true`, display math has `\tag`s rendered on the left instead of the
* right, like `\usepackage[leqno]{amsmath}` in LaTeX.
*
* @default false
*/
leqno?: boolean;
/**
* If `true`, display math renders flush left with a `2em` left margin,
* like `\documentclass[fleqn]` in LaTeX with the `amsmath` package.
*
* @default false
*/
fleqn?: boolean;
/**
* If `true`, KaTeX will throw a `ParseError` when it encounters an
* unsupported command or invalid LaTeX.
* If `false`, KaTeX will render unsupported commands as text, and render
* invalid LaTeX as its source code with hover text giving the error, in
* the color given by `errorColor`.
*
* @default true
*/
throwOnError?: boolean;
/**
* A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option
* determines the color that unsupported commands and invalid LaTeX are
* rendered in when `throwOnError` is set to `false`.
*
* @default "#cc0000"
*/
errorColor?: string;
/**
* A collection of custom macros.
* @see https://katex.org/docs/options
*/
macros?: Record<string, string | object | ((macroExpander:object)
=> string | object)>;
/**
* Specifies a minimum thickness, in ems, for fraction lines, `\sqrt` top
* lines, `{array}` vertical lines, `\hline`, `\hdashline`, `\underline`,
* `\overline`, and the borders of `\fbox`, `\boxed`, and `\fcolorbox`.
* The usual value for these items is `0.04`, so for `minRuleThickness`
* to be effective it should probably take a value slightly above `0.04`,
* say `0.05` or `0.06`. Negative values will be ignored.
*/
minRuleThickness?: number;
/**
* In early versions of both KaTeX (<0.8.0) and MathJax, the `\color`
* function expected the content to be a function argument, as in
* `\color{blue}{hello}`. In current KaTeX, `\color` is a switch, as in
* `\color{blue}` hello. This matches LaTeX behavior. If you want the old
* `\color` behavior, set option colorIsTextColor to true.
*/
colorIsTextColor?: boolean;
/**
* All user-specified sizes, e.g. in `\rule{500em}{500em}`, will be capped
* to `maxSize` ems. If set to `Infinity` (the default), users can make
* elements and spaces arbitrarily large.
*
* @default Infinity
*/
maxSize?: number;
/**
* Limit the number of macro expansions to the specified number, to prevent
* e.g. infinite macro loops. `\edef` expansion counts all expanded tokens.
* If set to `Infinity`, the macro expander will try to fully expand as in
* LaTeX.
*
* @default 1000
*/
maxExpand?: number;
/**
* If `false` or `"ignore"`, allow features that make writing LaTeX
* convenient but are not actually supported by (Xe)LaTeX
* (similar to MathJax).
* If `true` or `"error"` (LaTeX faithfulness mode), throw an error for any
* such transgressions.
* If `"warn"` (the default), warn about such behavior via `console.warn`.
* Provide a custom function `handler(errorCode, errorMsg, token)` to
* customize behavior depending on the type of transgression (summarized by
* the string code `errorCode` and detailed in `errorMsg`); this function
* can also return `"ignore"`, `"error"`, or `"warn"` to use a built-in
* behavior.
* @see https://katex.org/docs/options
*
* @default "warn"
*/
strict?:
| boolean
| "ignore" | "warn" | "error"
| StrictFunction;
/**
* If `false` (do not trust input), prevent any commands like
* `\includegraphics` that could enable adverse behavior, rendering them
* instead in `errorColor`.
* If `true` (trust input), allow all such commands.
* Provide a custom function `handler(context)` to customize behavior
* depending on the context (command, arguments e.g. a URL, etc.).
* @see https://katex.org/docs/options
*
* @default false
*/
trust?: boolean | ((context: TrustContext) => boolean);
/**
* Run KaTeX code in the global group. As a consequence, macros defined at
* the top level by `\def` and `\newcommand` are added to the macros
* argument and can be used in subsequent render calls. In LaTeX,
* constructs such as `\begin{equation}` and `$$` create a local group and
* prevent definitions other than `\gdef` from becoming visible outside of
* those blocks, so this is KaTeX's default behavior.
*
* @default false
*/
globalGroup?: boolean;
}
/**
* In-browser rendering
*
* Call the `render` function with a TeX expression and a DOM element to
* render into.
*
* @param {string} tex A TeX expression.
* @param {HTMLElement} element A HTML DOM element.
* @param {KatexOptions} options An options object.
* @returns {void}
* @see https://katex.org/docs/api
*/
export function render(
tex: string,
element: HTMLElement,
options?: KatexOptions,
): void;
/**
* Server-side rendering or rendering to a string
*
* Use the `renderToString` function to generate HTML on the server or to
* generate an HTML string of the rendered math.
*
* @param {string} tex A TeX expression.
* @param {KatexOptions} options An options object.
* @returns {string} The HTML string of the rendered math.
* @see https://katex.org/docs/api
*/
export function renderToString(tex: string, options?: KatexOptions): string;
/**
* If KaTeX encounters an error (invalid or unsupported LaTeX) and
* `throwOnError` hasn't been set to `false`, then `katex.render` and
* `katex.renderToString` will throw an exception of type
* `ParseError`. The message in this error includes some of the LaTeX source
* code, so needs to be escaped if you want to render it to HTML.
* @see https://katex.org/docs/error
*/
export class ParseError implements Error {
constructor(message: string, token?: object);
name: "ParseError";
position: number;
length: number;
rawMessage: string;
message: string;
}
export const version: string;
export as namespace katex;
declare const katex: {
version: string;
render: typeof render;
renderToString: typeof renderToString;
ParseError: typeof ParseError;
};
export default katex;