mmultiscripts.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2018-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Implements the CHTMLmmultiscripts wrapper for the MmlMmultiscripts object
  19. *
  20. * @author dpvc@mathjax.org (Davide Cervone)
  21. */
  22. import {CHTMLWrapper, Constructor} from '../Wrapper.js';
  23. import {CHTMLmsubsup} from './msubsup.js';
  24. import {CommonMmultiscriptsMixin} from '../../common/Wrappers/mmultiscripts.js';
  25. import {MmlMmultiscripts} from '../../../core/MmlTree/MmlNodes/mmultiscripts.js';
  26. import {BBox} from '../../../util/BBox.js';
  27. import {StyleList} from '../../../util/StyleList.js';
  28. import {split} from '../../../util/string.js';
  29. /*****************************************************************/
  30. /**
  31. * The CHTMLmmultiscripts wrapper for the MmlMmultiscripts object
  32. *
  33. * @template N The HTMLElement node class
  34. * @template T The Text node class
  35. * @template D The Document class
  36. */
  37. // @ts-ignore
  38. export class CHTMLmmultiscripts<N, T, D> extends
  39. CommonMmultiscriptsMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLmsubsup<any, any, any>>>(CHTMLmsubsup) {
  40. /**
  41. * The mmultiscripts wrapper
  42. */
  43. public static kind = MmlMmultiscripts.prototype.kind;
  44. /**
  45. * @override
  46. */
  47. public static styles: StyleList = {
  48. 'mjx-prescripts': {
  49. display: 'inline-table',
  50. 'padding-left': '.05em' // scriptspace
  51. },
  52. 'mjx-scripts': {
  53. display: 'inline-table',
  54. 'padding-right': '.05em' // scriptspace
  55. },
  56. 'mjx-prescripts > mjx-row > mjx-cell': {
  57. 'text-align': 'right'
  58. },
  59. '[script-align="left"] > mjx-row > mjx-cell': {
  60. 'text-align': 'left'
  61. },
  62. '[script-align="center"] > mjx-row > mjx-cell': {
  63. 'text-align': 'center'
  64. },
  65. '[script-align="right"] > mjx-row > mjx-cell': {
  66. 'text-align': 'right'
  67. }
  68. };
  69. /*************************************************************/
  70. /**
  71. * @override
  72. */
  73. public toCHTML(parent: N) {
  74. const chtml = this.standardCHTMLnode(parent);
  75. const data = this.scriptData;
  76. //
  77. // Get the alignment for the scripts
  78. //
  79. const scriptalign = this.node.getProperty('scriptalign') || 'right left';
  80. const [preAlign, postAlign] = split(scriptalign + ' ' + scriptalign);
  81. //
  82. // Combine the bounding boxes of the pre- and post-scripts,
  83. // and get the resulting baseline offsets
  84. //
  85. const sub = this.combinePrePost(data.sub, data.psub);
  86. const sup = this.combinePrePost(data.sup, data.psup);
  87. const [u, v] = this.getUVQ(sub, sup);
  88. //
  89. // Place the pre-scripts, then the base, then the post-scripts
  90. //
  91. if (data.numPrescripts) {
  92. const scripts = this.addScripts(u, -v, true, data.psub, data.psup, this.firstPrescript, data.numPrescripts);
  93. preAlign !== 'right' && this.adaptor.setAttribute(scripts, 'script-align', preAlign);
  94. }
  95. this.childNodes[0].toCHTML(chtml);
  96. if (data.numScripts) {
  97. const scripts = this.addScripts(u, -v, false, data.sub, data.sup, 1, data.numScripts);
  98. postAlign !== 'left' && this.adaptor.setAttribute(scripts, 'script-align', postAlign);
  99. }
  100. }
  101. /**
  102. * Create a table with the super and subscripts properly separated and aligned.
  103. *
  104. * @param {number} u The baseline offset for the superscripts
  105. * @param {number} v The baseline offset for the subscripts
  106. * @param {boolean} isPre True for prescripts, false for scripts
  107. * @param {BBox} sub The subscript bounding box
  108. * @param {BBox} sup The superscript bounding box
  109. * @param {number} i The starting index for the scripts
  110. * @param {number} n The number of sub/super-scripts
  111. * @return {N} The script table for these scripts
  112. */
  113. protected addScripts(u: number, v: number, isPre: boolean, sub: BBox, sup: BBox, i: number, n: number): N {
  114. const adaptor = this.adaptor;
  115. const q = (u - sup.d) + (v - sub.h); // separation of scripts
  116. const U = (u < 0 && v === 0 ? sub.h + u : u); // vertical offset of table
  117. const rowdef = (q > 0 ? {style: {height: this.em(q)}} : {});
  118. const tabledef = (U ? {style: {'vertical-align': this.em(U)}} : {});
  119. const supRow = this.html('mjx-row');
  120. const sepRow = this.html('mjx-row', rowdef);
  121. const subRow = this.html('mjx-row');
  122. const name = 'mjx-' + (isPre ? 'pre' : '') + 'scripts';
  123. let m = i + 2 * n;
  124. while (i < m) {
  125. this.childNodes[i++].toCHTML(adaptor.append(subRow, this.html('mjx-cell')) as N);
  126. this.childNodes[i++].toCHTML(adaptor.append(supRow, this.html('mjx-cell')) as N);
  127. }
  128. return adaptor.append(this.chtml, this.html(name, tabledef, [supRow, sepRow, subRow]));
  129. }
  130. }