mfrac.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2017-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 CHTMLmfrac wrapper for the MmlMfrac object
  19. *
  20. * @author dpvc@mathjax.org (Davide Cervone)
  21. */
  22. import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
  23. import {CommonMfracMixin} from '../../common/Wrappers/mfrac.js';
  24. import {MmlMfrac} from '../../../core/MmlTree/MmlNodes/mfrac.js';
  25. import {CHTMLmo} from './mo.js';
  26. import {StyleList} from '../../../util/StyleList.js';
  27. import {OptionList} from '../../../util/Options.js';
  28. /*****************************************************************/
  29. /**
  30. * The CHTMLmfrac wrapper for the MmlMfrac object
  31. *
  32. * @template N The HTMLElement node class
  33. * @template T The Text node class
  34. * @template D The Document class
  35. */
  36. export class CHTMLmfrac<N, T, D> extends CommonMfracMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
  37. /**
  38. * The mfrac wrapper
  39. */
  40. public static kind = MmlMfrac.prototype.kind;
  41. /**
  42. * @override
  43. */
  44. public static styles: StyleList = {
  45. 'mjx-frac': {
  46. display: 'inline-block',
  47. 'vertical-align': '0.17em', // axis_height - 1.5 * rule_thickness
  48. padding: '0 .22em' // nulldelimiterspace + .1 (for line's -.1em margin)
  49. },
  50. 'mjx-frac[type="d"]': {
  51. 'vertical-align': '.04em' // axis_height - 3.5 * rule_thickness
  52. },
  53. 'mjx-frac[delims]': {
  54. padding: '0 .1em' // .1 (for line's -.1em margin)
  55. },
  56. 'mjx-frac[atop]': {
  57. padding: '0 .12em' // nulldelimiterspace
  58. },
  59. 'mjx-frac[atop][delims]': {
  60. padding: '0'
  61. },
  62. 'mjx-dtable': {
  63. display: 'inline-table',
  64. width: '100%'
  65. },
  66. 'mjx-dtable > *': {
  67. 'font-size': '2000%'
  68. },
  69. 'mjx-dbox': {
  70. display: 'block',
  71. 'font-size': '5%'
  72. },
  73. 'mjx-num': {
  74. display: 'block',
  75. 'text-align': 'center'
  76. },
  77. 'mjx-den': {
  78. display: 'block',
  79. 'text-align': 'center'
  80. },
  81. 'mjx-mfrac[bevelled] > mjx-num': {
  82. display: 'inline-block'
  83. },
  84. 'mjx-mfrac[bevelled] > mjx-den': {
  85. display: 'inline-block'
  86. },
  87. 'mjx-den[align="right"], mjx-num[align="right"]': {
  88. 'text-align': 'right'
  89. },
  90. 'mjx-den[align="left"], mjx-num[align="left"]': {
  91. 'text-align': 'left'
  92. },
  93. 'mjx-nstrut': {
  94. display: 'inline-block',
  95. height: '.054em', // num2 - a - 1.5t
  96. width: 0,
  97. 'vertical-align': '-.054em' // ditto
  98. },
  99. 'mjx-nstrut[type="d"]': {
  100. height: '.217em', // num1 - a - 3.5t
  101. 'vertical-align': '-.217em', // ditto
  102. },
  103. 'mjx-dstrut': {
  104. display: 'inline-block',
  105. height: '.505em', // denom2 + a - 1.5t
  106. width: 0
  107. },
  108. 'mjx-dstrut[type="d"]': {
  109. height: '.726em', // denom1 + a - 3.5t
  110. },
  111. 'mjx-line': {
  112. display: 'block',
  113. 'box-sizing': 'border-box',
  114. 'min-height': '1px',
  115. height: '.06em', // t = rule_thickness
  116. 'border-top': '.06em solid', // t
  117. margin: '.06em -.1em', // t
  118. overflow: 'hidden'
  119. },
  120. 'mjx-line[type="d"]': {
  121. margin: '.18em -.1em' // 3t
  122. }
  123. };
  124. /**
  125. * An mop element to use for bevelled fractions
  126. */
  127. public bevel: CHTMLmo<N, T, D>;
  128. /************************************************/
  129. /**
  130. * @override
  131. */
  132. public toCHTML(parent: N) {
  133. this.standardCHTMLnode(parent);
  134. const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
  135. const display = this.isDisplay();
  136. if (bevelled) {
  137. this.makeBevelled(display);
  138. } else {
  139. const thickness = this.length2em(String(linethickness), .06);
  140. if (thickness === 0) {
  141. this.makeAtop(display);
  142. } else {
  143. this.makeFraction(display, thickness);
  144. }
  145. }
  146. }
  147. /************************************************/
  148. /**
  149. * @param {boolean} display True when fraction is in display mode
  150. * @param {number} t The rule line thickness
  151. */
  152. protected makeFraction(display: boolean, t: number) {
  153. const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
  154. const withDelims = this.node.getProperty('withDelims');
  155. //
  156. // Attributes to set for the different elements making up the fraction
  157. //
  158. const attr = (display ? {type: 'd'} : {}) as OptionList;
  159. const fattr = (withDelims ? {...attr, delims: 'true'} : {...attr}) as OptionList;
  160. const nattr = (numalign !== 'center' ? {align: numalign} : {}) as OptionList;
  161. const dattr = (denomalign !== 'center' ? {align: denomalign} : {}) as OptionList;
  162. const dsattr = {...attr}, nsattr = {...attr};
  163. //
  164. // Set the styles to handle the linethickness, if needed
  165. //
  166. const tex = this.font.params;
  167. if (t !== .06) {
  168. const a = tex.axis_height;
  169. const tEm = this.em(t);
  170. const {T, u, v} = this.getTUV(display, t);
  171. const m = (display ? this.em(3 * t) : tEm) + ' -.1em';
  172. attr.style = {height: tEm, 'border-top': tEm + ' solid', margin: m};
  173. const nh = this.em(Math.max(0, u));
  174. nsattr.style = {height: nh, 'vertical-align': '-' + nh};
  175. dsattr.style = {height: this.em(Math.max(0, v))};
  176. fattr.style = {'vertical-align': this.em(a - T)};
  177. }
  178. //
  179. // Create the DOM tree
  180. //
  181. let num, den;
  182. this.adaptor.append(this.chtml, this.html('mjx-frac', fattr, [
  183. num = this.html('mjx-num', nattr, [this.html('mjx-nstrut', nsattr)]),
  184. this.html('mjx-dbox', {}, [
  185. this.html('mjx-dtable', {}, [
  186. this.html('mjx-line', attr),
  187. this.html('mjx-row', {}, [
  188. den = this.html('mjx-den', dattr, [this.html('mjx-dstrut', dsattr)])
  189. ])
  190. ])
  191. ])
  192. ]));
  193. this.childNodes[0].toCHTML(num);
  194. this.childNodes[1].toCHTML(den);
  195. }
  196. /************************************************/
  197. /**
  198. * @param {boolean} display True when fraction is in display mode
  199. */
  200. protected makeAtop(display: boolean) {
  201. const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
  202. const withDelims = this.node.getProperty('withDelims');
  203. //
  204. // Attributes to set for the different elements making up the fraction
  205. //
  206. const attr = (display ? {type: 'd', atop: true} : {atop: true}) as OptionList;
  207. const fattr = (withDelims ? {...attr, delims: true} : {...attr}) as OptionList;
  208. const nattr = (numalign !== 'center' ? {align: numalign} : {}) as OptionList;
  209. const dattr = (denomalign !== 'center' ? {align: denomalign} : {}) as OptionList;
  210. //
  211. // Determine sparation and positioning
  212. //
  213. const {v, q} = this.getUVQ(display);
  214. nattr.style = {'padding-bottom': this.em(q)};
  215. fattr.style = {'vertical-align': this.em(-v)};
  216. //
  217. // Create the DOM tree
  218. //
  219. let num, den;
  220. this.adaptor.append(this.chtml, this.html('mjx-frac', fattr, [
  221. num = this.html('mjx-num', nattr),
  222. den = this.html('mjx-den', dattr)
  223. ]));
  224. this.childNodes[0].toCHTML(num);
  225. this.childNodes[1].toCHTML(den);
  226. }
  227. /************************************************/
  228. /**
  229. * @param {boolean} display True when fraction is in display mode
  230. */
  231. protected makeBevelled(display: boolean) {
  232. const adaptor = this.adaptor;
  233. //
  234. // Create HTML tree
  235. //
  236. adaptor.setAttribute(this.chtml, 'bevelled', 'ture');
  237. const num = adaptor.append(this.chtml, this.html('mjx-num'));
  238. this.childNodes[0].toCHTML(num);
  239. this.bevel.toCHTML(this.chtml);
  240. const den = adaptor.append(this.chtml, this.html('mjx-den'));
  241. this.childNodes[1].toCHTML(den);
  242. //
  243. // Place the parts
  244. //
  245. const {u, v, delta, nbox, dbox} = this.getBevelData(display);
  246. if (u) {
  247. adaptor.setStyle(num, 'verticalAlign', this.em(u / nbox.scale));
  248. }
  249. if (v) {
  250. adaptor.setStyle(den, 'verticalAlign', this.em(v / dbox.scale));
  251. }
  252. const dx = this.em(-delta / 2);
  253. adaptor.setStyle(this.bevel.chtml, 'marginLeft', dx);
  254. adaptor.setStyle(this.bevel.chtml, 'marginRight', dx);
  255. }
  256. }