multiline.js (30613B)
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/multiline.js 7 * 8 * Implements the CommonHTML output for <mrow>'s that contain line breaks. 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 33 // 34 // Penalties for the various line breaks 35 // 36 var PENALTY = { 37 newline: 0, 38 nobreak: 1000000, 39 goodbreak: [-200], 40 badbreak: [+200], 41 auto: [0], 42 43 toobig: 800, 44 nestfactor: 400, 45 spacefactor: -100, 46 spaceoffset: 2, 47 spacelimit: 1, // spaces larger than this get a penalty boost 48 fence: 500, 49 close: 500 50 }; 51 52 var ENDVALUES = {linebreakstyle: "after"}; 53 54 55 /**************************************************************************/ 56 57 MML.mbase.Augment({ 58 CHTMLlinebreakPenalty: PENALTY, 59 60 /****************************************************************/ 61 // 62 // Handle breaking an mrow into separate lines 63 // 64 CHTMLmultiline: function (node) { 65 66 // 67 // Find the parent element and mark it as multiline 68 // 69 var parent = this; 70 while (parent.inferred || (parent.parent && parent.parent.type === "mrow" && 71 parent.parent.data.length === 1)) {parent = parent.parent} 72 var isTop = ((parent.type === "math" && parent.Get("display") === "block") || 73 parent.type === "mtd"); 74 parent.isMultiline = true; 75 76 // 77 // Default values for the line-breaking parameters 78 // 79 var VALUES = this.getValues( 80 "linebreak","linebreakstyle","lineleading","linebreakmultchar", 81 "indentalign","indentshift", 82 "indentalignfirst","indentshiftfirst", 83 "indentalignlast","indentshiftlast" 84 ); 85 if (VALUES.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE) 86 VALUES.linebreakstyle = this.Get("infixlinebreakstyle"); 87 VALUES.lineleading = this.CHTMLlength2em(VALUES.lineleading,0.5); 88 89 // 90 // Break the math at its best line breaks 91 // 92 CHTML.BBOX.empty(this.CHTML); 93 var stack = CHTML.addElement(node,"mjx-stack"); 94 var state = { 95 BBOX: this.CHTML, 96 n: 0, Y: 0, 97 scale: (this.CHTML.scale||1), 98 isTop: isTop, 99 values: {}, 100 VALUES: VALUES 101 }, 102 align = this.CHTMLgetAlign(state,{}), 103 shift = this.CHTMLgetShift(state,{},align), 104 start = [], 105 end = { 106 index:[], penalty:PENALTY.nobreak, 107 w:0, W:shift, shift:shift, scanW:shift, 108 nest: 0 109 }, 110 broken = false; 111 112 while (this.CHTMLbetterBreak(end,state) && 113 (end.scanW >= CHTML.linebreakWidth || end.penalty === PENALTY.newline)) { 114 this.CHTMLaddLine(stack,start,end.index,state,end.values,broken); 115 start = end.index.slice(0); broken = true; 116 align = this.CHTMLgetAlign(state,end.values); 117 shift = this.CHTMLgetShift(state,end.values,align); 118 end.W = end.shift = end.scanW = shift; end.penalty = PENALTY.nobreak; 119 } 120 state.isLast = true; 121 this.CHTMLaddLine(stack,start,[],state,ENDVALUES,broken); 122 123 node.style.width = stack.style.width = this.CHTML.pwidth = "100%"; 124 this.CHTML.mwidth = CHTML.Em(this.CHTML.w); 125 this.CHTML.isMultiline = parent.CHTML.isMultiline = true; 126 stack.style.verticalAlign = CHTML.Em(state.d - this.CHTML.d); 127 128 return node; 129 }, 130 131 /****************************************************************/ 132 // 133 // Locate the next linebreak that is better than the current one 134 // 135 CHTMLbetterBreak: function (info,state) { 136 if (this.isToken) return false; // FIXME: handle breaking of token elements 137 if (this.isEmbellished()) { 138 info.embellished = this; 139 return this.CoreMO().CHTMLbetterBreak(info,state); 140 } 141 if (this.linebreakContainer) return false; 142 // 143 // Get the current breakpoint position and other data 144 // 145 var index = info.index.slice(0), i = info.index.shift(), 146 m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false; 147 if (i == null) i = -1; if (!broken) {i++; info.W += info.w; info.w = 0} 148 scanW = info.scanW = info.W; info.nest++; 149 // 150 // Look through the line for breakpoints, 151 // (as long as we are not too far past the breaking width) 152 // 153 while (i < m && info.scanW < 1.33*CHTML.linebreakWidth) { 154 if (this.data[i]) { 155 if (this.data[i].CHTMLbetterBreak(info,state)) { 156 better = true; index = [i].concat(info.index); W = info.W; w = info.w; 157 if (info.penalty === PENALTY.newline) { 158 info.index = index; 159 if (info.nest) {info.nest--} 160 return true; 161 } 162 } 163 scanW = (broken ? info.scanW : this.CHTMLaddWidth(i,info,scanW)); 164 } 165 info.index = []; i++; broken = false; 166 } 167 if (info.nest) {info.nest--} 168 info.index = index; 169 if (better) {info.W = W; info.w = w} 170 return better; 171 }, 172 CHTMLaddWidth: function (i,info,scanW) { 173 if (this.data[i]) { 174 var bbox = this.data[i].CHTML; 175 scanW += bbox.w + (bbox.L||0) + (bbox.R||0); 176 info.W = info.scanW = scanW; info.w = 0; 177 } 178 return scanW; 179 }, 180 181 /****************************************************************/ 182 // 183 // Create a new line and move the required elements into it 184 // Position it using proper alignment and indenting 185 // 186 CHTMLaddLine: function (stack,start,end,state,values,broken) { 187 // 188 // Create a box for the line, with empty BBox 189 // fill it with the proper elements, 190 // and clean up the bbox 191 // 192 var block = CHTML.addElement(stack,"mjx-block",{},[["mjx-box"]]), line = block.firstChild; 193 var bbox = state.bbox = CHTML.BBOX.empty(); 194 state.first = broken; state.last = true; 195 this.CHTMLmoveLine(start,end,line,state,values); 196 bbox.clean(); 197 // 198 // Get the alignment and shift values 199 // 200 var align = this.CHTMLgetAlign(state,values), 201 shift = this.CHTMLgetShift(state,values,align,true); 202 // 203 // Set the Y offset based on previous depth, leading, and current height 204 // 205 var dY = 0; 206 if (state.n > 0) { 207 var LHD = CHTML.FONTDATA.baselineskip; 208 var leading = (state.values.lineleading == null ? state.VALUES : state.values).lineleading * state.scale; 209 var Y = state.Y; 210 state.Y -= Math.max(LHD,state.d + bbox.h + leading); 211 dY = Y - state.Y - state.d - bbox.h; 212 } 213 // 214 // Place the new line 215 // 216 if (shift) line.style.margin = "0 "+CHTML.Em(-shift)+" 0 "+CHTML.Em(shift); 217 if (align !== MML.INDENTALIGN.LEFT) block.style.textAlign = align; 218 if (dY) block.style.paddingTop = CHTML.Em(dY); 219 state.BBOX.combine(bbox,shift,state.Y); 220 // 221 // Save the values needed for the future 222 // 223 state.d = state.bbox.d; state.values = values; state.n++; 224 }, 225 226 /****************************************************************/ 227 // 228 // Get alignment and shift values from the given data 229 // 230 CHTMLgetAlign: function (state,values) { 231 var cur = values, prev = state.values, def = state.VALUES, align; 232 if (state.n === 0) align = cur.indentalignfirst || prev.indentalignfirst || def.indentalignfirst; 233 else if (state.isLast) align = prev.indentalignlast || def.indentalignlast; 234 else align = prev.indentalign || def.indentalign; 235 if (align === MML.INDENTALIGN.INDENTALIGN) align = prev.indentalign || def.indentalign; 236 if (align === MML.INDENTALIGN.AUTO) align = (state.isTop ? CONFIG.displayAlign : MML.INDENTALIGN.LEFT); 237 return align; 238 }, 239 CHTMLgetShift: function (state,values,align,noadjust) { 240 var cur = values, prev = state.values, def = state.VALUES, shift; 241 if (state.n === 0) shift = cur.indentshiftfirst || prev.indentshiftfirst || def.indentshiftfirst; 242 else if (state.isLast) shift = prev.indentshiftlast || def.indentshiftlast; 243 else shift = prev.indentshift || def.indentshift; 244 if (shift === MML.INDENTSHIFT.INDENTSHIFT) shift = prev.indentshift || def.indentshift; 245 if (shift === "auto" || shift === "") shift = "0"; 246 shift = this.CHTMLlength2em(shift,CHTML.cwidth); 247 if (state.isTop && CONFIG.displayIndent !== "0") { 248 var indent = this.CHTMLlength2em(CONFIG.displayIndent,CHTML.cwidth); 249 shift += (align === MML.INDENTALIGN.RIGHT ? -indent : indent); 250 } 251 return (align === MML.INDENTALIGN.RIGHT && !noadjust ? -shift : shift); 252 }, 253 254 /****************************************************************/ 255 // 256 // Move the selected elements into the new line's box, 257 // moving whole items when possible, and parts of ones 258 // that are split by a line break. 259 // 260 CHTMLmoveLine: function (start,end,node,state,values) { 261 var i = start[0], j = end[0]; 262 if (i == null) i = -1; if (j == null) j = this.data.length-1; 263 if (i === j && start.length > 1) { 264 // 265 // If starting and ending in the same element move the subpiece to the new line 266 // 267 this.data[i].CHTMLmoveSlice(start.slice(1),end.slice(1),node,state,values,"marginLeft"); 268 } else { 269 // 270 // Otherwise, move the remainder of the initial item 271 // and any others up to the last one 272 // 273 var last = state.last; state.last = false; 274 while (i < j) { 275 if (this.data[i]) { 276 if (start.length <= 1) this.data[i].CHTMLmoveNode(node,state,values); 277 else this.data[i].CHTMLmoveSlice(start.slice(1),[],node,state,values,"marginLeft"); 278 } 279 i++; state.first = false; start = []; 280 } 281 // 282 // If the last item is complete, move it, 283 // otherwise move the first part of it up to the split 284 // 285 state.last = last; 286 if (this.data[i]) { 287 if (end.length <= 1) this.data[i].CHTMLmoveNode(node,state,values); 288 else this.data[i].CHTMLmoveSlice([],end.slice(1),node,state,values,"marginRight"); 289 } 290 } 291 }, 292 293 /****************************************************************/ 294 // 295 // Split an element and copy the selected items into the new part 296 // 297 CHTMLmoveSlice: function (start,end,node,state,values,margin) { 298 // 299 // Create a new box for the slice of the element 300 // Move the selected portion into the slice 301 // If it is the last slice 302 // Remove the original (now empty) node 303 // Rename the Continue-0 node with the original name (for CHTMLnodeElement) 304 // 305 var slice = this.CHTMLcreateSliceNode(node); 306 this.CHTMLmoveLine(start,end,slice,state,values); 307 if (slice.style[margin]) slice.style[margin] = ""; 308 if (this.CHTML.L) { 309 if (margin !== "marginLeft") state.bbox.w += this.CHTML.L; 310 else slice.className = slice.className.replace(/ MJXc-space\d/,""); 311 } 312 if (this.CHTML.R && margin !== "marginRight") state.bbox.w += this.CHTML.R; 313 if (end.length === 0) { 314 node = this.CHTMLnodeElement(); 315 node.parentNode.removeChild(node); 316 node.nextMathJaxNode.id = node.id; 317 } 318 return slice; 319 }, 320 321 /****************************************************************/ 322 // 323 // Create a new node for an element that is split in two 324 // Clone the original and update its ID. 325 // Link the old node to the new one so we can find it later 326 // 327 CHTMLcreateSliceNode: function (node) { 328 var NODE = this.CHTMLnodeElement(), n = 0; 329 var LAST = NODE; while (LAST.nextMathJaxNode) {LAST = LAST.nextMathJaxNode; n++} 330 var SLICE = NODE.cloneNode(false); LAST.nextMathJaxNode = SLICE; SLICE.nextMathJaxNode = null; 331 SLICE.id += "-MJX-Continue-"+n; 332 return node.appendChild(SLICE); 333 }, 334 335 /****************************************************************/ 336 // 337 // Move an element from its original node to its new location in 338 // a split element or the new line's node 339 // 340 CHTMLmoveNode: function (line,state,values) { 341 // FIXME: handle linebreakstyle === "duplicate" 342 // FIXME: handle linebreakmultchar 343 if (!(state.first || state.last) || 344 (state.first && state.values.linebreakstyle === MML.LINEBREAKSTYLE.BEFORE) || 345 (state.last && values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER)) { 346 // 347 // Move node 348 // 349 var node = this.CHTMLnodeElement(); 350 line.appendChild(node); 351 // 352 // If it is last, remove right margin 353 // If it is first, remove left margin 354 // 355 if (state.last) node.style.marginRight = ""; 356 if (state.first || state.nextIsFirst) { 357 node.style.marginLeft = ""; this.CHTML.L = 0; 358 node.className = node.className.replace(/ MJXc-space\d/,""); 359 } 360 if (state.first && this.CHTML.w === 0) state.nextIsFirst = true; 361 else delete state.nextIsFirst; 362 // 363 // Update bounding box 364 // 365 state.bbox.combine(this.CHTML,state.bbox.w,0); 366 } 367 } 368 }); 369 370 /**************************************************************************/ 371 372 MML.mfenced.Augment({ 373 CHTMLbetterBreak: function (info,state) { 374 // 375 // Get the current breakpoint position and other data 376 // 377 var index = info.index.slice(0), i = info.index.shift(), 378 m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false; 379 if (i == null) i = -1; if (!broken) {i++; info.W += info.w; info.w = 0} 380 scanW = info.scanW = info.W; info.nest++; 381 // 382 // Create indices that include the delimiters and separators 383 // 384 if (!this.dataI) { 385 this.dataI = []; 386 if (this.data.open) this.dataI.push("open"); 387 if (m) this.dataI.push(0); 388 for (var j = 1; j < m; j++) { 389 if (this.data["sep"+j]) this.dataI.push("sep"+j); 390 this.dataI.push(j); 391 } 392 if (this.data.close) this.dataI.push("close"); 393 } 394 m = this.dataI.length; 395 // 396 // Look through the line for breakpoints, including the open, close, and separators 397 // (as long as we are not too far past the breaking width) 398 // 399 while (i < m && info.scanW < 1.33*CHTML.linebreakWidth) { 400 var k = this.dataI[i]; 401 if (this.data[k]) { 402 if (this.data[k].CHTMLbetterBreak(info,state)) { 403 better = true; index = [i].concat(info.index); W = info.W; w = info.w; 404 if (info.penalty === PENALTY.newline) { 405 info.index = index; 406 if (info.nest) info.nest--; 407 return true; 408 } 409 } 410 scanW = (broken ? info.scanW : this.CHTMLaddWidth(i,info,scanW)); 411 } 412 info.index = []; i++; broken = false; 413 } 414 if (info.nest) info.nest--; 415 info.index = index; 416 if (better) {info.W = W; info.w = w} 417 return better; 418 }, 419 420 CHTMLmoveLine: function (start,end,node,state,values) { 421 var i = start[0], j = end[0]; 422 if (i == null) i = -1; if (j == null) j = this.dataI.length-1; 423 if (i === j && start.length > 1) { 424 // 425 // If starting and ending in the same element move the subpiece to the new line 426 // 427 this.data[this.dataI[i]].CHTMLmoveSlice(start.slice(1),end.slice(1),node,state,values,"marginLeft"); 428 } else { 429 // 430 // Otherwise, move the remainder of the initial item 431 // and any others (including open and separators) up to the last one 432 // 433 var last = state.last; state.last = false; var k = this.dataI[i]; 434 while (i < j) { 435 if (this.data[k]) { 436 if (start.length <= 1) this.data[k].CHTMLmoveNode(node,state,values); 437 else this.data[k].CHTMLmoveSlice(start.slice(1),[],node,state,values,"marginLeft"); 438 } 439 i++; k = this.dataI[i]; state.first = false; start = []; 440 } 441 // 442 // If the last item is complete, move it 443 // 444 state.last = last; 445 if (this.data[k]) { 446 if (end.length <= 1) this.data[k].CHTMLmoveNode(node,state,values); 447 else this.data[k].CHTMLmoveSlice([],end.slice(1),node,state,values,"marginRight"); 448 } 449 } 450 } 451 452 }); 453 454 /**************************************************************************/ 455 456 MML.msubsup.Augment({ 457 CHTMLbetterBreak: function (info,state) { 458 if (!this.data[this.base]) {return false} 459 // 460 // Get the current breakpoint position and other data 461 // 462 var index = info.index.slice(0), i = info.index.shift(), 463 W, w, scanW, broken = (info.index.length > 0), better = false; 464 if (!broken) {info.W += info.w; info.w = 0} 465 scanW = info.scanW = info.W; 466 // 467 // Record the width of the base and the super- and subscripts 468 // 469 if (i == null) { 470 this.CHTML.baseW = this.data[this.base].CHTML.w; 471 this.CHTML.dw = this.CHTML.w - this.CHTML.baseW; 472 } 473 // 474 // Check if the base can be broken 475 // 476 if (this.data[this.base].CHTMLbetterBreak(info,state)) { 477 better = true; index = [this.base].concat(info.index); W = info.W; w = info.w; 478 if (info.penalty === PENALTY.newline) better = broken = true; 479 } 480 // 481 // Add in the base if it is unbroken, and add the scripts 482 // 483 if (!broken) this.CHTMLaddWidth(this.base,info,scanW); 484 info.scanW += this.CHTML.dw; info.W = info.scanW; 485 info.index = []; if (better) {info.W = W; info.w = w; info.index = index} 486 return better; 487 }, 488 489 CHTMLmoveLine: function (start,end,node,state,values) { 490 // 491 // Move the proper part of the base 492 // 493 if (this.data[this.base]) { 494 var base = CHTML.addElement(node,"mjx-base"); 495 if (start.length > 1) { 496 this.data[this.base].CHTMLmoveSlice(start.slice(1),end.slice(1),base,state,values,"marginLeft"); 497 } else { 498 if (end.length <= 1) this.data[this.base].CHTMLmoveNode(base,state,values); 499 else this.data[this.base].CHTMLmoveSlice([],end.slice(1),base,state,values,"marginRight"); 500 } 501 } 502 // 503 // If this is the end, check for super and subscripts, and move those 504 // by moving the elements that contains them. Adjust the bounding box 505 // to include the super and subscripts. 506 // 507 if (end.length === 0) { 508 var NODE = this.CHTMLnodeElement(), 509 stack = CHTML.getNode(NODE,"mjx-stack"), 510 sup = CHTML.getNode(NODE,"mjx-sup"), 511 sub = CHTML.getNode(NODE,"mjx-sub"); 512 if (stack) node.appendChild(stack); 513 else if (sup) node.appendChild(sup); 514 else if (sub) node.appendChild(sub); 515 var w = state.bbox.w, bbox; 516 if (sup) { 517 bbox = this.data[this.sup].CHTML; 518 state.bbox.combine(bbox,w,bbox.Y); 519 } 520 if (sub) { 521 bbox = this.data[this.sub].CHTML; 522 state.bbox.combine(bbox,w,bbox.Y); 523 } 524 } 525 } 526 527 }); 528 529 /**************************************************************************/ 530 531 MML.mmultiscripts.Augment({ 532 CHTMLbetterBreak: function (info,state) { 533 if (!this.data[this.base]) return false; 534 // 535 // Get the current breakpoint position and other data 536 // 537 var index = info.index.slice(0); info.index.shift(); 538 var W, w, scanW, broken = (info.index.length > 0), better = false; 539 if (!broken) {info.W += info.w; info.w = 0} 540 info.scanW = info.W; 541 // 542 // Get the bounding boxes and the width of the scripts 543 // 544 var bbox = this.CHTML, base = this.data[this.base].CHTML; 545 var dw = bbox.w - base.w - bbox.X; 546 // 547 // Add in the width of the prescripts 548 // 549 info.scanW += bbox.X; scanW = info.scanW; 550 // 551 // Check if the base can be broken 552 // 553 if (this.data[this.base].CHTMLbetterBreak(info,state)) { 554 better = true; index = [this.base].concat(info.index); W = info.W; w = info.w; 555 if (info.penalty === PENALTY.newline) better = broken = true; 556 } 557 // 558 // Add in the base if it is unbroken, and add the scripts 559 // 560 if (!broken) this.CHTMLaddWidth(this.base,info,scanW); 561 info.scanW += dw; info.W = info.scanW; 562 info.index = []; if (better) {info.W = W; info.w = w; info.index = index} 563 return better; 564 }, 565 566 CHTMLmoveLine: function (start,end,node,state,values) { 567 var NODE = this.CHTMLnodeElement(), BOX = this.CHTMLbbox, w; 568 // 569 // If this is the start, move the prescripts, if any. 570 // 571 if (start.length < 1) { 572 NODE = this.CHTMLnodeElement(); 573 var prestack = CHTML.getNode(NODE,"mjx-prestack"), 574 presup = CHTML.getNode(NODE,"mjx-presup"), 575 presub = CHTML.getNode(NODE,"mjx-presub"); 576 if (prestack) node.appendChild(prestack); 577 else if (presup) node.appendChild(presup); 578 else if (presub) node.appendChild(presub); 579 w = state.bbox.w; 580 if (presup) state.bbox.combine(BOX.presup,w+BOX.presup.X,BOX.presup.Y); 581 if (presub) state.bbox.combine(BOX.presub,w+BOX.presub.X,BOX.presub.Y); 582 } 583 // 584 // Move the proper part of the base 585 // 586 if (this.data[this.base]) { 587 var base = CHTML.addElement(node,"mjx-base"); 588 if (start.length > 1) { 589 this.data[this.base].CHTMLmoveSlice(start.slice(1),end.slice(1),base,state,values,"marginLeft"); 590 } else { 591 if (end.length <= 1) this.data[this.base].CHTMLmoveNode(base,state,values); 592 else this.data[this.base].CHTMLmoveSlice([],end.slice(1),base,state,values,"marginRight"); 593 } 594 } 595 // 596 // If this is the end, check for super and subscripts, and move those 597 // by moving the elements that contains them. Adjust the bounding box 598 // to include the super and subscripts. 599 // 600 if (end.length === 0) { 601 NODE = this.CHTMLnodeElement(); 602 var stack = CHTML.getNode(NODE,"mjx-stack"), 603 sup = CHTML.getNode(NODE,"mjx-sup"), 604 sub = CHTML.getNode(NODE,"mjx-sub"); 605 if (stack) node.appendChild(stack); 606 else if (sup) node.appendChild(sup); 607 else if (sub) node.appendChild(sub); 608 w = state.bbox.w; 609 if (sup) state.bbox.combine(BOX.sup,w,BOX.sup.Y); 610 if (sub) state.bbox.combine(BOX.sub,w,BOX.sub.Y); 611 } 612 } 613 614 }); 615 616 /**************************************************************************/ 617 618 MML.mo.Augment({ 619 // 620 // Override the method for checking line breaks to properly handle <mo> 621 // 622 CHTMLbetterBreak: function (info,state) { 623 if (info.values && info.values.id === this.CHTMLnodeID) return false; 624 var values = this.getValues( 625 "linebreak","linebreakstyle","lineleading","linebreakmultchar", 626 "indentalign","indentshift", 627 "indentalignfirst","indentshiftfirst", 628 "indentalignlast","indentshiftlast", 629 "texClass", "fence" 630 ); 631 if (values.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE) 632 values.linebreakstyle = this.Get("infixlinebreakstyle"); 633 // 634 // Adjust nesting by TeX class (helps output that does not include 635 // mrows for nesting, but can leave these unbalanced. 636 // 637 if (values.texClass === MML.TEXCLASS.OPEN) info.nest++; 638 if (values.texClass === MML.TEXCLASS.CLOSE && info.nest) info.nest--; 639 // 640 // Get the default penalty for this location 641 // 642 var W = info.scanW; delete info.embellished; 643 var w = this.CHTML.w + (this.CHTML.L||0) + (this.CHTML.R||0); 644 if (values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER) {W += w; w = 0} 645 if (W - info.shift === 0 && values.linebreak !== MML.LINEBREAK.NEWLINE) 646 return false; // don't break at zero width (FIXME?) 647 var offset = CHTML.linebreakWidth - W; 648 // Adjust offest for explicit first-line indent and align 649 if (state.n === 0 && (values.indentshiftfirst !== state.VALUES.indentshiftfirst || 650 values.indentalignfirst !== state.VALUES.indentalignfirst)) { 651 var align = this.CHTMLgetAlign(state,values), 652 shift = this.CHTMLgetShift(state,values,align); 653 offset += (info.shift - shift); 654 } 655 // 656 var penalty = Math.floor(offset / CHTML.linebreakWidth * 1000); 657 if (penalty < 0) penalty = PENALTY.toobig - 3*penalty; 658 if (values.fence) penalty += PENALTY.fence; 659 if ((values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER && 660 values.texClass === MML.TEXCLASS.OPEN) || 661 values.texClass === MML.TEXCLASS.CLOSE) penalty += PENALTY.close; 662 penalty += info.nest * PENALTY.nestfactor; 663 // 664 // Get the penalty for this type of break and 665 // use it to modify the default penalty 666 // 667 var linebreak = PENALTY[values.linebreak||MML.LINEBREAK.AUTO]; 668 if (!(linebreak instanceof Array)) { 669 // for breaks past the width, don't modify penalty 670 if (offset >= 0) {penalty = linebreak * info.nest} 671 } else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)} 672 // 673 // If the penalty is no better than the current one, return false 674 // Otherwise save the data for this breakpoint and return true 675 // 676 if (penalty >= info.penalty) return false; 677 info.penalty = penalty; info.values = values; info.W = W; info.w = w; 678 values.lineleading = this.CHTMLlength2em(values.lineleading,state.VALUES.lineleading); 679 values.id = this.CHTMLnodeID; 680 return true; 681 } 682 }); 683 684 /**************************************************************************/ 685 686 MML.mspace.Augment({ 687 // 688 // Override the method for checking line breaks to properly handle <mspace> 689 // 690 CHTMLbetterBreak: function (info,state) { 691 if (info.values && info.values.id === this.CHTMLnodeID) return false; 692 var values = this.getValues("linebreak"); 693 var linebreakValue = values.linebreak; 694 if (!linebreakValue || this.hasDimAttr()) { 695 // The MathML spec says that the linebreak attribute should be ignored 696 // if any dimensional attribute is set. 697 linebreakValue = MML.LINEBREAK.AUTO; 698 } 699 // 700 // Get the default penalty for this location 701 // 702 var W = info.scanW, w = this.CHTML.w + (this.CHTML.L||0) + (this.CHTML.R||0); 703 if (W - info.shift === 0) return false; // don't break at zero width (FIXME?) 704 var offset = CHTML.linebreakWidth - W; 705 // 706 var penalty = Math.floor(offset / CHTML.linebreakWidth * 1000); 707 if (penalty < 0) penalty = PENALTY.toobig - 3*penalty; 708 penalty += info.nest * PENALTY.nestfactor; 709 // 710 // Get the penalty for this type of break and 711 // use it to modify the default penalty 712 // 713 var linebreak = PENALTY[linebreakValue]; 714 if (linebreakValue === MML.LINEBREAK.AUTO && w >= PENALTY.spacelimit && 715 !this.mathbackground && !this.background) 716 linebreak = [(w+PENALTY.spaceoffset)*PENALTY.spacefactor]; 717 if (!(linebreak instanceof Array)) { 718 // for breaks past the width, don't modify penalty 719 if (offset >= 0) {penalty = linebreak * info.nest} 720 } else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)} 721 // 722 // If the penalty is no better than the current one, return false 723 // Otherwise save the data for this breakpoint and return true 724 // 725 if (penalty >= info.penalty) return false; 726 info.penalty = penalty; info.values = values; info.W = W; info.w = w; 727 values.lineleading = state.VALUES.lineleading; 728 values.linebreakstyle = "before"; values.id = this.CHTMLnodeID; 729 return true; 730 } 731 }); 732 733 // 734 // Hook into the mathchoice extension 735 // 736 MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () { 737 MML.TeXmathchoice.Augment({ 738 CHTMLbetterBreak: function (info,state) { 739 return this.Core().CHTMLbetterBreak(info,state); 740 }, 741 CHTMLmoveLine: function (start,end,node,state,values) { 742 return this.Core().CHTMLmoveSlice(start,end,node,state,values); 743 } 744 }); 745 }); 746 747 // 748 // Have maction process only the selected item 749 // 750 MML.maction.Augment({ 751 CHTMLbetterBreak: function (info,state) { 752 return this.Core().CHTMLbetterBreak(info,state); 753 }, 754 CHTMLmoveLine: function (start,end,node,state,values) { 755 return this.Core().CHTMLmoveSlice(start,end,node,state,values); 756 } 757 }); 758 759 // 760 // Have semantics only do the first element 761 // (FIXME: do we need to do anything special about annotation-xml?) 762 // 763 MML.semantics.Augment({ 764 CHTMLbetterBreak: function (info,state) { 765 return (this.data[0] ? this.data[0].CHTMLbetterBreak(info,state) : false); 766 }, 767 CHTMLmoveLine: function (start,end,node,state,values) { 768 return (this.data[0] ? this.data[0].CHTMLmoveSlice(start,end,node,state,values) : null); 769 } 770 }); 771 772 /**************************************************************************/ 773 774 MathJax.Hub.Startup.signal.Post("CommonHTML multiline Ready"); 775 MathJax.Ajax.loadComplete(CHTML.autoloadDir+"/multiline.js"); 776 777 });