You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
13 KiB
JavaScript
361 lines
13 KiB
JavaScript
// @flow
|
|
/* eslint no-console:0 */
|
|
/**
|
|
* This is a module for storing settings passed into KaTeX. It correctly handles
|
|
* default settings.
|
|
*/
|
|
|
|
import utils from "./utils";
|
|
import ParseError from "./ParseError";
|
|
import {Token} from "./Token";
|
|
|
|
import type {AnyParseNode} from "./parseNode";
|
|
import type {MacroMap} from "./defineMacro";
|
|
|
|
export type StrictFunction =
|
|
(errorCode: string, errorMsg: string, token?: Token | AnyParseNode) =>
|
|
?(boolean | string);
|
|
|
|
export type TrustContextTypes = {
|
|
"\\href": {|
|
|
command: "\\href",
|
|
url: string,
|
|
protocol?: string,
|
|
|},
|
|
"\\includegraphics": {|
|
|
command: "\\includegraphics",
|
|
url: string,
|
|
protocol?: string,
|
|
|},
|
|
"\\url": {|
|
|
command: "\\url",
|
|
url: string,
|
|
protocol?: string,
|
|
|},
|
|
"\\htmlClass": {|
|
|
command: "\\htmlClass",
|
|
class: string,
|
|
|},
|
|
"\\htmlId": {|
|
|
command: "\\htmlId",
|
|
id: string,
|
|
|},
|
|
"\\htmlStyle": {|
|
|
command: "\\htmlStyle",
|
|
style: string,
|
|
|},
|
|
"\\htmlData": {|
|
|
command: "\\htmlData",
|
|
attributes: {[string]: string},
|
|
|},
|
|
};
|
|
export type AnyTrustContext = $Values<TrustContextTypes>;
|
|
export type TrustFunction = (context: AnyTrustContext) => ?boolean;
|
|
|
|
export type SettingsOptions = $Shape<Settings>;
|
|
|
|
type EnumType = {| enum: string[] |};
|
|
type Type = "boolean" | "string" | "number" | "object" | "function" | EnumType;
|
|
type Schema = {
|
|
[$Keys<SettingsOptions>]: {
|
|
/**
|
|
* Allowed type(s) of the value.
|
|
*/
|
|
type: Type | Type[];
|
|
/**
|
|
* The default value. If not specified, false for boolean, an empty string
|
|
* for string, 0 for number, an empty object for object, or the first item
|
|
* for enum will be used. If multiple types are allowed, the first allowed
|
|
* type will be used for determining the default value.
|
|
*/
|
|
default?: any;
|
|
/**
|
|
* The description.
|
|
*/
|
|
description?: string;
|
|
/**
|
|
* The function to process the option.
|
|
*/
|
|
processor?: (any) => any,
|
|
/**
|
|
* The command line argument. See Commander.js docs for more information.
|
|
* If not specified, the name prefixed with -- will be used. Set false not
|
|
* to add to the CLI.
|
|
*/
|
|
cli?: string | false;
|
|
/**
|
|
* The default value for the CLI.
|
|
*/
|
|
cliDefault?: any;
|
|
/**
|
|
* The description for the CLI. If not specified, the description for the
|
|
* option will be used.
|
|
*/
|
|
cliDescription?: string;
|
|
/**
|
|
* The custom argument processor for the CLI. See Commander.js docs for
|
|
* more information.
|
|
*/
|
|
cliProcessor?: (any, any) => any;
|
|
};
|
|
};
|
|
|
|
// TODO: automatically generate documentation
|
|
// TODO: check all properties on Settings exist
|
|
// TODO: check the type of a property on Settings matches
|
|
export const SETTINGS_SCHEMA: Schema = {
|
|
displayMode: {
|
|
type: "boolean",
|
|
description: "Render math in display mode, which puts the math in " +
|
|
"display style (so \\int and \\sum are large, for example), and " +
|
|
"centers the math on the page on its own line.",
|
|
cli: "-d, --display-mode",
|
|
},
|
|
output: {
|
|
type: {enum: ["htmlAndMathml", "html", "mathml"]},
|
|
description: "Determines the markup language of the output.",
|
|
cli: "-F, --format <type>",
|
|
},
|
|
leqno: {
|
|
type: "boolean",
|
|
description: "Render display math in leqno style (left-justified tags).",
|
|
},
|
|
fleqn: {
|
|
type: "boolean",
|
|
description: "Render display math flush left.",
|
|
},
|
|
throwOnError: {
|
|
type: "boolean",
|
|
default: true,
|
|
cli: "-t, --no-throw-on-error",
|
|
cliDescription: "Render errors (in the color given by --error-color) ins" +
|
|
"tead of throwing a ParseError exception when encountering an error.",
|
|
},
|
|
errorColor: {
|
|
type: "string",
|
|
default: "#cc0000",
|
|
cli: "-c, --error-color <color>",
|
|
cliDescription: "A color string given in the format 'rgb' or 'rrggbb' " +
|
|
"(no #). This option determines the color of errors rendered by the " +
|
|
"-t option.",
|
|
cliProcessor: (color) => "#" + color,
|
|
},
|
|
macros: {
|
|
type: "object",
|
|
cli: "-m, --macro <def>",
|
|
cliDescription: "Define custom macro of the form '\\foo:expansion' (use " +
|
|
"multiple -m arguments for multiple macros).",
|
|
cliDefault: [],
|
|
cliProcessor: (def, defs) => {
|
|
defs.push(def);
|
|
return defs;
|
|
},
|
|
},
|
|
minRuleThickness: {
|
|
type: "number",
|
|
description: "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`.",
|
|
processor: (t) => Math.max(0, t),
|
|
cli: "--min-rule-thickness <size>",
|
|
cliProcessor: parseFloat,
|
|
},
|
|
colorIsTextColor: {
|
|
type: "boolean",
|
|
description: "Makes \\color behave like LaTeX's 2-argument \\textcolor, " +
|
|
"instead of LaTeX's one-argument \\color mode change.",
|
|
cli: "-b, --color-is-text-color",
|
|
},
|
|
strict: {
|
|
type: [{enum: ["warn", "ignore", "error"]}, "boolean", "function"],
|
|
description: "Turn on strict / LaTeX faithfulness mode, which throws an " +
|
|
"error if the input uses features that are not supported by LaTeX.",
|
|
cli: "-S, --strict",
|
|
cliDefault: false,
|
|
},
|
|
trust: {
|
|
type: ["boolean", "function"],
|
|
description: "Trust the input, enabling all HTML features such as \\url.",
|
|
cli: "-T, --trust",
|
|
},
|
|
maxSize: {
|
|
type: "number",
|
|
default: Infinity,
|
|
description: "If non-zero, all user-specified sizes, e.g. in " +
|
|
"\\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, " +
|
|
"elements and spaces can be arbitrarily large",
|
|
processor: (s) => Math.max(0, s),
|
|
cli: "-s, --max-size <n>",
|
|
cliProcessor: parseInt,
|
|
},
|
|
maxExpand: {
|
|
type: "number",
|
|
default: 1000,
|
|
description: "Limit the number of macro expansions to the specified " +
|
|
"number, to prevent e.g. infinite macro loops. If set to Infinity, " +
|
|
"the macro expander will try to fully expand as in LaTeX.",
|
|
processor: (n) => Math.max(0, n),
|
|
cli: "-e, --max-expand <n>",
|
|
cliProcessor: (n) => (n === "Infinity" ? Infinity : parseInt(n)),
|
|
},
|
|
globalGroup: {
|
|
type: "boolean",
|
|
cli: false,
|
|
},
|
|
};
|
|
|
|
function getDefaultValue(schema): any {
|
|
if (schema.default) {
|
|
return schema.default;
|
|
}
|
|
const type = schema.type;
|
|
const defaultType = Array.isArray(type) ? type[0] : type;
|
|
if (typeof defaultType !== 'string') {
|
|
return defaultType.enum[0];
|
|
}
|
|
switch (defaultType) {
|
|
case 'boolean':
|
|
return false;
|
|
case 'string':
|
|
return '';
|
|
case 'number':
|
|
return 0;
|
|
case 'object':
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The main Settings object
|
|
*
|
|
* The current options stored are:
|
|
* - displayMode: Whether the expression should be typeset as inline math
|
|
* (false, the default), meaning that the math starts in
|
|
* \textstyle and is placed in an inline-block); or as display
|
|
* math (true), meaning that the math starts in \displaystyle
|
|
* and is placed in a block with vertical margin.
|
|
*/
|
|
export default class Settings {
|
|
displayMode: boolean;
|
|
output: "html" | "mathml" | "htmlAndMathml";
|
|
leqno: boolean;
|
|
fleqn: boolean;
|
|
throwOnError: boolean;
|
|
errorColor: string;
|
|
macros: MacroMap;
|
|
minRuleThickness: number;
|
|
colorIsTextColor: boolean;
|
|
strict: boolean | "ignore" | "warn" | "error" | StrictFunction;
|
|
trust: boolean | TrustFunction;
|
|
maxSize: number;
|
|
maxExpand: number;
|
|
globalGroup: boolean;
|
|
|
|
constructor(options: SettingsOptions) {
|
|
// allow null options
|
|
options = options || {};
|
|
for (const prop in SETTINGS_SCHEMA) {
|
|
if (SETTINGS_SCHEMA.hasOwnProperty(prop)) {
|
|
// $FlowFixMe
|
|
const schema = SETTINGS_SCHEMA[prop];
|
|
// TODO: validate options
|
|
// $FlowFixMe
|
|
this[prop] = options[prop] !== undefined ? (schema.processor
|
|
? schema.processor(options[prop]) : options[prop])
|
|
: getDefaultValue(schema);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report nonstrict (non-LaTeX-compatible) input.
|
|
* Can safely not be called if `this.strict` is false in JavaScript.
|
|
*/
|
|
reportNonstrict(errorCode: string, errorMsg: string,
|
|
token?: Token | AnyParseNode) {
|
|
let strict = this.strict;
|
|
if (typeof strict === "function") {
|
|
// Allow return value of strict function to be boolean or string
|
|
// (or null/undefined, meaning no further processing).
|
|
strict = strict(errorCode, errorMsg, token);
|
|
}
|
|
if (!strict || strict === "ignore") {
|
|
return;
|
|
} else if (strict === true || strict === "error") {
|
|
throw new ParseError(
|
|
"LaTeX-incompatible input and strict mode is set to 'error': " +
|
|
`${errorMsg} [${errorCode}]`, token);
|
|
} else if (strict === "warn") {
|
|
typeof console !== "undefined" && console.warn(
|
|
"LaTeX-incompatible input and strict mode is set to 'warn': " +
|
|
`${errorMsg} [${errorCode}]`);
|
|
} else { // won't happen in type-safe code
|
|
typeof console !== "undefined" && console.warn(
|
|
"LaTeX-incompatible input and strict mode is set to " +
|
|
`unrecognized '${strict}': ${errorMsg} [${errorCode}]`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check whether to apply strict (LaTeX-adhering) behavior for unusual
|
|
* input (like `\\`). Unlike `nonstrict`, will not throw an error;
|
|
* instead, "error" translates to a return value of `true`, while "ignore"
|
|
* translates to a return value of `false`. May still print a warning:
|
|
* "warn" prints a warning and returns `false`.
|
|
* This is for the second category of `errorCode`s listed in the README.
|
|
*/
|
|
useStrictBehavior(errorCode: string, errorMsg: string,
|
|
token?: Token | AnyParseNode): boolean {
|
|
let strict = this.strict;
|
|
if (typeof strict === "function") {
|
|
// Allow return value of strict function to be boolean or string
|
|
// (or null/undefined, meaning no further processing).
|
|
// But catch any exceptions thrown by function, treating them
|
|
// like "error".
|
|
try {
|
|
strict = strict(errorCode, errorMsg, token);
|
|
} catch (error) {
|
|
strict = "error";
|
|
}
|
|
}
|
|
if (!strict || strict === "ignore") {
|
|
return false;
|
|
} else if (strict === true || strict === "error") {
|
|
return true;
|
|
} else if (strict === "warn") {
|
|
typeof console !== "undefined" && console.warn(
|
|
"LaTeX-incompatible input and strict mode is set to 'warn': " +
|
|
`${errorMsg} [${errorCode}]`);
|
|
return false;
|
|
} else { // won't happen in type-safe code
|
|
typeof console !== "undefined" && console.warn(
|
|
"LaTeX-incompatible input and strict mode is set to " +
|
|
`unrecognized '${strict}': ${errorMsg} [${errorCode}]`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check whether to test potentially dangerous input, and return
|
|
* `true` (trusted) or `false` (untrusted). The sole argument `context`
|
|
* should be an object with `command` field specifying the relevant LaTeX
|
|
* command (as a string starting with `\`), and any other arguments, etc.
|
|
* If `context` has a `url` field, a `protocol` field will automatically
|
|
* get added by this function (changing the specified object).
|
|
*/
|
|
isTrusted(context: AnyTrustContext): boolean {
|
|
if (context.url && !context.protocol) {
|
|
const protocol = utils.protocolFromUrl(context.url);
|
|
if (protocol == null) {
|
|
return false;
|
|
}
|
|
context.protocol = protocol;
|
|
}
|
|
const trust = typeof this.trust === "function"
|
|
? this.trust(context)
|
|
: this.trust;
|
|
return Boolean(trust);
|
|
}
|
|
}
|