310 lines
9.5 KiB
JavaScript
310 lines
9.5 KiB
JavaScript
|
/* -*- 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.3"
|
||
|
};
|
||
|
|
||
|
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");
|