/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /************************************************************* * * MathJax/extensions/TeX/begingroup.js * * Implements \begingroup and \endgroup commands that make local * definitions possible and are removed when the \endgroup occurs. * * --------------------------------------------------------------------- * * Copyright (c) 2011-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. */ MathJax.Extension["TeX/begingroup"] = { version: "2.2" }; MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () { var TEX = MathJax.InputJax.TeX, TEXDEF = TEX.Definitions; /****************************************************/ // // A namespace for localizing macros and environments // (\begingroup and \endgroup create and destroy these) // var NSFRAME = MathJax.Object.Subclass({ macros: null, // the local macro definitions environments: null, // the local environments Init: function (macros,environments) { this.macros = (macros || {}); this.environments = (environments || {}); }, // // Find a macro or environment by name // Find: function (name,type) {if (this[type][name]) {return this[type][name]}}, // // Define or remove a macro or environment // Def: function (name,value,type) {this[type][name] = value}, Undef: function (name,type) {delete this[type][name]}, // // Merge two namespaces (used when the equation namespace is combined with the root one) // Merge: function (frame) { MathJax.Hub.Insert(this.macros,frame.macros); MathJax.Hub.Insert(this.environments,frame.environments); }, // // Move global macros to the stack (globally) and remove from the frame // MergeGlobals: function (stack) { var macros = this.macros; for (var cs in macros) {if (macros.hasOwnProperty(cs) && macros[cs].global) { stack.Def(cs,macros[cs],"macros",true); delete macros[cs].global; delete macros[cs]; }} }, // // Clear the macro and environment lists // (but not global macros unless "all" is true) // Clear: function (all) { this.environments = {}; if (all) {this.macros = {}} else { var macros = this.macros; for (var cs in macros) { if (macros.hasOwnProperty(cs) && !macros[cs].global) {delete macros[cs]} } } return this; } }); /****************************************************/ // // A Stack of namespace frames // var NSSTACK = TEX.nsStack = MathJax.Object.Subclass({ stack: null, // the namespace frames top: 0, // the current top one (we don't pop for real until the equation completes) isEqn: false, // true if this is the equation stack (not the global one) // // Set up the initial stack frame // Init: function (eqn) { this.isEqn = eqn; this.stack = []; if (!eqn) {this.Push(NSFRAME(TEXDEF.macros,TEXDEF.environment))} else {this.Push(NSFRAME())} }, // // Define a macro or environment in the top frame // Def: function (name,value,type,global) { var n = this.top-1; if (global) { // // Define global macros in the base frame and remove that cs // from all other frames. Mark the global ones in equations // so they can be made global when merged with the root stack. // while (n > 0) {this.stack[n].Undef(name,type); n--} if (!(value instanceof Array)) {value = [value]} if (this.isEqn) {value.global = true} } this.stack[n].Def(name,value,type); }, // // Push a new namespace frame on the stack // Push: function (frame) { this.stack.push(frame); this.top = this.stack.length; }, // // Pop the top stack frame // (if it is the root, just keep track of the pop so we can // reset it if the equation is reprocessed) // Pop: function () { var top; if (this.top > 1) { top = this.stack[--this.top]; if (this.isEqn) {this.stack.pop()} } else if (this.isEqn) { this.Clear(); } return top; }, // // Search the stack from top to bottom for the first // definition of the given control sequence in the given type // Find: function (name,type) { for (var i = this.top-1; i >= 0; i--) { var def = this.stack[i].Find(name,type); if (def) {return def} } return null; }, // // Combine the equation stack with the global one // (The bottom frame of the equation goes with the top frame of the global one, // and the remainder are pushed on the global stack, truncated to the // position where items were poped from it.) // Merge: function (stack) { stack.stack[0].MergeGlobals(this); this.stack[this.top-1].Merge(stack.stack[0]); var data = [this.top,this.stack.length-this.top].concat(stack.stack.slice(1)); this.stack.splice.apply(this.stack,data); this.top = this.stack.length; }, // // Put back the temporarily poped items // Reset: function () {this.top = this.stack.length}, // // Clear the stack and start with a blank frame // Clear: function (all) { this.stack = [this.stack[0].Clear()]; this.top = this.stack.length; } },{ nsFrame: NSFRAME }); /****************************************************/ // // Define the new macros // TEXDEF.Add({ macros: { begingroup: "BeginGroup", endgroup: "EndGroup", global: ["Extension","newcommand"], gdef: ["Extension","newcommand"] } },null,true); TEX.Parse.Augment({ // // Implement \begingroup // BeginGroup: function (name) { TEX.eqnStack.Push(NSFRAME()); }, // // Implements \endgroup // EndGroup: function (name) { // // If the equation has pushed frames, pop one, // Otherwise clear the equation stack and pop the top global one // if (TEX.eqnStack.top > 1) { TEX.eqnStack.Pop(); } else if (TEX.rootStack.top === 1) { TEX.Error(["ExtraEndMissingBegin","Extra %1 or missing \\begingroup",name]); } else { TEX.eqnStack.Clear(); TEX.rootStack.Pop(); } }, // // Replace the original routines with ones that looks through the // equation and root stacks for the given name // csFindMacro: function (name) { return (TEX.eqnStack.Find(name,"macros") || TEX.rootStack.Find(name,"macros")); }, envFindName: function (name) { return (TEX.eqnStack.Find(name,"environments") || TEX.rootStack.Find(name,"environments")); } }); /****************************************************/ TEX.rootStack = NSSTACK(); // the global namespace stack TEX.eqnStack = NSSTACK(true); // the equation stack // // Reset the global stack and clear the equation stack // (this gets us back to the initial stack state as it was // before the equation was first processed, in case the equation // get restarted due to an autoloaded file) // TEX.prefilterHooks.Add(function () {TEX.rootStack.Reset(); TEX.eqnStack.Clear(true)}); // // We only get here if there were no errors and the equation is fully // processed (all restarts are complete). So we merge the equation // stack into the global stack, thus making the changes from this // equation permanent. // TEX.postfilterHooks.Add(function () {TEX.rootStack.Merge(TEX.eqnStack)}); /*********************************************************/ MathJax.Hub.Register.StartupHook("TeX newcommand Ready",function () { // // Add the commands that depend on the newcommand code // TEXDEF.Add({ macros: { global: "Global", gdef: ["Macro","\\global\\def"] } },null,true); TEX.Parse.Augment({ // // Modify the way macros and environments are defined // to make them go into the equation namespace stack // setDef: function (name,value) { value.isUser = true; TEX.eqnStack.Def(name,value,"macros",this.stack.env.isGlobal); delete this.stack.env.isGlobal; }, setEnv: function (name,value) { value.isUser = true; TEX.eqnStack.Def(name,value,"environments") }, // // Implement \global (for \global\let, \global\def and \global\newcommand) // Global: function (name) { var i = this.i; var cs = this.GetCSname(name); this.i = i; if (cs !== "let" && cs !== "def" && cs !== "newcommand") { TEX.Error(["GlobalNotFollowedBy", "%1 not followed by \\let, \\def, or \\newcommand",name]); } this.stack.env.isGlobal = true; } }); }); MathJax.Hub.Startup.signal.Post("TeX begingroup Ready"); }); MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/begingroup.js");