text: /* -*- 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");
  