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