/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/TeX/color.js
 *  
 *  Implements LaTeX-compatible \color macro rather than MathJax's original
 *  (non-standard) version.  It includes the rgb, RGB, gray, and named color
 *  models, and the \textcolor, \definecolor, \colorbox, and \fcolorbox
 *  macros.
 *  
 *  ---------------------------------------------------------------------
 *  
 *  Copyright (c) 2011-2017 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.
 */

//
//  The configuration defaults, augmented by the user settings
//  
MathJax.Extension["TeX/color"] = {
  version: "2.7.1",

  config: MathJax.Hub.CombineConfig("TeX.color",{
    padding: "5px",
    border: "2px"
  }),

  colors: {
    Apricot:        "#FBB982",
    Aquamarine:     "#00B5BE",
    Bittersweet:    "#C04F17",
    Black:          "#221E1F",
    Blue:           "#2D2F92",
    BlueGreen:      "#00B3B8",
    BlueViolet:     "#473992",
    BrickRed:       "#B6321C",
    Brown:          "#792500",
    BurntOrange:    "#F7921D",
    CadetBlue:      "#74729A",
    CarnationPink:  "#F282B4",
    Cerulean:       "#00A2E3",
    CornflowerBlue: "#41B0E4",
    Cyan:           "#00AEEF",
    Dandelion:      "#FDBC42",
    DarkOrchid:     "#A4538A",
    Emerald:        "#00A99D",
    ForestGreen:    "#009B55",
    Fuchsia:        "#8C368C",
    Goldenrod:      "#FFDF42",
    Gray:           "#949698",
    Green:          "#00A64F",
    GreenYellow:    "#DFE674",
    JungleGreen:    "#00A99A",
    Lavender:       "#F49EC4",
    LimeGreen:      "#8DC73E",
    Magenta:        "#EC008C",
    Mahogany:       "#A9341F",
    Maroon:         "#AF3235",
    Melon:          "#F89E7B",
    MidnightBlue:   "#006795",
    Mulberry:       "#A93C93",
    NavyBlue:       "#006EB8",
    OliveGreen:     "#3C8031",
    Orange:         "#F58137",
    OrangeRed:      "#ED135A",
    Orchid:         "#AF72B0",
    Peach:          "#F7965A",
    Periwinkle:     "#7977B8",
    PineGreen:      "#008B72",
    Plum:           "#92268F",
    ProcessBlue:    "#00B0F0",
    Purple:         "#99479B",
    RawSienna:      "#974006",
    Red:            "#ED1B23",
    RedOrange:      "#F26035",
    RedViolet:      "#A1246B",
    Rhodamine:      "#EF559F",
    RoyalBlue:      "#0071BC",
    RoyalPurple:    "#613F99",
    RubineRed:      "#ED017D",
    Salmon:         "#F69289",
    SeaGreen:       "#3FBC9D",
    Sepia:          "#671800",
    SkyBlue:        "#46C5DD",
    SpringGreen:    "#C6DC67",
    Tan:            "#DA9D76",
    TealBlue:       "#00AEB3",
    Thistle:        "#D883B7",
    Turquoise:      "#00B4CE",
    Violet:         "#58429B",
    VioletRed:      "#EF58A0",
    White:          "#FFFFFF",
    WildStrawberry: "#EE2967",
    Yellow:         "#FFF200",
    YellowGreen:    "#98CC70",
    YellowOrange:   "#FAA21A"
  },

  /*
   *  Look up a color based on its model and definition
   */
  getColor: function (model,def) {
    if (!model) {model = "named"}
    var fn = this["get_"+model];
    if (!fn) {this.TEX.Error(["UndefinedColorModel","Color model '%1' not defined",model])}
    return fn.call(this,def);
  },
  
  /*
   *  Get an rgb color
   */
  get_rgb: function (rgb) {
    rgb = rgb.replace(/^\s+/,"").replace(/\s+$/,"").split(/\s*,\s*/); var RGB = "#";
    if (rgb.length !== 3)
      {this.TEX.Error(["ModelArg1","Color values for the %1 model require 3 numbers","rgb"])}
    for (var i = 0; i < 3; i++) {
      if (!rgb[i].match(/^(\d+(\.\d*)?|\.\d+)$/))
        {this.TEX.Error(["InvalidDecimalNumber","Invalid decimal number"])}
      var n = parseFloat(rgb[i]);
      if (n < 0 || n > 1) {
        this.TEX.Error(["ModelArg2",
                        "Color values for the %1 model must be between %2 and %3",
                        "rgb",0,1]);
      }
      n = Math.floor(n*255).toString(16); if (n.length < 2) {n = "0"+n}
      RGB += n;
    }
    return RGB;
  },
  
  /*
   *  Get an RGB color
   */
  get_RGB: function (rgb) {
    rgb = rgb.replace(/^\s+/,"").replace(/\s+$/,"").split(/\s*,\s*/); var RGB = "#";
    if (rgb.length !== 3)
      {this.TEX.Error(["ModelArg1","Color values for the %1 model require 3 numbers","RGB"])}
    for (var i = 0; i < 3; i++) {
      if (!rgb[i].match(/^\d+$/))
        {this.TEX.Error(["InvalidNumber","Invalid number"])}
      var n = parseInt(rgb[i]);
      if (n > 255) {
        this.TEX.Error(["ModelArg2",
                        "Color values for the %1 model must be between %2 and %3",
                        "RGB",0,255]);
      }
      n = n.toString(16); if (n.length < 2) {n = "0"+n}
      RGB += n;
    }
    return RGB;
  },
  
  /*
   *  Get a gray-scale value
   */
  get_gray: function (gray) {
    if (!gray.match(/^\s*(\d+(\.\d*)?|\.\d+)\s*$/))
      {this.TEX.Error(["InvalidDecimalNumber","Invalid decimal number"])}
    var n = parseFloat(gray);
    if (n < 0 || n > 1) {
      this.TEX.Error(["ModelArg2",
                      "Color values for the %1 model must be between %2 and %3",
                      "gray",0,1]);
    }
    n = Math.floor(n*255).toString(16); if (n.length < 2) {n = "0"+n}
    return "#"+n+n+n;
  },
  
  /*
   *  Get a named value
   */
  get_named: function (name) {
    if (this.colors[name]) {return this.colors[name]}
    return name;
  },
  
  padding: function () {
    var pad = "+"+this.config.padding;
    var unit = this.config.padding.replace(/^.*?([a-z]*)$/,"$1");
    var pad2 = "+"+(2*parseFloat(pad))+unit;
    return {width:pad2, height:pad, depth:pad, lspace:this.config.padding};
  }

};
  
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
  var TEX = MathJax.InputJax.TeX,
      MML = MathJax.ElementJax.mml;
  var STACKITEM = TEX.Stack.Item;
  var COLOR = MathJax.Extension["TeX/color"];

  COLOR.TEX = TEX; // for reference in getColor above

  TEX.Definitions.Add({
    macros: {
      color: "Color",
      textcolor: "TextColor",
      definecolor: "DefineColor",
      colorbox: "ColorBox",
      fcolorbox: "fColorBox"
    }
  },null,true);

  TEX.Parse.Augment({
    
    //
    //  Override \color macro definition
    //
    Color: function (name) {
      var model = this.GetBrackets(name),
          color = this.GetArgument(name);
      color = COLOR.getColor(model,color);
      var mml = STACKITEM.style().With({styles:{mathcolor:color}});
      this.stack.env.color = color;
      this.Push(mml);
    },
    
    TextColor: function (name) {
      var model = this.GetBrackets(name),
          color = this.GetArgument(name);
      color = COLOR.getColor(model,color);
      var old = this.stack.env.color; this.stack.env.color = color;
      var math = this.ParseArg(name);
      if (old) {this.stack.env.color} else {delete this.stack.env.color}
      this.Push(MML.mstyle(math).With({mathcolor: color}));
    },

    //
    //  Define the \definecolor macro
    //
    DefineColor: function (name) {
      var cname = this.GetArgument(name),
          model = this.GetArgument(name),
          def = this.GetArgument(name);
      COLOR.colors[cname] = COLOR.getColor(model,def);
    },
    
    //
    //  Produce a text box with a colored background
    //
    ColorBox: function (name) {
      var cname = this.GetArgument(name),
          arg = this.InternalMath(this.GetArgument(name));
      this.Push(MML.mpadded.apply(MML,arg).With({
        mathbackground:COLOR.getColor("named",cname)
      }).With(COLOR.padding()));
    },
    
    //
    //  Procude a framed text box with a colored background
    //
    fColorBox: function (name) {
      var fname = this.GetArgument(name),
          cname = this.GetArgument(name),
          arg = this.InternalMath(this.GetArgument(name));
      this.Push(MML.mpadded.apply(MML,arg).With({
        mathbackground: COLOR.getColor("named",cname),
        style: "border: "+COLOR.config.border+" solid "+COLOR.getColor("named",fname)
      }).With(COLOR.padding()));
    }

  });

  MathJax.Hub.Startup.signal.Post("TeX color Ready");

});

MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/color.js");
