Add preliminary support for line breaking. Still need to handle the compound constructs like msubsup, munderover, mfenced, etc. as special cases.
This commit is contained in:
parent
4a8cb8ad5b
commit
2b3cb0cb0d
780
unpacked/jax/output/CommonHTML/autoload/multiline.js
Normal file
780
unpacked/jax/output/CommonHTML/autoload/multiline.js
Normal file
|
@ -0,0 +1,780 @@
|
|||
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
|
||||
/*************************************************************
|
||||
*
|
||||
* MathJax/jax/output/CommonHTML/autoload/multiline.js
|
||||
*
|
||||
* Implements the CommonHTML output for <mrow>'s that contain line breaks.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2015 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.Hub.Register.StartupHook("CommonHTML Jax Ready",function () {
|
||||
var VERSION = "2.5.0";
|
||||
var MML = MathJax.ElementJax.mml,
|
||||
HTML = MathJax.HTML, CONFIG = MathJax.Hub.config,
|
||||
CHTML = MathJax.OutputJax.CommonHTML;
|
||||
|
||||
//
|
||||
// Penalties for the various line breaks
|
||||
//
|
||||
var PENALTY = {
|
||||
newline: 0,
|
||||
nobreak: 1000000,
|
||||
goodbreak: [-200],
|
||||
badbreak: [+200],
|
||||
auto: [0],
|
||||
|
||||
toobig: 800,
|
||||
nestfactor: 400,
|
||||
spacefactor: -100,
|
||||
spaceoffset: 2,
|
||||
spacelimit: 1, // spaces larger than this get a penalty boost
|
||||
fence: 500,
|
||||
close: 500
|
||||
};
|
||||
|
||||
var ENDVALUES = {linebreakstyle: "after"};
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MML.mbase.Augment({
|
||||
CHTMLlinebreakPenalty: PENALTY,
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Handle breaking an mrow into separate lines
|
||||
//
|
||||
CHTMLmultiline: function (node) {
|
||||
|
||||
//
|
||||
// Find the parent element and mark it as multiline
|
||||
//
|
||||
var parent = this;
|
||||
while (parent.inferred || (parent.parent && parent.parent.type === "mrow" &&
|
||||
parent.parent.data.length === 1)) {parent = parent.parent}
|
||||
var isTop = ((parent.type === "math" && parent.Get("display") === "block") ||
|
||||
parent.type === "mtd");
|
||||
parent.isMultiline = true;
|
||||
|
||||
//
|
||||
// Default values for the line-breaking parameters
|
||||
//
|
||||
var VALUES = this.getValues(
|
||||
"linebreak","linebreakstyle","lineleading","linebreakmultchar",
|
||||
"indentalign","indentshift",
|
||||
"indentalignfirst","indentshiftfirst",
|
||||
"indentalignlast","indentshiftlast"
|
||||
);
|
||||
if (VALUES.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE)
|
||||
VALUES.linebreakstyle = this.Get("infixlinebreakstyle");
|
||||
VALUES.lineleading = CHTML.length2em(VALUES.lineleading,0.5);
|
||||
|
||||
//
|
||||
// Break the math at its best line breaks
|
||||
//
|
||||
CHTML.BBOX.empty(this.CHTML);
|
||||
var stack = HTML.addElement(node,"mjx-stack");
|
||||
var state = {
|
||||
BBOX: this.CHTML,
|
||||
n: 0, Y: 0,
|
||||
scale: (this.CHTML.scale||1),
|
||||
isTop: isTop,
|
||||
values: {},
|
||||
VALUES: VALUES
|
||||
},
|
||||
align = this.CHTMLgetAlign(state,{}),
|
||||
shift = this.CHTMLgetShift(state,{},align),
|
||||
start = [],
|
||||
end = {
|
||||
index:[], penalty:PENALTY.nobreak,
|
||||
w:0, W:shift, shift:shift, scanW:shift,
|
||||
nest: 0
|
||||
},
|
||||
broken = false;
|
||||
|
||||
while (this.CHTMLbetterBreak(end,state) &&
|
||||
(end.scanW >= CHTML.linebreakWidth || end.penalty === PENALTY.newline)) {
|
||||
this.CHTMLaddLine(stack,start,end.index,state,end.values,broken);
|
||||
start = end.index.slice(0); broken = true;
|
||||
align = this.CHTMLgetAlign(state,end.values);
|
||||
shift = this.CHTMLgetShift(state,end.values,align);
|
||||
end.W = end.shift = end.scanW = shift; end.penalty = PENALTY.nobreak;
|
||||
}
|
||||
state.isLast = true;
|
||||
this.CHTMLaddLine(stack,start,[],state,ENDVALUES,broken);
|
||||
|
||||
node.style.width = stack.style.width = this.CHTML.pwidth = "100%";
|
||||
this.CHTML.isMultiline = parent.CHTML.isMultiline = true;
|
||||
stack.style.verticalAlign = CHTML.Em(state.d - this.CHTML.d);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Locate the next linebreak that is better than the current one
|
||||
//
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
if (this.isToken) return false; // FIXME: handle breaking of token elements
|
||||
if (this.isEmbellished()) {
|
||||
info.embellished = this;
|
||||
return this.CoreMO().CHTMLbetterBreak(info,state);
|
||||
}
|
||||
if (this.linebreakContainer) return false;
|
||||
//
|
||||
// Get the current breakpoint position and other data
|
||||
//
|
||||
var index = info.index.slice(0), i = info.index.shift(),
|
||||
m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false;
|
||||
if (i == null) i = -1; if (!broken) {i++; info.W += info.w; info.w = 0}
|
||||
scanW = info.scanW = info.W; info.nest++;
|
||||
//
|
||||
// Look through the line for breakpoints,
|
||||
// (as long as we are not too far past the breaking width)
|
||||
//
|
||||
while (i < m && info.scanW < 1.33*CHTML.linebreakWidth) {
|
||||
if (this.data[i]) {
|
||||
if (this.data[i].CHTMLbetterBreak(info,state)) {
|
||||
better = true; index = [i].concat(info.index); W = info.W; w = info.w;
|
||||
if (info.penalty === PENALTY.newline) {
|
||||
info.index = index;
|
||||
if (info.nest) {info.nest--}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
scanW = (broken ? info.scanW : this.CHTMLaddWidth(i,info,scanW));
|
||||
}
|
||||
info.index = []; i++; broken = false;
|
||||
}
|
||||
if (info.nest) {info.nest--}
|
||||
info.index = index;
|
||||
if (better) {info.W = W; info.w = w}
|
||||
return better;
|
||||
},
|
||||
CHTMLaddWidth: function (i,info,scanW) {
|
||||
if (this.data[i]) {
|
||||
var node = this.data[i].CHTMLnodeElement(), bbox = this.data[i].CHTML;
|
||||
scanW += bbox.w + (bbox.L||0) + (bbox.R||0);
|
||||
info.W = info.scanW = scanW; info.w = 0;
|
||||
}
|
||||
return scanW;
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Create a new line and move the required elements into it
|
||||
// Position it using proper alignment and indenting
|
||||
//
|
||||
CHTMLaddLine: function (stack,start,end,state,values,broken) {
|
||||
//
|
||||
// Create a box for the line, with empty BBox
|
||||
// fill it with the proper elements,
|
||||
// and clean up the bbox
|
||||
//
|
||||
var block = HTML.addElement(stack,"mjx-block",{},[["mjx-box"]]), line = block.firstChild;
|
||||
var bbox = state.bbox = CHTML.BBOX.empty();
|
||||
state.first = broken; state.last = true;
|
||||
this.CHTMLmoveLine(start,end,line,state,values);
|
||||
bbox.clean();
|
||||
//
|
||||
// Get the alignment and shift values
|
||||
//
|
||||
var align = this.CHTMLgetAlign(state,values),
|
||||
shift = this.CHTMLgetShift(state,values,align,true);
|
||||
//
|
||||
// Set the Y offset based on previous depth, leading, and current height
|
||||
//
|
||||
var dY = 0;
|
||||
if (state.n > 0) {
|
||||
var LHD = CHTML.FONTDATA.baselineskip;
|
||||
var leading = (state.values.lineleading == null ? state.VALUES : state.values).lineleading * state.scale;
|
||||
var Y = state.Y;
|
||||
state.Y -= Math.max(LHD,state.d + bbox.h + leading);
|
||||
dY = Y - state.Y - state.d - bbox.h;
|
||||
}
|
||||
//
|
||||
// Place the new line
|
||||
//
|
||||
if (shift) line.style.margin = "0 "+CHTML.Em(-shift)+" 0 "+CHTML.Em(shift);
|
||||
if (align !== MML.INDENTALIGN.LEFT) block.style.textAlign = align;
|
||||
if (dY) block.style.paddingTop = CHTML.Em(dY);
|
||||
state.BBOX.combine(bbox,shift,state.Y);
|
||||
//this.CHTMLdrawBBox(line,bbox);
|
||||
//
|
||||
// Save the values needed for the future
|
||||
//
|
||||
state.d = state.bbox.d; state.values = values; state.n++;
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Get alignment and shift values from the given data
|
||||
//
|
||||
CHTMLgetAlign: function (state,values) {
|
||||
var cur = values, prev = state.values, def = state.VALUES, align;
|
||||
if (state.n === 0) align = cur.indentalignfirst || prev.indentalignfirst || def.indentalignfirst;
|
||||
else if (state.isLast) align = prev.indentalignlast || def.indentalignlast;
|
||||
else align = prev.indentalign || def.indentalign;
|
||||
if (align === MML.INDENTALIGN.INDENTALIGN) align = prev.indentalign || def.indentalign;
|
||||
if (align === MML.INDENTALIGN.AUTO) align = (state.isTop ? CONFIG.displayAlign : MML.INDENTALIGN.LEFT);
|
||||
return align;
|
||||
},
|
||||
CHTMLgetShift: function (state,values,align,noadjust) {
|
||||
var cur = values, prev = state.values, def = state.VALUES, shift;
|
||||
if (state.n === 0) shift = cur.indentshiftfirst || prev.indentshiftfirst || def.indentshiftfirst;
|
||||
else if (state.isLast) shift = prev.indentshiftlast || def.indentshiftlast;
|
||||
else shift = prev.indentshift || def.indentshift;
|
||||
if (shift === MML.INDENTSHIFT.INDENTSHIFT) shift = prev.indentshift || def.indentshift;
|
||||
if (shift === "auto" || shift === "") shift = "0";
|
||||
shift = CHTML.length2em(shift,CHTML.cwidth);
|
||||
if (state.isTop && CONFIG.displayIndent !== "0") {
|
||||
var indent = CHTML.length2em(CONFIG.displayIndent,CHTML.cwidth);
|
||||
shift += (align === MML.INDENTALIGN.RIGHT ? -indent : indent);
|
||||
}
|
||||
return (align === MML.INDENTALIGN.RIGHT && !noadjust ? -shift : shift);
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Move the selected elements into the new line's box,
|
||||
// moving whole items when possible, and parts of ones
|
||||
// that are split by a line break.
|
||||
//
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
var i = start[0], j = end[0];
|
||||
if (i == null) i = -1; if (j == null) j = this.data.length-1;
|
||||
if (i === j && start.length > 1) {
|
||||
//
|
||||
// If starting and ending in the same element move the subpiece to the new line
|
||||
//
|
||||
this.data[i].CHTMLmoveSlice(start.slice(1),end.slice(1),node,state,values,"marginLeft");
|
||||
} else {
|
||||
//
|
||||
// Otherwise, move the remainder of the initial item
|
||||
// and any others up to the last one
|
||||
//
|
||||
var last = state.last; state.last = false;
|
||||
while (i < j) {
|
||||
if (this.data[i]) {
|
||||
if (start.length <= 1) {this.data[i].CHTMLmoveNode(node,state,values)}
|
||||
else {this.data[i].CHTMLmoveSlice(start.slice(1),[],node,state,values,"marginLeft")}
|
||||
}
|
||||
i++; state.first = false; start = [];
|
||||
}
|
||||
//
|
||||
// If the last item is complete, move it,
|
||||
// otherwise move the first part of it up to the split
|
||||
//
|
||||
state.last = last;
|
||||
if (this.data[i]) {
|
||||
if (end.length <= 1) {this.data[i].CHTMLmoveNode(node,state,values)}
|
||||
else {this.data[i].CHTMLmoveSlice([],end.slice(1),node,state,values,"marginRight")}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Split an element and copy the selected items into the new part
|
||||
//
|
||||
CHTMLmoveSlice: function (start,end,node,state,values,margin) {
|
||||
//
|
||||
// Create a new box for the slice of the element
|
||||
// Move the selected portion into the slice
|
||||
// If it is the last slice
|
||||
// Remove the original (now empty) node
|
||||
// Rename the Continue-0 node with the original name (for CHTMLnodeElement)
|
||||
//
|
||||
var slice = this.CHTMLcreateSliceNode(node);
|
||||
this.CHTMLmoveLine(start,end,slice,state,values);
|
||||
if (slice.style[margin]) slice.style[margin] = "";
|
||||
if (this.CHTML.L) {
|
||||
if (margin !== "marginLeft") state.bbox.w += this.CHTML.L;
|
||||
else slice.className = slice.className.replace(/ MJXc-space\d/,"");
|
||||
}
|
||||
if (this.CHTML.R && margin !== "marginRight") state.bbox.w += this.CHTML.R;
|
||||
if (end.length === 0) {
|
||||
node = this.CHTMLnodeElement();
|
||||
node.parentNode.removeChild(node);
|
||||
node.nextMathJaxNode.id = node.id;
|
||||
}
|
||||
return slice;
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Create a new node for an element that is split in two
|
||||
// Clone the original and update its ID.
|
||||
// Link the old node to the new one so we can find it later
|
||||
//
|
||||
CHTMLcreateSliceNode: function (node) {
|
||||
var NODE = this.CHTMLnodeElement(), n = 0;
|
||||
var LAST = NODE; while (LAST.nextMathJaxNode) {LAST = LAST.nextMathJaxNode; n++}
|
||||
var SLICE = NODE.cloneNode(false); LAST.nextMathJaxNode = SLICE; SLICE.nextMathJaxNode = null;
|
||||
SLICE.id += "-MJX-Continue-"+n;
|
||||
return node.appendChild(SLICE);
|
||||
},
|
||||
|
||||
/****************************************************************/
|
||||
//
|
||||
// Move an element from its original node to its new location in
|
||||
// a split element or the new line's node
|
||||
//
|
||||
CHTMLmoveNode: function (line,state,values) {
|
||||
// FIXME: handle linebreakstyle === "duplicate"
|
||||
// FIXME: handle linebreakmultchar
|
||||
if (!(state.first || state.last) ||
|
||||
(state.first && state.values.linebreakstyle === MML.LINEBREAKSTYLE.BEFORE) ||
|
||||
(state.last && values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER)) {
|
||||
//
|
||||
// Move node
|
||||
//
|
||||
var node = this.CHTMLnodeElement();
|
||||
line.appendChild(node);
|
||||
//
|
||||
// If it is last, remove right margin
|
||||
// If it is first, remove left margin
|
||||
//
|
||||
if (state.last) node.style.marginRight = "";
|
||||
if (state.first || state.nextIsFirst) {
|
||||
node.style.marginLeft = ""; this.CHTML.L = 0;
|
||||
node.className = node.className.replace(/ MJXc-space\d/,"");
|
||||
}
|
||||
if (state.first && this.CHTML.w === 0) {state.nextIsFirst = true}
|
||||
else {delete state.nextIsFirst}
|
||||
//
|
||||
// Update bounding box
|
||||
//
|
||||
state.bbox.combine(this.CHTML,state.bbox.w,0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MML.mfenced.Augment({
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
//
|
||||
// Get the current breakpoint position and other data
|
||||
//
|
||||
var index = info.index.slice(0), i = info.index.shift(),
|
||||
m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false;
|
||||
if (i == null) i = -1; if (!broken) {i++; info.W += info.w; info.w = 0}
|
||||
scanW = info.scanW = info.W; info.nest++;
|
||||
//
|
||||
// Create indices that include the delimiters and separators
|
||||
//
|
||||
if (!this.dataI) {
|
||||
this.dataI = [];
|
||||
if (this.data.open) this.dataI.push("open");
|
||||
if (m) this.dataI.push(0);
|
||||
for (var j = 1; j < m; j++) {
|
||||
if (this.data["sep"+j]) this.dataI.push("sep"+j);
|
||||
this.dataI.push(j);
|
||||
}
|
||||
if (this.data.close) this.dataI.push("close");
|
||||
}
|
||||
m = this.dataI.length;
|
||||
//
|
||||
// Look through the line for breakpoints, including the open, close, and separators
|
||||
// (as long as we are not too far past the breaking width)
|
||||
//
|
||||
while (i < m && info.scanW < 1.33*CHTML.linebreakWidth) {
|
||||
var k = this.dataI[i];
|
||||
if (this.data[k]) {
|
||||
if (this.data[k].CHTMLbetterBreak(info,state)) {
|
||||
better = true; index = [i].concat(info.index); W = info.W; w = info.w;
|
||||
if (info.penalty === PENALTY.newline) {
|
||||
info.index = index;
|
||||
if (info.nest) info.nest--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
scanW = (broken ? info.scanW : this.CHTMLaddWidth(i,info,scanW));
|
||||
}
|
||||
info.index = []; i++; broken = false;
|
||||
}
|
||||
if (info.nest) info.nest--;
|
||||
info.index = index;
|
||||
if (better) {info.W = W; info.w = w}
|
||||
return better;
|
||||
},
|
||||
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
var i = start[0], j = end[0];
|
||||
if (i == null) i = -1; if (j == null) j = this.dataI.length-1;
|
||||
if (i === j && start.length > 1) {
|
||||
//
|
||||
// If starting and ending in the same element move the subpiece to the new line
|
||||
//
|
||||
this.data[this.dataI[i]].CHTMLmoveSlice(start.slice(1),end.slice(1),node,state,values,"marginLeft");
|
||||
} else {
|
||||
//
|
||||
// Otherwise, move the remainder of the initial item
|
||||
// and any others (including open and separators) up to the last one
|
||||
//
|
||||
var last = state.last; state.last = false; var k = this.dataI[i];
|
||||
while (i < j) {
|
||||
if (this.data[k]) {
|
||||
if (start.length <= 1) {this.data[k].CHTMLmoveNode(node,state,values)}
|
||||
else {this.data[k].CHTMLmoveSlice(start.slice(1),[],node,state,values,"marginLeft")}
|
||||
}
|
||||
i++; k = this.dataI[i]; state.first = false; start = [];
|
||||
}
|
||||
//
|
||||
// If the last item is complete, move it
|
||||
//
|
||||
state.last = last;
|
||||
if (this.data[k]) {
|
||||
if (end.length <= 1) {this.data[k].CHTMLmoveNode(node,state,values)}
|
||||
else {this.data[k].CHTMLmoveSlice([],end.slice(1),node,state,values,"marginRight")}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MML.msubsup.Augment({
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
if (!this.data[this.base]) {return false}
|
||||
//
|
||||
// Get the current breakpoint position and other data
|
||||
//
|
||||
var index = info.index.slice(0), i = info.index.shift(),
|
||||
W, w, scanW, broken = (info.index.length > 0), better = false;
|
||||
if (!broken) {info.W += info.w; info.w = 0}
|
||||
scanW = info.scanW = info.W;
|
||||
//
|
||||
// Record the width of the base and the super- and subscripts
|
||||
//
|
||||
if (i == null) {
|
||||
this.CHTML.baseW = this.data[this.base].CHTML.w;
|
||||
this.CHTML.dw = this.CHTML.w - this.CHTML.baseW;
|
||||
}
|
||||
//
|
||||
// Check if the base can be broken
|
||||
//
|
||||
if (this.data[this.base].CHTMLbetterBreak(info,state)) {
|
||||
better = true; index = [this.base].concat(info.index); W = info.W; w = info.w;
|
||||
if (info.penalty === PENALTY.newline) better = broken = true;
|
||||
}
|
||||
//
|
||||
// Add in the base if it is unbroken, and add the scripts
|
||||
//
|
||||
if (!broken) this.CHTMLaddWidth(this.base,info,scanW);
|
||||
info.scanW += this.CHTML.dw; info.W = info.scanW;
|
||||
info.index = []; if (better) {info.W = W; info.w = w; info.index = index}
|
||||
return better;
|
||||
},
|
||||
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
//
|
||||
// Move the proper part of the base
|
||||
//
|
||||
if (this.data[this.base]) {
|
||||
if (start.length > 1) {
|
||||
this.data[this.base].CHTMLmoveSlice(start.slice(1),end.slice(1),node,state,values,"marginLeft");
|
||||
} else {
|
||||
if (end.length <= 1) {this.data[this.base].CHTMLmoveNode(node,state,values)}
|
||||
else {this.data[this.base].CHTMLmoveSlice([],end.slice(1),node,state,values,"marginRight")}
|
||||
}
|
||||
}
|
||||
//
|
||||
// If this is the end, check for super and subscripts, and move those
|
||||
// by moving the stack that contains them, and shifting by the amount of the
|
||||
// base that has been removed. Remove the empty base box from the stack.
|
||||
//
|
||||
if (end.length === 0) {
|
||||
var s = this.data[this.sup] || this.data[this.sub];
|
||||
if (s && this.CHTMLnotEmpty(s)) {
|
||||
var box = s.CHTMLnodeElement().parentNode, stack = box.parentNode;
|
||||
if (this.data[this.base]) stack.removeChild(stack.firstChild);
|
||||
for (box = stack.firstChild; box; box = box.nextSibling)
|
||||
{box.style.left = CHTML.Em(CHTML.unEm(box.style.left)-this.CHTML.baseW)}
|
||||
// stack.bbox.w -= this.CHTML.baseW; stack.style.width = CHTML.Em(stack.bbox.w);
|
||||
// this.HTMLcombineBBoxes(stack,span.bbox);
|
||||
node.appendChild(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MML.mmultiscripts.Augment({
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
if (!this.data[this.base]) return false;
|
||||
//
|
||||
// Get the current breakpoint position and other data
|
||||
//
|
||||
var index = info.index.slice(0); info.index.shift();
|
||||
var W, w, scanW, broken = (info.index.length > 0), better = false;
|
||||
if (!broken) {info.W += info.w; info.w = 0}
|
||||
info.scanW = info.W;
|
||||
//
|
||||
// Get the bounding boxes and the width of the scripts
|
||||
//
|
||||
var bbox = this.CHTML,
|
||||
base = this.data[this.base].CHTML;
|
||||
var dw = bbox.w - base.w;
|
||||
//
|
||||
// Add in the prescripts
|
||||
//
|
||||
info.scanW += bbox.dx; scanW = info.scanW;
|
||||
//
|
||||
// Check if the base can be broken
|
||||
//
|
||||
if (this.data[this.base].CHTMLbetterBreak(info,state)) {
|
||||
better = true; index = [this.base].concat(info.index); W = info.W; w = info.w;
|
||||
if (info.penalty === PENALTY.newline) better = broken = true;
|
||||
}
|
||||
//
|
||||
// Add in the base if it is unbroken, and add the scripts
|
||||
//
|
||||
if (!broken) this.CHTMLaddWidth(this.base,info,scanW);
|
||||
info.scanW += dw; info.W = info.scanW;
|
||||
info.index = []; if (better) {info.W = W; info.w = w; info.index = index}
|
||||
return better;
|
||||
},
|
||||
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
var NODE = this.CHTMLnodeElement(), data = this.CHTML,
|
||||
stack = NODE.firstChild, BOX = {};
|
||||
var box = stack.firstChild;
|
||||
|
||||
//
|
||||
// Get the boxes for the scripts (if any)
|
||||
//
|
||||
// while (box) {
|
||||
// if (box.bbox && box.bbox.name) {BOX[box.bbox.name] = box}
|
||||
// box = box.nextSibling;
|
||||
// }
|
||||
//
|
||||
// If this is the start, move the prescripts, if any.
|
||||
//
|
||||
if (start.length < 1) {
|
||||
if (BOX.presub || BOX.presup) {
|
||||
var STACK = HTMLCSS.createStack(node);
|
||||
if (BOX.presup) {
|
||||
HTMLCSS.addBox(STACK,BOX.presup);
|
||||
HTMLCSS.placeBox(BOX.presup,data.dx-BOX.presup.bbox.w,data.u);
|
||||
}
|
||||
if (BOX.presub) {
|
||||
HTMLCSS.addBox(STACK,BOX.presub);
|
||||
HTMLCSS.placeBox(BOX.presub,data.dx+data.delta-BOX.presub.bbox.w,-data.v);
|
||||
}
|
||||
this.HTMLcombineBBoxes(STACK,node.bbox);
|
||||
node.appendChild(STACK);
|
||||
STACK.style.width = HTMLCSS.Em(data.dx);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Move the proper part of the base
|
||||
//
|
||||
if (this.data[this.base]) {
|
||||
if (start.length > 1) {
|
||||
this.data[this.base].CHTMLmoveSlice(start.slice(1),end.slice(1),node,state,values,"marginLeft");
|
||||
} else {
|
||||
if (end.length <= 1) {this.data[this.base].CHTMLmoveNode(node,state,values)}
|
||||
else {this.data[this.base].CHTMLmoveSlice([],end.slice(1),node,state,values,"marginRight")}
|
||||
}
|
||||
}
|
||||
//
|
||||
// If this is the end, check for super and subscripts, and move those
|
||||
// by moving the stack that contains them, and shifting by the amount of the
|
||||
// base that has been removed. Remove the empty base box from the stack.
|
||||
//
|
||||
if (end.length === 0) {
|
||||
if (this.data[this.base]) stack.removeChild(stack.firstChild);
|
||||
for (box = stack.firstChild; box; box = box.nextSibling)
|
||||
box.style.left = CHTML.Em(CHTML.unEm(box.style.left)-data.px);
|
||||
// stack.bbox.w -= data.px; stack.style.width = HTMLCSS.Em(stack.bbox.w);
|
||||
// this.HTMLcombineBBoxes(stack,node.bbox);
|
||||
node.appendChild(stack);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MML.mo.Augment({
|
||||
//
|
||||
// Override the method for checking line breaks to properly handle <mo>
|
||||
//
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
if (info.values && info.values.id === this.CHTMLnodeID) return false;
|
||||
var values = this.getValues(
|
||||
"linebreak","linebreakstyle","lineleading","linebreakmultchar",
|
||||
"indentalign","indentshift",
|
||||
"indentalignfirst","indentshiftfirst",
|
||||
"indentalignlast","indentshiftlast",
|
||||
"texClass", "fence"
|
||||
);
|
||||
if (values.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE)
|
||||
values.linebreakstyle = this.Get("infixlinebreakstyle");
|
||||
//
|
||||
// Adjust nesting by TeX class (helps output that does not include
|
||||
// mrows for nesting, but can leave these unbalanced.
|
||||
//
|
||||
if (values.texClass === MML.TEXCLASS.OPEN) info.nest++;
|
||||
if (values.texClass === MML.TEXCLASS.CLOSE && info.nest) info.nest--;
|
||||
//
|
||||
// Get the default penalty for this location
|
||||
//
|
||||
var W = info.scanW, mo = (info.embellished||this); delete info.embellished;
|
||||
var w = this.CHTML.w + (this.CHTML.L||0) + (this.CHTML.R||0);
|
||||
if (values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER) {W += w; w = 0}
|
||||
if (W - info.shift === 0 && values.linebreak !== MML.LINEBREAK.NEWLINE)
|
||||
return false; // don't break at zero width (FIXME?)
|
||||
var offset = CHTML.linebreakWidth - W;
|
||||
// Adjust offest for explicit first-line indent and align
|
||||
if (state.n === 0 && (values.indentshiftfirst !== state.VALUES.indentshiftfirst ||
|
||||
values.indentalignfirst !== state.VALUES.indentalignfirst)) {
|
||||
var align = this.CHTMLgetAlign(state,values),
|
||||
shift = this.CHTMLgetShift(state,values,align);
|
||||
offset += (info.shift - shift);
|
||||
}
|
||||
//
|
||||
var penalty = Math.floor(offset / CHTML.linebreakWidth * 1000);
|
||||
if (penalty < 0) penalty = PENALTY.toobig - 3*penalty;
|
||||
if (values.fence) penalty += PENALTY.fence;
|
||||
if ((values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER &&
|
||||
values.texClass === MML.TEXCLASS.OPEN) ||
|
||||
values.texClass === MML.TEXCLASS.CLOSE) penalty += PENALTY.close;
|
||||
penalty += info.nest * PENALTY.nestfactor;
|
||||
//
|
||||
// Get the penalty for this type of break and
|
||||
// use it to modify the default penalty
|
||||
//
|
||||
var linebreak = PENALTY[values.linebreak||MML.LINEBREAK.AUTO];
|
||||
if (!(linebreak instanceof Array)) {
|
||||
// for breaks past the width, don't modify penalty
|
||||
if (offset >= 0) {penalty = linebreak * info.nest}
|
||||
} else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)}
|
||||
//
|
||||
// If the penalty is no better than the current one, return false
|
||||
// Otherwise save the data for this breakpoint and return true
|
||||
//
|
||||
if (penalty >= info.penalty) return false;
|
||||
info.penalty = penalty; info.values = values; info.W = W; info.w = w;
|
||||
values.lineleading = CHTML.length2em(values.lineleading,state.VALUES.lineleading);
|
||||
values.id = this.CHTMLnodeID;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MML.mspace.Augment({
|
||||
//
|
||||
// Override the method for checking line breaks to properly handle <mspace>
|
||||
//
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
if (info.values && info.values.id === this.CHTMLnodeID) return false;
|
||||
var values = this.getValues("linebreak");
|
||||
var linebreakValue = values.linebreak;
|
||||
if (!linebreakValue || this.hasDimAttr()) {
|
||||
// The MathML spec says that the linebreak attribute should be ignored
|
||||
// if any dimensional attribute is set.
|
||||
linebreakValue = MML.LINEBREAK.AUTO;
|
||||
}
|
||||
//
|
||||
// Get the default penalty for this location
|
||||
//
|
||||
var W = info.scanW, w = this.CHTML.w + (this.CHTML.L||0) + (this.CHTML.R||0);
|
||||
if (W - info.shift === 0) return false; // don't break at zero width (FIXME?)
|
||||
var offset = CHTML.linebreakWidth - W;
|
||||
//
|
||||
var penalty = Math.floor(offset / CHTML.linebreakWidth * 1000);
|
||||
if (penalty < 0) penalty = PENALTY.toobig - 3*penalty;
|
||||
penalty += info.nest * PENALTY.nestfactor;
|
||||
//
|
||||
// Get the penalty for this type of break and
|
||||
// use it to modify the default penalty
|
||||
//
|
||||
var linebreak = PENALTY[linebreakValue];
|
||||
if (linebreakValue === MML.LINEBREAK.AUTO && w >= PENALTY.spacelimit &&
|
||||
!this.mathbackground && !this.background)
|
||||
linebreak = [(w+PENALTY.spaceoffset)*PENALTY.spacefactor];
|
||||
if (!(linebreak instanceof Array)) {
|
||||
// for breaks past the width, don't modify penalty
|
||||
if (offset >= 0) {penalty = linebreak * info.nest}
|
||||
} else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)}
|
||||
//
|
||||
// If the penalty is no better than the current one, return false
|
||||
// Otherwise save the data for this breakpoint and return true
|
||||
//
|
||||
if (penalty >= info.penalty) return false;
|
||||
info.penalty = penalty; info.values = values; info.W = W; info.w = w;
|
||||
values.lineleading = state.VALUES.lineleading;
|
||||
values.linebreakstyle = "before"; values.id = this.CHTMLnodeID;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Hook into the mathchoice extension
|
||||
//
|
||||
MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () {
|
||||
MML.TeXmathchoice.Augment({
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
return this.Core().CHTMLbetterBreak(info,state);
|
||||
},
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
return this.Core().CHTMLmoveSlice(start,end,node,state,values);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// Have maction process only the selected item
|
||||
//
|
||||
MML.maction.Augment({
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
return this.Core().CHTMLbetterBreak(info,state);
|
||||
},
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
return this.Core().CHTMLmoveSlice(start,end,node,state,values);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Have semantics only do the first element
|
||||
// (FIXME: do we need to do anything special about annotation-xml?)
|
||||
//
|
||||
MML.semantics.Augment({
|
||||
CHTMLbetterBreak: function (info,state) {
|
||||
return (this.data[0] ? this.data[0].CHTMLbetterBreak(info,state) : false);
|
||||
},
|
||||
CHTMLmoveLine: function (start,end,node,state,values) {
|
||||
return (this.data[0] ? this.data[0].CHTMLmoveSlice(start,end,node,state,values) : null);
|
||||
}
|
||||
});
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
MathJax.Hub.Startup.signal.Post("CommonHTML multiline Ready");
|
||||
MathJax.Ajax.loadComplete(CHTML.autoloadDir+"/multiline.js");
|
||||
|
||||
});
|
|
@ -184,6 +184,7 @@
|
|||
|
||||
var BIGDIMEN = 1000000;
|
||||
var V = "V", H = "H";
|
||||
var LINEBREAKS = {};
|
||||
|
||||
CHTML.Augment({
|
||||
settings: HUB.config.menuSettings,
|
||||
|
@ -197,6 +198,7 @@
|
|||
if (settings.scale) {this.config.scale = settings.scale}
|
||||
this.require.push(this.fontDir+"/TeX/fontdata.js");
|
||||
this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js");
|
||||
LINEBREAKS = this.config.linebreaks;
|
||||
},
|
||||
|
||||
Startup: function () {
|
||||
|
@ -286,8 +288,7 @@
|
|||
// Get linebreaking information
|
||||
//
|
||||
var maxwidth = 100000, relwidth = false, cwidth = 0,
|
||||
linebreak = this.config.linebreaks.automatic,
|
||||
width = this.config.linebreaks.width;
|
||||
linebreak = LINEBREAKS.automatic, width = LINEBREAKS.width;
|
||||
if (linebreak) {
|
||||
relwidth = !!width.match(/^\s*(\d+(\.\d*)?%\s*)?container\s*$/);
|
||||
if (relwidth) {width = width.replace(/\s*container\s*/,"")}
|
||||
|
@ -354,7 +355,7 @@
|
|||
if (style.maxWidth !== "none") {cwidth = parseFloat(style.maxWidth); break}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
if (relwidth) maxwidth = cwidth;
|
||||
scale = (this.config.matchFontHeight ? ex/this.TEX.x_height/em : 1);
|
||||
scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale)*this.config.scale);
|
||||
jax.CHTML.scale = scale/100; jax.CHTML.fontSize = scale+"%";
|
||||
|
@ -1224,6 +1225,7 @@
|
|||
toCommonHTML: function (node,options) {
|
||||
return this.CHTMLdefaultNode(node,options);
|
||||
},
|
||||
CHTMLmultiline: function () {MML.mbase.CHTMLautoloadFile("multiline")},
|
||||
|
||||
CHTMLdefaultNode: function (node,options) {
|
||||
if (!options) options = {};
|
||||
|
@ -1384,7 +1386,7 @@
|
|||
|
||||
CHTMLhandleBBox: function (node) {
|
||||
var BBOX = this.CHTML, style = node.style;
|
||||
if (this.data.length === 1 && this.data[0].CHTML.pwidth) {
|
||||
if (this.data.length === 1 && (this.data[0].CHTML||{}).pwidth) {
|
||||
BBOX.pwidth = this.data[0].CHTML.pwidth;
|
||||
BBOX.mwidth = this.data[0].CHTML.mwidth;
|
||||
style.width = "100%";
|
||||
|
@ -1483,8 +1485,8 @@
|
|||
//
|
||||
// Debugging function to see if internal BBox matches actual bbox
|
||||
//
|
||||
CHTMLdrawBBox: function (node) {
|
||||
var bbox = this.CHTML;
|
||||
CHTMLdrawBBox: function (node,bbox) {
|
||||
if (!bbox) bbox = this.CHTML;
|
||||
var box = HTML.Element("mjx-box",
|
||||
{style:{"font-size":node.style.fontSize, opacity:.25,"margin-left":CHTML.Em(-(bbox.w+(bbox.R||0)))}},[
|
||||
["mjx-box",{style:{
|
||||
|
@ -2356,8 +2358,13 @@
|
|||
node = this.CHTMLdefaultNode(node);
|
||||
var bbox = this.CHTML, H = bbox.h, D = bbox.d;
|
||||
for (var i = 0, m = this.data.length; i < m; i++) this.CHTMLstretchChildV(i,H,D);
|
||||
if (this.CHTMLlineBreaks()) this.CHTMLmultiline(node);
|
||||
return node;
|
||||
},
|
||||
CHTMLlineBreaks: function () {
|
||||
if (!LINEBREAKS.automatic || !this.parent.linebreakContainer) return false;
|
||||
return (this.CHTML.w > CHTML.linebreakWidth) || this.hasNewline();
|
||||
},
|
||||
CHTMLstretchV: function (h,d) {
|
||||
this.CHTMLstretchChildV(this.CoreIndex(),h,d);
|
||||
return this.CHTML;
|
||||
|
|
Loading…
Reference in New Issue
Block a user