// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0 /************************************************************* * * MathJax/extensions/TeX/text-macros.js * * Implements the processing of some text-mode macros inside * \text{} and other text boxes. * * --------------------------------------------------------------------- * * Copyright (c) 2018-2020 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. */ MathJax.Extension["TeX/text-macros"] = { version: "2.7.9" }; MathJax.Hub.Register.StartupHook("TeX Jax Ready", function () { var MML = MathJax.ElementJax.mml; var TEX = MathJax.InputJax.TeX; var TEXDEF = TEX.Definitions; TEX.Parse.Augment({ // // Replace InternalMath with parser that handles some text macros // InternalMath: function(text,level) { var mml = TextParser(text, {}).Parse(); if (level != null) { mml = [MML.mstyle.apply(MML,mml).With({displaystyle:false, scriptlevel:level})]; } else if (mml.length > 1) { mml = [MML.mrow.apply(MML,mml)]; } return mml; }, // // Correctly skip newline as well as comment // Comment: function (c) { while (this.i < this.string.length && this.string.charAt(this.i) != "\n") {this.i++} this.i++; }, // // Correctly skip trailing space as well // GetCS: function () { var CS = this.string.slice(this.i).match(/^([a-z]+|.) ?/i); if (CS) {this.i += CS[0].length; return CS[1]} else {this.i++; return " "} } }); // // The text parser is a subclass of the math parser, so we can use // some of the existing methods (like GetArgument()), and some of the // control sequence implementations (like Macro, Spacer, etc.) // var TextParser = TEX.TextParser = TEX.Parse.Subclass({ Init: function (text, env) { this.env = MathJax.Hub.Insert({},env); this.stack = {env: this.env}; this.string = text; this.i = 0; this.mml = []; // the accumulated MathML elements this.text = ''; // the accumulated text so far }, // // These are the special characters in text mode // textSpecial: { '\\': 'ControlSequence', '$': 'Math', '%': 'Comment', '^': 'MathModeOnly', '_': 'MathModeOnly', '&': 'Misplaced', '#': 'Misplaced', '~': 'Tilde', ' ': 'Space', '\t': 'Space', '\r': 'Space', '\n': 'Space', '\u00A0': 'Tilde', '{': 'OpenBrace', '}': 'CloseBrace', '`': 'OpenQuote', "'": 'CloseQuote' }, // // These are text-mode macros we support // textMacros: { '(': 'Math', '$': 'SelfQuote', '_': 'SelfQuote', '%': 'SelfQuote', '{': 'SelfQuote', '}': 'SelfQuote', ' ': 'SelfQuote', '&': 'SelfQuote', '#': 'SelfQuote', '\\': 'SelfQuote', "'": ['Accent', '\u00B4'], '`': ['Accent', '\u0060'], '^': ['Accent', '^'], '"': ['Accent', '\u00A8'], '~': ['Accent', '~'], '=': ['Accent', '\u00AF'], '.': ['Accent', '\u02D9'], 'u': ['Accent', '\u02D8'], 'v': ['Accent', '\u02C7'], emph: 'Emph', rm: ['SetFont',MML.VARIANT.NORMAL], mit: ['SetFont',MML.VARIANT.ITALIC], oldstyle: ['SetFont',MML.VARIANT.OLDSTYLE], cal: ['SetFont',MML.VARIANT.CALIGRAPHIC], it: ['SetFont','-tex-mathit'], // needs special handling bf: ['SetFont',MML.VARIANT.BOLD], bbFont: ['SetFont',MML.VARIANT.DOUBLESTRUCK], scr: ['SetFont',MML.VARIANT.SCRIPT], frak: ['SetFont',MML.VARIANT.FRAKTUR], sf: ['SetFont',MML.VARIANT.SANSSERIF], tt: ['SetFont',MML.VARIANT.MONOSPACE], tiny: ['SetSize',0.5], Tiny: ['SetSize',0.6], // non-standard scriptsize: ['SetSize',0.7], small: ['SetSize',0.85], normalsize: ['SetSize',1.0], large: ['SetSize',1.2], Large: ['SetSize',1.44], LARGE: ['SetSize',1.73], huge: ['SetSize',2.07], Huge: ['SetSize',2.49], mathcal: 'MathModeOnly', mathscr: 'MathModeOnly', mathrm: 'MathModeOnly', mathbf: 'MathModeOnly', mathbb: 'MathModeOnly', mathit: 'MathModeOnly', mathfrak: 'MathModeOnly', mathsf: 'MathModeOnly', mathtt: 'MathModeOnly', Bbb: ['Macro','{\\bbFont #1}',1], textrm: ['Macro','{\\rm #1}',1], textit: ['Macro','{\\it #1}',1], textbf: ['Macro','{\\bf #1}',1], textsf: ['Macro','{\\sf #1}',1], texttt: ['Macro','{\\tt #1}',1], dagger: ['Insert', '\u2020'], ddagger: ['Insert', '\u2021'], S: ['Insert', '\u00A7'] }, // // These are the original macros that are allowed in text mode // useMathMacros: { ',': true, ':': true, '>': true, ';': true, '!': true, enspace: true, quad: true, qquad: true, thinspace: true, negthinspace: true, hskip: true, hspace: true, kern: true, mskip: true, mspace: true, mkern: true, rule: true, Rule: true, Space: true, color: true, href: true, unicode: true, ref: true, eqref: true }, // // Look through the text for special characters and process them. // Save any accumulated text aat the end and return the MathML // elements produced. // Parse: function () { var c; while ((c = this.string.charAt(this.i++))) { if (this.textSpecial.hasOwnProperty(c)) { this[this.textSpecial[c]](c); } else { this.text += c; } } this.SaveText(); return this.mml; }, // // Handle a control sequence name // If it is a text-mode macro, use it. // Otherwise look for it in the math-mode lists // Report an error if it is not there // Otherwise check if it is a macro or one of the allowed control sequences // Run the macro (with arguments if given) // ControlSequence: function (c) { var cs = this.GetCS(), name = c + cs, cmd; if (this.textMacros.hasOwnProperty(cs)) { cmd = this.textMacros[cs]; } else { cmd = this.LookupCS(cs); if (!cmd) { this.Error(["UndefinedControlSequence","Undefined control sequence %1",name]); } if ((!(cmd instanceof Array) || cmd[0] !== 'Macro') && !this.useMathMacros.hasOwnProperty(cs)) { this.Error(["MathMacro","'%1' is only supported in math mode",name]); } } if (cmd instanceof Array) { if (!this.hasOwnProperty[cmd[0]]) this.SaveText(); this[cmd[0]].apply(this,[name].concat(cmd.slice(1))); } else { if (!this.hasOwnProperty[cmd]) this.SaveText(); this[cmd].call(this,name); } }, // // Lookup the CS as a math-mode macro // LookupCS: function(cs) { if (TEXDEF.macros.hasOwnProperty(cs)) return TEXDEF.macros[cs]; if (TEXDEF.mathchar0mi.hasOwnProperty(cs)) return TEXDEF.mathchar0mi[cs]; if (TEXDEF.mathchar0mo.hasOwnProperty(cs)) return TEXDEF.mathchar0mo[cs]; if (TEXDEF.mathchar7.hasOwnProperty(cs)) return TEXDEF.mathchar7[cs]; if (TEXDEF.delimiter.hasOwnProperty('\\'+cs)) return TEXDEF.delimiter['\\'+cs]; return null; }, // // Handle internal math mode // Look for the close delimiter and process the contents // Math: function (open) { this.SaveText(); var i = this.i, j; var braces = 0, c; while ((c = this.GetNext())) { j = this.i++; switch(c) { case '\\': var cs = this.GetCS(); if (cs === ')') c = '\\('; case '$': if (braces === 0 && open === c) { this.Push(TEX.Parse(this.string.substr(i, j-i),this.env).mml()); return; } break; case '{': braces++; break; case '}': if (braces == 0) { this.Error(["ExtraCloseMissingOpen","Extra close brace or missing open brace"]); } braces--; break; } } this.Error(["MathNotTerminated","Math not terminated in text box"]); }, // // Character can only be used in math mode // MathModeOnly: function (c) { this.Error(["MathModeOnly","'%1' allowed only in math mode",c]); }, // // Character is being used out of place // Misplaced: function (c) { this.Error(["Misplaced","'%1' can not be used here",c]); }, // // Braces start new environments // OpenBrace: function (c) { var env = this.env; this.env = MathJax.Hub.Insert({}, env); this.env.oldEnv = env; }, CloseBrace: function (c) { if (this.env.oldEnv) { this.SaveText(); this.env = this.env.oldEnv; } else { this.Error(["ExtraCloseMissingOpen","Extra close brace or missing open brace"]); } }, // // Handle open and close quotes // OpenQuote: function (c) { if (this.string.charAt(this.i) === c) { this.text += "\u201C"; this.i++; } else { this.text += "\u2018" } }, CloseQuote: function (c) { if (this.string.charAt(this.i) === c) { this.text += "\u201D"; this.i++; } else { this.text += "\u2019" } }, // // Handle non-breaking and regular spaces // Tilde: function (c) { this.text += '\u00A0'; }, Space: function (c) { this.text += ' '; while (this.GetNext().match(/\s/)) this.i++; }, // // Insert the escaped characer // SelfQuote: function (name) { this.text += name.substr(1); }, // // Insert a given character // Insert: function (name, c) { this.text += c; }, // // Create an accented character using mover // Accent: function (name, c) { this.SaveText(); var base = this.ParseArg(name); var accent = MML.mo(MML.chars(c)); if (this.env.mathvariant) accent.mathvariant = this.env.mathvariant; this.Push(MML.mover(base,accent)); }, // // Switch to/from italics // Emph: function (name) { this.UseFont(name, this.env.mathvariant === '-tex-mathit' ? 'normal' : '-tex-mathit'); }, // // Use a given font on its argument // UseFont: function (name, variant) { this.SaveText(); this.Push(this.ParseTextArg(name,{mathvariant: variant})); }, // // Set a font for the rest of the text // SetFont: function (name, variant) { this.SaveText(); this.env.mathvariant = variant; }, // // Set the size to use // SetSize: function (name, size) { this.SaveText(); this.env.mathsize = size; }, // // Process the argument as text with the given environment settings // ParseTextArg: function (name, env) { var text = this.GetArgument(name); env = MathJax.Hub.Insert(MathJax.Hub.Insert({}, this.env), env); delete env.oldEnv; return TextParser(text, env).Parse(); }, // // Process an argument as text (overrides the math-mode version) // ParseArg: function (name) { var mml = TextParser(this.GetArgument(name), this.env).Parse(); if (mml.length === 0) return mml[0]; return MML.mrow.apply(MML.mrow, mml); }, // // Create an mtext element with the accumulated text, if any // and set it variant // SaveText: function () { if (this.text) { var text = MML.mtext(MML.chars(this.text)); if (this.env.mathvariant) text.mathvariant = this.env.mathvariant; this.Push(text); } this.text = ""; }, // // Save a MathML element or array, setting its size and color, if any // Push: function (mml) { if (mml instanceof Array) { if (this.env.mathsize || this.env.mathcolor) { mml = MML.mstyle.apply(MML,mml); if (this.env.mathsize) mml.mathsize = this.env.mathsize; if (this.env.mathcolor) mml.mathcolor = this.env.mathcolor; } this.mml.push.apply(this.mml,mml); } else { if (this.env.mathsize && !mml.mathsize) mml.mathsize = this.env.mathsize; if (this.env.mathcolor && !mml.mathcolor) mml.mathcolor = this.env.mathcolor; this.mml.push(mml); } }, // // Throw an error // Error: function (message) { TEX.Error(message); } }); MathJax.Hub.Startup.signal.Post('TeX text-macros Ready'); }); MathJax.Ajax.loadComplete('[MathJax]/extensions/TeX/text-macros.js'); // @license-end