123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*************************************************************
- *
- * Copyright (c) 2018-2022 The MathJax Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Configuration options for the TexParser.
- *
- * @author v.sorge@mathjax.org (Volker Sorge)
- */
- import {HandlerConfig, FallbackConfig} from './MapHandler.js';
- import {StackItemClass} from './StackItem.js';
- import {TagsClass} from './Tags.js';
- import {userOptions, defaultOptions, OptionList} from '../../util/Options.js';
- import {SubHandlers} from './MapHandler.js';
- import {FunctionList} from '../../util/FunctionList.js';
- import {TeX} from '../tex.js';
- import {PrioritizedList} from '../../util/PrioritizedList.js';
- import {TagsFactory} from './Tags.js';
- export type StackItemConfig = {[kind: string]: StackItemClass};
- export type TagsConfig = {[kind: string]: TagsClass};
- export type Processor<T> = [T, number];
- export type ProtoProcessor<T> = Processor<T> | T;
- export type ProcessorList = Processor<Function>[];
- export type ConfigMethod = (c: ParserConfiguration, j: TeX<any, any, any>) => void;
- export type InitMethod = (c: ParserConfiguration) => void;
- export class Configuration {
- /**
- * Creates a function priority pair.
- * @param {ProtoProcessor<T>} func The function or processor.
- * @param {number} priority The default priority.
- * @return {Processor} The processor pair.
- * @template T
- */
- private static makeProcessor<T>(func: ProtoProcessor<T>, priority: number): Processor<T> {
- return Array.isArray(func) ? func : [func, priority];
- }
- /**
- * Creates a configuration for a package.
- * @param {string} name The package name or empty string.
- * @param {Object} config See `create` method.
- * @return {Configuration} The newly generated configuration.
- */
- private static _create(name: string,
- config: {handler?: HandlerConfig,
- fallback?: FallbackConfig,
- items?: StackItemConfig,
- tags?: TagsConfig,
- options?: OptionList,
- nodes?: {[key: string]: any},
- preprocessors?: ProtoProcessor<Function>[],
- postprocessors?: ProtoProcessor<Function>[],
- init?: ProtoProcessor<InitMethod>,
- config?: ProtoProcessor<ConfigMethod>,
- priority?: number,
- parser?: string,
- } = {}): Configuration {
- let priority = config.priority || PrioritizedList.DEFAULTPRIORITY;
- let init = config.init ? this.makeProcessor(config.init, priority) : null;
- let conf = config.config ? this.makeProcessor(config.config, priority) : null;
- let preprocessors = (config.preprocessors || []).map(
- pre => this.makeProcessor(pre, priority));
- let postprocessors = (config.postprocessors || []).map(
- post => this.makeProcessor(post, priority));
- let parser = config.parser || 'tex';
- return new Configuration(
- name,
- config.handler || {},
- config.fallback || {},
- config.items || {},
- config.tags || {},
- config.options || {},
- config.nodes || {},
- preprocessors, postprocessors, init, conf, priority,
- parser
- );
- }
- /**
- * Creator pattern for creating a named package configuration. This will be
- * administered in the configuration handler and can be retrieved again.
- * @param {string} name The package name.
- * @param {Object} config The configuration parameters:
- * Configuration for the TexParser consist of the following:
- * * _handler_ configuration mapping handler types to lists of symbol mappings.
- * * _fallback_ configuration mapping handler types to fallback methods.
- * * _items_ for the StackItem factory.
- * * _tags_ mapping tagging configurations to tagging objects.
- * * _options_ parse options for the packages.
- * * _nodes_ for the Node factory.
- * * _preprocessors_ list of functions for preprocessing the LaTeX
- * string wrt. to given parse options. Can contain a priority.
- * * _postprocessors_ list of functions for postprocessing the MmlNode
- * wrt. to given parse options. Can contain a priority.
- * * _init_ init method and optionally its priority.
- * * _config_ config method and optionally its priority.
- * * _priority_ default priority of the configuration.
- * * _parser_ the name of the parser that this configuration targets.
- * @return {Configuration} The newly generated configuration.
- */
- public static create(name: string,
- config: {handler?: HandlerConfig,
- fallback?: FallbackConfig,
- items?: StackItemConfig,
- tags?: TagsConfig,
- options?: OptionList,
- nodes?: {[key: string]: any},
- preprocessors?: ProtoProcessor<Function>[],
- postprocessors?: ProtoProcessor<Function>[],
- init?: ProtoProcessor<InitMethod>,
- config?: ProtoProcessor<ConfigMethod>,
- priority?: number,
- parser?: string,
- } = {}): Configuration {
- let configuration = Configuration._create(name, config);
- ConfigurationHandler.set(name, configuration);
- return configuration;
- }
- /**
- * Creates an unnamed, ephemeral package configuration. It will not added to
- * the configuration handler.
- * @param {Object} config See `create` method.
- * @return {Configuration} The ephemeral package configuration.
- */
- public static local(config: {handler?: HandlerConfig,
- fallback?: FallbackConfig,
- items?: StackItemConfig,
- tags?: TagsConfig,
- options?: OptionList,
- nodes?: {[key: string]: any},
- preprocessors?: ProtoProcessor<Function>[],
- postprocessors?: ProtoProcessor<Function>[],
- init?: ProtoProcessor<InitMethod>,
- config?: ProtoProcessor<ConfigMethod>,
- priority?: number,
- parser?: string,
- } = {}): Configuration {
- return Configuration._create('', config);
- }
- /**
- * @constructor
- */
- private constructor(readonly name: string,
- readonly handler: HandlerConfig = {},
- readonly fallback: FallbackConfig = {},
- readonly items: StackItemConfig = {},
- readonly tags: TagsConfig = {},
- readonly options: OptionList = {},
- readonly nodes: {[key: string]: any} = {},
- readonly preprocessors: ProcessorList = [],
- readonly postprocessors: ProcessorList = [],
- readonly initMethod: Processor<InitMethod> = null,
- readonly configMethod: Processor<ConfigMethod> = null,
- public priority: number,
- readonly parser: string
- ) {
- this.handler = Object.assign(
- {character: [], delimiter: [], macro: [], environment: []}, handler);
- }
- /**
- * The init method.
- * @type {Function}
- */
- public get init(): InitMethod {
- return this.initMethod ? this.initMethod[0] : null;
- }
- /**
- * The config method to call once jax is ready.
- * @type {FunctionList}
- */
- public get config(): ConfigMethod {
- return this.configMethod ? this.configMethod[0] : null;
- }
- }
- export namespace ConfigurationHandler {
- let maps: Map<string, Configuration> = new Map();
- /**
- * Adds a new configuration to the handler overwriting old ones.
- *
- * @param {string} name The name of the configuration.
- * @param {Configuration} map The configuration mapping.
- */
- export let set = function(name: string, map: Configuration): void {
- maps.set(name, map);
- };
- /**
- * Looks up a configuration.
- *
- * @param {string} name The name of the configuration.
- * @return {Configuration} The configuration with the given name or null.
- */
- export let get = function(name: string): Configuration {
- return maps.get(name);
- };
- /**
- * @return {string[]} All configurations in the handler.
- */
- export let keys = function(): IterableIterator<string> {
- return maps.keys();
- };
- }
- /**
- * Parser configuration combines the configurations of the currently selected
- * packages.
- * @constructor
- */
- export class ParserConfiguration {
- /**
- * Priority list of init methods.
- * @type {FunctionList}
- */
- protected initMethod: FunctionList = new FunctionList();
- /**
- * Priority list of init methods to call once jax is ready.
- * @type {FunctionList}
- */
- protected configMethod: FunctionList = new FunctionList();
- /**
- * An ordered list of cofigurations.
- * @type {PrioritizedList<Configuration>}
- */
- protected configurations: PrioritizedList<Configuration> = new PrioritizedList();
- /**
- * The list of parsers this configuration targets
- */
- protected parsers: string[] = [];
- /**
- * The subhandlers for this configuration.
- * @type {SubHandlers}
- */
- public handlers: SubHandlers = new SubHandlers();
- /**
- * The collated stack items.
- * @type {StackItemConfig}
- */
- public items: StackItemConfig = {};
- /**
- * The collated tag configurations.
- * @type {TagsConfig}
- */
- public tags: TagsConfig = {};
- /**
- * The collated options.
- * @type {OptionList}
- */
- public options: OptionList = {};
- /**
- * The collated node creators.
- * @type {{[key: string]: any}}
- */
- public nodes: {[key: string]: any} = {};
- /**
- * @constructor
- * @param {(string|[string,number])[]} packages A list of packages with
- * optional priorities.
- * @parm {string[]} parsers The names of the parsers this package targets
- */
- constructor(packages: (string | [string, number])[], parsers: string[] = ['tex']) {
- this.parsers = parsers;
- for (const pkg of packages.slice().reverse()) {
- this.addPackage(pkg);
- }
- for (let {item: config, priority: priority} of this.configurations) {
- this.append(config, priority);
- }
- }
- /**
- * Init method for the configuration;
- */
- public init() {
- this.initMethod.execute(this);
- }
- /**
- * Init method for when the jax is ready
- * @param {TeX} jax The TeX jax for this configuration
- */
- public config(jax: TeX<any, any, any>) {
- this.configMethod.execute(this, jax);
- for (const config of this.configurations) {
- this.addFilters(jax, config.item);
- }
- }
- /**
- * Retrieves and adds configuration for a package with priority.
- * @param {(string | [string, number]} pkg Package with priority.
- */
- public addPackage(pkg: (string | [string, number])) {
- const name = typeof pkg === 'string' ? pkg : pkg[0];
- const conf = this.getPackage(name);
- conf && this.configurations.add(conf, typeof pkg === 'string' ? conf.priority : pkg[1]);
- }
- /**
- * Adds a configuration after the input jax is created. (Used by \require.)
- * Sets items, nodes and runs configuration method explicitly.
- *
- * @param {string} name The name of the package to add
- * @param {TeX} jax The TeX jax where it is being registered
- * @param {OptionList=} options The options for the configuration.
- */
- public add(name: string, jax: TeX<any, any, any>, options: OptionList = {}) {
- const config = this.getPackage(name);
- this.append(config);
- this.configurations.add(config, config.priority);
- this.init();
- const parser = jax.parseOptions;
- parser.nodeFactory.setCreators(config.nodes);
- for (const kind of Object.keys(config.items)) {
- parser.itemFactory.setNodeClass(kind, config.items[kind]);
- }
- TagsFactory.addTags(config.tags);
- defaultOptions(parser.options, config.options);
- userOptions(parser.options, options);
- this.addFilters(jax, config);
- if (config.config) {
- config.config(this, jax);
- }
- }
- /**
- * Find a package and check that it is for the targeted parser
- *
- * @param {string} name The name of the package to check
- * @return {Configuration} The configuration for the package
- */
- protected getPackage(name: string): Configuration {
- const config = ConfigurationHandler.get(name);
- if (config && this.parsers.indexOf(config.parser) < 0) {
- throw Error(`Package ${name} doesn't target the proper parser`);
- }
- return config;
- }
- /**
- * Appends a configuration to the overall configuration object.
- * @param {Configuration} config A configuration.
- * @param {number} priority The configurations optional priority.
- */
- public append(config: Configuration, priority?: number) {
- priority = priority || config.priority;
- if (config.initMethod) {
- this.initMethod.add(config.initMethod[0], config.initMethod[1]);
- }
- if (config.configMethod) {
- this.configMethod.add(config.configMethod[0], config.configMethod[1]);
- }
- this.handlers.add(config.handler, config.fallback, priority);
- Object.assign(this.items, config.items);
- Object.assign(this.tags, config.tags);
- defaultOptions(this.options, config.options);
- Object.assign(this.nodes, config.nodes);
- }
- /**
- * Adds pre- and postprocessor as filters to the jax.
- * @param {TeX<any} jax The TeX Jax.
- * @param {Configuration} config The configuration whose processors are added.
- */
- private addFilters(jax: TeX<any, any, any>, config: Configuration) {
- for (const [pre, priority] of config.preprocessors) {
- jax.preFilters.add(pre, priority);
- }
- for (const [post, priority] of config.postprocessors) {
- jax.postFilters.add(post, priority);
- }
- }
- }
|