www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

mtable.js (15091B)


      1 /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 
      4 /*************************************************************
      5  *
      6  *  MathJax/jax/output/SVG/autoload/mtable.js
      7  *  
      8  *  Implements the SVG output for <mtable> elements.
      9  *
     10  *  ---------------------------------------------------------------------
     11  *  
     12  *  Copyright (c) 2011-2015 The MathJax Consortium
     13  * 
     14  *  Licensed under the Apache License, Version 2.0 (the "License");
     15  *  you may not use this file except in compliance with the License.
     16  *  You may obtain a copy of the License at
     17  * 
     18  *      http://www.apache.org/licenses/LICENSE-2.0
     19  * 
     20  *  Unless required by applicable law or agreed to in writing, software
     21  *  distributed under the License is distributed on an "AS IS" BASIS,
     22  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     23  *  See the License for the specific language governing permissions and
     24  *  limitations under the License.
     25  */
     26 
     27 MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
     28   var VERSION = "2.6.0";
     29   var MML = MathJax.ElementJax.mml,
     30       SVG = MathJax.OutputJax.SVG,
     31       BBOX = SVG.BBOX;
     32   
     33   MML.mtable.Augment({
     34     toSVG: function (span) {
     35       this.SVGgetStyles();
     36       var svg = this.SVG(), scale = this.SVGgetScale(svg);
     37       if (this.data.length === 0) {this.SVGsaveData(svg);return svg}
     38       var values = this.getValues("columnalign","rowalign","columnspacing","rowspacing",
     39                                   "columnwidth","equalcolumns","equalrows",
     40                                   "columnlines","rowlines","frame","framespacing",
     41                                   "align","useHeight","width","side","minlabelspacing");
     42       //  Handle relative width as fixed width in relation to container
     43       if (values.width.match(/%$/))
     44         {svg.width = values.width = SVG.Em((SVG.cwidth/1000)*(parseFloat(values.width)/100))}
     45 
     46       var mu = this.SVGgetMu(svg);
     47       var LABEL = -1;
     48 
     49       var H = [], D = [], W = [], A = [], C = [], i, j, J = -1,
     50           m, M, s, row, cell, mo, HD;
     51       var LH = SVG.FONTDATA.lineH * scale * values.useHeight,
     52           LD = SVG.FONTDATA.lineD * scale * values.useHeight;
     53 
     54       //
     55       //  Create cells and measure columns and rows
     56       //
     57       for (i = 0, m = this.data.length; i < m; i++) {
     58         row = this.data[i]; s = (row.type === "mlabeledtr" ? LABEL : 0);
     59         A[i] = []; H[i] = LH; D[i] = LD;
     60         for (j = s, M = row.data.length + s; j < M; j++) {
     61           if (W[j] == null) {
     62             if (j > J) {J = j}
     63             C[j] = BBOX.G();
     64             W[j] = -SVG.BIGDIMEN;
     65           }
     66           cell = row.data[j-s];
     67           A[i][j] = cell.toSVG();
     68 //          if (row.data[j-s].isMultiline) {A[i][j].style.width = "100%"}
     69           if (cell.isEmbellished()) {
     70             mo = cell.CoreMO();
     71             var min = mo.Get("minsize",true);
     72             if (min) {
     73               if (mo.SVGcanStretch("Vertical")) {
     74                 HD = mo.SVGdata.h + mo.SVGdata.d;
     75                 if (HD) {
     76                   min = SVG.length2em(min,mu,HD);
     77                   if (min*mo.SVGdata.h/HD > H[i]) {H[i] = min*mo.SVGdata.h/HD}
     78                   if (min*mo.SVGdata.d/HD > D[i]) {D[i] = min*mo.SVGdata.d/HD}
     79                 }
     80               } else if (mo.SVGcanStretch("Horizontal")) {
     81                 min = SVG.length2em(min,mu,mo.SVGdata.w);
     82                 if (min > W[j]) {W[j] = min}
     83               }
     84             }
     85           }
     86           if (A[i][j].h > H[i]) {H[i] = A[i][j].h}
     87           if (A[i][j].d > D[i]) {D[i] = A[i][j].d}
     88           if (A[i][j].w > W[j]) {W[j] = A[i][j].w}
     89         }
     90       }
     91 
     92       //
     93       //  Determine spacing and alignment
     94       //
     95       var SPLIT = MathJax.Hub.SplitList;
     96       var CSPACE = SPLIT(values.columnspacing),
     97           RSPACE = SPLIT(values.rowspacing),
     98           CALIGN = SPLIT(values.columnalign),
     99           RALIGN = SPLIT(values.rowalign),
    100           CLINES = SPLIT(values.columnlines),
    101           RLINES = SPLIT(values.rowlines),
    102           CWIDTH = SPLIT(values.columnwidth),
    103           RCALIGN = [];
    104       for (i = 0, m = CSPACE.length; i < m; i++) {CSPACE[i] = SVG.length2em(CSPACE[i],mu)}
    105       for (i = 0, m = RSPACE.length; i < m; i++) {RSPACE[i] = SVG.length2em(RSPACE[i],mu)}
    106       while (CSPACE.length <  J) {CSPACE.push(CSPACE[CSPACE.length-1])}
    107       while (CALIGN.length <= J) {CALIGN.push(CALIGN[CALIGN.length-1])}
    108       while (CLINES.length <  J) {CLINES.push(CLINES[CLINES.length-1])}
    109       while (CWIDTH.length <= J) {CWIDTH.push(CWIDTH[CWIDTH.length-1])}
    110       while (RSPACE.length <  A.length) {RSPACE.push(RSPACE[RSPACE.length-1])}
    111       while (RALIGN.length <= A.length) {RALIGN.push(RALIGN[RALIGN.length-1])}
    112       while (RLINES.length <  A.length) {RLINES.push(RLINES[RLINES.length-1])}
    113       if (C[LABEL]) {
    114         CALIGN[LABEL] = (values.side.substr(0,1) === "l" ? "left" : "right");
    115         CSPACE[LABEL] = -W[LABEL];
    116       }
    117       //
    118       //  Override row data
    119       //
    120       for (i = 0, m = A.length; i < m; i++) {
    121         row = this.data[i]; RCALIGN[i] = [];
    122         if (row.rowalign) {RALIGN[i] = row.rowalign}
    123         if (row.columnalign) {
    124           RCALIGN[i] = SPLIT(row.columnalign);
    125           while (RCALIGN[i].length <= J) {RCALIGN[i].push(RCALIGN[i][RCALIGN[i].length-1])}
    126         }
    127       }
    128 
    129       //
    130       //  Handle equal heights
    131       //
    132       if (values.equalrows) {
    133         // FIXME:  should really be based on row align (below is for baseline)
    134         var Hm = Math.max.apply(Math,H), Dm = Math.max.apply(Math,D);
    135         for (i = 0, m = A.length; i < m; i++)
    136           {s = ((Hm + Dm) - (H[i] + D[i])) / 2;  H[i] += s; D[i] += s}
    137       }
    138 
    139       //  FIXME:  do background colors for entire cell (include half the intercolumn space?)
    140       
    141       //
    142       //  Determine array total height
    143       //
    144       HD = H[0] + D[A.length-1];
    145       for (i = 0, m = A.length-1; i < m; i++)
    146         {HD += Math.max(0,D[i]+H[i+1]+RSPACE[i])}
    147       //
    148       //  Determine frame and line sizes
    149       //
    150       var fx = 0, fy = 0, fW, fH = HD;
    151       if (values.frame !== "none" ||
    152          (values.columnlines+values.rowlines).match(/solid|dashed/)) {
    153         var frameSpacing = SPLIT(values.framespacing);
    154         if (frameSpacing.length != 2) {
    155           // invalid attribute value: use the default.
    156           frameSpacing = SPLIT(this.defaults.framespacing);
    157         }
    158         fx = SVG.length2em(frameSpacing[0],mu);
    159         fy = SVG.length2em(frameSpacing[1],mu);
    160         fH = HD + 2*fy; // fW waits until svg.w is determined
    161       }
    162       //
    163       //  Compute alignment
    164       //
    165       var Y, fY, n = "";
    166       if (typeof(values.align) !== "string") {values.align = String(values.align)}
    167       if (values.align.match(/(top|bottom|center|baseline|axis)( +(-?\d+))?/))
    168         {n = RegExp.$3||""; values.align = RegExp.$1} else {values.align = this.defaults.align}
    169       if (n !== "") {
    170         //
    171         //  Find the height of the given row
    172         //
    173         n = parseInt(n);
    174         if (n < 0) {n = A.length + 1 + n}
    175         if (n < 1) {n = 1} else if (n > A.length) {n = A.length}
    176         Y = 0; fY = -(HD + fy) + H[0];
    177         for (i = 0, m = n-1; i < m; i++) {
    178           // FIXME:  Should handle values.align for final row
    179           var dY = Math.max(0,D[i]+H[i+1]+RSPACE[i]);
    180           Y += dY; fY += dY;
    181         }
    182       } else {
    183         Y = ({
    184           top:    -(H[0] + fy),
    185           bottom:   HD + fy - H[0],
    186           center:   HD/2 - H[0],
    187           baseline: HD/2 - H[0],
    188           axis:     HD/2 + SVG.TeX.axis_height*scale - H[0]
    189         })[values.align];
    190         fY = ({
    191           top:      -(HD + 2*fy),
    192           bottom:   0,
    193           center:   -(HD/2 + fy),
    194           baseline: -(HD/2 + fy),
    195           axis:     SVG.TeX.axis_height*scale - HD/2 - fy
    196         })[values.align];
    197       }
    198             
    199       var WW, WP = 0, Wt = 0, Wp = 0, p = 0, f = 0, P = [], F = [], Wf = 1;
    200       //
    201       if (values.equalcolumns && values.width !== "auto") {
    202         //
    203         //  Handle equalcolumns for percent-width and fixed-width tables
    204         //
    205 
    206         //  Get total width minus column spacing
    207         WW = SVG.length2em(values.width,mu);
    208         for (i = 0, m = Math.min(J,CSPACE.length); i < m; i++) {WW -= CSPACE[i]}
    209         //  Determine individual column widths
    210         WW /= J;
    211         for (i = 0, m = Math.min(J+1,CWIDTH.length); i < m; i++) {W[i] = WW}
    212       } else {
    213         //
    214         //  Get column widths for fit and percentage columns
    215         //
    216         //  Calculate the natural widths and percentage widths,
    217         //    while keeping track of the fit and percentage columns
    218         for(i = 0, m = Math.min(J+1,CWIDTH.length); i < m; i++) {
    219           if (CWIDTH[i] === "auto") {Wt += W[i]}
    220           else if (CWIDTH[i] === "fit") {F[f] = i; f++; Wt += W[i]}
    221           else if (CWIDTH[i].match(/%$/))
    222             {P[p] = i; p++; Wp += W[i]; WP += SVG.length2em(CWIDTH[i],mu,1)}
    223           else {W[i] = SVG.length2em(CWIDTH[i],mu); Wt += W[i]}
    224         }
    225         // Get the full width (excluding inter-column spacing)
    226         if (values.width === "auto") {
    227           if (WP > .98) {Wf = Wp/(Wt+Wp); WW = Wt + Wp} else {WW = Wt / (1-WP)}
    228         } else {
    229           WW = SVG.length2em(values.width,mu);
    230           for (i = 0, m = Math.min(J,CSPACE.length); i < m; i++) {WW -= CSPACE[i]}
    231         }
    232         //  Determine the relative column widths
    233         for (i = 0, m = P.length; i < m; i++) {
    234           W[P[i]] = SVG.length2em(CWIDTH[P[i]],mu,WW*Wf); Wt += W[P[i]];
    235         }
    236         //  Stretch fit columns, if any, otherwise stretch (or shrink) everything
    237         if (Math.abs(WW - Wt) > .01) {
    238           if (f && WW > Wt) {
    239             WW = (WW - Wt) / f; for (i = 0, m = F.length; i < m; i++) {W[F[i]] += WW}
    240           } else {WW = WW/Wt; for (j = 0; j <= J; j++) {W[j] *= WW}}
    241         }
    242         //
    243         //  Handle equal columns
    244         //
    245         if (values.equalcolumns) {
    246           var Wm = Math.max.apply(Math,W);
    247           for (j = 0; j <= J; j++) {W[j] = Wm}
    248         }
    249       }
    250       
    251       //
    252       //  Lay out array columns
    253       //
    254       var y = Y, dy, align; s = (C[LABEL] ? LABEL : 0);
    255       for (j = s; j <= J; j++) {
    256         C[j].w = W[j];
    257         for (i = 0, m = A.length; i < m; i++) {
    258           if (A[i][j]) {
    259             s = (this.data[i].type === "mlabeledtr" ? LABEL : 0);
    260             cell = this.data[i].data[j-s];
    261 	    if (cell.SVGcanStretch("Horizontal")) {
    262 	      A[i][j] = cell.SVGstretchH(W[j]);
    263 	    } else if (cell.SVGcanStretch("Vertical")) {
    264 	      mo = cell.CoreMO();
    265 	      var symmetric = mo.symmetric; mo.symmetric = false;
    266 	      A[i][j] = cell.SVGstretchV(H[i],D[i]);
    267 	      mo.symmetric = symmetric;
    268 	    }
    269             align = cell.rowalign||this.data[i].rowalign||RALIGN[i];
    270             dy = ({top:    H[i] - A[i][j].h,
    271                    bottom: A[i][j].d - D[i],
    272                    center: ((H[i]-D[i]) - (A[i][j].h-A[i][j].d))/2,
    273                    baseline: 0, axis: 0})[align] || 0; // FIXME:  handle axis better?
    274             align = (cell.columnalign||RCALIGN[i][j]||CALIGN[j])
    275             C[j].Align(A[i][j],align,0,y+dy);
    276           }
    277           if (i < A.length-1) {y -= Math.max(0,D[i]+H[i+1]+RSPACE[i])}
    278         }
    279         y = Y;
    280       }
    281 
    282       //
    283       //  Place the columns and add column lines
    284       //
    285       var lw = 1.5*SVG.em;
    286       var x = fx - lw/2;
    287       for (j = 0; j <= J; j++) {
    288         svg.Add(C[j],x,0); x += W[j] + CSPACE[j];
    289         if (CLINES[j] !== "none" && j < J && j !== LABEL)
    290         {svg.Add(BBOX.VLINE(fH,lw,CLINES[j]),x-CSPACE[j]/2,fY)}
    291       }
    292       svg.w += fx; svg.d = -fY; svg.h = fH+fY;
    293       fW = svg.w;
    294       
    295       //
    296       //  Add frame
    297       //
    298       if (values.frame !== "none") {
    299         svg.Add(BBOX.HLINE(fW,lw,values.frame),0,fY+fH-lw);
    300         svg.Add(BBOX.HLINE(fW,lw,values.frame),0,fY);
    301         svg.Add(BBOX.VLINE(fH,lw,values.frame),0,fY);
    302         svg.Add(BBOX.VLINE(fH,lw,values.frame),fW-lw,fY);
    303       }
    304 
    305       //
    306       //  Add row lines
    307       //
    308       y = Y - lw/2;
    309       for (i = 0, m = A.length-1; i < m; i++) {
    310         dy = Math.max(0,D[i]+H[i+1]+RSPACE[i]);
    311         if (RLINES[i] !== "none")
    312           {svg.Add(BBOX.HLINE(fW,lw,RLINES[i]),0,y-D[i]-(dy-D[i]-H[i+1])/2)}
    313         y -= dy;
    314       }
    315       
    316       //
    317       //  Finish the table
    318       //
    319       svg.Clean();
    320       this.SVGhandleSpace(svg);
    321       this.SVGhandleColor(svg);
    322       
    323       //
    324       //  Place the labels, if any
    325       //
    326       if (C[LABEL]) {
    327         svg.tw = Math.max(svg.w,svg.r) - Math.min(0,svg.l);
    328         var indent = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
    329         if (indent.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {indent.indentalign = indent.indentalignfirst}
    330         if (indent.indentalign === MML.INDENTALIGN.AUTO) {indent.indentalign = this.displayAlign}
    331         if (indent.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {indent.indentshift = indent.indentshiftfirst}
    332         if (indent.indentshift === "auto" || indent.indentshift === "") {indent.indentshift = "0"}
    333         var shift = SVG.length2em(indent.indentshift,mu,SVG.cwidth);
    334         var labelspace = SVG.length2em(values.minlabelspacing,mu,SVG.cwidth);
    335         var labelW = labelspace + C[LABEL].w, labelshift = 0, tw = svg.w;
    336         var dIndent = SVG.length2em(this.displayIndent,mu,SVG.cwidth);
    337         s = (CALIGN[LABEL] === MML.INDENTALIGN.RIGHT ? -1 : 1);
    338         if (indent.indentalign === MML.INDENTALIGN.CENTER) {
    339           var dx = (SVG.cwidth-tw)/2; shift += dIndent;
    340           if (labelW + s*labelshift > dx + s*shift) {
    341             indent.indentalign = CALIGN[LABEL];
    342             shift = s*(labelW + s*labelshift); tw += labelW + Math.max(0,shift);
    343           }
    344         } else if (CALIGN[LABEL] === indent.indentalign) {
    345           if (dIndent < 0) {labelshift = s*dIndent; dIndent = 0}
    346           shift += s*dIndent; if (labelW > s*shift) shift = s*labelW; shift += labelshift;
    347           tw += s*shift;
    348         } else {
    349           shift -= s*dIndent;
    350           if (tw - s*shift + labelW > SVG.cwidth) {
    351             shift = s*(tw + labelW - SVG.cwidth);
    352             if (s*shift > 0) {tw = SVG.cwidth + s*shift; shift = 0}
    353           }
    354         }
    355         var eqn = svg; svg = this.SVG();
    356         svg.hasIndent = true;
    357         svg.w = svg.r = Math.max(tw,SVG.cwidth); 
    358         svg.Align(C[LABEL],CALIGN[LABEL],0,0,labelshift);
    359         svg.Align(eqn,indent.indentalign,0,0,shift);
    360         svg.tw = tw;
    361       }
    362       
    363       this.SVGsaveData(svg);
    364       return svg;
    365     },
    366     SVGhandleSpace: function (svg) {
    367       if (!this.hasFrame && !svg.width) {svg.x = svg.X = 167}
    368       this.SUPER(arguments).SVGhandleSpace.call(this,svg);
    369     }
    370   });
    371   
    372   MML.mtd.Augment({
    373     toSVG: function (HW,D) {
    374       var svg = this.svg = this.SVG();
    375       if (this.data[0]) {
    376         svg.Add(this.SVGdataStretched(0,HW,D));
    377         svg.Clean();
    378       }
    379       this.SVGhandleColor(svg);
    380       this.SVGsaveData(svg);
    381       return svg;
    382     }
    383   });
    384 
    385   MathJax.Hub.Startup.signal.Post("SVG mtable Ready");
    386   MathJax.Ajax.loadComplete(SVG.autoloadDir+"/mtable.js");
    387   
    388 });
    389