www

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

mtable.js (22450B)


      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/CommonHTML/autoload/mtable.js
      7  *  
      8  *  Implements the CommonHTML output for <mtable> elements.
      9  *
     10  *  ---------------------------------------------------------------------
     11  *  
     12  *  Copyright (c) 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("CommonHTML Jax Ready",function () {
     28   var VERSION = "2.6.0";
     29   var MML = MathJax.ElementJax.mml,
     30       CONFIG = MathJax.Hub.config,
     31       CHTML = MathJax.OutputJax.CommonHTML,
     32       SPLIT = MathJax.Hub.SplitList;
     33   
     34   var LABEL = -1,
     35       BIGDIMEN = 1000000;
     36 
     37   MML.mtable.Augment({
     38     toCommonHTML: function (node) {
     39       //
     40       //  Create the table nodes and put them in a table
     41       //  (so that its bottom is on the baseline, rather than aligned on the top row)
     42       //
     43       var state = {rows:[], labels:[], labeled: false};
     44       node = this.CHTMLdefaultNode(node,{noBBox:true, childOptions:state});
     45       var table = CHTML.Element("mjx-table");
     46       while (node.firstChild) table.appendChild(node.firstChild);
     47       node.appendChild(table);
     48       //
     49       //  Get the table attributes
     50       //
     51       var values = this.getValues("columnalign","rowalign","columnspacing","rowspacing",
     52                                   "columnwidth","equalcolumns","equalrows",
     53                                   "columnlines","rowlines","frame","framespacing",
     54                                   "align","width","side","minlabelspacing","useHeight");
     55       var t = CHTML.TEX.min_rule_thickness/CHTML.em;
     56       state.t = CHTML.Px(t*this.CHTML.scale,1);
     57       //
     58       //  Create the table
     59       //
     60       this.CHTMLgetBoxSizes(values,state);
     61       this.CHTMLgetAttributes(values,state);
     62       if (values.equalrows) {
     63         state.HD = true;
     64         state.HH = Math.max.apply(Math,state.H);
     65         state.DD = Math.max.apply(Math,state.D);
     66       }
     67       this.CHTMLadjustCells(values,state);
     68       if (values.frame) table.style.border = state.t+" "+values.frame;
     69       this.CHTMLalignV(values,state,node);
     70       this.CHTMLcolumnWidths(values,state,node);
     71       this.CHTMLstretchCells(values,state);
     72       if (state.labeled) this.CHTMLaddLabels(values,state,node,table);
     73       //
     74       //  Set the bounding box (ignores overlapping outside of the table)
     75       //
     76       var BBOX = this.CHTML;
     77       BBOX.w = BBOX.r = state.R;
     78       BBOX.h = BBOX.t = state.T-state.B;
     79       BBOX.d = BBOX.b = state.B;
     80       if (!values.frame && !BBOX.pwidth) {
     81         node.style.padding = "0 "+CHTML.Em(1/6);
     82         BBOX.L = BBOX.R = 1/6;
     83       }
     84       //
     85       //  Add any needed space and color
     86       //
     87       this.CHTMLhandleSpace(node);
     88       this.CHTMLhandleBBox(node);
     89       this.CHTMLhandleColor(node);
     90       //
     91       //  Return the completed node
     92       //
     93       return node;
     94     },
     95     //
     96     //  Get the natural height, depth, and widths of the rows and columns
     97     //
     98     CHTMLgetBoxSizes: function (values,state) {
     99       var LH = CHTML.FONTDATA.lineH * values.useHeight,
    100           LD = CHTML.FONTDATA.lineD * values.useHeight;
    101       var H = [], D = [], W = [], J = -1;
    102       for (var i = 0, m = this.data.length; i < m; i++) {
    103         var  row = this.data[i], s = (row.type === "mtr" ? 0 : LABEL);
    104         H[i] = LH; D[i] = LD;
    105         for (var j = s, M = row.data.length + s; j < M; j++) {
    106           if (W[j] == null) {W[j] = -BIGDIMEN; if (j > J) J = j}
    107           var cbox = row.data[j-s].CHTML;
    108           if (cbox.h > H[i]) H[i] = cbox.h;
    109           if (cbox.d > D[i]) D[i] = cbox.d;
    110           if (cbox.w > W[j]) W[j] = cbox.w;
    111         }
    112       }
    113       state.H = H; state.D = D; state.W = W, state.J = J;
    114     },
    115     //
    116     //  Pad the spacing and alignment attributes to match the size of the table
    117     //
    118     CHTMLgetAttributes: function (values,state) {
    119       var CSPACE = SPLIT(values.columnspacing),
    120           RSPACE = SPLIT(values.rowspacing),
    121           CALIGN = SPLIT(values.columnalign),
    122           RALIGN = SPLIT(values.rowalign),
    123           CLINES = SPLIT(values.columnlines),
    124           RLINES = SPLIT(values.rowlines),
    125           CWIDTH = SPLIT(values.columnwidth),
    126           RCALIGN = [], i, m, J = state.J, M = state.rows.length-1;
    127       for (i = 0, m = CSPACE.length; i < m; i++) CSPACE[i] = this.CHTMLlength2em(CSPACE[i]);
    128       for (i = 0, m = RSPACE.length; i < m; i++) RSPACE[i] = this.CHTMLlength2em(RSPACE[i]);
    129       while (CSPACE.length <  J) CSPACE.push(CSPACE[CSPACE.length-1]);
    130       while (CALIGN.length <= J) CALIGN.push(CALIGN[CALIGN.length-1]);
    131       while (CLINES.length <  J) CLINES.push(CLINES[CLINES.length-1]);
    132       while (CWIDTH.length <= J) CWIDTH.push(CWIDTH[CWIDTH.length-1]);
    133       while (RSPACE.length <  M) RSPACE.push(RSPACE[RSPACE.length-1]);
    134       while (RALIGN.length <= M) RALIGN.push(RALIGN[RALIGN.length-1]);
    135       while (RLINES.length <  M) RLINES.push(RLINES[RLINES.length-1]);
    136       CALIGN[LABEL] = (values.side.substr(0,1) === "l" ? "left" : "right");
    137       //
    138       //  Override aligment data based on row-specific attributes
    139       //
    140       for (i = 0; i <= M; i++) {
    141         var row = this.data[i]; RCALIGN[i] = [];
    142         if (row.rowalign) RALIGN[i] = row.rowalign;
    143         if (row.columnalign) {
    144           RCALIGN[i] = SPLIT(row.columnalign);
    145           while (RCALIGN[i].length <= J) RCALIGN[i].push(RCALIGN[i][RCALIGN[i].length-1]);
    146         }
    147       }
    148       //
    149       //  Handle framespacing
    150       //
    151       var FSPACE = SPLIT(values.framespacing);
    152       if (FSPACE.length != 2) FSPACE = SPLIT(this.defaults.framespacing);
    153       FSPACE[0] = Math.max(0,this.CHTMLlength2em(FSPACE[0]));
    154       FSPACE[1] = Math.max(0,this.CHTMLlength2em(FSPACE[1]));
    155       if (values.columnlines.replace(/none/g,"").replace(/ /g,"") !== "" ||
    156           values.rowlines.replace(/none/g,"").replace(/ /g,"") !== "") values.fspace = true;
    157       //
    158       //  Pad arrays so that final column can be treated as all the others
    159       //
    160       if (values.frame === MML.LINES.NONE) delete values.frame; else values.fspace = true;
    161       if (values.frame) {
    162         FSPACE[0] = Math.max(0,FSPACE[0]);
    163         FSPACE[1] = Math.max(0,FSPACE[1]);
    164       }
    165       if (values.fspace) {
    166         CSPACE[J] = FSPACE[0]; RSPACE[M] = FSPACE[1];
    167       } else {
    168         CSPACE[J] = RSPACE[M] = 0;
    169       }
    170       CLINES[J] = RLINES[M] = MML.LINES.NONE;
    171       //
    172       //  Save everything in the state
    173       //
    174       state.CSPACE = CSPACE; state.RSPACE = RSPACE;
    175       state.CALIGN = CALIGN; state.RALIGN = RALIGN;
    176       state.CLINES = CLINES; state.RLINES = RLINES;
    177       state.CWIDTH = CWIDTH; state.RCALIGN = RCALIGN;
    178       state.FSPACE = FSPACE;
    179     },
    180     //
    181     //  Add styles to cells to handle borders, spacing, alignment, etc.
    182     //
    183     CHTMLadjustCells: function(values,state) {
    184       var ROWS = state.rows,
    185           CSPACE = state.CSPACE, CLINES = state.CLINES,
    186           RSPACE = state.RSPACE, RLINES = state.RLINES,
    187           CALIGN = state.CALIGN, RALIGN = state.RALIGN,
    188           RCALIGN = state.RCALIGN;
    189       CSPACE[state.J] *= 2; RSPACE[ROWS.length-1] *= 2; // since halved below
    190       var LH = CHTML.FONTDATA.lineH * values.useHeight,
    191           LD = CHTML.FONTDATA.lineD * values.useHeight;
    192       var T = "0", B, R, L, border, cbox, align;
    193       if (values.fspace) T = CHTML.Em(state.FSPACE[1]);
    194       for (var i = 0, m = ROWS.length; i < m; i++) {
    195         var row = ROWS[i], rdata = this.data[i];
    196         //
    197         //  Space and borders between rows
    198         //
    199         B = RSPACE[i]/2; border = null; L = "0";
    200         if (RLINES[i] !== MML.LINES.NONE) {
    201           border = state.t+" "+RLINES[i];
    202           B -= 1/CHTML.em/2;
    203         }
    204         B = CHTML.Em(Math.max(0,B));
    205         //
    206         //  Frame space for initial cell
    207         //
    208         if (values.fspace) L = CHTML.Em(state.FSPACE[0]);
    209         //
    210         //  The cells in the row
    211         //
    212         for (var j = 0, M = row.length; j < M; j++) {
    213           var s = (rdata.type === "mtr" ? 0 : LABEL);
    214           cell = row[j].style; cbox = rdata.data[j-s].CHTML;
    215           //
    216           //  Space and borders between columns
    217           //
    218           R = CSPACE[j]/2;
    219           if (CLINES[j] !== MML.LINES.NONE) {
    220             cell.borderRight = state.t+" "+CLINES[j];
    221             R -= 1/CHTML.em/2;
    222           }
    223           R = CHTML.Em(Math.max(0,R));
    224           cell.padding = T+" "+R+" "+B+" "+L;
    225           if (border) cell.borderBottom = border;
    226           L = R;
    227           //
    228           //  Handle vertical and horizontal alignment
    229           //
    230           align = (rdata.data[j-s].rowalign||this.data[i].rowalign||RALIGN[i]);
    231           align = ({top:"top", bottom:"bottom", center:"middle"})[align];
    232           if (align) cell.verticalAlign = align;
    233           align = (rdata.data[j-s].columnalign||RCALIGN[i][j]||CALIGN[j]);
    234           if (align !== MML.ALIGN.CENTER) cell.textAlign = align;
    235           //
    236           //  Equal heights forced by adding an element of the proper size
    237           //  (setting style.height seems to work very strangely)
    238           //
    239           if (state.HD && j === 0) {
    240             CHTML.addElement(row[j].parentNode,"mjx-mtd",{style:{padding:T+" 0 "+B}},
    241               [["mjx-box",{style:{
    242                 height:CHTML.Em(state.HH+state.DD),
    243                 "vertical-align":CHTML.Em(-state.DD)
    244               }}]]
    245             );
    246           }
    247           //
    248           //  Pad cells that are too short
    249           //
    250           cell = row[j].firstChild.style;
    251           if (cbox.h < LH) cell.marginTop    = CHTML.Em(LH-cbox.h);
    252           if (cbox.d < LD) cell.marginBottom = CHTML.Em(LD-cbox.d);
    253         }
    254         T = B;
    255       }
    256       CSPACE[state.J] /= 2; RSPACE[ROWS.length-1] /= 2; // back to normal
    257     },
    258     //
    259     //  Align the table vertically according to the align attribute
    260     //
    261     CHTMLalignV: function (values,state,node) {
    262       var n, M = state.rows.length, H = state.H, D = state.D, RSPACE = state.RSPACE;
    263       //
    264       //  Get alignment type and row number
    265       //
    266       if (typeof(values.align) !== "string") values.align = String(values.align);
    267       if (values.align.match(/(top|bottom|center|baseline|axis)( +(-?\d+))?/)) {
    268         n = parseInt(RegExp.$3||"0");
    269         values.align = RegExp.$1
    270         if (n < 0) n += state.rows.length + 1;
    271         if (n > M || n <= 0) n = null;
    272       } else {
    273         values.align = this.defaults.align;
    274       }
    275       //
    276       //  Get table height and baseline offset
    277       //
    278       var T = 0, B = 0, a = CHTML.TEX.axis_height;
    279       if (values.fspace) T += state.FSPACE[1];
    280       if (values.frame) {T += 2/CHTML.em; B += 1/CHTML.em}
    281       var h = state.HH, d = state.DD;
    282       for (var i = 0; i < M; i++) {
    283         if (!state.HD) {h = H[i]; d = D[i]}
    284         T += h + d + RSPACE[i];
    285         if (n) {
    286           if (i === n-1) {
    287             B += ({top:h+d, bottom:0, center:(h+d)/2,
    288                    baseline:d, axis:a+d})[values.align] + RSPACE[i];
    289           }
    290           if (i >= n) B += h + d + RSPACE[i];
    291         }
    292       }
    293       if (!n) B = ({top:T, bottom:0, center:T/2, baseline:T/2, axis:T/2-a})[values.align];
    294       //
    295       //  Place the node and save the values
    296       //
    297       if (B) node.style.verticalAlign = CHTML.Em(-B);
    298       state.T = T; state.B = B;
    299     },
    300     //
    301     //  Determine column widths and set the styles for the columns
    302     //
    303     CHTMLcolumnWidths: function (values,state,node) {
    304       var CWIDTH = state.CWIDTH, CSPACE = state.CSPACE, J = state.J, j;
    305       var WW = 0, setWidths = false, relWidth = values.width.match(/%$/);
    306       var i, m, w;
    307       //
    308       //  Handle equal columns by adjusting the CWIDTH array
    309       //
    310       if (values.width !== "auto" && !relWidth) {
    311         WW = Math.max(0,this.CHTMLlength2em(values.width,state.R));
    312         setWidths = true;
    313       }
    314       if (values.equalcolumns) {
    315         if (relWidth) {
    316           //
    317           //  Use percent of total (not perfect, but best we can do)
    318           //
    319           var p = CHTML.Percent(1/(J+1));
    320           for (j = 0; j <= J; j++) CWIDTH[j] = p;
    321         } else {
    322           //
    323           //  For width = auto, make all widths equal the widest,
    324           //  otherwise, for specific width, remove intercolumn space
    325           //  and divide by number of columns to get widest space.
    326           //
    327           w = Math.max.apply(Math,state.W);
    328           if (values.width !== "auto") {
    329             var S = (values.fspace ? state.FSPACE[0] + (values.frame ? 2/CHTML.em : 0) : 0);
    330             for (j = 0; j <= J; j++) S += CSPACE[j];
    331             w = Math.max((WW-S)/(J+1),w);
    332           }
    333           w = CHTML.Em(w);
    334           for (j = 0; j <= J; j++) CWIDTH[j] = w;
    335         }
    336         setWidths = true;
    337       }
    338       //
    339       //  Compute natural table width
    340       //
    341       var TW = 0; if (values.fspace) TW = state.FSPACE[0];
    342       var auto = [], fit = [], percent = [], W = [];
    343       var row = state.rows[0];
    344       for (j = 0; j <= J; j++) {
    345         W[j] = state.W[j];
    346         if (CWIDTH[j] === "auto") auto.push(j)
    347         else if (CWIDTH[j] === "fit") fit.push(j)
    348         else if (CWIDTH[j].match(/%$/)) percent.push(j)
    349         else W[j] = this.CHTMLlength2em(CWIDTH[j],W[j]);
    350         TW += W[j] + CSPACE[j];
    351         if (row[j]) row[j].style.width = CHTML.Em(W[j]);
    352       }
    353       if (values.frame) TW += 2/CHTML.em;
    354       var hasFit = (fit.length > 0);
    355       //
    356       //  Adjust widths of columns
    357       //
    358       if (setWidths) {
    359         if (relWidth) {
    360           //
    361           //  Attach appropriate widths to the columns
    362           //  
    363           for (j = 0; j <= J; j++) {
    364             cell = row[j].style;
    365             if (CWIDTH[j] === "auto" && !hasFit) cell.width = "";
    366             else if (CWIDTH[j] === "fit") cell.width = "";
    367             else if (CWIDTH[j].match(/%$/)) cell.width = CWIDTH[j];
    368             else cell.minWidth = cell.maxWidth = cell.width;
    369           }
    370         } else {
    371           //
    372           //  Compute percentage widths
    373           //
    374           if (WW > TW) {
    375             var extra = 0;
    376             for (i = 0, m = percent.length; i < m; i++) {
    377               j = percent[i];
    378               w = Math.max(W[j],this.CHTMLlength2em(CWIDTH[j],WW));
    379               extra += w-W[j]; W[j] = w;
    380               row[j].style.width = CHTML.Em(w);
    381             }
    382             TW += extra;
    383           }
    384           //
    385           //  Compute "fit" widths
    386           //
    387           if (!hasFit) fit = auto;
    388           if (WW > TW && fit.length) {
    389             var dw = (WW - TW) / fit.length;
    390             for (i = 0, m = fit.length; i < m; i++) {
    391               j = fit[i]; W[j] += dw;
    392               row[j].style.width = CHTML.Em(W[j]);
    393             }
    394             TW = WW;
    395           }
    396         }
    397       }
    398       W[LABEL] = state.W[LABEL];
    399       state.W = W;
    400       state.R = TW;
    401       //
    402       //  Set variable width on DOM nodes
    403       //
    404       if (relWidth) {
    405         this.CHTML.pwidth = values.width; this.CHTML.mwidth = CHTML.Em(TW);
    406         node.style.width = node.firstChild.style.width = "100%";
    407       }
    408     },
    409     //
    410     //  Stretch any cells that can be stretched
    411     //
    412     CHTMLstretchCells: function (values,state) {
    413       var ROWS = state.rows, H = state.H, D = state.D, W = state.W,
    414           J = state.J, M = ROWS.length-1;
    415       var h = state.HH, d = state.DD;
    416       for (var i = 0; i <= M; i++) {
    417         var row = ROWS[i], rdata = this.data[i];
    418         if (!state.HD) {h = H[i]; d = D[i]}
    419         for (var j = 0; j <= J; j++) {
    420           var cell = row[j], cdata = rdata.data[j];
    421           if (!cdata) continue;
    422           if (cdata.CHTML.stretch === "V") cdata.CHTMLstretchV(h,d);
    423           else if (cdata.CHTML.stretch === "H") cdata.CHTMLstretchH(cell,W[j]);
    424         }
    425       }
    426     },
    427     //
    428     //  Add labels to a table
    429     //
    430     CHTMLaddLabels: function (values,state,node,table) {
    431       //
    432       //  Get indentation and alignment
    433       //
    434       var indent = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
    435       if (indent.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) indent.indentalign = indent.indentalignfirst;
    436       if (indent.indentalign === MML.INDENTALIGN.AUTO) indent.indentalign = CONFIG.displayAlign;
    437       if (indent.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) indent.indentshift = indent.indentshiftfirst;
    438       if (indent.indentshift === "auto") indent.indentshift = "0";
    439       var shift = this.CHTMLlength2em(indent.indentshift,CHTML.cwidth);
    440       var labelspace = this.CHTMLlength2em(values.minlabelspacing,.8);
    441       var labelW = labelspace + state.W[LABEL], labelshift = 0, tw = state.R;
    442       var dIndent = this.CHTMLlength2em(CONFIG.displayIndent,CHTML.cwidth);
    443       var s = (state.CALIGN[LABEL] === MML.INDENTALIGN.RIGHT ? -1 : 1);
    444       if (indent.indentalign === MML.INDENTALIGN.CENTER) {
    445         tw += 2 * (labelW - s*(shift + dIndent));
    446         shift += dIndent;
    447       } else if (state.CALIGN[LABEL] === indent.indentalign) {
    448         if (dIndent < 0) {labelshift = s*dIndent; dIndent = 0}
    449         shift += s*dIndent; if (labelW > s*shift) shift = s*labelW; shift += labelshift;
    450         shift *= s; tw += shift;
    451       } else {
    452         tw += labelW - s*shift + dIndent;
    453         shift -= s*dIndent; shift *= -s;
    454       }
    455       //
    456       //  Create boxes for table and labels
    457       //
    458       var box = CHTML.addElement(node,"mjx-box",{
    459         style:{width:"100%","text-align":indent.indentalign}
    460       }); box.appendChild(table);
    461       var labels = CHTML.Element("mjx-stack");
    462       table.style.display = "inline-table"; if (!table.style.width) table.style.width = "auto";
    463       labels.style.verticalAlign = "top";
    464       table.style.verticalAlign = CHTML.Em(state.T-state.B-state.H[0]);
    465       node.style.verticalAlign = "";
    466       if (shift) {
    467         if (indent.indentalign === MML.INDENTALIGN.CENTER) {
    468           table.style.marginLeft = CHTML.Em(shift);
    469           table.style.marginRight = CHTML.Em(-shift);
    470         } else {
    471           var margin = "margin" + (indent.indentalign === MML.INDENTALIGN.RIGHT ? "Right" : "Left");
    472           table.style[margin] = CHTML.Em(shift);
    473         }
    474       }
    475       //
    476       //  Add labels on correct side
    477       //
    478       if (state.CALIGN[LABEL] === "left") {
    479         node.insertBefore(labels,box);
    480         labels.style.marginRight = CHTML.Em(-state.W[LABEL]-labelshift);
    481         if (labelshift) labels.style.marginLeft = CHTML.Em(labelshift);
    482       } else {
    483         node.appendChild(labels);
    484         labels.style.marginLeft = CHTML.Em(-state.W[LABEL]+labelshift);
    485       }
    486       //
    487       //  Vertically align the labels with their rows
    488       //
    489       var LABELS = state.labels, T = 0, H = state.H, D = state.D, RSPACE = state.RSPACE;
    490       if (values.fspace) T = state.FSPACE[0] + (values.frame ? 1/CHTML.em : 0);
    491       var h = state.HH, d = state.DD;
    492       for (var i = 0, m = LABELS.length; i < m; i++) {
    493         if (!state.HD) {h = H[i]; d = D[i]}
    494         if (LABELS[i] && this.data[i].data[0]) {
    495           labels.appendChild(LABELS[i]);
    496           var lbox = this.data[i].data[0].CHTML;
    497           T += h - lbox.h;
    498           if (T) LABELS[i].style.marginTop = CHTML.Em(T);
    499           T = d - lbox.d;
    500         } else {
    501           T += h + d;
    502         }
    503         T += RSPACE[i];
    504       }
    505       //
    506       //  Propagate full-width equations, and reserve room for equation plus label
    507       //
    508       node.style.width = this.CHTML.pwidth = "100%";
    509       node.style.minWidth = this.CHTML.mwidth = CHTML.Em(Math.max(0,tw));
    510     }
    511   });
    512   
    513   MML.mtr.Augment({
    514     toCommonHTML: function (node,options) {
    515       //
    516       //  Create the row node
    517       //
    518       node = this.CHTMLcreateNode(node);
    519       this.CHTMLhandleStyle(node);
    520       this.CHTMLhandleScale(node);
    521       //
    522       //  Add a new row with no label
    523       //
    524       if (!options) options = {rows:[],labels:[]};
    525       var row = []; options.rows.push(row);
    526       options.labels.push(null);
    527       //
    528       //  Add the cells to the row
    529       //
    530       for (var i = 0, m = this.data.length; i < m; i++)
    531         row.push(this.CHTMLaddChild(node,i,options));
    532       //
    533       this.CHTMLhandleColor(node);
    534       return node;
    535     }
    536   });
    537   MML.mlabeledtr.Augment({
    538     toCommonHTML: function (node,options) {
    539       //
    540       //  Create the row node
    541       //
    542       node = this.CHTMLcreateNode(node);
    543       this.CHTMLhandleStyle(node);
    544       this.CHTMLhandleScale(node);
    545       //
    546       //  Add a new row, and get the label
    547       //
    548       if (!options) options = {rows:[],labels:[]};
    549       var row = []; options.rows.push(row);
    550       var label = CHTML.Element("mjx-label"); options.labels.push(label);
    551       this.CHTMLaddChild(label,0,options);
    552       if (this.data[0]) options.labeled = true;
    553       //
    554       //  Add the cells to the row
    555       //
    556       for (var i = 1, m = this.data.length; i < m; i++)
    557         row.push(this.CHTMLaddChild(node,i,options));
    558       //
    559       this.CHTMLhandleColor(node);
    560       return node;
    561     }
    562   });
    563   MML.mtd.Augment({
    564     toCommonHTML: function (node,options) {
    565       node = this.CHTMLdefaultNode(node,options);
    566       //
    567       //  Determine if this is stretchy or not
    568       //
    569       if (this.isEmbellished()) {
    570         var mo = this.CoreMO(), BBOX = this.CHTML;
    571         if (mo.CHTMLcanStretch("Vertical")) BBOX.stretch = "V";
    572         else if (mo.CHTMLcanStretch("Horizontal")) BBOX.stretch = "H";
    573         if (BBOX.stretch) {
    574           var min = mo.Get("minsize",true);
    575           if (min) {
    576             if (BBOX.stretch === "V") {
    577               var HD = BBOX.h + BBOX.d;
    578               if (HD) {
    579                 var r = this.CHTMLlength2em(min,HD)/HD;
    580                 if (r > 1) {BBOX.h *= r; BBOX.d *= r}
    581               }
    582             } else {
    583               BBOX.w = Math.max(BBOX.w,this.CHTMLlength2em(min,BBOX.w));
    584             }
    585           }
    586         }
    587       }
    588       return node;
    589     }
    590   });
    591 
    592   
    593   MathJax.Hub.Startup.signal.Post("CommonHTML mtable Ready");
    594   MathJax.Ajax.loadComplete(CHTML.autoloadDir+"/mtable.js");
    595 });
    596