add initial marp implementation with sample content and build configuration
This commit is contained in:
+308
@@ -0,0 +1,308 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTML OutputJax object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CommonOutputJax} from './common/OutputJax.js';
|
||||
import {CommonWrapper} from './common/Wrapper.js';
|
||||
import {StyleList} from '../util/Styles.js';
|
||||
import {StyleList as CssStyleList, CssStyles} from '../util/StyleList.js';
|
||||
import {OptionList} from '../util/Options.js';
|
||||
import {MathDocument} from '../core/MathDocument.js';
|
||||
import {MathItem} from '../core/MathItem.js';
|
||||
import {MmlNode} from '../core/MmlTree/MmlNode.js';
|
||||
import {CHTMLWrapper} from './chtml/Wrapper.js';
|
||||
import {CHTMLWrapperFactory} from './chtml/WrapperFactory.js';
|
||||
import {CHTMLFontData} from './chtml/FontData.js';
|
||||
import {Usage} from './chtml/Usage.js';
|
||||
import {TeXFont} from './chtml/fonts/tex.js';
|
||||
import * as LENGTHS from '../util/lengths.js';
|
||||
import {unicodeChars} from '../util/string.js';
|
||||
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* Implements the CHTML class (extends AbstractOutputJax)
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTML<N, T, D> extends
|
||||
CommonOutputJax<N, T, D, CHTMLWrapper<N, T, D>, CHTMLWrapperFactory<N, T, D>, CHTMLFontData, typeof CHTMLFontData> {
|
||||
|
||||
/**
|
||||
* The name of this output jax
|
||||
*/
|
||||
public static NAME: string = 'CHTML';
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static OPTIONS: OptionList = {
|
||||
...CommonOutputJax.OPTIONS,
|
||||
adaptiveCSS: true, // true means only produce CSS that is used in the processed equations
|
||||
matchFontHeight: true, // true to match ex-height of surrounding font
|
||||
};
|
||||
|
||||
/**
|
||||
* The default styles for CommonHTML
|
||||
*/
|
||||
public static commonStyles: CssStyleList = {
|
||||
'mjx-container[jax="CHTML"]': {'line-height': 0},
|
||||
|
||||
'mjx-container [space="1"]': {'margin-left': '.111em'},
|
||||
'mjx-container [space="2"]': {'margin-left': '.167em'},
|
||||
'mjx-container [space="3"]': {'margin-left': '.222em'},
|
||||
'mjx-container [space="4"]': {'margin-left': '.278em'},
|
||||
'mjx-container [space="5"]': {'margin-left': '.333em'},
|
||||
|
||||
'mjx-container [rspace="1"]': {'margin-right': '.111em'},
|
||||
'mjx-container [rspace="2"]': {'margin-right': '.167em'},
|
||||
'mjx-container [rspace="3"]': {'margin-right': '.222em'},
|
||||
'mjx-container [rspace="4"]': {'margin-right': '.278em'},
|
||||
'mjx-container [rspace="5"]': {'margin-right': '.333em'},
|
||||
|
||||
'mjx-container [size="s"]' : {'font-size': '70.7%'},
|
||||
'mjx-container [size="ss"]': {'font-size': '50%'},
|
||||
'mjx-container [size="Tn"]': {'font-size': '60%'},
|
||||
'mjx-container [size="sm"]': {'font-size': '85%'},
|
||||
'mjx-container [size="lg"]': {'font-size': '120%'},
|
||||
'mjx-container [size="Lg"]': {'font-size': '144%'},
|
||||
'mjx-container [size="LG"]': {'font-size': '173%'},
|
||||
'mjx-container [size="hg"]': {'font-size': '207%'},
|
||||
'mjx-container [size="HG"]': {'font-size': '249%'},
|
||||
|
||||
'mjx-container [width="full"]': {width: '100%'},
|
||||
|
||||
'mjx-box': {display: 'inline-block'},
|
||||
'mjx-block': {display: 'block'},
|
||||
'mjx-itable': {display: 'inline-table'},
|
||||
'mjx-row': {display: 'table-row'},
|
||||
'mjx-row > *': {display: 'table-cell'},
|
||||
|
||||
//
|
||||
// These don't have Wrapper subclasses, so add their styles here
|
||||
//
|
||||
'mjx-mtext': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
'mjx-mstyle': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
'mjx-merror': {
|
||||
display: 'inline-block',
|
||||
color: 'red',
|
||||
'background-color': 'yellow'
|
||||
},
|
||||
'mjx-mphantom': {
|
||||
visibility: 'hidden'
|
||||
},
|
||||
|
||||
//
|
||||
// WebKit-specific CSS to handle bug with clipped characters.
|
||||
// (test found at https://browserstrangeness.bitbucket.io/css_hacks.html#safari)
|
||||
//
|
||||
'_::-webkit-full-page-media, _:future, :root mjx-container': {
|
||||
'will-change': 'opacity'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The ID for the stylesheet element for the styles for the SVG output
|
||||
*/
|
||||
public static STYLESHEETID = 'MJX-CHTML-styles';
|
||||
|
||||
/**
|
||||
* Used to store the CHTMLWrapper factory.
|
||||
*/
|
||||
public factory: CHTMLWrapperFactory<N, T, D>;
|
||||
|
||||
/**
|
||||
* The usage information for the wrapper classes
|
||||
*/
|
||||
public wrapperUsage: Usage<string>;
|
||||
|
||||
/**
|
||||
* The CHTML stylesheet, once it is constructed
|
||||
*/
|
||||
public chtmlStyles: N = null;
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @constructor
|
||||
*/
|
||||
constructor(options: OptionList = null) {
|
||||
super(options, CHTMLWrapperFactory as any, TeXFont);
|
||||
this.font.adaptiveCSS(this.options.adaptiveCSS);
|
||||
this.wrapperUsage = new Usage<string>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public escaped(math: MathItem<N, T, D>, html: MathDocument<N, T, D>) {
|
||||
this.setDocument(html);
|
||||
return this.html('span', {}, [this.text(math.math)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public styleSheet(html: MathDocument<N, T, D>) {
|
||||
if (this.chtmlStyles) {
|
||||
if (this.options.adaptiveCSS) {
|
||||
//
|
||||
// Update the style sheet rules
|
||||
//
|
||||
const styles = new CssStyles();
|
||||
this.addWrapperStyles(styles);
|
||||
this.updateFontStyles(styles);
|
||||
this.adaptor.insertRules(this.chtmlStyles, styles.getStyleRules());
|
||||
}
|
||||
return this.chtmlStyles; // stylesheet is already added to the document
|
||||
}
|
||||
const sheet = this.chtmlStyles = super.styleSheet(html);
|
||||
this.adaptor.setAttribute(sheet, 'id', CHTML.STYLESHEETID);
|
||||
this.wrapperUsage.update();
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CssStyles} styles The styles to update with newly used character styles
|
||||
*/
|
||||
protected updateFontStyles(styles: CssStyles) {
|
||||
styles.addStyles(this.font.updateStyles({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected addWrapperStyles(styles: CssStyles) {
|
||||
if (!this.options.adaptiveCSS) {
|
||||
super.addWrapperStyles(styles);
|
||||
return;
|
||||
}
|
||||
for (const kind of this.wrapperUsage.update()) {
|
||||
const wrapper = this.factory.getNodeClass(kind) as any as typeof CommonWrapper;
|
||||
wrapper && this.addClassStyles(wrapper, styles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected addClassStyles(wrapper: typeof CommonWrapper, styles: CssStyles) {
|
||||
const CLASS = wrapper as typeof CHTMLWrapper;
|
||||
if (CLASS.autoStyle && CLASS.kind !== 'unknown') {
|
||||
styles.addStyles({
|
||||
['mjx-' + CLASS.kind]: {
|
||||
display: 'inline-block',
|
||||
'text-align': 'left'
|
||||
}
|
||||
});
|
||||
}
|
||||
this.wrapperUsage.add(CLASS.kind);
|
||||
super.addClassStyles(wrapper, styles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MmlNode} math The MML node whose HTML is to be produced
|
||||
* @param {N} parent The HTML node to contain the HTML
|
||||
*/
|
||||
protected processMath(math: MmlNode, parent: N) {
|
||||
this.factory.wrap(math).toCHTML(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of which items need their styles to be output
|
||||
*/
|
||||
public clearCache() {
|
||||
this.cssStyles.clear();
|
||||
this.font.clearCache();
|
||||
this.wrapperUsage.clear();
|
||||
this.chtmlStyles = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public reset() {
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public unknownText(text: string, variant: string, width: number = null) {
|
||||
const styles: StyleList = {};
|
||||
const scale = 100 / this.math.metrics.scale;
|
||||
if (scale !== 100) {
|
||||
styles['font-size'] = this.fixed(scale, 1) + '%';
|
||||
styles.padding = LENGTHS.em(75 / scale) + ' 0 ' + LENGTHS.em(20 / scale) + ' 0';
|
||||
}
|
||||
if (variant !== '-explicitFont') {
|
||||
const c = unicodeChars(text);
|
||||
if (c.length !== 1 || c[0] < 0x1D400 || c[0] > 0x1D7FF) {
|
||||
this.cssFontStyles(this.font.getCssFont(variant), styles);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Work around Safari bug with the MJXZERO font by forcing the width.
|
||||
// (If MJXZERO can be made to work with Safari, then remove width parameter
|
||||
// and call to getBBox().w in TextNode.ts)
|
||||
//
|
||||
if (width !== null) {
|
||||
const metrics = this.math.metrics;
|
||||
styles.width = Math.round(width * metrics.em * metrics.scale) + 'px';
|
||||
}
|
||||
//
|
||||
return this.html('mjx-utext', {variant: variant, style: styles}, [this.text(text)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure the width of a text element by placing it in the page
|
||||
* and looking up its size (fake the height and depth, since we can't measure that)
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
|
||||
public measureTextNode(textNode: N) {
|
||||
const adaptor = this.adaptor;
|
||||
const text = adaptor.clone(textNode);
|
||||
//
|
||||
// Work arround Safari bug with the MJXZERO font.
|
||||
//
|
||||
adaptor.setStyle(text, 'font-family', adaptor.getStyle(text, 'font-family').replace(/MJXZERO, /g, ''));
|
||||
//
|
||||
const style = {position: 'absolute', 'white-space': 'nowrap'};
|
||||
const node = this.html('mjx-measure-text', {style}, [text]);
|
||||
adaptor.append(adaptor.parent(this.math.start.node), this.container);
|
||||
adaptor.append(this.container, node);
|
||||
let w = adaptor.nodeSize(text, this.math.metrics.em)[0] / this.math.metrics.scale;
|
||||
adaptor.remove(this.container);
|
||||
adaptor.remove(node);
|
||||
return {w: w, h: .75, d: .2};
|
||||
}
|
||||
|
||||
}
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLFontData class and AddCSS() function.
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CharMap, CharOptions, CharData, VariantData, DelimiterData, FontData, DIRECTION} from '../common/FontData.js';
|
||||
import {Usage} from './Usage.js';
|
||||
import {StringMap} from './Wrapper.js';
|
||||
import {StyleList, StyleData} from '../../util/StyleList.js';
|
||||
import {em} from '../../util/lengths.js';
|
||||
|
||||
export * from '../common/FontData.js';
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Add the extra data needed for CharOptions in CHTML
|
||||
*/
|
||||
export interface CHTMLCharOptions extends CharOptions {
|
||||
c?: string; // the content value (for css)
|
||||
f?: string; // the font postfix (for css)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthands for CHTML char maps and char data
|
||||
*/
|
||||
export type CHTMLCharMap = CharMap<CHTMLCharOptions>;
|
||||
export type CHTMLCharData = CharData<CHTMLCharOptions>;
|
||||
|
||||
/**
|
||||
* The extra data needed for a Variant in CHTML output
|
||||
*/
|
||||
export interface CHTMLVariantData extends VariantData<CHTMLCharOptions> {
|
||||
classes?: string; // the classes to use for this variant
|
||||
letter: string; // the font letter(s) for the default font for this variant
|
||||
}
|
||||
|
||||
/**
|
||||
* The extra data needed for a Delimiter in CHTML output
|
||||
*/
|
||||
export interface CHTMLDelimiterData extends DelimiterData {
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* The CHTML FontData class
|
||||
*/
|
||||
export class CHTMLFontData extends FontData<CHTMLCharOptions, CHTMLVariantData, CHTMLDelimiterData> {
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
public static OPTIONS = {
|
||||
...FontData.OPTIONS,
|
||||
fontURL: 'js/output/chtml/fonts/tex-woff-v2'
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static JAX = 'CHTML';
|
||||
|
||||
/**
|
||||
* The default class names to use for each variant
|
||||
*/
|
||||
protected static defaultVariantClasses: StringMap = {};
|
||||
|
||||
/**
|
||||
* The default font letter to use for each variant
|
||||
*/
|
||||
protected static defaultVariantLetters: StringMap = {};
|
||||
|
||||
/**
|
||||
* The CSS styles needed for this font.
|
||||
*/
|
||||
protected static defaultStyles = {
|
||||
'mjx-c::before': {
|
||||
display: 'block',
|
||||
width: 0
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The default @font-face declarations with %%URL%% where the font path should go
|
||||
*/
|
||||
protected static defaultFonts = {
|
||||
'@font-face /* 0 */': {
|
||||
'font-family': 'MJXZERO',
|
||||
src: 'url("%%URL%%/MathJax_Zero.woff") format("woff")'
|
||||
}
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/**
|
||||
* Data about the characters used (for adaptive CSS)
|
||||
*/
|
||||
public charUsage: Usage<[string, number]> = new Usage<[string, number]>();
|
||||
|
||||
/**
|
||||
* Data about the delimiters used (for adpative CSS)
|
||||
*/
|
||||
public delimUsage: Usage<number> = new Usage<number>();
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static charOptions(font: CHTMLCharMap, n: number) {
|
||||
return super.charOptions(font, n) as CHTMLCharOptions;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} adapt Whether to use adaptive CSS or not
|
||||
*/
|
||||
public adaptiveCSS(adapt: boolean) {
|
||||
this.options.adaptiveCSS = adapt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of which characters have been used
|
||||
*/
|
||||
public clearCache() {
|
||||
if (this.options.adaptiveCSS) {
|
||||
this.charUsage.clear();
|
||||
this.delimUsage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public createVariant(name: string, inherit: string = null, link: string = null) {
|
||||
super.createVariant(name, inherit, link);
|
||||
let CLASS = (this.constructor as CHTMLFontDataClass);
|
||||
this.variant[name].classes = CLASS.defaultVariantClasses[name];
|
||||
this.variant[name].letter = CLASS.defaultVariantLetters[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public defineChars(name: string, chars: CHTMLCharMap) {
|
||||
super.defineChars(name, chars);
|
||||
const letter = this.variant[name].letter;
|
||||
for (const n of Object.keys(chars)) {
|
||||
const options = CHTMLFontData.charOptions(chars, parseInt(n));
|
||||
if (options.f === undefined) {
|
||||
options.f = letter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/**
|
||||
* @return {StyleList} The (computed) styles for this font
|
||||
*/
|
||||
get styles(): StyleList {
|
||||
const CLASS = this.constructor as typeof CHTMLFontData;
|
||||
//
|
||||
// Include the default styles
|
||||
//
|
||||
const styles: StyleList = {...CLASS.defaultStyles};
|
||||
//
|
||||
// Add fonts with proper URL
|
||||
//
|
||||
this.addFontURLs(styles, CLASS.defaultFonts, this.options.fontURL);
|
||||
//
|
||||
// Add the styles for delimiters and characters
|
||||
//
|
||||
if (this.options.adaptiveCSS) {
|
||||
this.updateStyles(styles);
|
||||
} else {
|
||||
this.allStyles(styles);
|
||||
}
|
||||
//
|
||||
// Return the final style sheet
|
||||
//
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the styles for any newly used characters and delimiters
|
||||
*
|
||||
* @param {StyleList} styles The style list to add delimiter styles to.
|
||||
* @return {StyleList} The modified style list.
|
||||
*/
|
||||
public updateStyles(styles: StyleList): StyleList {
|
||||
for (const N of this.delimUsage.update()) {
|
||||
this.addDelimiterStyles(styles, N, this.delimiters[N]);
|
||||
}
|
||||
for (const [name, N] of this.charUsage.update()) {
|
||||
const variant = this.variant[name];
|
||||
this.addCharStyles(styles, variant.letter, N, variant.chars[N]);
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style list to add characters to
|
||||
*/
|
||||
protected allStyles(styles: StyleList) {
|
||||
//
|
||||
// Create styles needed for the delimiters
|
||||
//
|
||||
for (const n of Object.keys(this.delimiters)) {
|
||||
const N = parseInt(n);
|
||||
this.addDelimiterStyles(styles, N, this.delimiters[N]);
|
||||
}
|
||||
//
|
||||
// Add style for all character data
|
||||
//
|
||||
for (const name of Object.keys(this.variant)) {
|
||||
const variant = this.variant[name];
|
||||
const vletter = variant.letter;
|
||||
for (const n of Object.keys(variant.chars)) {
|
||||
const N = parseInt(n);
|
||||
const char = variant.chars[N];
|
||||
if ((char[3] || {}).smp) continue;
|
||||
if (char.length < 4) {
|
||||
(char as CHTMLCharData)[3] = {};
|
||||
}
|
||||
this.addCharStyles(styles, vletter, N, char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {StyleList} fonts The default font-face directives with %%URL%% where the url should go
|
||||
* @param {string} url The actual URL to insert into the src strings
|
||||
*/
|
||||
protected addFontURLs(styles: StyleList, fonts: StyleList, url: string) {
|
||||
for (const name of Object.keys(fonts)) {
|
||||
const font = {...fonts[name]};
|
||||
font.src = (font.src as string).replace(/%%URL%%/, url);
|
||||
styles[name] = font;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************/
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {number} n The unicode character number of the delimiter
|
||||
* @param {CHTMLDelimiterData} data The data for the delimiter whose CSS is to be added
|
||||
*/
|
||||
protected addDelimiterStyles(styles: StyleList, n: number, data: CHTMLDelimiterData) {
|
||||
let c = this.charSelector(n);
|
||||
if (data.c && data.c !== n) {
|
||||
c = this.charSelector(data.c);
|
||||
styles['.mjx-stretched mjx-c' + c + '::before'] = {
|
||||
content: this.charContent(data.c)
|
||||
};
|
||||
}
|
||||
if (!data.stretch) return;
|
||||
if (data.dir === DIRECTION.Vertical) {
|
||||
this.addDelimiterVStyles(styles, c, data);
|
||||
} else {
|
||||
this.addDelimiterHStyles(styles, c, data);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************/
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {string} c The delimiter character string
|
||||
* @param {CHTMLDelimiterData} data The data for the delimiter whose CSS is to be added
|
||||
*/
|
||||
protected addDelimiterVStyles(styles: StyleList, c: string, data: CHTMLDelimiterData) {
|
||||
const HDW = data.HDW as CHTMLCharData;
|
||||
const [beg, ext, end, mid] = data.stretch;
|
||||
const Hb = this.addDelimiterVPart(styles, c, 'beg', beg, HDW);
|
||||
this.addDelimiterVPart(styles, c, 'ext', ext, HDW);
|
||||
const He = this.addDelimiterVPart(styles, c, 'end', end, HDW);
|
||||
const css: StyleData = {};
|
||||
if (mid) {
|
||||
const Hm = this.addDelimiterVPart(styles, c, 'mid', mid, HDW);
|
||||
css.height = '50%';
|
||||
styles['mjx-stretchy-v' + c + ' > mjx-mid'] = {
|
||||
'margin-top': this.em(-Hm / 2),
|
||||
'margin-bottom': this.em(-Hm / 2)
|
||||
};
|
||||
}
|
||||
if (Hb) {
|
||||
css['border-top-width'] = this.em0(Hb - .03);
|
||||
}
|
||||
if (He) {
|
||||
css['border-bottom-width'] = this.em0(He - .03);
|
||||
styles['mjx-stretchy-v' + c + ' > mjx-end'] = {'margin-top': this.em(-He)};
|
||||
}
|
||||
if (Object.keys(css).length) {
|
||||
styles['mjx-stretchy-v' + c + ' > mjx-ext'] = css;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {string} c The vertical character whose part is being added
|
||||
* @param {string} part The name of the part (beg, ext, end, mid) that is being added
|
||||
* @param {number} n The unicode character to use for the part
|
||||
* @param {number} HDW The height-depth-width data for the stretchy delimiter
|
||||
* @return {number} The total height of the character
|
||||
*/
|
||||
protected addDelimiterVPart(styles: StyleList, c: string, part: string, n: number, HDW: CHTMLCharData): number {
|
||||
if (!n) return 0;
|
||||
const data = this.getDelimiterData(n);
|
||||
const dw = (HDW[2] - data[2]) / 2;
|
||||
const css: StyleData = {content: this.charContent(n)};
|
||||
if (part !== 'ext') {
|
||||
css.padding = this.padding(data, dw);
|
||||
} else {
|
||||
css.width = this.em0(HDW[2]);
|
||||
if (dw) {
|
||||
css['padding-left'] = this.em0(dw);
|
||||
}
|
||||
}
|
||||
styles['mjx-stretchy-v' + c + ' mjx-' + part + ' mjx-c::before'] = css;
|
||||
return data[0] + data[1];
|
||||
}
|
||||
|
||||
/*******************************************************/
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {string} c The delimiter character string
|
||||
* @param {CHTMLDelimiterData} data The data for the delimiter whose CSS is to be added
|
||||
*/
|
||||
protected addDelimiterHStyles(styles: StyleList, c: string, data: CHTMLDelimiterData) {
|
||||
const [beg, ext, end, mid] = data.stretch;
|
||||
const HDW = data.HDW as CHTMLCharData;
|
||||
this.addDelimiterHPart(styles, c, 'beg', beg, HDW);
|
||||
this.addDelimiterHPart(styles, c, 'ext', ext, HDW);
|
||||
this.addDelimiterHPart(styles, c, 'end', end, HDW);
|
||||
if (mid) {
|
||||
this.addDelimiterHPart(styles, c, 'mid', mid, HDW);
|
||||
styles['mjx-stretchy-h' + c + ' > mjx-ext'] = {width: '50%'};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {string} c The vertical character whose part is being added
|
||||
* @param {string} part The name of the part (beg, ext, end, mid) that is being added
|
||||
* @param {number} n The unicode character to use for the part
|
||||
* @param {CHTMLCharData} HDW The height-depth-width data for the stretchy character
|
||||
*/
|
||||
protected addDelimiterHPart(styles: StyleList, c: string, part: string, n: number, HDW: CHTMLCharData) {
|
||||
if (!n) return;
|
||||
const data = this.getDelimiterData(n);
|
||||
const options = data[3] as CHTMLCharOptions;
|
||||
const css: StyleData = {content: (options && options.c ? '"' + options.c + '"' : this.charContent(n))};
|
||||
css.padding = this.padding(HDW as CHTMLCharData, 0, -HDW[2]);
|
||||
styles['mjx-stretchy-h' + c + ' mjx-' + part + ' mjx-c::before'] = css;
|
||||
}
|
||||
|
||||
/*******************************************************/
|
||||
|
||||
/**
|
||||
* @param {StyleList} styles The style object to add styles to
|
||||
* @param {string} vletter The variant class letter (e.g., `B`, `SS`) where this character is being defined
|
||||
* @param {number} n The unicode character being defined
|
||||
* @param {CHTMLCharData} data The bounding box data and options for the character
|
||||
*/
|
||||
protected addCharStyles(styles: StyleList, vletter: string, n: number, data: CHTMLCharData) {
|
||||
const options = data[3] as CHTMLCharOptions;
|
||||
const letter = (options.f !== undefined ? options.f : vletter);
|
||||
const selector = 'mjx-c' + this.charSelector(n) + (letter ? '.TEX-' + letter : '');
|
||||
styles[selector + '::before'] = {
|
||||
padding: this.padding(data, 0, options.ic || 0),
|
||||
content: (options.c != null ? '"' + options.c + '"' : this.charContent(n))
|
||||
};
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/**
|
||||
* @param {number} n The character number to find
|
||||
* @return {CHTMLCharData} The data for that character to be used for stretchy delimiters
|
||||
*/
|
||||
protected getDelimiterData(n: number): CHTMLCharData {
|
||||
return this.getChar('-smallop', n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The number of ems
|
||||
* @return {string} The string representing the number with units of "em"
|
||||
*/
|
||||
public em(n: number): string {
|
||||
return em(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The number of ems (will be restricted to non-negative values)
|
||||
* @return {string} The string representing the number with units of "em"
|
||||
*/
|
||||
public em0(n: number): string {
|
||||
return em(Math.max(0, n));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CHTMLCharData} data The [h, d, w] data for the character
|
||||
* @param {number} dw The (optional) left offset of the glyph
|
||||
* @param {number} ic The (optional) italic correction value
|
||||
* @return {string} The padding string for the h, d, w.
|
||||
*/
|
||||
public padding([h, d, w]: CHTMLCharData, dw: number = 0, ic: number = 0): string {
|
||||
return [h, w + ic, d, dw].map(this.em0).join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n A unicode code point to be converted to character content for use with the
|
||||
* CSS rules for fonts (either a literal character for most ASCII values, or \nnnn
|
||||
* for higher values, or for the double quote and backslash characters).
|
||||
* @return {string} The character as a properly encoded string in quotes.
|
||||
*/
|
||||
public charContent(n: number): string {
|
||||
return '"' + (n >= 0x20 && n <= 0x7E && n !== 0x22 && n !== 0x27 && n !== 0x5C ?
|
||||
String.fromCharCode(n) : '\\' + n.toString(16).toUpperCase()) + '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n A unicode code point to be converted to a selector for use with the
|
||||
* CSS rules for fonts
|
||||
* @return {string} The character as a selector value.
|
||||
*/
|
||||
public charSelector(n: number): string {
|
||||
return '.mjx-c' + n.toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The CHTMLFontData constructor class
|
||||
*/
|
||||
export type CHTMLFontDataClass = typeof CHTMLFontData;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Data needed for AddCSS()
|
||||
*/
|
||||
export type CharOptionsMap = {[name: number]: CHTMLCharOptions};
|
||||
export type CssMap = {[name: number]: number};
|
||||
|
||||
/**
|
||||
* @param {CHTMLCharMap} font The font to augment
|
||||
* @param {CharOptionsMap} options Any additional options for characters in the font
|
||||
* @return {CHTMLCharMap} The augmented font
|
||||
*/
|
||||
export function AddCSS(font: CHTMLCharMap, options: CharOptionsMap): CHTMLCharMap {
|
||||
for (const c of Object.keys(options)) {
|
||||
const n = parseInt(c);
|
||||
Object.assign(FontData.charOptions(font, n), options[n]);
|
||||
}
|
||||
return font;
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements utilities for notations for menclose elements
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLmenclose} from './Wrappers/menclose.js';
|
||||
import * as Notation from '../common/Notation.js';
|
||||
export * from '../common/Notation.js';
|
||||
|
||||
/*
|
||||
* Shorthands for common types
|
||||
*/
|
||||
export type RENDERER<N, T, D> = Notation.Renderer<CHTMLmenclose<N, T, D>, N>;
|
||||
export type DEFPAIR<N, T, D> = Notation.DefPair<CHTMLmenclose<N, T, D>, N>;
|
||||
|
||||
/**
|
||||
* Create a named element (handled by CSS), and adjust it if thickness is non-standard
|
||||
*
|
||||
* @param {string} name The name of the element to create
|
||||
* @param {string} offset The offset direction to adjust if thickness is non-standard
|
||||
* @return {RENDERER} The renderer function for the given element name
|
||||
*/
|
||||
export const RenderElement = function<N, T, D>(name: string, offset: string = ''): RENDERER<N, T, D> {
|
||||
return ((node, _child) => {
|
||||
const shape = node.adjustBorder(node.html('mjx-' + name));
|
||||
if (offset) {
|
||||
const d = node.getOffset(offset);
|
||||
if (node.thickness !== Notation.THICKNESS || d) {
|
||||
const transform = `translate${offset}(${node.em(node.thickness / 2 - d)})`;
|
||||
node.adaptor.setStyle(shape, 'transform', transform);
|
||||
}
|
||||
}
|
||||
node.adaptor.append(node.chtml, shape);
|
||||
}) as Notation.Renderer<CHTMLmenclose<N, T, D>, N>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Notation.Side} side The side on which a border should appear
|
||||
* @return {DEFPAIR} The notation definition for the notation having a line on the given side
|
||||
*/
|
||||
export const Border = function<N, T, D>(side: Notation.Side): DEFPAIR<N, T, D> {
|
||||
return Notation.CommonBorder<CHTMLmenclose<N, T, D>, N>((node, child) => {
|
||||
node.adaptor.setStyle(child, 'border-' + side, node.em(node.thickness) + ' solid');
|
||||
})(side);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the notation to define
|
||||
* @param {Notation.Side} side1 The first side to get a border
|
||||
* @param {Notation.Side} side2 The second side to get a border
|
||||
* @return {DEFPAIR} The notation definition for the notation having lines on two sides
|
||||
*/
|
||||
export const Border2 = function<N, T, D>(name: string, side1: Notation.Side, side2: Notation.Side): DEFPAIR<N, T, D> {
|
||||
return Notation.CommonBorder2<CHTMLmenclose<N, T, D>, N>((node, child) => {
|
||||
const border = node.em(node.thickness) + ' solid';
|
||||
node.adaptor.setStyle(child, 'border-' + side1, border);
|
||||
node.adaptor.setStyle(child, 'border-' + side2, border);
|
||||
})(name, side1, side2);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the diagonal strike to define
|
||||
* @param {number} neg 1 or -1 to use with the angle
|
||||
* @return {DEFPAIR} The notation definition for the diagonal strike
|
||||
*/
|
||||
export const DiagonalStrike = function<N, T, D>(name: string, neg: number): DEFPAIR<N, T, D> {
|
||||
return Notation.CommonDiagonalStrike<CHTMLmenclose<N, T, D>, N>((cname: string) => (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
const [a, W] = node.getArgMod(w, h + d);
|
||||
const t = neg * node.thickness / 2;
|
||||
const strike = node.adjustBorder(node.html(cname, {style: {
|
||||
width: node.em(W),
|
||||
transform: 'rotate(' + node.fixed(-neg * a) + 'rad) translateY(' + t + 'em)',
|
||||
}}));
|
||||
node.adaptor.append(node.chtml, strike);
|
||||
})(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the diagonal arrow to define
|
||||
* @return {DEFPAIR} The notation definition for the diagonal arrow
|
||||
*/
|
||||
export const DiagonalArrow = function<N, T, D>(name: string): DEFPAIR<N, T, D> {
|
||||
return Notation.CommonDiagonalArrow<CHTMLmenclose<N, T, D>, N>((node, arrow) => {
|
||||
node.adaptor.append(node.chtml, arrow);
|
||||
})(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the horizontal or vertical arrow to define
|
||||
* @return {DEFPAIR} The notation definition for the arrow
|
||||
*/
|
||||
export const Arrow = function<N, T, D>(name: string): DEFPAIR<N, T, D> {
|
||||
return Notation.CommonArrow<CHTMLmenclose<N, T, D>, N>((node, arrow) => {
|
||||
node.adaptor.append(node.chtml, arrow);
|
||||
})(name);
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2021-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Keeps track of usage of font characters and wrappers
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class used for tracking usage of font characters or wrappers
|
||||
*/
|
||||
export class Usage<T> {
|
||||
|
||||
/**
|
||||
* The used items.
|
||||
*/
|
||||
protected used: Set<string> = new Set<string>();
|
||||
|
||||
/**
|
||||
* The items marked as used since last update.
|
||||
*/
|
||||
protected needsUpdate: T[] = [];
|
||||
|
||||
/**
|
||||
* @param {T} item The item that has been used
|
||||
*/
|
||||
public add(item: T) {
|
||||
const name = JSON.stringify(item);
|
||||
if (!this.used.has(name)) {
|
||||
this.needsUpdate.push(item);
|
||||
}
|
||||
this.used.add(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} item The item to check for being used
|
||||
* @return {boolean} True if the item has been used
|
||||
*/
|
||||
public has(item: T): boolean {
|
||||
return this.used.has(JSON.stringify(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the usage information
|
||||
*/
|
||||
public clear() {
|
||||
this.used.clear();
|
||||
this.needsUpdate = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the items marked as used since the last update.
|
||||
*/
|
||||
public update() {
|
||||
const update = this.needsUpdate;
|
||||
this.needsUpdate = [];
|
||||
return update;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLWrapper class
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {OptionList} from '../../util/Options.js';
|
||||
import * as LENGTHS from '../../util/lengths.js';
|
||||
import {CommonWrapper, AnyWrapperClass, Constructor, StringMap} from '../common/Wrapper.js';
|
||||
import {CHTML} from '../chtml.js';
|
||||
import {CHTMLWrapperFactory} from './WrapperFactory.js';
|
||||
import {BBox} from '../../util/BBox.js';
|
||||
import {CHTMLFontData, CHTMLCharOptions, CHTMLDelimiterData} from './FontData.js';
|
||||
|
||||
export {Constructor, StringMap} from '../common/Wrapper.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Some standard sizes to use in predefind CSS properties
|
||||
*/
|
||||
export const FONTSIZE: StringMap = {
|
||||
'70.7%': 's',
|
||||
'70%': 's',
|
||||
'50%': 'ss',
|
||||
'60%': 'Tn',
|
||||
'85%': 'sm',
|
||||
'120%': 'lg',
|
||||
'144%': 'Lg',
|
||||
'173%': 'LG',
|
||||
'207%': 'hg',
|
||||
'249%': 'HG'
|
||||
};
|
||||
|
||||
export const SPACE: StringMap = {
|
||||
/* tslint:disable:whitespace */
|
||||
[LENGTHS.em(2/18)]: '1',
|
||||
[LENGTHS.em(3/18)]: '2',
|
||||
[LENGTHS.em(4/18)]: '3',
|
||||
[LENGTHS.em(5/18)]: '4',
|
||||
[LENGTHS.em(6/18)]: '5'
|
||||
/* tslint:enable */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shorthand for making a CHTMLWrapper constructor
|
||||
*/
|
||||
export type CHTMLConstructor<N, T, D> = Constructor<CHTMLWrapper<N, T, D>>;
|
||||
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The type of the CHTMLWrapper class (used when creating the wrapper factory for this class)
|
||||
*/
|
||||
export interface CHTMLWrapperClass extends AnyWrapperClass {
|
||||
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* If true, this causes a style for the node type to be generated automatically
|
||||
* that sets display:inline-block (as needed for the output for MmlNodes).
|
||||
*/
|
||||
autoStyle: boolean;
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The base CHTMLWrapper class
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLWrapper<N, T, D> extends
|
||||
CommonWrapper<
|
||||
CHTML<N, T, D>,
|
||||
CHTMLWrapper<N, T, D>,
|
||||
CHTMLWrapperClass,
|
||||
CHTMLCharOptions,
|
||||
CHTMLDelimiterData,
|
||||
CHTMLFontData
|
||||
> {
|
||||
|
||||
/**
|
||||
* The wrapper type
|
||||
*/
|
||||
public static kind: string = 'unknown';
|
||||
|
||||
/**
|
||||
* If true, this causes a style for the node type to be generated automatically
|
||||
* that sets display:inline-block (as needed for the output for MmlNodes).
|
||||
*/
|
||||
public static autoStyle = true;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected factory: CHTMLWrapperFactory<N, T, D>;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public parent: CHTMLWrapper<N, T, D>;
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public childNodes: CHTMLWrapper<N, T, D>[];
|
||||
|
||||
/**
|
||||
* The HTML element generated for this wrapped node
|
||||
*/
|
||||
public chtml: N = null;
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
/**
|
||||
* Create the HTML for the wrapped node.
|
||||
*
|
||||
* @param {N} parent The HTML node where the output is added
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
for (const child of this.childNodes) {
|
||||
child.toCHTML(chtml);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
/**
|
||||
* Create the standard CHTML element for the given wrapped node.
|
||||
*
|
||||
* @param {N} parent The HTML element in which the node is to be created
|
||||
* @returns {N} The root of the HTML tree for the wrapped node's output
|
||||
*/
|
||||
protected standardCHTMLnode(parent: N): N {
|
||||
this.markUsed();
|
||||
const chtml = this.createCHTMLnode(parent);
|
||||
this.handleStyles();
|
||||
this.handleVariant();
|
||||
this.handleScale();
|
||||
this.handleColor();
|
||||
this.handleSpace();
|
||||
this.handleAttributes();
|
||||
this.handlePWidth();
|
||||
return chtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this class as having been typeset (so its styles will be output)
|
||||
*/
|
||||
public markUsed() {
|
||||
this.jax.wrapperUsage.add(this.kind);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} parent The HTML element in which the node is to be created
|
||||
* @returns {N} The root of the HTML tree for the wrapped node's output
|
||||
*/
|
||||
protected createCHTMLnode(parent: N): N {
|
||||
const href = this.node.attributes.get('href');
|
||||
if (href) {
|
||||
parent = this.adaptor.append(parent, this.html('a', {href: href})) as N;
|
||||
}
|
||||
this.chtml = this.adaptor.append(parent, this.html('mjx-' + this.node.kind)) as N;
|
||||
return this.chtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSS styles for the chtml element
|
||||
*/
|
||||
protected handleStyles() {
|
||||
if (!this.styles) return;
|
||||
const styles = this.styles.cssText;
|
||||
if (styles) {
|
||||
this.adaptor.setAttribute(this.chtml, 'style', styles);
|
||||
const family = this.styles.get('font-family');
|
||||
if (family) {
|
||||
this.adaptor.setStyle(this.chtml, 'font-family', 'MJXZERO, ' + family);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSS for the math variant
|
||||
*/
|
||||
protected handleVariant() {
|
||||
if (this.node.isToken && this.variant !== '-explicitFont') {
|
||||
this.adaptor.setAttribute(this.chtml, 'class',
|
||||
(this.font.getVariant(this.variant) || this.font.getVariant('normal')).classes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the (relative) scaling factor for the node
|
||||
*/
|
||||
protected handleScale() {
|
||||
this.setScale(this.chtml, this.bbox.rscale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} chtml The HTML node to scale
|
||||
* @param {number} rscale The relatie scale to apply
|
||||
* @return {N} The HTML node (for chaining)
|
||||
*/
|
||||
protected setScale(chtml: N, rscale: number): N {
|
||||
const scale = (Math.abs(rscale - 1) < .001 ? 1 : rscale);
|
||||
if (chtml && scale !== 1) {
|
||||
const size = this.percent(scale);
|
||||
if (FONTSIZE[size]) {
|
||||
this.adaptor.setAttribute(chtml, 'size', FONTSIZE[size]);
|
||||
} else {
|
||||
this.adaptor.setStyle(chtml, 'fontSize', size);
|
||||
}
|
||||
}
|
||||
return chtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the proper spacing
|
||||
*/
|
||||
protected handleSpace() {
|
||||
for (const data of [[this.bbox.L, 'space', 'marginLeft'],
|
||||
[this.bbox.R, 'rspace', 'marginRight']]) {
|
||||
const [dimen, name, margin] = data as [number, string, string];
|
||||
if (dimen) {
|
||||
const space = this.em(dimen);
|
||||
if (SPACE[space]) {
|
||||
this.adaptor.setAttribute(this.chtml, name, SPACE[space]);
|
||||
} else {
|
||||
this.adaptor.setStyle(this.chtml, margin, space);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the foreground and background colors
|
||||
* (Only look at explicit attributes, since inherited ones will
|
||||
* be applied to a parent element, and we will inherit from that)
|
||||
*/
|
||||
protected handleColor() {
|
||||
const attributes = this.node.attributes;
|
||||
const mathcolor = attributes.getExplicit('mathcolor') as string;
|
||||
const color = attributes.getExplicit('color') as string;
|
||||
const mathbackground = attributes.getExplicit('mathbackground') as string;
|
||||
const background = attributes.getExplicit('background') as string;
|
||||
if (mathcolor || color) {
|
||||
this.adaptor.setStyle(this.chtml, 'color', mathcolor || color);
|
||||
}
|
||||
if (mathbackground || background) {
|
||||
this.adaptor.setStyle(this.chtml, 'backgroundColor', mathbackground || background);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy RDFa, aria, and other tags from the MathML to the CHTML output nodes.
|
||||
* Don't copy those in the skipAttributes list, or anything that already exists
|
||||
* as a property of the node (e.g., no "onlick", etc.). If a name in the
|
||||
* skipAttributes object is set to false, then the attribute WILL be copied.
|
||||
* Add the class to any other classes already in use.
|
||||
*/
|
||||
protected handleAttributes() {
|
||||
const attributes = this.node.attributes;
|
||||
const defaults = attributes.getAllDefaults();
|
||||
const skip = CHTMLWrapper.skipAttributes;
|
||||
for (const name of attributes.getExplicitNames()) {
|
||||
if (skip[name] === false || (!(name in defaults) && !skip[name] &&
|
||||
!this.adaptor.hasAttribute(this.chtml, name))) {
|
||||
this.adaptor.setAttribute(this.chtml, name, attributes.getExplicit(name) as string);
|
||||
}
|
||||
}
|
||||
if (attributes.get('class')) {
|
||||
const names = (attributes.get('class') as string).trim().split(/ +/);
|
||||
for (const name of names) {
|
||||
this.adaptor.addClass(this.chtml, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the attributes needed for percentage widths
|
||||
*/
|
||||
protected handlePWidth() {
|
||||
if (this.bbox.pwidth) {
|
||||
if (this.bbox.pwidth === BBox.fullWidth) {
|
||||
this.adaptor.setAttribute(this.chtml, 'width', 'full');
|
||||
} else {
|
||||
this.adaptor.setStyle(this.chtml, 'width', this.bbox.pwidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
/**
|
||||
* @param {N} chtml The HTML node whose indentation is to be adjusted
|
||||
* @param {string} align The alignment for the node
|
||||
* @param {number} shift The indent (positive or negative) for the node
|
||||
*/
|
||||
protected setIndent(chtml: N, align: string, shift: number) {
|
||||
const adaptor = this.adaptor;
|
||||
if (align === 'center' || align === 'left') {
|
||||
const L = this.getBBox().L;
|
||||
adaptor.setStyle(chtml, 'margin-left', this.em(shift + L));
|
||||
}
|
||||
if (align === 'center' || align === 'right') {
|
||||
const R = this.getBBox().R;
|
||||
adaptor.setStyle(chtml, 'margin-right', this.em(-shift + R));
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/**
|
||||
* For debugging
|
||||
*/
|
||||
|
||||
public drawBBox() {
|
||||
let {w, h, d, R} = this.getBBox();
|
||||
const box = this.html('mjx-box', {style: {
|
||||
opacity: .25, 'margin-left': this.em(-w - R)
|
||||
}}, [
|
||||
this.html('mjx-box', {style: {
|
||||
height: this.em(h),
|
||||
width: this.em(w),
|
||||
'background-color': 'red'
|
||||
}}),
|
||||
this.html('mjx-box', {style: {
|
||||
height: this.em(d),
|
||||
width: this.em(w),
|
||||
'margin-left': this.em(-w),
|
||||
'vertical-align': this.em(-d),
|
||||
'background-color': 'green'
|
||||
}})
|
||||
] as N[]);
|
||||
const node = this.chtml || this.parent.chtml;
|
||||
const size = this.adaptor.getAttribute(node, 'size');
|
||||
if (size) {
|
||||
this.adaptor.setAttribute(box, 'size', size);
|
||||
}
|
||||
const fontsize = this.adaptor.getStyle(node, 'fontSize');
|
||||
if (fontsize) {
|
||||
this.adaptor.setStyle(box, 'fontSize', fontsize);
|
||||
}
|
||||
this.adaptor.append(this.adaptor.parent(node), box);
|
||||
this.adaptor.setStyle(node, 'backgroundColor', '#FFEE00');
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/*
|
||||
* Easy access to some utility routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} type The tag name of the HTML node to be created
|
||||
* @param {OptionList} def The properties to set for the created node
|
||||
* @param {(N|T)[]} content The child nodes for the created HTML node
|
||||
* @return {N} The generated HTML tree
|
||||
*/
|
||||
public html(type: string, def: OptionList = {}, content: (N | T)[] = []): N {
|
||||
return this.jax.html(type, def, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text The text from which to create an HTML text node
|
||||
* @return {T} The generated text node with the given text
|
||||
*/
|
||||
public text(text: string): T {
|
||||
return this.jax.text(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n A unicode code point to be converted to a character className reference.
|
||||
* @return {string} The className for the character
|
||||
*/
|
||||
protected char(n: number): string {
|
||||
return this.font.charSelector(n).substr(1);
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLWrapperFactory class
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTML} from '../chtml.js';
|
||||
import {CommonWrapperFactory} from '../common/WrapperFactory.js';
|
||||
import {CHTMLWrapper, CHTMLWrapperClass} from './Wrapper.js';
|
||||
import {CHTMLWrappers} from './Wrappers.js';
|
||||
import {CHTMLCharOptions, CHTMLDelimiterData, CHTMLFontData} from './FontData.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLWrapperFactory class for creating CHTMLWrapper nodes
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLWrapperFactory<N, T, D> extends
|
||||
CommonWrapperFactory<
|
||||
CHTML<N, T, D>,
|
||||
CHTMLWrapper<N, T, D>,
|
||||
CHTMLWrapperClass,
|
||||
CHTMLCharOptions,
|
||||
CHTMLDelimiterData,
|
||||
CHTMLFontData
|
||||
> {
|
||||
|
||||
/**
|
||||
* The default list of wrapper nodes this factory can create
|
||||
*/
|
||||
public static defaultNodes = CHTMLWrappers;
|
||||
|
||||
/**
|
||||
* The CHTML output jax associated with this factory
|
||||
*/
|
||||
public jax: CHTML<N, T, D>;
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview An object listing all the CHTMLWrapper classes
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {WrapperConstructor} from '../common/Wrapper.js';
|
||||
import {CHTMLWrapper} from './Wrapper.js';
|
||||
import {CHTMLmath} from './Wrappers/math.js';
|
||||
import {CHTMLmi} from './Wrappers/mi.js';
|
||||
import {CHTMLmo} from './Wrappers/mo.js';
|
||||
import {CHTMLmn} from './Wrappers/mn.js';
|
||||
import {CHTMLms} from './Wrappers/ms.js';
|
||||
import {CHTMLmtext} from './Wrappers/mtext.js';
|
||||
import {CHTMLmspace} from './Wrappers/mspace.js';
|
||||
import {CHTMLmpadded} from './Wrappers/mpadded.js';
|
||||
import {CHTMLmenclose} from './Wrappers/menclose.js';
|
||||
import {CHTMLmrow, CHTMLinferredMrow} from './Wrappers/mrow.js';
|
||||
import {CHTMLmfenced} from './Wrappers/mfenced.js';
|
||||
import {CHTMLmfrac} from './Wrappers/mfrac.js';
|
||||
import {CHTMLmsqrt} from './Wrappers/msqrt.js';
|
||||
import {CHTMLmroot} from './Wrappers/mroot.js';
|
||||
import {CHTMLmsub, CHTMLmsup, CHTMLmsubsup} from './Wrappers/msubsup.js';
|
||||
import {CHTMLmover, CHTMLmunder, CHTMLmunderover} from './Wrappers/munderover.js';
|
||||
import {CHTMLmmultiscripts} from './Wrappers/mmultiscripts.js';
|
||||
import {CHTMLmtable} from './Wrappers/mtable.js';
|
||||
import {CHTMLmtr, CHTMLmlabeledtr} from './Wrappers/mtr.js';
|
||||
import {CHTMLmtd} from './Wrappers/mtd.js';
|
||||
import {CHTMLmaction} from './Wrappers/maction.js';
|
||||
import {CHTMLmglyph} from './Wrappers/mglyph.js';
|
||||
import {CHTMLsemantics, CHTMLannotation, CHTMLannotationXML, CHTMLxml} from './Wrappers/semantics.js';
|
||||
import {CHTMLTeXAtom} from './Wrappers/TeXAtom.js';
|
||||
import {CHTMLTextNode} from './Wrappers/TextNode.js';
|
||||
|
||||
export const CHTMLWrappers: {[kind: string]: WrapperConstructor} = {
|
||||
[CHTMLmath.kind]: CHTMLmath,
|
||||
[CHTMLmrow.kind]: CHTMLmrow,
|
||||
[CHTMLinferredMrow.kind]: CHTMLinferredMrow,
|
||||
[CHTMLmi.kind]: CHTMLmi,
|
||||
[CHTMLmo.kind]: CHTMLmo,
|
||||
[CHTMLmn.kind]: CHTMLmn,
|
||||
[CHTMLms.kind]: CHTMLms,
|
||||
[CHTMLmtext.kind]: CHTMLmtext,
|
||||
[CHTMLmspace.kind]: CHTMLmspace,
|
||||
[CHTMLmpadded.kind]: CHTMLmpadded,
|
||||
[CHTMLmenclose.kind]: CHTMLmenclose,
|
||||
[CHTMLmfrac.kind]: CHTMLmfrac,
|
||||
[CHTMLmsqrt.kind]: CHTMLmsqrt,
|
||||
[CHTMLmroot.kind]: CHTMLmroot,
|
||||
[CHTMLmsub.kind]: CHTMLmsub,
|
||||
[CHTMLmsup.kind]: CHTMLmsup,
|
||||
[CHTMLmsubsup.kind]: CHTMLmsubsup,
|
||||
[CHTMLmunder.kind]: CHTMLmunder,
|
||||
[CHTMLmover.kind]: CHTMLmover,
|
||||
[CHTMLmunderover.kind]: CHTMLmunderover,
|
||||
[CHTMLmmultiscripts.kind]: CHTMLmmultiscripts,
|
||||
[CHTMLmfenced.kind]: CHTMLmfenced,
|
||||
[CHTMLmtable.kind]: CHTMLmtable,
|
||||
[CHTMLmtr.kind]: CHTMLmtr,
|
||||
[CHTMLmlabeledtr.kind]: CHTMLmlabeledtr,
|
||||
[CHTMLmtd.kind]: CHTMLmtd,
|
||||
[CHTMLmaction.kind]: CHTMLmaction,
|
||||
[CHTMLmglyph.kind]: CHTMLmglyph,
|
||||
[CHTMLsemantics.kind]: CHTMLsemantics,
|
||||
[CHTMLannotation.kind]: CHTMLannotation,
|
||||
[CHTMLannotationXML.kind]: CHTMLannotationXML,
|
||||
[CHTMLxml.kind]: CHTMLxml,
|
||||
[CHTMLTeXAtom.kind]: CHTMLTeXAtom,
|
||||
[CHTMLTextNode.kind]: CHTMLTextNode,
|
||||
[CHTMLWrapper.kind]: CHTMLWrapper
|
||||
};
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLTeXAtom wrapper for the MmlTeXAtom object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonTeXAtomMixin} from '../../common/Wrappers/TeXAtom.js';
|
||||
import {TeXAtom} from '../../../core/MmlTree/MmlNodes/TeXAtom.js';
|
||||
import {TEXCLASS, TEXCLASSNAMES} from '../../../core/MmlTree/MmlNode.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLTeXAtom wrapper for the TeXAtom object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLTeXAtom<N, T, D> extends
|
||||
CommonTeXAtomMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The TeXAtom wrapper
|
||||
*/
|
||||
public static kind = TeXAtom.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
super.toCHTML(parent);
|
||||
this.adaptor.setAttribute(this.chtml, 'texclass', TEXCLASSNAMES[this.node.texClass]);
|
||||
//
|
||||
// Center VCENTER atoms vertically
|
||||
//
|
||||
if (this.node.texClass === TEXCLASS.VCENTER) {
|
||||
const bbox = this.childNodes[0].getBBox(); // get unmodified bbox of children
|
||||
const {h, d} = bbox;
|
||||
const a = this.font.params.axis_height;
|
||||
const dh = ((h + d) / 2 + a) - h; // new height minus old height
|
||||
this.adaptor.setStyle(this.chtml, 'verticalAlign', this.em(dh));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLTextNode wrapper for the TextNode object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {TextNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonTextNodeMixin} from '../../common/Wrappers/TextNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLTextNode wrapper for the TextNode object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLTextNode<N, T, D> extends
|
||||
CommonTextNodeMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The TextNode wrapper
|
||||
*/
|
||||
public static kind = TextNode.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static autoStyle = false;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-c': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
'mjx-utext': {
|
||||
display: 'inline-block',
|
||||
padding: '.75em 0 .2em 0'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
this.markUsed();
|
||||
const adaptor = this.adaptor;
|
||||
const variant = this.parent.variant;
|
||||
const text = (this.node as TextNode).getText();
|
||||
if (text.length === 0) return;
|
||||
if (variant === '-explicitFont') {
|
||||
adaptor.append(parent, this.jax.unknownText(text, variant, this.getBBox().w));
|
||||
} else {
|
||||
const chars = this.remappedText(text, variant);
|
||||
for (const n of chars) {
|
||||
const data = this.getVariantChar(variant, n)[3];
|
||||
const font = (data.f ? ' TEX-' + data.f : '');
|
||||
const node = (data.unknown ?
|
||||
this.jax.unknownText(String.fromCodePoint(n), variant) :
|
||||
this.html('mjx-c', {class: this.char(n) + font}));
|
||||
adaptor.append(parent, node);
|
||||
!data.unknown && this.font.charUsage.add([variant, n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmaction wrapper for the MmlMaction object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMactionMixin} from '../../common/Wrappers/maction.js';
|
||||
import {ActionDef} from '../../common/Wrappers/maction.js';
|
||||
import {EventHandler, TooltipData} from '../../common/Wrappers/maction.js';
|
||||
import {MmlMaction} from '../../../core/MmlTree/MmlNodes/maction.js';
|
||||
import {TextNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmaction wrapper for the MmlMaction object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmaction<N, T, D> extends
|
||||
CommonMactionMixin<CHTMLWrapper<any, any, any>, CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The maction wrapper
|
||||
*/
|
||||
public static kind = MmlMaction.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-maction': {
|
||||
position: 'relative'
|
||||
},
|
||||
'mjx-maction > mjx-tool': {
|
||||
display: 'none',
|
||||
position: 'absolute',
|
||||
bottom: 0, right: 0,
|
||||
width: 0, height: 0,
|
||||
'z-index': 500
|
||||
},
|
||||
'mjx-tool > mjx-tip': {
|
||||
display: 'inline-block',
|
||||
padding: '.2em',
|
||||
border: '1px solid #888',
|
||||
'font-size': '70%',
|
||||
'background-color': '#F8F8F8',
|
||||
color: 'black',
|
||||
'box-shadow': '2px 2px 5px #AAAAAA'
|
||||
},
|
||||
'mjx-maction[toggle]': {
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'mjx-status': {
|
||||
display: 'block',
|
||||
position: 'fixed',
|
||||
left: '1em',
|
||||
bottom: '1em',
|
||||
'min-width': '25%',
|
||||
padding: '.2em .4em',
|
||||
border: '1px solid #888',
|
||||
'font-size': '90%',
|
||||
'background-color': '#F8F8F8',
|
||||
color: 'black'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The valid action types and their handlers
|
||||
*/
|
||||
public static actions = new Map([
|
||||
['toggle', [(node, _data) => {
|
||||
//
|
||||
// Mark which child is selected
|
||||
//
|
||||
node.adaptor.setAttribute(node.chtml, 'toggle', node.node.attributes.get('selection') as string);
|
||||
//
|
||||
// Cache the data needed to select another node
|
||||
//
|
||||
const math = node.factory.jax.math;
|
||||
const document = node.factory.jax.document;
|
||||
const mml = node.node as MmlMaction;
|
||||
//
|
||||
// Add a click handler that changes the selection and rerenders the expression
|
||||
//
|
||||
node.setEventHandler('click', (event: Event) => {
|
||||
if (!math.end.node) {
|
||||
//
|
||||
// If the MathItem was created by hand, it might not have a node
|
||||
// telling it where to replace the existing math, so set it.
|
||||
//
|
||||
math.start.node = math.end.node = math.typesetRoot;
|
||||
math.start.n = math.end.n = 0;
|
||||
}
|
||||
mml.nextToggleSelection();
|
||||
math.rerender(document);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}, {}]],
|
||||
|
||||
['tooltip', [(node, data) => {
|
||||
const tip = node.childNodes[1];
|
||||
if (!tip) return;
|
||||
if (tip.node.isKind('mtext')) {
|
||||
//
|
||||
// Text tooltips are handled through title attributes
|
||||
//
|
||||
const text = (tip.node as TextNode).getText();
|
||||
node.adaptor.setAttribute(node.chtml, 'title', text);
|
||||
} else {
|
||||
//
|
||||
// Math tooltips are handled through hidden nodes and event handlers
|
||||
//
|
||||
const adaptor = node.adaptor;
|
||||
const tool = adaptor.append(node.chtml, node.html('mjx-tool', {
|
||||
style: {bottom: node.em(-node.dy), right: node.em(-node.dx)}
|
||||
}, [node.html('mjx-tip')]));
|
||||
tip.toCHTML(adaptor.firstChild(tool));
|
||||
//
|
||||
// Set up the event handlers to display and remove the tooltip
|
||||
//
|
||||
node.setEventHandler('mouseover', (event: Event) => {
|
||||
data.stopTimers(node, data);
|
||||
const timeout = setTimeout(() => adaptor.setStyle(tool, 'display', 'block'), data.postDelay);
|
||||
data.hoverTimer.set(node, timeout);
|
||||
event.stopPropagation();
|
||||
});
|
||||
node.setEventHandler('mouseout', (event: Event) => {
|
||||
data.stopTimers(node, data);
|
||||
const timeout = setTimeout(() => adaptor.setStyle(tool, 'display', ''), data.clearDelay);
|
||||
data.clearTimer.set(node, timeout);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
}, TooltipData]],
|
||||
|
||||
['statusline', [(node, data) => {
|
||||
const tip = node.childNodes[1];
|
||||
if (!tip) return;
|
||||
if (tip.node.isKind('mtext')) {
|
||||
const adaptor = node.adaptor;
|
||||
const text = (tip.node as TextNode).getText();
|
||||
adaptor.setAttribute(node.chtml, 'statusline', text);
|
||||
//
|
||||
// Set up event handlers to change the status window
|
||||
//
|
||||
node.setEventHandler('mouseover', (event: Event) => {
|
||||
if (data.status === null) {
|
||||
const body = adaptor.body(adaptor.document);
|
||||
data.status = adaptor.append(body, node.html('mjx-status', {}, [node.text(text)]));
|
||||
}
|
||||
event.stopPropagation();
|
||||
});
|
||||
node.setEventHandler('mouseout', (event: Event) => {
|
||||
if (data.status) {
|
||||
adaptor.remove(data.status);
|
||||
data.status = null;
|
||||
}
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
}, {
|
||||
status: null // cached status line
|
||||
}]]
|
||||
|
||||
] as ActionDef<CHTMLmaction<any, any, any>>[]);
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
const child = this.selected;
|
||||
child.toCHTML(chtml);
|
||||
this.action(this, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event handler to the output for this maction
|
||||
*/
|
||||
public setEventHandler(type: string, handler: EventHandler) {
|
||||
(this.chtml as any).addEventListener(type, handler);
|
||||
}
|
||||
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmath wrapper for the MmlMath object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMathMixin} from '../../common/Wrappers/math.js';
|
||||
import {MmlMath} from '../../../core/MmlTree/MmlNodes/math.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmath wrapper for the MmlMath object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmath<N, T, D> extends
|
||||
CommonMathMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The math wrapper
|
||||
*/
|
||||
public static kind = MmlMath.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-math': {
|
||||
'line-height': 0,
|
||||
'text-align': 'left',
|
||||
'text-indent': 0,
|
||||
'font-style': 'normal',
|
||||
'font-weight': 'normal',
|
||||
'font-size': '100%',
|
||||
'font-size-adjust': 'none',
|
||||
'letter-spacing': 'normal',
|
||||
'border-collapse': 'collapse',
|
||||
'word-wrap': 'normal',
|
||||
'word-spacing': 'normal',
|
||||
'white-space': 'nowrap',
|
||||
'direction': 'ltr',
|
||||
'padding': '1px 0'
|
||||
},
|
||||
'mjx-container[jax="CHTML"][display="true"]': {
|
||||
display: 'block',
|
||||
'text-align': 'center',
|
||||
margin: '1em 0'
|
||||
},
|
||||
'mjx-container[jax="CHTML"][display="true"][width="full"]': {
|
||||
display: 'flex'
|
||||
},
|
||||
'mjx-container[jax="CHTML"][display="true"] mjx-math': {
|
||||
padding: 0
|
||||
},
|
||||
'mjx-container[jax="CHTML"][justify="left"]': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
'mjx-container[jax="CHTML"][justify="right"]': {
|
||||
'text-align': 'right'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
super.toCHTML(parent);
|
||||
const chtml = this.chtml;
|
||||
const adaptor = this.adaptor;
|
||||
const display = (this.node.attributes.get('display') === 'block');
|
||||
if (display) {
|
||||
adaptor.setAttribute(chtml, 'display', 'true');
|
||||
adaptor.setAttribute(parent, 'display', 'true');
|
||||
this.handleDisplay(parent);
|
||||
} else {
|
||||
this.handleInline(parent);
|
||||
}
|
||||
adaptor.addClass(chtml, 'MJX-TEX');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle displayed equations (set min-width, and so on).
|
||||
*/
|
||||
protected handleDisplay(parent: N) {
|
||||
const adaptor = this.adaptor;
|
||||
const [align, shift] = this.getAlignShift();
|
||||
if (align !== 'center') {
|
||||
adaptor.setAttribute(parent, 'justify', align);
|
||||
}
|
||||
if (this.bbox.pwidth === BBox.fullWidth) {
|
||||
adaptor.setAttribute(parent, 'width', 'full');
|
||||
if (this.jax.table) {
|
||||
let {L, w, R} = this.jax.table.getOuterBBox();
|
||||
if (align === 'right') {
|
||||
R = Math.max(R || -shift, -shift);
|
||||
} else if (align === 'left') {
|
||||
L = Math.max(L || shift, shift);
|
||||
} else if (align === 'center') {
|
||||
w += 2 * Math.abs(shift);
|
||||
}
|
||||
const W = this.em(Math.max(0, L + w + R));
|
||||
adaptor.setStyle(parent, 'min-width', W);
|
||||
adaptor.setStyle(this.jax.table.chtml, 'min-width', W);
|
||||
}
|
||||
} else {
|
||||
this.setIndent(this.chtml, align, shift);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle in-line expressions
|
||||
*/
|
||||
protected handleInline(parent: N) {
|
||||
//
|
||||
// Transfer right margin to container (for things like $x\hskip -2em y$)
|
||||
//
|
||||
const adaptor = this.adaptor;
|
||||
const margin = adaptor.getStyle(this.chtml, 'margin-right');
|
||||
if (margin) {
|
||||
adaptor.setStyle(this.chtml, 'margin-right', '');
|
||||
adaptor.setStyle(parent, 'margin-right', margin);
|
||||
adaptor.setStyle(parent, 'width', '0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public setChildPWidths(recompute: boolean, w: number = null, clear: boolean = true) {
|
||||
return (this.parent ? super.setChildPWidths(recompute, w, clear) : false);
|
||||
}
|
||||
|
||||
}
|
||||
+489
@@ -0,0 +1,489 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmenclose wrapper for the MmlMenclose object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMencloseMixin} from '../../common/Wrappers/menclose.js';
|
||||
import {CHTMLmsqrt} from './msqrt.js';
|
||||
import * as Notation from '../Notation.js';
|
||||
import {MmlMenclose} from '../../../core/MmlTree/MmlNodes/menclose.js';
|
||||
import {OptionList} from '../../../util/Options.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {em} from '../../../util/lengths.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* The skew angle needed for the arrow head pieces
|
||||
*/
|
||||
function Angle(x: number, y: number) {
|
||||
return Math.atan2(x, y).toFixed(3).replace(/\.?0+$/, '');
|
||||
}
|
||||
|
||||
const ANGLE = Angle(Notation.ARROWDX, Notation.ARROWY);
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmenclose wrapper for the MmlMenclose object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmenclose<N, T, D> extends
|
||||
CommonMencloseMixin<
|
||||
CHTMLWrapper<any, any, any>,
|
||||
CHTMLmsqrt<any, any, any>,
|
||||
any,
|
||||
CHTMLConstructor<any, any, any>
|
||||
>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The menclose wrapper
|
||||
*/
|
||||
public static kind = MmlMenclose.prototype.kind;
|
||||
|
||||
/**
|
||||
* Styles needed for the various notations
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-menclose': {
|
||||
position: 'relative'
|
||||
},
|
||||
'mjx-menclose > mjx-dstrike': {
|
||||
display: 'inline-block',
|
||||
left: 0, top: 0,
|
||||
position: 'absolute',
|
||||
'border-top': Notation.SOLID,
|
||||
'transform-origin': 'top left'
|
||||
},
|
||||
'mjx-menclose > mjx-ustrike': {
|
||||
display: 'inline-block',
|
||||
left: 0, bottom: 0,
|
||||
position: 'absolute',
|
||||
'border-top': Notation.SOLID,
|
||||
'transform-origin': 'bottom left'
|
||||
},
|
||||
'mjx-menclose > mjx-hstrike': {
|
||||
'border-top': Notation.SOLID,
|
||||
position: 'absolute',
|
||||
left: 0, right: 0, bottom: '50%',
|
||||
transform: 'translateY(' + em(Notation.THICKNESS / 2) + ')'
|
||||
},
|
||||
'mjx-menclose > mjx-vstrike': {
|
||||
'border-left': Notation.SOLID,
|
||||
position: 'absolute',
|
||||
top: 0, bottom: 0, right: '50%',
|
||||
transform: 'translateX(' + em(Notation.THICKNESS / 2) + ')'
|
||||
},
|
||||
'mjx-menclose > mjx-rbox': {
|
||||
position: 'absolute',
|
||||
top: 0, bottom: 0, right: 0, left: 0,
|
||||
'border': Notation.SOLID,
|
||||
'border-radius': em(Notation.THICKNESS + Notation.PADDING)
|
||||
},
|
||||
'mjx-menclose > mjx-cbox': {
|
||||
position: 'absolute',
|
||||
top: 0, bottom: 0, right: 0, left: 0,
|
||||
'border': Notation.SOLID,
|
||||
'border-radius': '50%'
|
||||
},
|
||||
'mjx-menclose > mjx-arrow': {
|
||||
position: 'absolute',
|
||||
left: 0, bottom: '50%', height: 0, width: 0
|
||||
},
|
||||
'mjx-menclose > mjx-arrow > *': {
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
'transform-origin': 'bottom',
|
||||
'border-left': em(Notation.THICKNESS * Notation.ARROWX) + ' solid',
|
||||
'border-right': 0,
|
||||
'box-sizing': 'border-box'
|
||||
},
|
||||
'mjx-menclose > mjx-arrow > mjx-aline': {
|
||||
left: 0, top: em(-Notation.THICKNESS / 2),
|
||||
right: em(Notation.THICKNESS * (Notation.ARROWX - 1)), height: 0,
|
||||
'border-top': em(Notation.THICKNESS) + ' solid',
|
||||
'border-left': 0
|
||||
},
|
||||
'mjx-menclose > mjx-arrow[double] > mjx-aline': {
|
||||
left: em(Notation.THICKNESS * (Notation.ARROWX - 1)), height: 0,
|
||||
},
|
||||
'mjx-menclose > mjx-arrow > mjx-rthead': {
|
||||
transform: 'skewX(' + ANGLE + 'rad)',
|
||||
right: 0, bottom: '-1px',
|
||||
'border-bottom': '1px solid transparent',
|
||||
'border-top': em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent'
|
||||
},
|
||||
'mjx-menclose > mjx-arrow > mjx-rbhead': {
|
||||
transform: 'skewX(-' + ANGLE + 'rad)',
|
||||
'transform-origin': 'top',
|
||||
right: 0, top: '-1px',
|
||||
'border-top': '1px solid transparent',
|
||||
'border-bottom': em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent'
|
||||
},
|
||||
'mjx-menclose > mjx-arrow > mjx-lthead': {
|
||||
transform: 'skewX(-' + ANGLE + 'rad)',
|
||||
left: 0, bottom: '-1px',
|
||||
'border-left': 0,
|
||||
'border-right': em(Notation.THICKNESS * Notation.ARROWX) + ' solid',
|
||||
'border-bottom': '1px solid transparent',
|
||||
'border-top': em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent'
|
||||
},
|
||||
'mjx-menclose > mjx-arrow > mjx-lbhead': {
|
||||
transform: 'skewX(' + ANGLE + 'rad)',
|
||||
'transform-origin': 'top',
|
||||
left: 0, top: '-1px',
|
||||
'border-left': 0,
|
||||
'border-right': em(Notation.THICKNESS * Notation.ARROWX) + ' solid',
|
||||
'border-top': '1px solid transparent',
|
||||
'border-bottom': em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent'
|
||||
},
|
||||
'mjx-menclose > dbox': {
|
||||
position: 'absolute',
|
||||
top: 0, bottom: 0, left: em(-1.5 * Notation.PADDING),
|
||||
width: em(3 * Notation.PADDING),
|
||||
border: em(Notation.THICKNESS) + ' solid',
|
||||
'border-radius': '50%',
|
||||
'clip-path': 'inset(0 0 0 ' + em(1.5 * Notation.PADDING) + ')',
|
||||
'box-sizing': 'border-box'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The definitions of the various notations
|
||||
*/
|
||||
public static notations: Notation.DefList<CHTMLmenclose<any, any, any>, any> = new Map([
|
||||
|
||||
Notation.Border('top'),
|
||||
Notation.Border('right'),
|
||||
Notation.Border('bottom'),
|
||||
Notation.Border('left'),
|
||||
|
||||
Notation.Border2('actuarial', 'top', 'right'),
|
||||
Notation.Border2('madruwb', 'bottom', 'right'),
|
||||
|
||||
Notation.DiagonalStrike('up', 1),
|
||||
Notation.DiagonalStrike('down', -1),
|
||||
|
||||
['horizontalstrike', {
|
||||
renderer: Notation.RenderElement('hstrike', 'Y'),
|
||||
bbox: (node) => [0, node.padding, 0, node.padding]
|
||||
}],
|
||||
|
||||
['verticalstrike', {
|
||||
renderer: Notation.RenderElement('vstrike', 'X'),
|
||||
bbox: (node) => [node.padding, 0, node.padding, 0]
|
||||
}],
|
||||
|
||||
['box', {
|
||||
renderer: (node, child) => {
|
||||
node.adaptor.setStyle(child, 'border', node.em(node.thickness) + ' solid');
|
||||
},
|
||||
bbox: Notation.fullBBox,
|
||||
border: Notation.fullBorder,
|
||||
remove: 'left right top bottom'
|
||||
}],
|
||||
|
||||
['roundedbox', {
|
||||
renderer: Notation.RenderElement('rbox'),
|
||||
bbox: Notation.fullBBox
|
||||
}],
|
||||
|
||||
['circle', {
|
||||
renderer: Notation.RenderElement('cbox'),
|
||||
bbox: Notation.fullBBox
|
||||
}],
|
||||
|
||||
['phasorangle', {
|
||||
//
|
||||
// Use a bottom border and an upward strike properly angled
|
||||
//
|
||||
renderer: (node, child) => {
|
||||
const {h, d} = node.getBBox();
|
||||
const [a, W] = node.getArgMod(1.75 * node.padding, h + d);
|
||||
const t = node.thickness * Math.sin(a) * .9;
|
||||
node.adaptor.setStyle(child, 'border-bottom', node.em(node.thickness) + ' solid');
|
||||
const strike = node.adjustBorder(node.html('mjx-ustrike', {style: {
|
||||
width: node.em(W),
|
||||
transform: 'translateX(' + node.em(t) + ') rotate(' + node.fixed(-a) + 'rad)',
|
||||
}}));
|
||||
node.adaptor.append(node.chtml, strike);
|
||||
},
|
||||
bbox: (node) => {
|
||||
const p = node.padding / 2;
|
||||
const t = node.thickness;
|
||||
return [2 * p, p, p + t, 3 * p + t];
|
||||
},
|
||||
border: (node) => [0, 0, node.thickness, 0],
|
||||
remove: 'bottom'
|
||||
}],
|
||||
|
||||
Notation.Arrow('up'),
|
||||
Notation.Arrow('down'),
|
||||
Notation.Arrow('left'),
|
||||
Notation.Arrow('right'),
|
||||
|
||||
Notation.Arrow('updown'),
|
||||
Notation.Arrow('leftright'),
|
||||
|
||||
Notation.DiagonalArrow('updiagonal'), // backward compatibility
|
||||
Notation.DiagonalArrow('northeast'),
|
||||
Notation.DiagonalArrow('southeast'),
|
||||
Notation.DiagonalArrow('northwest'),
|
||||
Notation.DiagonalArrow('southwest'),
|
||||
|
||||
Notation.DiagonalArrow('northeastsouthwest'),
|
||||
Notation.DiagonalArrow('northwestsoutheast'),
|
||||
|
||||
['longdiv', {
|
||||
//
|
||||
// Use a line along the top followed by a half ellipse at the left
|
||||
//
|
||||
renderer: (node, child) => {
|
||||
const adaptor = node.adaptor;
|
||||
adaptor.setStyle(child, 'border-top', node.em(node.thickness) + ' solid');
|
||||
const arc = adaptor.append(node.chtml, node.html('dbox'));
|
||||
const t = node.thickness;
|
||||
const p = node.padding;
|
||||
if (t !== Notation.THICKNESS) {
|
||||
adaptor.setStyle(arc, 'border-width', node.em(t));
|
||||
}
|
||||
if (p !== Notation.PADDING) {
|
||||
adaptor.setStyle(arc, 'left', node.em(-1.5 * p));
|
||||
adaptor.setStyle(arc, 'width', node.em(3 * p));
|
||||
adaptor.setStyle(arc, 'clip-path', 'inset(0 0 0 ' + node.em(1.5 * p) + ')');
|
||||
}
|
||||
},
|
||||
bbox: (node) => {
|
||||
const p = node.padding;
|
||||
const t = node.thickness;
|
||||
return [p + t, p, p, 2 * p + t / 2];
|
||||
}
|
||||
}],
|
||||
|
||||
['radical', {
|
||||
//
|
||||
// Use the msqrt rendering, but remove the extra space due to the radical
|
||||
// (it is added in at the end, so other notations overlap the root)
|
||||
//
|
||||
renderer: (node, child) => {
|
||||
node.msqrt.toCHTML(child);
|
||||
const TRBL = node.sqrtTRBL();
|
||||
node.adaptor.setStyle(node.msqrt.chtml, 'margin', TRBL.map(x => node.em(-x)).join(' '));
|
||||
},
|
||||
//
|
||||
// Create the needed msqrt wrapper
|
||||
//
|
||||
init: (node) => {
|
||||
node.msqrt = node.createMsqrt(node.childNodes[0]);
|
||||
},
|
||||
//
|
||||
// Add back in the padding for the square root
|
||||
//
|
||||
bbox: (node) => node.sqrtTRBL(),
|
||||
//
|
||||
// This notation replaces the child
|
||||
//
|
||||
renderChild: true
|
||||
}]
|
||||
|
||||
] as Notation.DefPair<CHTMLmenclose<any, any, any>, any>[]);
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const adaptor = this.adaptor;
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
//
|
||||
// Create a box for the child (that can have padding and borders added by the notations)
|
||||
// and add the child HTML into it
|
||||
//
|
||||
const block = adaptor.append(chtml, this.html('mjx-box')) as N;
|
||||
if (this.renderChild) {
|
||||
this.renderChild(this, block);
|
||||
} else {
|
||||
this.childNodes[0].toCHTML(block);
|
||||
}
|
||||
//
|
||||
// Render all the notations for this menclose element
|
||||
//
|
||||
for (const name of Object.keys(this.notations)) {
|
||||
const notation = this.notations[name];
|
||||
!notation.renderChild && notation.renderer(this, block);
|
||||
}
|
||||
//
|
||||
// Add the needed padding, if any
|
||||
//
|
||||
const pbox = this.getPadding();
|
||||
for (const name of Notation.sideNames) {
|
||||
const i = Notation.sideIndex[name];
|
||||
pbox[i] > 0 && adaptor.setStyle(block, 'padding-' + name, this.em(pbox[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* Create an arrow using HTML elements
|
||||
*
|
||||
* @param {number} w The length of the arrow
|
||||
* @param {number} a The angle for the arrow
|
||||
* @param {boolean} double True if this is a double-headed arrow
|
||||
* @param {string} offset 'X' for vertical arrow, 'Y' for horizontal
|
||||
* @param {number} dist Distance to translate in the offset direction
|
||||
* @return {N} The newly created arrow
|
||||
*/
|
||||
public arrow(w: number, a: number, double: boolean, offset: string = '', dist: number = 0): N {
|
||||
const W = this.getBBox().w;
|
||||
const style = {width: this.em(w)} as OptionList;
|
||||
if (W !== w) {
|
||||
style.left = this.em((W - w) / 2);
|
||||
}
|
||||
if (a) {
|
||||
style.transform = 'rotate(' + this.fixed(a) + 'rad)';
|
||||
}
|
||||
const arrow = this.html('mjx-arrow', {style: style}, [
|
||||
this.html('mjx-aline'), this.html('mjx-rthead'), this.html('mjx-rbhead')
|
||||
]);
|
||||
if (double) {
|
||||
this.adaptor.append(arrow, this.html('mjx-lthead'));
|
||||
this.adaptor.append(arrow, this.html('mjx-lbhead'));
|
||||
this.adaptor.setAttribute(arrow, 'double', 'true');
|
||||
}
|
||||
this.adjustArrow(arrow, double);
|
||||
this.moveArrow(arrow, offset, dist);
|
||||
return arrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} arrow The arrow whose thickness and arrow head is to be adjusted
|
||||
* @param {boolean} double True if the arrow is double-headed
|
||||
*/
|
||||
protected adjustArrow(arrow: N, double: boolean) {
|
||||
const t = this.thickness;
|
||||
const head = this.arrowhead;
|
||||
if (head.x === Notation.ARROWX && head.y === Notation.ARROWY &&
|
||||
head.dx === Notation.ARROWDX && t === Notation.THICKNESS) return;
|
||||
const [x, y] = [t * head.x, t * head.y].map(x => this.em(x));
|
||||
const a = Angle(head.dx, head.y);
|
||||
const [line, rthead, rbhead, lthead, lbhead] = this.adaptor.childNodes(arrow);
|
||||
this.adjustHead(rthead, [y, '0', '1px', x], a);
|
||||
this.adjustHead(rbhead, ['1px', '0', y, x], '-' + a);
|
||||
this.adjustHead(lthead, [y, x, '1px', '0'], '-' + a);
|
||||
this.adjustHead(lbhead, ['1px', x, y, '0'], a);
|
||||
this.adjustLine(line, t, head.x, double);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} head The piece of arrow head to be adjusted
|
||||
* @param {string[]} border The border sizes [T, R, B, L]
|
||||
* @param {string} a The skew angle for the piece
|
||||
*/
|
||||
protected adjustHead(head: N, border: string[], a: string) {
|
||||
if (head) {
|
||||
this.adaptor.setStyle(head, 'border-width', border.join(' '));
|
||||
this.adaptor.setStyle(head, 'transform', 'skewX(' + a + 'rad)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} line The arrow shaft to be adjusted
|
||||
* @param {number} t The arrow shaft thickness
|
||||
* @param {number} x The arrow head x size
|
||||
* @param {boolean} double True if the arrow is double-headed
|
||||
*/
|
||||
protected adjustLine(line: N, t: number, x: number, double: boolean) {
|
||||
this.adaptor.setStyle(line, 'borderTop', this.em(t) + ' solid');
|
||||
this.adaptor.setStyle(line, 'top', this.em(-t / 2));
|
||||
this.adaptor.setStyle(line, 'right', this.em(t * (x - 1)));
|
||||
if (double) {
|
||||
this.adaptor.setStyle(line, 'left', this.em(t * (x - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} arrow The arrow whose position is to be adjusted
|
||||
* @param {string} offset The direction to move the arrow
|
||||
* @param {number} d The distance to translate in that direction
|
||||
*/
|
||||
protected moveArrow(arrow: N, offset: string, d: number) {
|
||||
if (!d) return;
|
||||
const transform = this.adaptor.getStyle(arrow, 'transform');
|
||||
this.adaptor.setStyle(
|
||||
arrow, 'transform', `translate${offset}(${this.em(-d)})${(transform ? ' ' + transform : '')}`
|
||||
);
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* @param {N} node The HTML element whose border width must be
|
||||
* adjusted if the thickness isn't the default
|
||||
* @return {N} The adjusted element
|
||||
*/
|
||||
public adjustBorder(node: N): N {
|
||||
if (this.thickness !== Notation.THICKNESS) {
|
||||
this.adaptor.setStyle(node, 'borderWidth', this.em(this.thickness));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} shape The svg element whose stroke-thickness must be
|
||||
* adjusted if the thickness isn't the default
|
||||
* @return {N} The adjusted element
|
||||
*/
|
||||
public adjustThickness(shape: N): N {
|
||||
if (this.thickness !== Notation.THICKNESS) {
|
||||
this.adaptor.setStyle(shape, 'strokeWidth', this.fixed(this.thickness));
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* @param {number} m A number to be shown with a fixed number of digits
|
||||
* @param {number=} n The number of digits to use
|
||||
* @return {string} The formatted number
|
||||
*/
|
||||
public fixed(m: number, n: number = 3): string {
|
||||
if (Math.abs(m) < .0006) {
|
||||
return '0';
|
||||
}
|
||||
return m.toFixed(n).replace(/\.?0+$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* (make it public so it can be called by the notation functions)
|
||||
*/
|
||||
public em(m: number) {
|
||||
return super.em(m);
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmfenced wrapper for the MmlMfenced object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMfencedMixin} from '../../common/Wrappers/mfenced.js';
|
||||
import {MmlMfenced} from '../../../core/MmlTree/MmlNodes/mfenced.js';
|
||||
import {CHTMLinferredMrow} from './mrow.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmfenced wrapper for the MmlMfenced object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLmfenced<N, T, D> extends CommonMfencedMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mfenced wrapper
|
||||
*/
|
||||
public static kind = MmlMfenced.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
(this.mrow as CHTMLinferredMrow<N, T, D>).toCHTML(chtml);
|
||||
}
|
||||
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmfrac wrapper for the MmlMfrac object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMfracMixin} from '../../common/Wrappers/mfrac.js';
|
||||
import {MmlMfrac} from '../../../core/MmlTree/MmlNodes/mfrac.js';
|
||||
import {CHTMLmo} from './mo.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {OptionList} from '../../../util/Options.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmfrac wrapper for the MmlMfrac object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLmfrac<N, T, D> extends CommonMfracMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mfrac wrapper
|
||||
*/
|
||||
public static kind = MmlMfrac.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-frac': {
|
||||
display: 'inline-block',
|
||||
'vertical-align': '0.17em', // axis_height - 1.5 * rule_thickness
|
||||
padding: '0 .22em' // nulldelimiterspace + .1 (for line's -.1em margin)
|
||||
},
|
||||
'mjx-frac[type="d"]': {
|
||||
'vertical-align': '.04em' // axis_height - 3.5 * rule_thickness
|
||||
},
|
||||
'mjx-frac[delims]': {
|
||||
padding: '0 .1em' // .1 (for line's -.1em margin)
|
||||
},
|
||||
'mjx-frac[atop]': {
|
||||
padding: '0 .12em' // nulldelimiterspace
|
||||
},
|
||||
'mjx-frac[atop][delims]': {
|
||||
padding: '0'
|
||||
},
|
||||
'mjx-dtable': {
|
||||
display: 'inline-table',
|
||||
width: '100%'
|
||||
},
|
||||
'mjx-dtable > *': {
|
||||
'font-size': '2000%'
|
||||
},
|
||||
'mjx-dbox': {
|
||||
display: 'block',
|
||||
'font-size': '5%'
|
||||
},
|
||||
'mjx-num': {
|
||||
display: 'block',
|
||||
'text-align': 'center'
|
||||
},
|
||||
'mjx-den': {
|
||||
display: 'block',
|
||||
'text-align': 'center'
|
||||
},
|
||||
'mjx-mfrac[bevelled] > mjx-num': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
'mjx-mfrac[bevelled] > mjx-den': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
|
||||
'mjx-den[align="right"], mjx-num[align="right"]': {
|
||||
'text-align': 'right'
|
||||
},
|
||||
'mjx-den[align="left"], mjx-num[align="left"]': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
|
||||
'mjx-nstrut': {
|
||||
display: 'inline-block',
|
||||
height: '.054em', // num2 - a - 1.5t
|
||||
width: 0,
|
||||
'vertical-align': '-.054em' // ditto
|
||||
},
|
||||
'mjx-nstrut[type="d"]': {
|
||||
height: '.217em', // num1 - a - 3.5t
|
||||
'vertical-align': '-.217em', // ditto
|
||||
},
|
||||
'mjx-dstrut': {
|
||||
display: 'inline-block',
|
||||
height: '.505em', // denom2 + a - 1.5t
|
||||
width: 0
|
||||
},
|
||||
'mjx-dstrut[type="d"]': {
|
||||
height: '.726em', // denom1 + a - 3.5t
|
||||
},
|
||||
|
||||
'mjx-line': {
|
||||
display: 'block',
|
||||
'box-sizing': 'border-box',
|
||||
'min-height': '1px',
|
||||
height: '.06em', // t = rule_thickness
|
||||
'border-top': '.06em solid', // t
|
||||
margin: '.06em -.1em', // t
|
||||
overflow: 'hidden'
|
||||
},
|
||||
'mjx-line[type="d"]': {
|
||||
margin: '.18em -.1em' // 3t
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* An mop element to use for bevelled fractions
|
||||
*/
|
||||
public bevel: CHTMLmo<N, T, D>;
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
this.standardCHTMLnode(parent);
|
||||
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
|
||||
const display = this.isDisplay();
|
||||
if (bevelled) {
|
||||
this.makeBevelled(display);
|
||||
} else {
|
||||
const thickness = this.length2em(String(linethickness), .06);
|
||||
if (thickness === 0) {
|
||||
this.makeAtop(display);
|
||||
} else {
|
||||
this.makeFraction(display, thickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} display True when fraction is in display mode
|
||||
* @param {number} t The rule line thickness
|
||||
*/
|
||||
protected makeFraction(display: boolean, t: number) {
|
||||
const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
|
||||
const withDelims = this.node.getProperty('withDelims');
|
||||
//
|
||||
// Attributes to set for the different elements making up the fraction
|
||||
//
|
||||
const attr = (display ? {type: 'd'} : {}) as OptionList;
|
||||
const fattr = (withDelims ? {...attr, delims: 'true'} : {...attr}) as OptionList;
|
||||
const nattr = (numalign !== 'center' ? {align: numalign} : {}) as OptionList;
|
||||
const dattr = (denomalign !== 'center' ? {align: denomalign} : {}) as OptionList;
|
||||
const dsattr = {...attr}, nsattr = {...attr};
|
||||
//
|
||||
// Set the styles to handle the linethickness, if needed
|
||||
//
|
||||
const tex = this.font.params;
|
||||
if (t !== .06) {
|
||||
const a = tex.axis_height;
|
||||
const tEm = this.em(t);
|
||||
const {T, u, v} = this.getTUV(display, t);
|
||||
const m = (display ? this.em(3 * t) : tEm) + ' -.1em';
|
||||
attr.style = {height: tEm, 'border-top': tEm + ' solid', margin: m};
|
||||
const nh = this.em(Math.max(0, u));
|
||||
nsattr.style = {height: nh, 'vertical-align': '-' + nh};
|
||||
dsattr.style = {height: this.em(Math.max(0, v))};
|
||||
fattr.style = {'vertical-align': this.em(a - T)};
|
||||
}
|
||||
//
|
||||
// Create the DOM tree
|
||||
//
|
||||
let num, den;
|
||||
this.adaptor.append(this.chtml, this.html('mjx-frac', fattr, [
|
||||
num = this.html('mjx-num', nattr, [this.html('mjx-nstrut', nsattr)]),
|
||||
this.html('mjx-dbox', {}, [
|
||||
this.html('mjx-dtable', {}, [
|
||||
this.html('mjx-line', attr),
|
||||
this.html('mjx-row', {}, [
|
||||
den = this.html('mjx-den', dattr, [this.html('mjx-dstrut', dsattr)])
|
||||
])
|
||||
])
|
||||
])
|
||||
]));
|
||||
this.childNodes[0].toCHTML(num);
|
||||
this.childNodes[1].toCHTML(den);
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} display True when fraction is in display mode
|
||||
*/
|
||||
protected makeAtop(display: boolean) {
|
||||
const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
|
||||
const withDelims = this.node.getProperty('withDelims');
|
||||
//
|
||||
// Attributes to set for the different elements making up the fraction
|
||||
//
|
||||
const attr = (display ? {type: 'd', atop: true} : {atop: true}) as OptionList;
|
||||
const fattr = (withDelims ? {...attr, delims: true} : {...attr}) as OptionList;
|
||||
const nattr = (numalign !== 'center' ? {align: numalign} : {}) as OptionList;
|
||||
const dattr = (denomalign !== 'center' ? {align: denomalign} : {}) as OptionList;
|
||||
//
|
||||
// Determine sparation and positioning
|
||||
//
|
||||
const {v, q} = this.getUVQ(display);
|
||||
nattr.style = {'padding-bottom': this.em(q)};
|
||||
fattr.style = {'vertical-align': this.em(-v)};
|
||||
//
|
||||
// Create the DOM tree
|
||||
//
|
||||
let num, den;
|
||||
this.adaptor.append(this.chtml, this.html('mjx-frac', fattr, [
|
||||
num = this.html('mjx-num', nattr),
|
||||
den = this.html('mjx-den', dattr)
|
||||
]));
|
||||
this.childNodes[0].toCHTML(num);
|
||||
this.childNodes[1].toCHTML(den);
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {boolean} display True when fraction is in display mode
|
||||
*/
|
||||
protected makeBevelled(display: boolean) {
|
||||
const adaptor = this.adaptor;
|
||||
//
|
||||
// Create HTML tree
|
||||
//
|
||||
adaptor.setAttribute(this.chtml, 'bevelled', 'ture');
|
||||
const num = adaptor.append(this.chtml, this.html('mjx-num'));
|
||||
this.childNodes[0].toCHTML(num);
|
||||
this.bevel.toCHTML(this.chtml);
|
||||
const den = adaptor.append(this.chtml, this.html('mjx-den'));
|
||||
this.childNodes[1].toCHTML(den);
|
||||
//
|
||||
// Place the parts
|
||||
//
|
||||
const {u, v, delta, nbox, dbox} = this.getBevelData(display);
|
||||
if (u) {
|
||||
adaptor.setStyle(num, 'verticalAlign', this.em(u / nbox.scale));
|
||||
}
|
||||
if (v) {
|
||||
adaptor.setStyle(den, 'verticalAlign', this.em(v / dbox.scale));
|
||||
}
|
||||
const dx = this.em(-delta / 2);
|
||||
adaptor.setStyle(this.bevel.chtml, 'marginLeft', dx);
|
||||
adaptor.setStyle(this.bevel.chtml, 'marginRight', dx);
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmglyph wrapper for the MmlMglyph object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMglyphMixin} from '../../common/Wrappers/mglyph.js';
|
||||
import {MmlMglyph} from '../../../core/MmlTree/MmlNodes/mglyph.js';
|
||||
import {CHTMLTextNode} from './TextNode.js';
|
||||
import {StyleList, StyleData} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmglyph wrapper for the MmlMglyph object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmglyph<N, T, D> extends
|
||||
CommonMglyphMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mglyph wrapper
|
||||
*/
|
||||
public static kind = MmlMglyph.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mglyph > img': {
|
||||
display: 'inline-block',
|
||||
border: 0,
|
||||
padding: 0
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
if (this.charWrapper) {
|
||||
(this.charWrapper as CHTMLTextNode<N, T, D>).toCHTML(chtml);
|
||||
return;
|
||||
}
|
||||
const {src, alt} = this.node.attributes.getList('src', 'alt');
|
||||
const styles: StyleData = {
|
||||
width: this.em(this.width),
|
||||
height: this.em(this.height)
|
||||
};
|
||||
if (this.valign) {
|
||||
styles.verticalAlign = this.em(this.valign);
|
||||
}
|
||||
const img = this.html('img', {src: src, style: styles, alt: alt, title: alt});
|
||||
this.adaptor.append(chtml, img);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmi wrapper for the MmlMi object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMiMixin} from '../../common/Wrappers/mi.js';
|
||||
import {MmlMi} from '../../../core/MmlTree/MmlNodes/mi.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmi wrapper for the MmlMi object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmi<N, T, D> extends
|
||||
CommonMiMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mi wrapper
|
||||
*/
|
||||
public static kind = MmlMi.prototype.kind;
|
||||
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmmultiscripts wrapper for the MmlMmultiscripts object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, Constructor} from '../Wrapper.js';
|
||||
import {CHTMLmsubsup} from './msubsup.js';
|
||||
import {CommonMmultiscriptsMixin} from '../../common/Wrappers/mmultiscripts.js';
|
||||
import {MmlMmultiscripts} from '../../../core/MmlTree/MmlNodes/mmultiscripts.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {split} from '../../../util/string.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmmultiscripts wrapper for the MmlMmultiscripts object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmmultiscripts<N, T, D> extends
|
||||
CommonMmultiscriptsMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLmsubsup<any, any, any>>>(CHTMLmsubsup) {
|
||||
|
||||
/**
|
||||
* The mmultiscripts wrapper
|
||||
*/
|
||||
public static kind = MmlMmultiscripts.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-prescripts': {
|
||||
display: 'inline-table',
|
||||
'padding-left': '.05em' // scriptspace
|
||||
},
|
||||
'mjx-scripts': {
|
||||
display: 'inline-table',
|
||||
'padding-right': '.05em' // scriptspace
|
||||
},
|
||||
'mjx-prescripts > mjx-row > mjx-cell': {
|
||||
'text-align': 'right'
|
||||
},
|
||||
'[script-align="left"] > mjx-row > mjx-cell': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
'[script-align="center"] > mjx-row > mjx-cell': {
|
||||
'text-align': 'center'
|
||||
},
|
||||
'[script-align="right"] > mjx-row > mjx-cell': {
|
||||
'text-align': 'right'
|
||||
}
|
||||
};
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
const data = this.scriptData;
|
||||
//
|
||||
// Get the alignment for the scripts
|
||||
//
|
||||
const scriptalign = this.node.getProperty('scriptalign') || 'right left';
|
||||
const [preAlign, postAlign] = split(scriptalign + ' ' + scriptalign);
|
||||
//
|
||||
// Combine the bounding boxes of the pre- and post-scripts,
|
||||
// and get the resulting baseline offsets
|
||||
//
|
||||
const sub = this.combinePrePost(data.sub, data.psub);
|
||||
const sup = this.combinePrePost(data.sup, data.psup);
|
||||
const [u, v] = this.getUVQ(sub, sup);
|
||||
//
|
||||
// Place the pre-scripts, then the base, then the post-scripts
|
||||
//
|
||||
if (data.numPrescripts) {
|
||||
const scripts = this.addScripts(u, -v, true, data.psub, data.psup, this.firstPrescript, data.numPrescripts);
|
||||
preAlign !== 'right' && this.adaptor.setAttribute(scripts, 'script-align', preAlign);
|
||||
}
|
||||
this.childNodes[0].toCHTML(chtml);
|
||||
if (data.numScripts) {
|
||||
const scripts = this.addScripts(u, -v, false, data.sub, data.sup, 1, data.numScripts);
|
||||
postAlign !== 'left' && this.adaptor.setAttribute(scripts, 'script-align', postAlign);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a table with the super and subscripts properly separated and aligned.
|
||||
*
|
||||
* @param {number} u The baseline offset for the superscripts
|
||||
* @param {number} v The baseline offset for the subscripts
|
||||
* @param {boolean} isPre True for prescripts, false for scripts
|
||||
* @param {BBox} sub The subscript bounding box
|
||||
* @param {BBox} sup The superscript bounding box
|
||||
* @param {number} i The starting index for the scripts
|
||||
* @param {number} n The number of sub/super-scripts
|
||||
* @return {N} The script table for these scripts
|
||||
*/
|
||||
protected addScripts(u: number, v: number, isPre: boolean, sub: BBox, sup: BBox, i: number, n: number): N {
|
||||
const adaptor = this.adaptor;
|
||||
const q = (u - sup.d) + (v - sub.h); // separation of scripts
|
||||
const U = (u < 0 && v === 0 ? sub.h + u : u); // vertical offset of table
|
||||
const rowdef = (q > 0 ? {style: {height: this.em(q)}} : {});
|
||||
const tabledef = (U ? {style: {'vertical-align': this.em(U)}} : {});
|
||||
const supRow = this.html('mjx-row');
|
||||
const sepRow = this.html('mjx-row', rowdef);
|
||||
const subRow = this.html('mjx-row');
|
||||
const name = 'mjx-' + (isPre ? 'pre' : '') + 'scripts';
|
||||
let m = i + 2 * n;
|
||||
while (i < m) {
|
||||
this.childNodes[i++].toCHTML(adaptor.append(subRow, this.html('mjx-cell')) as N);
|
||||
this.childNodes[i++].toCHTML(adaptor.append(supRow, this.html('mjx-cell')) as N);
|
||||
}
|
||||
return adaptor.append(this.chtml, this.html(name, tabledef, [supRow, sepRow, subRow]));
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmn wrapper for the MmlMn object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMnMixin} from '../../common/Wrappers/mn.js';
|
||||
import {MmlMn} from '../../../core/MmlTree/MmlNodes/mn.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmn wrapper for the MmlMn object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmn<N, T, D> extends
|
||||
CommonMnMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mn wrapper
|
||||
*/
|
||||
public static kind = MmlMn.prototype.kind;
|
||||
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmo wrapper for the MmlMo object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor, StringMap} from '../Wrapper.js';
|
||||
import {CommonMoMixin, DirectionVH} from '../../common/Wrappers/mo.js';
|
||||
import {MmlMo} from '../../../core/MmlTree/MmlNodes/mo.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {DIRECTION} from '../FontData.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmo wrapper for the MmlMo object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmo<N, T, D> extends
|
||||
CommonMoMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mo wrapper
|
||||
*/
|
||||
public static kind = MmlMo.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-stretchy-h': {
|
||||
display: 'inline-table',
|
||||
width: '100%'
|
||||
},
|
||||
'mjx-stretchy-h > *': {
|
||||
display: 'table-cell',
|
||||
width: 0
|
||||
},
|
||||
'mjx-stretchy-h > * > mjx-c': {
|
||||
display: 'inline-block',
|
||||
transform: 'scalex(1.0000001)' // improves blink positioning
|
||||
},
|
||||
'mjx-stretchy-h > * > mjx-c::before': {
|
||||
display: 'inline-block',
|
||||
width: 'initial'
|
||||
},
|
||||
'mjx-stretchy-h > mjx-ext': {
|
||||
'/* IE */ overflow': 'hidden',
|
||||
'/* others */ overflow': 'clip visible',
|
||||
width: '100%'
|
||||
},
|
||||
'mjx-stretchy-h > mjx-ext > mjx-c::before': {
|
||||
transform: 'scalex(500)'
|
||||
},
|
||||
'mjx-stretchy-h > mjx-ext > mjx-c': {
|
||||
width: 0
|
||||
},
|
||||
'mjx-stretchy-h > mjx-beg > mjx-c': {
|
||||
'margin-right': '-.1em'
|
||||
},
|
||||
'mjx-stretchy-h > mjx-end > mjx-c': {
|
||||
'margin-left': '-.1em'
|
||||
},
|
||||
|
||||
'mjx-stretchy-v': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
'mjx-stretchy-v > *': {
|
||||
display: 'block'
|
||||
},
|
||||
'mjx-stretchy-v > mjx-beg': {
|
||||
height: 0
|
||||
},
|
||||
'mjx-stretchy-v > mjx-end > mjx-c': {
|
||||
display: 'block'
|
||||
},
|
||||
'mjx-stretchy-v > * > mjx-c': {
|
||||
transform: 'scaley(1.0000001)', // improves Firefox and blink positioning
|
||||
'transform-origin': 'left center',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
'mjx-stretchy-v > mjx-ext': {
|
||||
display: 'block',
|
||||
height: '100%',
|
||||
'box-sizing': 'border-box',
|
||||
border: '0px solid transparent',
|
||||
'/* IE */ overflow': 'hidden',
|
||||
'/* others */ overflow': 'visible clip',
|
||||
},
|
||||
'mjx-stretchy-v > mjx-ext > mjx-c::before': {
|
||||
width: 'initial',
|
||||
'box-sizing': 'border-box'
|
||||
},
|
||||
'mjx-stretchy-v > mjx-ext > mjx-c': {
|
||||
transform: 'scaleY(500) translateY(.075em)',
|
||||
overflow: 'visible'
|
||||
},
|
||||
'mjx-mark': {
|
||||
display: 'inline-block',
|
||||
height: '0px'
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const attributes = this.node.attributes;
|
||||
const symmetric = (attributes.get('symmetric') as boolean) && this.stretch.dir !== DIRECTION.Horizontal;
|
||||
const stretchy = this.stretch.dir !== DIRECTION.None;
|
||||
if (stretchy && this.size === null) {
|
||||
this.getStretchedVariant([]);
|
||||
}
|
||||
let chtml = this.standardCHTMLnode(parent);
|
||||
if (stretchy && this.size < 0) {
|
||||
this.stretchHTML(chtml);
|
||||
} else {
|
||||
if (symmetric || attributes.get('largeop')) {
|
||||
const u = this.em(this.getCenterOffset());
|
||||
if (u !== '0') {
|
||||
this.adaptor.setStyle(chtml, 'verticalAlign', u);
|
||||
}
|
||||
}
|
||||
if (this.node.getProperty('mathaccent')) {
|
||||
this.adaptor.setStyle(chtml, 'width', '0');
|
||||
this.adaptor.setStyle(chtml, 'margin-left', this.em(this.getAccentOffset()));
|
||||
}
|
||||
for (const child of this.childNodes) {
|
||||
child.toCHTML(chtml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the HTML for a multi-character stretchy delimiter
|
||||
*
|
||||
* @param {N} chtml The parent element in which to put the delimiter
|
||||
*/
|
||||
protected stretchHTML(chtml: N) {
|
||||
const c = this.getText().codePointAt(0);
|
||||
this.font.delimUsage.add(c);
|
||||
this.childNodes[0].markUsed();
|
||||
const delim = this.stretch;
|
||||
const stretch = delim.stretch;
|
||||
const content: N[] = [];
|
||||
//
|
||||
// Set up the beginning, extension, and end pieces
|
||||
//
|
||||
if (stretch[0]) {
|
||||
content.push(this.html('mjx-beg', {}, [this.html('mjx-c')]));
|
||||
}
|
||||
content.push(this.html('mjx-ext', {}, [this.html('mjx-c')]));
|
||||
if (stretch.length === 4) {
|
||||
//
|
||||
// Braces have a middle and second extensible piece
|
||||
//
|
||||
content.push(
|
||||
this.html('mjx-mid', {}, [this.html('mjx-c')]),
|
||||
this.html('mjx-ext', {}, [this.html('mjx-c')])
|
||||
);
|
||||
}
|
||||
if (stretch[2]) {
|
||||
content.push(this.html('mjx-end', {}, [this.html('mjx-c')]));
|
||||
}
|
||||
//
|
||||
// Set the styles needed
|
||||
//
|
||||
const styles: StringMap = {};
|
||||
const {h, d, w} = this.bbox;
|
||||
if (delim.dir === DIRECTION.Vertical) {
|
||||
//
|
||||
// Vertical needs an extra (empty) element to get vertical position right
|
||||
// in some browsers (e.g., Safari)
|
||||
//
|
||||
content.push(this.html('mjx-mark'));
|
||||
styles.height = this.em(h + d);
|
||||
styles.verticalAlign = this.em(-d);
|
||||
} else {
|
||||
styles.width = this.em(w);
|
||||
}
|
||||
//
|
||||
// Make the main element and add it to the parent
|
||||
//
|
||||
const dir = DirectionVH[delim.dir];
|
||||
const properties = {class: this.char(delim.c || c), style: styles};
|
||||
const html = this.html('mjx-stretchy-' + dir, properties, content);
|
||||
this.adaptor.append(chtml, html);
|
||||
}
|
||||
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmpadded wrapper for the MmlMpadded object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor, StringMap} from '../Wrapper.js';
|
||||
import {CommonMpaddedMixin} from '../../common/Wrappers/mpadded.js';
|
||||
import {MmlMpadded} from '../../../core/MmlTree/MmlNodes/mpadded.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmpadded wrapper for the MmlMpadded object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmpadded<N, T, D> extends
|
||||
CommonMpaddedMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mpadded wrapper
|
||||
*/
|
||||
public static kind = MmlMpadded.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mpadded': {
|
||||
display: 'inline-block'
|
||||
},
|
||||
'mjx-rbox': {
|
||||
display: 'inline-block',
|
||||
position: 'relative'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
let chtml = this.standardCHTMLnode(parent);
|
||||
const content: N[] = [];
|
||||
const style: StringMap = {};
|
||||
const [ , , W, dh, dd, dw, x, y, dx] = this.getDimens();
|
||||
//
|
||||
// If the width changed, set the width explicitly
|
||||
//
|
||||
if (dw) {
|
||||
style.width = this.em(W + dw);
|
||||
}
|
||||
//
|
||||
// If the height or depth changed, use margin to make the change
|
||||
//
|
||||
if (dh || dd) {
|
||||
style.margin = this.em(dh) + ' 0 ' + this.em(dd);
|
||||
}
|
||||
//
|
||||
// If there is a horizontal or vertical shift,
|
||||
// use relative positioning to move the contents
|
||||
//
|
||||
if (x + dx || y) {
|
||||
style.position = 'relative';
|
||||
const rbox = this.html('mjx-rbox', {
|
||||
style: {left: this.em(x + dx), top: this.em(-y), 'max-width': style.width}
|
||||
});
|
||||
if (x + dx && this.childNodes[0].getBBox().pwidth) {
|
||||
this.adaptor.setAttribute(rbox, 'width', 'full');
|
||||
this.adaptor.setStyle(rbox, 'left', this.em(x));
|
||||
}
|
||||
content.push(rbox);
|
||||
}
|
||||
//
|
||||
// Create the HTML with the proper styles and content
|
||||
//
|
||||
chtml = this.adaptor.append(chtml, this.html('mjx-block', {style: style}, content)) as N;
|
||||
for (const child of this.childNodes) {
|
||||
child.toCHTML(content[0] || chtml);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLMroot wrapper for the MmlMroot object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper} from '../Wrapper.js';
|
||||
import {CHTMLmsqrt} from './msqrt.js';
|
||||
import {CommonMrootMixin, MrootConstructor} from '../../common/Wrappers/mroot.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {MmlMroot} from '../../../core/MmlTree/MmlNodes/mroot.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmroot wrapper for the MmlMroot object (extends CHTMLmsqrt)
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLmroot<N, T, D> extends CommonMrootMixin<MrootConstructor>(CHTMLmsqrt) {
|
||||
|
||||
/**
|
||||
* The mroot wrapper
|
||||
*/
|
||||
public static kind = MmlMroot.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected addRoot(ROOT: N, root: CHTMLWrapper<N, T, D>, sbox: BBox, H: number) {
|
||||
root.toCHTML(ROOT);
|
||||
const [x, h, dx] = this.getRootDimens(sbox, H);
|
||||
this.adaptor.setStyle(ROOT, 'verticalAlign', this.em(h));
|
||||
this.adaptor.setStyle(ROOT, 'width', this.em(x));
|
||||
if (dx) {
|
||||
this.adaptor.setStyle(this.adaptor.firstChild(ROOT) as N, 'paddingLeft', this.em(dx));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmrow wrapper for the MmlMrow object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonMrowMixin} from '../../common/Wrappers/mrow.js';
|
||||
import {CommonInferredMrowMixin} from '../../common/Wrappers/mrow.js';
|
||||
import {MmlMrow, MmlInferredMrow} from '../../../core/MmlTree/MmlNodes/mrow.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmrow wrapper for the MmlMrow object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmrow<N, T, D> extends
|
||||
CommonMrowMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mrow wrapper
|
||||
*/
|
||||
public static kind = MmlMrow.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = (this.node.isInferred ? (this.chtml = parent) : this.standardCHTMLnode(parent));
|
||||
let hasNegative = false;
|
||||
for (const child of this.childNodes) {
|
||||
child.toCHTML(chtml);
|
||||
if (child.bbox.w < 0) {
|
||||
hasNegative = true;
|
||||
}
|
||||
}
|
||||
// FIXME: handle line breaks
|
||||
if (hasNegative) {
|
||||
const {w} = this.getBBox();
|
||||
if (w) {
|
||||
this.adaptor.setStyle(chtml, 'width', this.em(Math.max(0, w)));
|
||||
if (w < 0) {
|
||||
this.adaptor.setStyle(chtml, 'marginRight', this.em(w));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLinferredMrow wrapper for the MmlInferredMrow object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLinferredMrow<N, T, D> extends
|
||||
CommonInferredMrowMixin<Constructor<CHTMLmrow<any, any, any>>>(CHTMLmrow) {
|
||||
|
||||
/**
|
||||
* The inferred-mrow wrapper
|
||||
*/
|
||||
public static kind = MmlInferredMrow.prototype.kind;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLms wrapper for the MmlMs object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMsMixin} from '../../common/Wrappers/ms.js';
|
||||
import {MmlMs} from '../../../core/MmlTree/MmlNodes/ms.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLms wrapper for the MmlMs object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLms<N, T, D> extends
|
||||
CommonMsMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The ms wrapper
|
||||
*/
|
||||
public static kind = MmlMs.prototype.kind;
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmspace wrapper for the MmlMspace object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMspaceMixin} from '../../common/Wrappers/mspace.js';
|
||||
import {MmlMspace} from '../../../core/MmlTree/MmlNodes/mspace.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmspace wrapper for the MmlMspace object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmspace<N, T, D> extends
|
||||
CommonMspaceMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mspace wrapper
|
||||
*/
|
||||
public static kind = MmlMspace.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
let chtml = this.standardCHTMLnode(parent);
|
||||
let {w, h, d} = this.getBBox();
|
||||
if (w < 0) {
|
||||
this.adaptor.setStyle(chtml, 'marginRight', this.em(w));
|
||||
w = 0;
|
||||
}
|
||||
if (w) {
|
||||
this.adaptor.setStyle(chtml, 'width', this.em(w));
|
||||
}
|
||||
h = Math.max(0, h + d);
|
||||
if (h) {
|
||||
this.adaptor.setStyle(chtml, 'height', this.em(Math.max(0, h)));
|
||||
}
|
||||
if (d) {
|
||||
this.adaptor.setStyle(chtml, 'verticalAlign', this.em(-d));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmsqrt wrapper for the MmlMsqrt object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMsqrtMixin} from '../../common/Wrappers/msqrt.js';
|
||||
import {CHTMLmo} from './mo.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {MmlMsqrt} from '../../../core/MmlTree/MmlNodes/msqrt.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmsqrt wrapper for the MmlMsqrt object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLmsqrt<N, T, D> extends CommonMsqrtMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The msqrt wrapper
|
||||
*/
|
||||
public static kind = MmlMsqrt.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-root': {
|
||||
display: 'inline-block',
|
||||
'white-space': 'nowrap'
|
||||
},
|
||||
'mjx-surd': {
|
||||
display: 'inline-block',
|
||||
'vertical-align': 'top'
|
||||
},
|
||||
'mjx-sqrt': {
|
||||
display: 'inline-block',
|
||||
'padding-top': '.07em'
|
||||
},
|
||||
'mjx-sqrt > mjx-box': {
|
||||
'border-top': '.07em solid'
|
||||
},
|
||||
'mjx-sqrt.mjx-tall > mjx-box': {
|
||||
'padding-left': '.3em',
|
||||
'margin-left': '-.3em'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const surd = this.childNodes[this.surd] as CHTMLmo<N, T, D>;
|
||||
const base = this.childNodes[this.base];
|
||||
//
|
||||
// Get the parameters for the spacing of the parts
|
||||
//
|
||||
const sbox = surd.getBBox();
|
||||
const bbox = base.getOuterBBox();
|
||||
const [ , q] = this.getPQ(sbox);
|
||||
const t = this.font.params.rule_thickness;
|
||||
const H = bbox.h + q + t;
|
||||
//
|
||||
// Create the HTML structure for the root
|
||||
//
|
||||
const CHTML = this.standardCHTMLnode(parent);
|
||||
let SURD, BASE, ROOT, root;
|
||||
if (this.root != null) {
|
||||
ROOT = this.adaptor.append(CHTML, this.html('mjx-root')) as N;
|
||||
root = this.childNodes[this.root];
|
||||
}
|
||||
const SQRT = this.adaptor.append(CHTML, this.html('mjx-sqrt', {}, [
|
||||
SURD = this.html('mjx-surd'),
|
||||
BASE = this.html('mjx-box', {style: {paddingTop: this.em(q)}})
|
||||
])) as N;
|
||||
//
|
||||
// Add the child content
|
||||
//
|
||||
this.addRoot(ROOT, root, sbox, H);
|
||||
surd.toCHTML(SURD);
|
||||
base.toCHTML(BASE);
|
||||
if (surd.size < 0) {
|
||||
//
|
||||
// size < 0 means surd is multi-character. The angle glyph at the
|
||||
// top is hard to align with the horizontal line, so overlap them
|
||||
// using CSS.
|
||||
//
|
||||
this.adaptor.addClass(SQRT, 'mjx-tall');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add root HTML (overridden in mroot)
|
||||
*
|
||||
* @param {N} ROOT The container for the root
|
||||
* @param {CHTMLWrapper} root The wrapped MML root content
|
||||
* @param {BBox} sbox The bounding box of the surd
|
||||
* @param {number} H The height of the root as a whole
|
||||
*/
|
||||
protected addRoot(_ROOT: N, _root: CHTMLWrapper<N, T, D>, _sbox: BBox, _H: number) {
|
||||
}
|
||||
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmsubsup wrapper for the MmlMsubsup object
|
||||
* and the special cases CHTMLmsub and CHTMLmsup
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, Constructor} from '../Wrapper.js';
|
||||
import {CHTMLscriptbase} from './scriptbase.js';
|
||||
import {CommonMsubMixin} from '../../common/Wrappers/msubsup.js';
|
||||
import {CommonMsupMixin} from '../../common/Wrappers/msubsup.js';
|
||||
import {CommonMsubsupMixin} from '../../common/Wrappers/msubsup.js';
|
||||
import {MmlMsubsup, MmlMsub, MmlMsup} from '../../../core/MmlTree/MmlNodes/msubsup.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmsub wrapper for the MmlMsub object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmsub<N, T, D> extends
|
||||
CommonMsubMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLscriptbase<any, any, any>>>(CHTMLscriptbase) {
|
||||
|
||||
/**
|
||||
* The msub wrapper
|
||||
*/
|
||||
public static kind = MmlMsub.prototype.kind;
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmsup wrapper for the MmlMsup object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmsup<N, T, D> extends
|
||||
CommonMsupMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLscriptbase<any, any, any>>>(CHTMLscriptbase) {
|
||||
|
||||
/**
|
||||
* The msup wrapper
|
||||
*/
|
||||
public static kind = MmlMsup.prototype.kind;
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmsubsup wrapper for the MmlMsubsup object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmsubsup<N, T, D> extends
|
||||
CommonMsubsupMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLscriptbase<any, any, any>>>(CHTMLscriptbase) {
|
||||
|
||||
/**
|
||||
* The msubsup wrapper
|
||||
*/
|
||||
public static kind = MmlMsubsup.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-script': {
|
||||
display: 'inline-block',
|
||||
'padding-right': '.05em', // scriptspace
|
||||
'padding-left': '.033em' // extra_ic
|
||||
},
|
||||
'mjx-script > mjx-spacer': {
|
||||
display: 'block'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const adaptor = this.adaptor;
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
const [base, sup, sub] = [this.baseChild, this.supChild, this.subChild];
|
||||
const [ , v, q] = this.getUVQ();
|
||||
const style = {'vertical-align': this.em(v)};
|
||||
base.toCHTML(chtml);
|
||||
const stack = adaptor.append(chtml, this.html('mjx-script', {style})) as N;
|
||||
sup.toCHTML(stack);
|
||||
adaptor.append(stack, this.html('mjx-spacer', {style: {'margin-top': this.em(q)}}));
|
||||
sub.toCHTML(stack);
|
||||
const ic = this.getAdjustedIc();
|
||||
if (ic) {
|
||||
adaptor.setStyle(sup.chtml, 'marginLeft', this.em(ic / sup.bbox.rscale));
|
||||
}
|
||||
if (this.baseRemoveIc) {
|
||||
adaptor.setStyle(stack, 'marginLeft', this.em(-this.baseIc));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmtable wrapper for the MmlMtable object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CHTMLWrapperFactory} from '../WrapperFactory.js';
|
||||
import {CommonMtableMixin} from '../../common/Wrappers/mtable.js';
|
||||
import {CHTMLmtr} from './mtr.js';
|
||||
import {CHTMLmtd} from './mtd.js';
|
||||
import {MmlMtable} from '../../../core/MmlTree/MmlNodes/mtable.js';
|
||||
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
import {isPercent} from '../../../util/string.js';
|
||||
import {OptionList} from '../../../util/Options.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmtable wrapper for the MmlMtable object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLmtable<N, T, D> extends
|
||||
CommonMtableMixin<CHTMLmtd<any, any, any>, CHTMLmtr<any, any, any>, CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mtable wrapper
|
||||
*/
|
||||
public static kind = MmlMtable.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mtable': {
|
||||
'vertical-align': '.25em',
|
||||
'text-align': 'center',
|
||||
'position': 'relative',
|
||||
'box-sizing': 'border-box',
|
||||
'border-spacing': 0, // prevent this from being inherited from an outer table
|
||||
'border-collapse': 'collapse' // similarly here
|
||||
},
|
||||
'mjx-mstyle[size="s"] mjx-mtable': {
|
||||
'vertical-align': '.354em'
|
||||
},
|
||||
'mjx-labels': {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
'mjx-table': {
|
||||
'display': 'inline-block',
|
||||
'vertical-align': '-.5ex',
|
||||
'box-sizing': 'border-box'
|
||||
},
|
||||
'mjx-table > mjx-itable': {
|
||||
'vertical-align': 'middle',
|
||||
'text-align': 'left',
|
||||
'box-sizing': 'border-box'
|
||||
},
|
||||
'mjx-labels > mjx-itable': {
|
||||
position: 'absolute',
|
||||
top: 0
|
||||
},
|
||||
'mjx-mtable[justify="left"]': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
'mjx-mtable[justify="right"]': {
|
||||
'text-align': 'right'
|
||||
},
|
||||
'mjx-mtable[justify="left"][side="left"]': {
|
||||
'padding-right': '0 ! important'
|
||||
},
|
||||
'mjx-mtable[justify="left"][side="right"]': {
|
||||
'padding-left': '0 ! important'
|
||||
},
|
||||
'mjx-mtable[justify="right"][side="left"]': {
|
||||
'padding-right': '0 ! important'
|
||||
},
|
||||
'mjx-mtable[justify="right"][side="right"]': {
|
||||
'padding-left': '0 ! important'
|
||||
},
|
||||
'mjx-mtable[align]': {
|
||||
'vertical-align': 'baseline'
|
||||
},
|
||||
'mjx-mtable[align="top"] > mjx-table': {
|
||||
'vertical-align': 'top'
|
||||
},
|
||||
'mjx-mtable[align="bottom"] > mjx-table': {
|
||||
'vertical-align': 'bottom'
|
||||
},
|
||||
'mjx-mtable[side="right"] mjx-labels': {
|
||||
'min-width': '100%'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The column for labels
|
||||
*/
|
||||
public labels: N;
|
||||
|
||||
/**
|
||||
* The inner table DOM node
|
||||
*/
|
||||
public itable: N;
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
constructor(factory: CHTMLWrapperFactory<N, T, D>, node: MmlNode, parent: CHTMLWrapper<N, T, D> = null) {
|
||||
super(factory, node, parent);
|
||||
this.itable = this.html('mjx-itable');
|
||||
this.labels = this.html('mjx-itable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getAlignShift() {
|
||||
const data = super.getAlignShift();
|
||||
if (!this.isTop) {
|
||||
data[1] = 0;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
//
|
||||
// Create the rows inside an mjx-itable (which will be used to center the table on the math axis)
|
||||
//
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
this.adaptor.append(chtml, this.html('mjx-table', {}, [this.itable]));
|
||||
for (const child of this.childNodes) {
|
||||
child.toCHTML(this.itable);
|
||||
}
|
||||
//
|
||||
// Pad the rows of the table, if needed
|
||||
// Then set the column and row attributes for alignment, spacing, and lines
|
||||
// Finally, add the frame, if needed
|
||||
//
|
||||
this.padRows();
|
||||
this.handleColumnSpacing();
|
||||
this.handleColumnLines();
|
||||
this.handleColumnWidths();
|
||||
this.handleRowSpacing();
|
||||
this.handleRowLines();
|
||||
this.handleRowHeights();
|
||||
this.handleFrame();
|
||||
this.handleWidth();
|
||||
this.handleLabels();
|
||||
this.handleAlign();
|
||||
this.handleJustify();
|
||||
this.shiftColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move background color (if any) to inner itable node so that labeled tables are
|
||||
* only colored on the main part of the table.
|
||||
*/
|
||||
protected shiftColor() {
|
||||
const adaptor = this.adaptor;
|
||||
const color = adaptor.getStyle(this.chtml, 'backgroundColor');
|
||||
if (color) {
|
||||
adaptor.setStyle(this.chtml, 'backgroundColor', '');
|
||||
adaptor.setStyle(this.itable, 'backgroundColor', color);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* Pad any short rows with extra cells
|
||||
*/
|
||||
protected padRows() {
|
||||
const adaptor = this.adaptor;
|
||||
for (const row of adaptor.childNodes(this.itable) as N[]) {
|
||||
while (adaptor.childNodes(row).length < this.numCols) {
|
||||
adaptor.append(row, this.html('mjx-mtd', {'extra': true}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inter-column spacing for all columns
|
||||
* (Use frame spacing on the outsides, if needed, and use half the column spacing on each
|
||||
* neighboring column, so that if column lines are needed, they fall in the middle
|
||||
* of the column space.)
|
||||
*/
|
||||
protected handleColumnSpacing() {
|
||||
const scale = (this.childNodes[0] ? 1 / this.childNodes[0].getBBox().rscale : 1);
|
||||
const spacing = this.getEmHalfSpacing(this.fSpace[0], this.cSpace, scale);
|
||||
const frame = this.frame;
|
||||
//
|
||||
// For each row...
|
||||
//
|
||||
for (const row of this.tableRows) {
|
||||
let i = 0;
|
||||
//
|
||||
// For each cell in the row...
|
||||
//
|
||||
for (const cell of row.tableCells) {
|
||||
//
|
||||
// Get the left and right-hand spacing
|
||||
//
|
||||
const lspace = spacing[i++];
|
||||
const rspace = spacing[i];
|
||||
//
|
||||
// Set the style for the spacing, if it is needed, and isn't the
|
||||
// default already set in the mtd styles
|
||||
//
|
||||
const styleNode = (cell ? cell.chtml : this.adaptor.childNodes(row.chtml)[i] as N);
|
||||
if ((i > 1 && lspace !== '0.4em') || (frame && i === 1)) {
|
||||
this.adaptor.setStyle(styleNode, 'paddingLeft', lspace);
|
||||
}
|
||||
if ((i < this.numCols && rspace !== '0.4em') || (frame && i === this.numCols)) {
|
||||
this.adaptor.setStyle(styleNode, 'paddingRight', rspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add borders to the left of cells to make the column lines
|
||||
*/
|
||||
protected handleColumnLines() {
|
||||
if (this.node.attributes.get('columnlines') === 'none') return;
|
||||
const lines = this.getColumnAttributes('columnlines');
|
||||
for (const row of this.childNodes) {
|
||||
let i = 0;
|
||||
for (const cell of this.adaptor.childNodes(row.chtml).slice(1) as N[]) {
|
||||
const line = lines[i++];
|
||||
if (line === 'none') continue;
|
||||
this.adaptor.setStyle(cell, 'borderLeft', '.07em ' + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add widths to the cells for the column widths
|
||||
*/
|
||||
protected handleColumnWidths() {
|
||||
for (const row of this.childNodes) {
|
||||
let i = 0;
|
||||
for (const cell of this.adaptor.childNodes(row.chtml) as N[]) {
|
||||
const w = this.cWidths[i++];
|
||||
if (w !== null) {
|
||||
const width = (typeof w === 'number' ? this.em(w) : w);
|
||||
this.adaptor.setStyle(cell, 'width', width);
|
||||
this.adaptor.setStyle(cell, 'maxWidth', width);
|
||||
this.adaptor.setStyle(cell, 'minWidth', width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inter-row spacing for all rows
|
||||
* (Use frame spacing on the outsides, if needed, and use half the row spacing on each
|
||||
* neighboring row, so that if row lines are needed, they fall in the middle
|
||||
* of the row space.)
|
||||
*/
|
||||
protected handleRowSpacing() {
|
||||
const scale = (this.childNodes[0] ? 1 / this.childNodes[0].getBBox().rscale : 1);
|
||||
const spacing = this.getEmHalfSpacing(this.fSpace[1], this.rSpace, scale);
|
||||
const frame = this.frame;
|
||||
//
|
||||
// For each row...
|
||||
//
|
||||
let i = 0;
|
||||
for (const row of this.childNodes) {
|
||||
//
|
||||
// Get the top and bottom spacing
|
||||
//
|
||||
const tspace = spacing[i++];
|
||||
const bspace = spacing[i];
|
||||
//
|
||||
// For each cell in the row...
|
||||
//
|
||||
for (const cell of row.childNodes) {
|
||||
//
|
||||
// Set the style for the spacing, if it is needed, and isn't the
|
||||
// default already set in the mtd styles
|
||||
//
|
||||
if ((i > 1 && tspace !== '0.215em') || (frame && i === 1)) {
|
||||
this.adaptor.setStyle(cell.chtml, 'paddingTop', tspace);
|
||||
}
|
||||
if ((i < this.numRows && bspace !== '0.215em') || (frame && i === this.numRows)) {
|
||||
this.adaptor.setStyle(cell.chtml, 'paddingBottom', bspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add borders to the tops of cells to make the row lines
|
||||
*/
|
||||
protected handleRowLines() {
|
||||
if (this.node.attributes.get('rowlines') === 'none') return;
|
||||
const lines = this.getRowAttributes('rowlines');
|
||||
let i = 0;
|
||||
for (const row of this.childNodes.slice(1)) {
|
||||
const line = lines[i++];
|
||||
if (line === 'none') continue;
|
||||
for (const cell of this.adaptor.childNodes(row.chtml) as N[]) {
|
||||
this.adaptor.setStyle(cell, 'borderTop', '.07em ' + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust row heights for equal-sized rows
|
||||
*/
|
||||
protected handleRowHeights() {
|
||||
if (this.node.attributes.get('equalrows')) {
|
||||
this.handleEqualRows();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the heights of all rows to be the same, and properly center
|
||||
* baseline or axis rows within the newly sized
|
||||
*/
|
||||
protected handleEqualRows() {
|
||||
const space = this.getRowHalfSpacing();
|
||||
const {H, D, NH, ND} = this.getTableData();
|
||||
const HD = this.getEqualRowHeight();
|
||||
//
|
||||
// Loop through the rows and set their heights
|
||||
//
|
||||
for (let i = 0; i < this.numRows; i++) {
|
||||
const row = this.childNodes[i];
|
||||
this.setRowHeight(row, HD + space[i] + space[i + 1] + this.rLines[i]);
|
||||
if (HD !== NH[i] + ND[i]) {
|
||||
this.setRowBaseline(row, HD, (HD - H[i] + D[i]) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CHTMLWrapper} row The row whose height is to be set
|
||||
* @param {number} HD The height to be set for the row
|
||||
*/
|
||||
protected setRowHeight(row: CHTMLWrapper<N, T, D>, HD: number) {
|
||||
this.adaptor.setStyle(row.chtml, 'height', this.em(HD));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the baseline is in the right position for cells
|
||||
* that are row aligned to baseline ot axis
|
||||
*
|
||||
* @param {CHTMLWrapper} row The row to be set
|
||||
* @param {number} HD The total height+depth for the row
|
||||
* @param {number] D The new depth for the row
|
||||
*/
|
||||
protected setRowBaseline(row: CHTMLWrapper<N, T, D>, HD: number, D: number) {
|
||||
const ralign = row.node.attributes.get('rowalign') as string;
|
||||
//
|
||||
// Loop through the cells and set the strut height and depth.
|
||||
// The strut is the last element in the cell.
|
||||
//
|
||||
for (const cell of row.childNodes) {
|
||||
if (this.setCellBaseline(cell, ralign, HD, D)) break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the baseline is in the correct place for cells aligned on baseline or axis
|
||||
*
|
||||
* @param {CHTMLWrapper} cell The cell to modify
|
||||
* @param {string} ralign The alignment of the row
|
||||
* @param {number} HD The total height+depth for the row
|
||||
* @param {number] D The new depth for the row
|
||||
* @return {boolean} True if no other cells in this row need to be processed
|
||||
*/
|
||||
protected setCellBaseline(cell: CHTMLWrapper<N, T, D>, ralign: string, HD: number, D: number): boolean {
|
||||
const calign = cell.node.attributes.get('rowalign');
|
||||
if (calign === 'baseline' || calign === 'axis') {
|
||||
const adaptor = this.adaptor;
|
||||
const child = adaptor.lastChild(cell.chtml) as N;
|
||||
adaptor.setStyle(child, 'height', this.em(HD));
|
||||
adaptor.setStyle(child, 'verticalAlign', this.em(-D));
|
||||
const row = cell.parent;
|
||||
if ((!row.node.isKind('mlabeledtr') || cell !== row.childNodes[0]) &&
|
||||
(ralign === 'baseline' || ralign === 'axis')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a frame to the mtable, if needed
|
||||
*/
|
||||
protected handleFrame() {
|
||||
if (this.frame && this.fLine) {
|
||||
this.adaptor.setStyle(this.itable, 'border', '.07em ' + this.node.attributes.get('frame'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle percentage widths and fixed widths
|
||||
*/
|
||||
protected handleWidth() {
|
||||
const adaptor = this.adaptor;
|
||||
const {w, L, R} = this.getBBox();
|
||||
adaptor.setStyle(this.chtml, 'minWidth', this.em(L + w + R));
|
||||
let W = this.node.attributes.get('width') as string;
|
||||
if (isPercent(W)) {
|
||||
adaptor.setStyle(this.chtml, 'width', '');
|
||||
adaptor.setAttribute(this.chtml, 'width', 'full');
|
||||
} else if (!this.hasLabels) {
|
||||
if (W === 'auto') return;
|
||||
W = this.em(this.length2em(W) + 2 * this.fLine);
|
||||
}
|
||||
const table = adaptor.firstChild(this.chtml) as N;
|
||||
adaptor.setStyle(table, 'width', W);
|
||||
adaptor.setStyle(table, 'minWidth', this.em(w));
|
||||
if (L || R) {
|
||||
adaptor.setStyle(this.chtml, 'margin', '');
|
||||
const style = (this.node.attributes.get('data-width-includes-label') ? 'padding' : 'margin');
|
||||
if (L === R) {
|
||||
adaptor.setStyle(table, style, '0 ' + this.em(R));
|
||||
} else {
|
||||
adaptor.setStyle(table, style, '0 ' + this.em(R) + ' 0 ' + this.em(L));
|
||||
}
|
||||
}
|
||||
adaptor.setAttribute(this.itable, 'width', 'full');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle alignment of table to surrounding baseline
|
||||
*/
|
||||
protected handleAlign() {
|
||||
const [align, row] = this.getAlignmentRow();
|
||||
if (row === null) {
|
||||
if (align !== 'axis') {
|
||||
this.adaptor.setAttribute(this.chtml, 'align', align);
|
||||
}
|
||||
} else {
|
||||
const y = this.getVerticalPosition(row, align);
|
||||
this.adaptor.setAttribute(this.chtml, 'align', 'top');
|
||||
this.adaptor.setStyle(this.chtml, 'verticalAlign', this.em(y));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the alignment of the table
|
||||
*/
|
||||
protected handleJustify() {
|
||||
const align = this.getAlignShift()[0];
|
||||
if (align !== 'center') {
|
||||
this.adaptor.setAttribute(this.chtml, 'justify', align);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* Handle addition of labels to the table
|
||||
*/
|
||||
protected handleLabels() {
|
||||
if (!this.hasLabels) return;
|
||||
const labels = this.labels;
|
||||
const attributes = this.node.attributes;
|
||||
const adaptor = this.adaptor;
|
||||
//
|
||||
// Set the side for the labels
|
||||
//
|
||||
const side = attributes.get('side') as string;
|
||||
adaptor.setAttribute(this.chtml, 'side', side);
|
||||
adaptor.setAttribute(labels, 'align', side);
|
||||
adaptor.setStyle(labels, side, '0');
|
||||
//
|
||||
// Make sure labels don't overlap table
|
||||
//
|
||||
const [align, shift] = this.addLabelPadding(side);
|
||||
//
|
||||
// Handle indentation
|
||||
//
|
||||
if (shift) {
|
||||
const table = adaptor.firstChild(this.chtml) as N;
|
||||
this.setIndent(table, align, shift);
|
||||
}
|
||||
//
|
||||
// Add the labels to the table
|
||||
//
|
||||
this.updateRowHeights();
|
||||
this.addLabelSpacing();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} side The side for the labels
|
||||
* @return {[string, number]} The alignment and shift values
|
||||
*/
|
||||
protected addLabelPadding(side: string): [string, number] {
|
||||
const [ , align, shift] = this.getPadAlignShift(side);
|
||||
const styles: OptionList = {};
|
||||
if (side === 'right' && !this.node.attributes.get('data-width-includes-label')) {
|
||||
const W = this.node.attributes.get('width') as string;
|
||||
const {w, L, R} = this.getBBox();
|
||||
styles.style = {
|
||||
width: (isPercent(W) ? 'calc(' + W + ' + ' + this.em(L + R) + ')' : this.em(L + w + R))
|
||||
};
|
||||
}
|
||||
this.adaptor.append(this.chtml, this.html('mjx-labels', styles, [this.labels]));
|
||||
return [align, shift] as [string, number];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any rows that are not naturally tall enough for the labels,
|
||||
* and set the baseline for labels that are baseline aligned.
|
||||
*/
|
||||
protected updateRowHeights() {
|
||||
let {H, D, NH, ND} = this.getTableData();
|
||||
const space = this.getRowHalfSpacing();
|
||||
for (let i = 0; i < this.numRows; i++) {
|
||||
const row = this.childNodes[i];
|
||||
this.setRowHeight(row, H[i] + D[i] + space[i] + space[i + 1] + this.rLines[i]);
|
||||
if (H[i] !== NH[i] || D[i] !== ND[i]) {
|
||||
this.setRowBaseline(row, H[i] + D[i], D[i]);
|
||||
} else if (row.node.isKind('mlabeledtr')) {
|
||||
this.setCellBaseline(row.childNodes[0], '', H[i] + D[i], D[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add spacing elements between the label rows to align them with the rest of the table
|
||||
*/
|
||||
protected addLabelSpacing() {
|
||||
const adaptor = this.adaptor;
|
||||
const equal = this.node.attributes.get('equalrows') as boolean;
|
||||
const {H, D} = this.getTableData();
|
||||
const HD = (equal ? this.getEqualRowHeight() : 0);
|
||||
const space = this.getRowHalfSpacing();
|
||||
//
|
||||
// Start with frame size and add in spacing, height and depth,
|
||||
// and line thickness for each non-labeled row.
|
||||
//
|
||||
let h = this.fLine;
|
||||
let current = adaptor.firstChild(this.labels) as N;
|
||||
for (let i = 0; i < this.numRows; i++) {
|
||||
const row = this.childNodes[i];
|
||||
if (row.node.isKind('mlabeledtr')) {
|
||||
h && adaptor.insert(this.html('mjx-mtr', {style: {height: this.em(h)}}), current);
|
||||
adaptor.setStyle(current, 'height', this.em((equal ? HD : H[i] + D[i]) + space[i] + space[i + 1]));
|
||||
current = adaptor.next(current) as N;
|
||||
h = this.rLines[i];
|
||||
} else {
|
||||
h += space[i] + (equal ? HD : H[i] + D[i]) + space[i + 1] + this.rLines[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmtd wrapper for the MmlMtd object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMtdMixin} from '../../common/Wrappers/mtd.js';
|
||||
import {MmlMtd} from '../../../core/MmlTree/MmlNodes/mtd.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmtd wrapper for the MmlMtd object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmtd<N, T, D> extends
|
||||
CommonMtdMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mtd wrapper
|
||||
*/
|
||||
public static kind = MmlMtd.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mtd': {
|
||||
display: 'table-cell',
|
||||
'text-align': 'center',
|
||||
'padding': '.215em .4em'
|
||||
},
|
||||
'mjx-mtd:first-child': {
|
||||
'padding-left': 0
|
||||
},
|
||||
'mjx-mtd:last-child': {
|
||||
'padding-right': 0
|
||||
},
|
||||
'mjx-mtable > * > mjx-itable > *:first-child > mjx-mtd': {
|
||||
'padding-top': 0
|
||||
},
|
||||
'mjx-mtable > * > mjx-itable > *:last-child > mjx-mtd': {
|
||||
'padding-bottom': 0
|
||||
},
|
||||
'mjx-tstrut': {
|
||||
display: 'inline-block',
|
||||
height: '1em',
|
||||
'vertical-align': '-.25em'
|
||||
},
|
||||
'mjx-labels[align="left"] > mjx-mtr > mjx-mtd': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
'mjx-labels[align="right"] > mjx-mtr > mjx-mtd': {
|
||||
'text-align': 'right'
|
||||
},
|
||||
'mjx-mtd[extra]': {
|
||||
padding: 0
|
||||
},
|
||||
'mjx-mtd[rowalign="top"]': {
|
||||
'vertical-align': 'top'
|
||||
},
|
||||
'mjx-mtd[rowalign="center"]': {
|
||||
'vertical-align': 'middle'
|
||||
},
|
||||
'mjx-mtd[rowalign="bottom"]': {
|
||||
'vertical-align': 'bottom'
|
||||
},
|
||||
'mjx-mtd[rowalign="baseline"]': {
|
||||
'vertical-align': 'baseline'
|
||||
},
|
||||
'mjx-mtd[rowalign="axis"]': {
|
||||
'vertical-align': '.25em'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
super.toCHTML(parent);
|
||||
const ralign = this.node.attributes.get('rowalign') as string;
|
||||
const calign = this.node.attributes.get('columnalign') as string;
|
||||
const palign = this.parent.node.attributes.get('rowalign') as string;
|
||||
if (ralign !== palign) {
|
||||
this.adaptor.setAttribute(this.chtml, 'rowalign', ralign);
|
||||
}
|
||||
if (calign !== 'center' &&
|
||||
(this.parent.kind !== 'mlabeledtr' || this !== this.parent.childNodes[0] ||
|
||||
calign !== this.parent.parent.node.attributes.get('side'))) {
|
||||
this.adaptor.setStyle(this.chtml, 'textAlign', calign);
|
||||
}
|
||||
//
|
||||
// If we are using minimum row heights,
|
||||
// Include a strut to force minimum height and depth
|
||||
//
|
||||
if (this.parent.parent.node.getProperty('useHeight')) {
|
||||
this.adaptor.append(this.chtml, this.html('mjx-tstrut'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2019-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmtext wrapper for the MmlMtext object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonMtextMixin} from '../../common/Wrappers/mtext.js';
|
||||
import {MmlMtext} from '../../../core/MmlTree/MmlNodes/mtext.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmtext wrapper for the MmlMtext object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmtext<N, T, D> extends
|
||||
CommonMtextMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mtext wrapper
|
||||
*/
|
||||
public static kind = MmlMtext.prototype.kind;
|
||||
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmtr wrapper for the MmlMtr object
|
||||
* and CHTMLmlabeledtr for MmlMlabeledtr
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonMtrMixin} from '../../common/Wrappers/mtr.js';
|
||||
import {CommonMlabeledtrMixin} from '../../common/Wrappers/mtr.js';
|
||||
import {CHTMLmtable} from './mtable.js';
|
||||
import {CHTMLmtd} from './mtd.js';
|
||||
import {MmlMtr, MmlMlabeledtr} from '../../../core/MmlTree/MmlNodes/mtr.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmtr wrapper for the MmlMtr object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmtr<N, T, D> extends
|
||||
CommonMtrMixin<CHTMLmtd<any, any, any>, CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The mtr wrapper
|
||||
*/
|
||||
public static kind = MmlMtr.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mtr': {
|
||||
display: 'table-row',
|
||||
},
|
||||
'mjx-mtr[rowalign="top"] > mjx-mtd': {
|
||||
'vertical-align': 'top'
|
||||
},
|
||||
'mjx-mtr[rowalign="center"] > mjx-mtd': {
|
||||
'vertical-align': 'middle'
|
||||
},
|
||||
'mjx-mtr[rowalign="bottom"] > mjx-mtd': {
|
||||
'vertical-align': 'bottom'
|
||||
},
|
||||
'mjx-mtr[rowalign="baseline"] > mjx-mtd': {
|
||||
'vertical-align': 'baseline'
|
||||
},
|
||||
'mjx-mtr[rowalign="axis"] > mjx-mtd': {
|
||||
'vertical-align': '.25em'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
super.toCHTML(parent);
|
||||
const align = this.node.attributes.get('rowalign') as string;
|
||||
if (align !== 'baseline') {
|
||||
this.adaptor.setAttribute(this.chtml, 'rowalign', align);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLlabeledmtr wrapper for the MmlMlabeledtr object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLmlabeledtr<N, T, D> extends
|
||||
CommonMlabeledtrMixin<CHTMLmtd<any, any, any>, Constructor<CHTMLmtr<any, any, any>>>(CHTMLmtr) {
|
||||
|
||||
/**
|
||||
* The mlabeledtr wrapper
|
||||
*/
|
||||
public static kind = MmlMlabeledtr.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mlabeledtr': {
|
||||
display: 'table-row'
|
||||
},
|
||||
'mjx-mlabeledtr[rowalign="top"] > mjx-mtd': {
|
||||
'vertical-align': 'top'
|
||||
},
|
||||
'mjx-mlabeledtr[rowalign="center"] > mjx-mtd': {
|
||||
'vertical-align': 'middle'
|
||||
},
|
||||
'mjx-mlabeledtr[rowalign="bottom"] > mjx-mtd': {
|
||||
'vertical-align': 'bottom'
|
||||
},
|
||||
'mjx-mlabeledtr[rowalign="baseline"] > mjx-mtd': {
|
||||
'vertical-align': 'baseline'
|
||||
},
|
||||
'mjx-mlabeledtr[rowalign="axis"] > mjx-mtd': {
|
||||
'vertical-align': '.25em'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
super.toCHTML(parent);
|
||||
const child = this.adaptor.firstChild(this.chtml) as N;
|
||||
if (child) {
|
||||
//
|
||||
// Remove label and put it into the labels box inside a row
|
||||
//
|
||||
this.adaptor.remove(child);
|
||||
const align = this.node.attributes.get('rowalign') as string;
|
||||
const attr = (align !== 'baseline' && align !== 'axis' ? {rowalign: align} : {});
|
||||
const row = this.html('mjx-mtr', attr, [child]);
|
||||
this.adaptor.append((this.parent as CHTMLmtable<N, T, D>).labels, row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public markUsed() {
|
||||
super.markUsed();
|
||||
this.jax.wrapperUsage.add(CHTMLmtr.kind);
|
||||
}
|
||||
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLmunderover wrapper for the MmlMunderover object
|
||||
* and the special cases CHTMLmunder and CHTMLmsup
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, Constructor} from '../Wrapper.js';
|
||||
import {CHTMLmsubsup, CHTMLmsub, CHTMLmsup} from './msubsup.js';
|
||||
import {CommonMunderMixin} from '../../common/Wrappers/munderover.js';
|
||||
import {CommonMoverMixin} from '../../common/Wrappers/munderover.js';
|
||||
import {CommonMunderoverMixin} from '../../common/Wrappers/munderover.js';
|
||||
import {MmlMunderover, MmlMunder, MmlMover} from '../../../core/MmlTree/MmlNodes/munderover.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmunder wrapper for the MmlMunder object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmunder<N, T, D> extends
|
||||
CommonMunderMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLmsub<any, any, any>>>(CHTMLmsub) {
|
||||
|
||||
/**
|
||||
* The munder wrapper
|
||||
*/
|
||||
public static kind = MmlMunder.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-over': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
'mjx-munder:not([limits="false"])': {
|
||||
display: 'inline-table',
|
||||
},
|
||||
'mjx-munder > mjx-row': {
|
||||
'text-align': 'left'
|
||||
},
|
||||
'mjx-under': {
|
||||
'padding-bottom': '.1em' // big_op_spacing5
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
if (this.hasMovableLimits()) {
|
||||
super.toCHTML(parent);
|
||||
this.adaptor.setAttribute(this.chtml, 'limits', 'false');
|
||||
return;
|
||||
}
|
||||
this.chtml = this.standardCHTMLnode(parent);
|
||||
const base = this.adaptor.append(
|
||||
this.adaptor.append(this.chtml, this.html('mjx-row')) as N,
|
||||
this.html('mjx-base')
|
||||
) as N;
|
||||
const under = this.adaptor.append(
|
||||
this.adaptor.append(this.chtml, this.html('mjx-row')) as N,
|
||||
this.html('mjx-under')
|
||||
) as N;
|
||||
this.baseChild.toCHTML(base);
|
||||
this.scriptChild.toCHTML(under);
|
||||
const basebox = this.baseChild.getOuterBBox();
|
||||
const underbox = this.scriptChild.getOuterBBox();
|
||||
const k = this.getUnderKV(basebox, underbox)[0];
|
||||
const delta = (this.isLineBelow ? 0 : this.getDelta(true));
|
||||
this.adaptor.setStyle(under, 'paddingTop', this.em(k));
|
||||
this.setDeltaW([base, under], this.getDeltaW([basebox, underbox], [0, -delta]));
|
||||
this.adjustUnderDepth(under, underbox);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLmover wrapper for the MmlMover object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmover<N, T, D> extends
|
||||
CommonMoverMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLmsup<any, any, any>>>(CHTMLmsup) {
|
||||
|
||||
/**
|
||||
* The mover wrapper
|
||||
*/
|
||||
public static kind = MmlMover.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-mover:not([limits="false"])': {
|
||||
'padding-top': '.1em' // big_op_spacing5
|
||||
},
|
||||
'mjx-mover:not([limits="false"]) > *': {
|
||||
display: 'block',
|
||||
'text-align': 'left'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
if (this.hasMovableLimits()) {
|
||||
super.toCHTML(parent);
|
||||
this.adaptor.setAttribute(this.chtml, 'limits', 'false');
|
||||
return;
|
||||
}
|
||||
this.chtml = this.standardCHTMLnode(parent);
|
||||
const over = this.adaptor.append(this.chtml, this.html('mjx-over')) as N;
|
||||
const base = this.adaptor.append(this.chtml, this.html('mjx-base')) as N;
|
||||
this.scriptChild.toCHTML(over);
|
||||
this.baseChild.toCHTML(base);
|
||||
const overbox = this.scriptChild.getOuterBBox();
|
||||
const basebox = this.baseChild.getOuterBBox();
|
||||
this.adjustBaseHeight(base, basebox);
|
||||
const k = this.getOverKU(basebox, overbox)[0];
|
||||
const delta = (this.isLineAbove ? 0 : this.getDelta());
|
||||
this.adaptor.setStyle(over, 'paddingBottom', this.em(k));
|
||||
this.setDeltaW([base, over], this.getDeltaW([basebox, overbox], [0, delta]));
|
||||
this.adjustOverDepth(over, overbox);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/*
|
||||
* The CHTMLmunderover wrapper for the MmlMunderover object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLmunderover<N, T, D> extends
|
||||
CommonMunderoverMixin<CHTMLWrapper<any, any, any>, Constructor<CHTMLmsubsup<any, any, any>>>(CHTMLmsubsup) {
|
||||
|
||||
/**
|
||||
* The munderover wrapper
|
||||
*/
|
||||
public static kind = MmlMunderover.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-munderover:not([limits="false"])': {
|
||||
'padding-top': '.1em' // big_op_spacing5
|
||||
},
|
||||
'mjx-munderover:not([limits="false"]) > *': {
|
||||
display: 'block'
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
if (this.hasMovableLimits()) {
|
||||
super.toCHTML(parent);
|
||||
this.adaptor.setAttribute(this.chtml, 'limits', 'false');
|
||||
return;
|
||||
}
|
||||
this.chtml = this.standardCHTMLnode(parent);
|
||||
const over = this.adaptor.append(this.chtml, this.html('mjx-over')) as N;
|
||||
const table = this.adaptor.append(
|
||||
this.adaptor.append(this.chtml, this.html('mjx-box')) as N,
|
||||
this.html('mjx-munder')
|
||||
) as N;
|
||||
const base = this.adaptor.append(
|
||||
this.adaptor.append(table, this.html('mjx-row')) as N,
|
||||
this.html('mjx-base')
|
||||
) as N;
|
||||
const under = this.adaptor.append(
|
||||
this.adaptor.append(table, this.html('mjx-row')) as N,
|
||||
this.html('mjx-under')
|
||||
) as N;
|
||||
this.overChild.toCHTML(over);
|
||||
this.baseChild.toCHTML(base);
|
||||
this.underChild.toCHTML(under);
|
||||
const overbox = this.overChild.getOuterBBox();
|
||||
const basebox = this.baseChild.getOuterBBox();
|
||||
const underbox = this.underChild.getOuterBBox();
|
||||
this.adjustBaseHeight(base, basebox);
|
||||
const ok = this.getOverKU(basebox, overbox)[0];
|
||||
const uk = this.getUnderKV(basebox, underbox)[0];
|
||||
const delta = this.getDelta();
|
||||
this.adaptor.setStyle(over, 'paddingBottom', this.em(ok));
|
||||
this.adaptor.setStyle(under, 'paddingTop', this.em(uk));
|
||||
this.setDeltaW([base, under, over],
|
||||
this.getDeltaW([basebox, underbox, overbox],
|
||||
[0, this.isLineBelow ? 0 : -delta, this.isLineAbove ? 0 : delta]));
|
||||
this.adjustOverDepth(over, overbox);
|
||||
this.adjustUnderDepth(under, underbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure styles get output when called from munderover with movable limits
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public markUsed() {
|
||||
super.markUsed();
|
||||
this.jax.wrapperUsage.add(CHTMLmsubsup.kind);
|
||||
}
|
||||
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the a base class for CHTMLmsubsup, CHTMLmunderover
|
||||
* and their relatives. (Since munderover can become msubsup
|
||||
* when movablelimits is set, munderover needs to be able to
|
||||
* do the same thing as msubsup in some cases.)
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonScriptbaseMixin} from '../../common/Wrappers/scriptbase.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {StyleData} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* A base class for msup/msub/msubsup and munder/mover/munderover
|
||||
* wrapper implementations
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLscriptbase<N, T, D> extends
|
||||
CommonScriptbaseMixin<CHTMLWrapper<any, any, any>, CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The scriptbase wrapper
|
||||
*/
|
||||
public static kind = 'scriptbase';
|
||||
|
||||
/**
|
||||
* This gives the common output for msub and msup. It is overridden
|
||||
* for all the others (msubsup, munder, mover, munderover).
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
this.chtml = this.standardCHTMLnode(parent);
|
||||
const [x, v] = this.getOffset();
|
||||
const dx = x - (this.baseRemoveIc ? this.baseIc : 0);
|
||||
const style: StyleData = {'vertical-align': this.em(v)};
|
||||
if (dx) {
|
||||
style['margin-left'] = this.em(dx);
|
||||
}
|
||||
this.baseChild.toCHTML(this.chtml);
|
||||
this.scriptChild.toCHTML(this.adaptor.append(this.chtml, this.html('mjx-script', {style})) as N);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N[]} nodes The HTML elements to be centered in a stack
|
||||
* @param {number[]} dx The x offsets needed to center the elements
|
||||
*/
|
||||
protected setDeltaW(nodes: N[], dx: number[]) {
|
||||
for (let i = 0; i < dx.length; i++) {
|
||||
if (dx[i]) {
|
||||
this.adaptor.setStyle(nodes[i], 'paddingLeft', this.em(dx[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} over The HTML element for the overscript
|
||||
* @param {BBox} overbox The bbox for the overscript
|
||||
*/
|
||||
protected adjustOverDepth(over: N, overbox: BBox) {
|
||||
if (overbox.d >= 0) return;
|
||||
this.adaptor.setStyle(over, 'marginBottom', this.em(overbox.d * overbox.rscale));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} under The HTML element for the underscript
|
||||
* @param {BBox} underbox The bbox for the underscript
|
||||
*/
|
||||
protected adjustUnderDepth(under: N, underbox: BBox) {
|
||||
if (underbox.d >= 0) return;
|
||||
const adaptor = this.adaptor;
|
||||
const v = this.em(underbox.d);
|
||||
const box = this.html('mjx-box', {style: {'margin-bottom': v, 'vertical-align': v}});
|
||||
for (const child of adaptor.childNodes(adaptor.firstChild(under) as N) as N[]) {
|
||||
adaptor.append(box, child);
|
||||
}
|
||||
adaptor.append(adaptor.firstChild(under) as N, box);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} base The HTML element for the base
|
||||
* @param {BBox} basebox The bbox for the base
|
||||
*/
|
||||
protected adjustBaseHeight(base: N, basebox: BBox) {
|
||||
if (this.node.attributes.get('accent')) {
|
||||
const minH = this.font.params.x_height * basebox.scale;
|
||||
if (basebox.h < minH) {
|
||||
this.adaptor.setStyle(base, 'paddingTop', this.em(minH - basebox.h));
|
||||
basebox.h = minH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CHTMLsemantics wrapper for the MmlSemantics object
|
||||
* and the associated wrappers for annotations
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLWrapper, CHTMLConstructor} from '../Wrapper.js';
|
||||
import {CommonSemanticsMixin} from '../../common/Wrappers/semantics.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {MmlSemantics, MmlAnnotation, MmlAnnotationXML} from '../../../core/MmlTree/MmlNodes/semantics.js';
|
||||
import {XMLNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {StyleList} from '../../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLsemantics wrapper for the MmlSemantics object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
// @ts-ignore
|
||||
export class CHTMLsemantics<N, T, D> extends
|
||||
CommonSemanticsMixin<CHTMLConstructor<any, any, any>>(CHTMLWrapper) {
|
||||
|
||||
/**
|
||||
* The semantics wrapper
|
||||
*/
|
||||
public static kind = MmlSemantics.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
const chtml = this.standardCHTMLnode(parent);
|
||||
if (this.childNodes.length) {
|
||||
this.childNodes[0].toCHTML(chtml);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLannotation wrapper for the MmlAnnotation object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLannotation<N, T, D> extends CHTMLWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The annotation wrapper
|
||||
*/
|
||||
public static kind = MmlAnnotation.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
// FIXME: output as plain text
|
||||
super.toCHTML(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox() {
|
||||
// FIXME: compute using the DOM, if possible
|
||||
return this.bbox;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLannotationXML wrapper for the MmlAnnotationXML object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLannotationXML<N, T, D> extends CHTMLWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The annotation-xml wrapper
|
||||
*/
|
||||
public static kind = MmlAnnotationXML.prototype.kind;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static styles: StyleList = {
|
||||
'mjx-annotation-xml': {
|
||||
'font-family': 'initial',
|
||||
'line-height': 'normal'
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CHTMLxml wrapper for the XMLNode object
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
*/
|
||||
export class CHTMLxml<N, T, D> extends CHTMLWrapper<N, T, D> {
|
||||
|
||||
/**
|
||||
* The xml wrapper
|
||||
*/
|
||||
public static kind = XMLNode.prototype.kind;
|
||||
|
||||
/**
|
||||
* Don't set up inline-block styles for this
|
||||
*/
|
||||
public static autoStyle = false;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public toCHTML(parent: N) {
|
||||
this.chtml = this.adaptor.append(parent, this.adaptor.clone((this.node as XMLNode).getXML() as N));
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, _recompute: boolean = false) {
|
||||
const {w, h, d} = this.jax.measureXMLnode((this.node as XMLNode).getXML() as N);
|
||||
bbox.w = w;
|
||||
bbox.h = h;
|
||||
bbox.d = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected getStyles() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected getScale() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected getVariant() {}
|
||||
|
||||
}
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
+371
@@ -0,0 +1,371 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The MathJax TeXFont object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CHTMLFontData, CHTMLCharOptions, CHTMLVariantData, CHTMLDelimiterData, CHTMLFontDataClass,
|
||||
DelimiterMap, CharMapMap} from '../FontData.js';
|
||||
import {CommonTeXFontMixin} from '../../common/fonts/tex.js';
|
||||
import {StringMap} from '../Wrapper.js';
|
||||
|
||||
import {boldItalic} from './tex/bold-italic.js';
|
||||
import {bold} from './tex/bold.js';
|
||||
import {doubleStruck} from './tex/double-struck.js';
|
||||
import {frakturBold} from './tex/fraktur-bold.js';
|
||||
import {fraktur} from './tex/fraktur.js';
|
||||
import {italic} from './tex/italic.js';
|
||||
import {largeop} from './tex/largeop.js';
|
||||
import {monospace} from './tex/monospace.js';
|
||||
import {normal} from './tex/normal.js';
|
||||
import {sansSerifBoldItalic} from './tex/sans-serif-bold-italic.js';
|
||||
import {sansSerifBold} from './tex/sans-serif-bold.js';
|
||||
import {sansSerifItalic} from './tex/sans-serif-italic.js';
|
||||
import {sansSerif} from './tex/sans-serif.js';
|
||||
import {scriptBold} from './tex/script-bold.js';
|
||||
import {script} from './tex/script.js';
|
||||
import {smallop} from './tex/smallop.js';
|
||||
import {texCalligraphicBold} from './tex/tex-calligraphic-bold.js';
|
||||
import {texCalligraphic} from './tex/tex-calligraphic.js';
|
||||
import {texMathit} from './tex/tex-mathit.js';
|
||||
import {texOldstyleBold} from './tex/tex-oldstyle-bold.js';
|
||||
import {texOldstyle} from './tex/tex-oldstyle.js';
|
||||
import {texSize3} from './tex/tex-size3.js';
|
||||
import {texSize4} from './tex/tex-size4.js';
|
||||
import {texVariant} from './tex/tex-variant.js';
|
||||
|
||||
import {delimiters} from '../../common/fonts/tex/delimiters.js';
|
||||
|
||||
/*=================================================================================*/
|
||||
/**
|
||||
* The TeXFont class
|
||||
*/
|
||||
export class TeXFont extends
|
||||
CommonTeXFontMixin<CHTMLCharOptions, CHTMLVariantData, CHTMLDelimiterData, CHTMLFontDataClass>(CHTMLFontData) {
|
||||
|
||||
/**
|
||||
* Fonts to prefix any explicit ones
|
||||
*/
|
||||
protected static defaultCssFamilyPrefix = 'MJXZERO';
|
||||
|
||||
/**
|
||||
* The classes to use for each variant
|
||||
*/
|
||||
protected static defaultVariantClasses: StringMap = {
|
||||
'normal': 'mjx-n',
|
||||
'bold': 'mjx-b',
|
||||
'italic': 'mjx-i',
|
||||
'bold-italic': 'mjx-b mjx-i',
|
||||
'double-struck': 'mjx-ds mjx-b',
|
||||
'fraktur': 'mjx-fr',
|
||||
'bold-fraktur': 'mjx-fr mjx-b',
|
||||
'script': 'mjx-sc mjx-i',
|
||||
'bold-script': 'mjx-sc mjx-b mjx-i',
|
||||
'sans-serif': 'mjx-ss',
|
||||
'bold-sans-serif': 'mjx-ss mjx-b',
|
||||
'sans-serif-italic': 'mjx-ss mjx-i',
|
||||
'sans-serif-bold-italic': 'mjx-ss mjx-b mjx-i',
|
||||
'monospace': 'mjx-ty',
|
||||
'-smallop': 'mjx-sop',
|
||||
'-largeop': 'mjx-lop',
|
||||
'-size3': 'mjx-s3',
|
||||
'-size4': 'mjx-s4',
|
||||
'-tex-calligraphic': 'mjx-cal mjx-i',
|
||||
'-tex-bold-calligraphic': 'mjx-cal mjx-b',
|
||||
'-tex-mathit': 'mjx-mit mjx-i',
|
||||
'-tex-oldstyle': 'mjx-os',
|
||||
'-tex-bold-oldstyle': 'mjx-os mjx-b',
|
||||
'-tex-variant': 'mjx-var'
|
||||
};
|
||||
|
||||
/**
|
||||
* The letters that identify the default font for each varaint
|
||||
*/
|
||||
protected static defaultVariantLetters: StringMap = {
|
||||
'normal': '',
|
||||
'bold': 'B',
|
||||
'italic': 'MI',
|
||||
'bold-italic': 'BI',
|
||||
'double-struck': 'A',
|
||||
'fraktur': 'FR',
|
||||
'bold-fraktur': 'FRB',
|
||||
'script': 'SC',
|
||||
'bold-script': 'SCB',
|
||||
'sans-serif': 'SS',
|
||||
'bold-sans-serif': 'SSB',
|
||||
'sans-serif-italic': 'SSI',
|
||||
'sans-serif-bold-italic': 'SSBI',
|
||||
'monospace': 'T',
|
||||
'-smallop': 'S1',
|
||||
'-largeop': 'S2',
|
||||
'-size3': 'S3',
|
||||
'-size4': 'S4',
|
||||
'-tex-calligraphic': 'C',
|
||||
'-tex-bold-calligraphic': 'CB',
|
||||
'-tex-mathit': 'MI',
|
||||
'-tex-oldstyle': 'C',
|
||||
'-tex-bold-oldstyle': 'CB',
|
||||
'-tex-variant': 'A'
|
||||
};
|
||||
|
||||
/**
|
||||
* The stretchy delimiter data
|
||||
*/
|
||||
protected static defaultDelimiters: DelimiterMap<CHTMLDelimiterData> = delimiters;
|
||||
|
||||
/**
|
||||
* The character data by variant
|
||||
*/
|
||||
protected static defaultChars: CharMapMap<CHTMLCharOptions> = {
|
||||
'normal': normal,
|
||||
'bold': bold,
|
||||
'italic': italic,
|
||||
'bold-italic': boldItalic,
|
||||
'double-struck': doubleStruck,
|
||||
'fraktur': fraktur,
|
||||
'bold-fraktur': frakturBold,
|
||||
'script': script,
|
||||
'bold-script': scriptBold,
|
||||
'sans-serif': sansSerif,
|
||||
'bold-sans-serif': sansSerifBold,
|
||||
'sans-serif-italic': sansSerifItalic,
|
||||
'sans-serif-bold-italic': sansSerifBoldItalic,
|
||||
'monospace': monospace,
|
||||
'-smallop': smallop,
|
||||
'-largeop': largeop,
|
||||
'-size3': texSize3,
|
||||
'-size4': texSize4,
|
||||
'-tex-calligraphic': texCalligraphic,
|
||||
'-tex-bold-calligraphic': texCalligraphicBold,
|
||||
'-tex-mathit': texMathit,
|
||||
'-tex-oldstyle': texOldstyle,
|
||||
'-tex-bold-oldstyle': texOldstyleBold,
|
||||
'-tex-variant': texVariant
|
||||
};
|
||||
|
||||
/*=====================================================*/
|
||||
/**
|
||||
* The CSS styles needed for this font.
|
||||
*/
|
||||
protected static defaultStyles = {
|
||||
...CHTMLFontData.defaultStyles,
|
||||
|
||||
'.MJX-TEX': {
|
||||
'font-family': 'MJXZERO, MJXTEX'
|
||||
},
|
||||
|
||||
'.TEX-B': {
|
||||
'font-family': 'MJXZERO, MJXTEX-B'
|
||||
},
|
||||
|
||||
'.TEX-I': {
|
||||
'font-family': 'MJXZERO, MJXTEX-I'
|
||||
},
|
||||
|
||||
'.TEX-MI': {
|
||||
'font-family': 'MJXZERO, MJXTEX-MI'
|
||||
},
|
||||
|
||||
'.TEX-BI': {
|
||||
'font-family': 'MJXZERO, MJXTEX-BI'
|
||||
},
|
||||
|
||||
'.TEX-S1': {
|
||||
'font-family': 'MJXZERO, MJXTEX-S1'
|
||||
},
|
||||
|
||||
'.TEX-S2': {
|
||||
'font-family': 'MJXZERO, MJXTEX-S2'
|
||||
},
|
||||
|
||||
'.TEX-S3': {
|
||||
'font-family': 'MJXZERO, MJXTEX-S3'
|
||||
},
|
||||
|
||||
'.TEX-S4': {
|
||||
'font-family': 'MJXZERO, MJXTEX-S4'
|
||||
},
|
||||
|
||||
'.TEX-A': {
|
||||
'font-family': 'MJXZERO, MJXTEX-A'
|
||||
},
|
||||
|
||||
'.TEX-C': {
|
||||
'font-family': 'MJXZERO, MJXTEX-C'
|
||||
},
|
||||
|
||||
'.TEX-CB': {
|
||||
'font-family': 'MJXZERO, MJXTEX-CB'
|
||||
},
|
||||
|
||||
'.TEX-FR': {
|
||||
'font-family': 'MJXZERO, MJXTEX-FR'
|
||||
},
|
||||
|
||||
'.TEX-FRB': {
|
||||
'font-family': 'MJXZERO, MJXTEX-FRB'
|
||||
},
|
||||
|
||||
'.TEX-SS': {
|
||||
'font-family': 'MJXZERO, MJXTEX-SS'
|
||||
},
|
||||
|
||||
'.TEX-SSB': {
|
||||
'font-family': 'MJXZERO, MJXTEX-SSB'
|
||||
},
|
||||
|
||||
'.TEX-SSI': {
|
||||
'font-family': 'MJXZERO, MJXTEX-SSI'
|
||||
},
|
||||
|
||||
'.TEX-SC': {
|
||||
'font-family': 'MJXZERO, MJXTEX-SC'
|
||||
},
|
||||
|
||||
'.TEX-T': {
|
||||
'font-family': 'MJXZERO, MJXTEX-T'
|
||||
},
|
||||
|
||||
'.TEX-V': {
|
||||
'font-family': 'MJXZERO, MJXTEX-V'
|
||||
},
|
||||
|
||||
'.TEX-VB': {
|
||||
'font-family': 'MJXZERO, MJXTEX-VB'
|
||||
},
|
||||
|
||||
'mjx-stretchy-v mjx-c, mjx-stretchy-h mjx-c': {
|
||||
'font-family': 'MJXZERO, MJXTEX-S1, MJXTEX-S4, MJXTEX, MJXTEX-A ! important'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The default @font-face declarations with %%URL%% where the font path should go
|
||||
*/
|
||||
protected static defaultFonts = {
|
||||
...CHTMLFontData.defaultFonts,
|
||||
|
||||
'@font-face /* 1 */': {
|
||||
'font-family': 'MJXTEX',
|
||||
src: 'url("%%URL%%/MathJax_Main-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 2 */': {
|
||||
'font-family': 'MJXTEX-B',
|
||||
src: 'url("%%URL%%/MathJax_Main-Bold.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 3 */': {
|
||||
'font-family': 'MJXTEX-I',
|
||||
src: 'url("%%URL%%/MathJax_Math-Italic.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 4 */': {
|
||||
'font-family': 'MJXTEX-MI',
|
||||
src: 'url("%%URL%%/MathJax_Main-Italic.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 5 */': {
|
||||
'font-family': 'MJXTEX-BI',
|
||||
src: 'url("%%URL%%/MathJax_Math-BoldItalic.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 6 */': {
|
||||
'font-family': 'MJXTEX-S1',
|
||||
src: 'url("%%URL%%/MathJax_Size1-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 7 */': {
|
||||
'font-family': 'MJXTEX-S2',
|
||||
src: 'url("%%URL%%/MathJax_Size2-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 8 */': {
|
||||
'font-family': 'MJXTEX-S3',
|
||||
src: 'url("%%URL%%/MathJax_Size3-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 9 */': {
|
||||
'font-family': 'MJXTEX-S4',
|
||||
src: 'url("%%URL%%/MathJax_Size4-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 10 */': {
|
||||
'font-family': 'MJXTEX-A',
|
||||
src: 'url("%%URL%%/MathJax_AMS-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 11 */': {
|
||||
'font-family': 'MJXTEX-C',
|
||||
src: 'url("%%URL%%/MathJax_Calligraphic-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 12 */': {
|
||||
'font-family': 'MJXTEX-CB',
|
||||
src: 'url("%%URL%%/MathJax_Calligraphic-Bold.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 13 */': {
|
||||
'font-family': 'MJXTEX-FR',
|
||||
src: 'url("%%URL%%/MathJax_Fraktur-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 14 */': {
|
||||
'font-family': 'MJXTEX-FRB',
|
||||
src: 'url("%%URL%%/MathJax_Fraktur-Bold.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 15 */': {
|
||||
'font-family': 'MJXTEX-SS',
|
||||
src: 'url("%%URL%%/MathJax_SansSerif-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 16 */': {
|
||||
'font-family': 'MJXTEX-SSB',
|
||||
src: 'url("%%URL%%/MathJax_SansSerif-Bold.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 17 */': {
|
||||
'font-family': 'MJXTEX-SSI',
|
||||
src: 'url("%%URL%%/MathJax_SansSerif-Italic.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 18 */': {
|
||||
'font-family': 'MJXTEX-SC',
|
||||
src: 'url("%%URL%%/MathJax_Script-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 19 */': {
|
||||
'font-family': 'MJXTEX-T',
|
||||
src: 'url("%%URL%%/MathJax_Typewriter-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 20 */': {
|
||||
'font-family': 'MJXTEX-V',
|
||||
src: 'url("%%URL%%/MathJax_Vector-Regular.woff") format("woff")'
|
||||
},
|
||||
|
||||
'@font-face /* 21 */': {
|
||||
'font-family': 'MJXTEX-VB',
|
||||
src: 'url("%%URL%%/MathJax_Vector-Bold.woff") format("woff")'
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {boldItalic as font} from '../../../common/fonts/tex/bold-italic.js';
|
||||
|
||||
export const boldItalic: CHTMLCharMap = AddCSS(font, {
|
||||
0x131: {f: 'B'},
|
||||
0x237: {f: 'B'},
|
||||
0x2044: {c: '/'},
|
||||
0x2206: {c: '\\394'},
|
||||
0x29F8: {c: '/'},
|
||||
});
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {bold as font} from '../../../common/fonts/tex/bold.js';
|
||||
|
||||
export const bold: CHTMLCharMap = AddCSS(font, {
|
||||
0xB7: {c: '\\22C5'},
|
||||
0x131: {f: ''},
|
||||
0x237: {f: ''},
|
||||
0x2B9: {c: '\\2032'},
|
||||
0x2002: {c: ''},
|
||||
0x2003: {c: ''},
|
||||
0x2004: {c: ''},
|
||||
0x2005: {c: ''},
|
||||
0x2006: {c: ''},
|
||||
0x2009: {c: ''},
|
||||
0x200A: {c: ''},
|
||||
0x2015: {c: '\\2014'},
|
||||
0x2016: {c: '\\2225'},
|
||||
0x2017: {c: '_'},
|
||||
0x2022: {c: '\\2219'},
|
||||
0x2033: {c: '\\2032\\2032'},
|
||||
0x2034: {c: '\\2032\\2032\\2032'},
|
||||
0x203E: {c: '\\2C9'},
|
||||
0x2044: {c: '/'},
|
||||
0x2057: {c: '\\2032\\2032\\2032\\2032'},
|
||||
0x20D7: {c: '\\2192', f: 'VB'},
|
||||
0x219A: {c: '\\2190\\338'},
|
||||
0x219B: {c: '\\2192\\338'},
|
||||
0x21AE: {c: '\\2194\\338'},
|
||||
0x21CD: {c: '\\21D0\\338'},
|
||||
0x21CE: {c: '\\21D4\\338'},
|
||||
0x21CF: {c: '\\21D2\\338'},
|
||||
0x2204: {c: '\\2203\\338'},
|
||||
0x2206: {c: '\\394'},
|
||||
0x220C: {c: '\\220B\\338'},
|
||||
0x2224: {c: '\\2223\\338'},
|
||||
0x2226: {c: '\\2225\\338'},
|
||||
0x2241: {c: '\\223C\\338'},
|
||||
0x2244: {c: '\\2243\\338'},
|
||||
0x2247: {c: '\\2245\\338'},
|
||||
0x2249: {c: '\\2248\\338'},
|
||||
0x2262: {c: '\\2261\\338'},
|
||||
0x226D: {c: '\\224D\\338'},
|
||||
0x226E: {c: '<\\338'},
|
||||
0x226F: {c: '>\\338'},
|
||||
0x2270: {c: '\\2264\\338'},
|
||||
0x2271: {c: '\\2265\\338'},
|
||||
0x2280: {c: '\\227A\\338'},
|
||||
0x2281: {c: '\\227B\\338'},
|
||||
0x2284: {c: '\\2282\\338'},
|
||||
0x2285: {c: '\\2283\\338'},
|
||||
0x2288: {c: '\\2286\\338'},
|
||||
0x2289: {c: '\\2287\\338'},
|
||||
0x22AC: {c: '\\22A2\\338'},
|
||||
0x22AD: {c: '\\22A8\\338'},
|
||||
0x22E2: {c: '\\2291\\338'},
|
||||
0x22E3: {c: '\\2292\\338'},
|
||||
0x2329: {c: '\\27E8'},
|
||||
0x232A: {c: '\\27E9'},
|
||||
0x25B5: {c: '\\25B3'},
|
||||
0x25BF: {c: '\\25BD'},
|
||||
0x2758: {c: '\\2223'},
|
||||
0x29F8: {c: '/', f: 'BI'},
|
||||
0x2A2F: {c: '\\D7'},
|
||||
0x3008: {c: '\\27E8'},
|
||||
0x3009: {c: '\\27E9'},
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {doubleStruck} from '../../../common/fonts/tex/double-struck.js';
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {frakturBold as font} from '../../../common/fonts/tex/fraktur-bold.js';
|
||||
|
||||
export const frakturBold: CHTMLCharMap = AddCSS(font, {
|
||||
0x2044: {c: '/'},
|
||||
});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {fraktur as font} from '../../../common/fonts/tex/fraktur.js';
|
||||
|
||||
export const fraktur: CHTMLCharMap = AddCSS(font, {
|
||||
0x2044: {c: '/'},
|
||||
});
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {italic as font} from '../../../common/fonts/tex/italic.js';
|
||||
|
||||
export const italic: CHTMLCharMap = AddCSS(font, {
|
||||
0x2F: {f: 'I'},
|
||||
0x3DD: {c: '\\E008', f: 'A'},
|
||||
0x2015: {c: '\\2014'},
|
||||
0x2017: {c: '_'},
|
||||
0x2044: {c: '/', f: 'I'},
|
||||
0x2206: {c: '\\394', f: 'I'},
|
||||
0x29F8: {c: '/', f: 'I'},
|
||||
});
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {largeop as font} from '../../../common/fonts/tex/largeop.js';
|
||||
|
||||
export const largeop: CHTMLCharMap = AddCSS(font, {
|
||||
0x2016: {f: 'S1'},
|
||||
0x2044: {c: '/'},
|
||||
0x2191: {f: 'S1'},
|
||||
0x2193: {f: 'S1'},
|
||||
0x21D1: {f: 'S1'},
|
||||
0x21D3: {f: 'S1'},
|
||||
0x2223: {f: 'S1'},
|
||||
0x2225: {f: 'S1'},
|
||||
0x2329: {c: '\\27E8'},
|
||||
0x232A: {c: '\\27E9'},
|
||||
0x23D0: {f: 'S1'},
|
||||
0x2758: {c: '\\2223', f: 'S1'},
|
||||
0x2A0C: {c: '\\222C\\222C'},
|
||||
0x3008: {c: '\\27E8'},
|
||||
0x3009: {c: '\\27E9'},
|
||||
});
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {monospace as font} from '../../../common/fonts/tex/monospace.js';
|
||||
|
||||
export const monospace: CHTMLCharMap = AddCSS(font, {
|
||||
0x2B9: {c: '\\2032'},
|
||||
0x391: {c: 'A'},
|
||||
0x392: {c: 'B'},
|
||||
0x395: {c: 'E'},
|
||||
0x396: {c: 'Z'},
|
||||
0x397: {c: 'H'},
|
||||
0x399: {c: 'I'},
|
||||
0x39A: {c: 'K'},
|
||||
0x39C: {c: 'M'},
|
||||
0x39D: {c: 'N'},
|
||||
0x39F: {c: 'O'},
|
||||
0x3A1: {c: 'P'},
|
||||
0x3A4: {c: 'T'},
|
||||
0x3A7: {c: 'X'},
|
||||
0x2017: {c: '_'},
|
||||
0x2033: {c: '\\2032\\2032'},
|
||||
0x2034: {c: '\\2032\\2032\\2032'},
|
||||
0x2044: {c: '/'},
|
||||
0x2057: {c: '\\2032\\2032\\2032\\2032'},
|
||||
0x2206: {c: '\\394'},
|
||||
});
|
||||
+1043
File diff suppressed because it is too large
Load Diff
+23
@@ -0,0 +1,23 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {sansSerifBoldItalic as font} from '../../../common/fonts/tex/sans-serif-bold-italic.js';
|
||||
|
||||
export const sansSerifBoldItalic: CHTMLCharMap = AddCSS(font, {
|
||||
0x131: {f: 'SSB'},
|
||||
0x237: {f: 'SSB'},
|
||||
});
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {sansSerifBold as font} from '../../../common/fonts/tex/sans-serif-bold.js';
|
||||
|
||||
export const sansSerifBold: CHTMLCharMap = AddCSS(font, {
|
||||
0x2015: {c: '\\2014'},
|
||||
0x2017: {c: '_'},
|
||||
0x2044: {c: '/'},
|
||||
0x2206: {c: '\\394'},
|
||||
});
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {sansSerifItalic as font} from '../../../common/fonts/tex/sans-serif-italic.js';
|
||||
|
||||
export const sansSerifItalic: CHTMLCharMap = AddCSS(font, {
|
||||
0x391: {c: 'A'},
|
||||
0x392: {c: 'B'},
|
||||
0x395: {c: 'E'},
|
||||
0x396: {c: 'Z'},
|
||||
0x397: {c: 'H'},
|
||||
0x399: {c: 'I'},
|
||||
0x39A: {c: 'K'},
|
||||
0x39C: {c: 'M'},
|
||||
0x39D: {c: 'N'},
|
||||
0x39F: {c: 'O'},
|
||||
0x3A1: {c: 'P'},
|
||||
0x3A4: {c: 'T'},
|
||||
0x3A7: {c: 'X'},
|
||||
0x2015: {c: '\\2014'},
|
||||
0x2017: {c: '_'},
|
||||
0x2044: {c: '/'},
|
||||
0x2206: {c: '\\394'},
|
||||
});
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {sansSerif as font} from '../../../common/fonts/tex/sans-serif.js';
|
||||
|
||||
export const sansSerif: CHTMLCharMap = AddCSS(font, {
|
||||
0x391: {c: 'A'},
|
||||
0x392: {c: 'B'},
|
||||
0x395: {c: 'E'},
|
||||
0x396: {c: 'Z'},
|
||||
0x397: {c: 'H'},
|
||||
0x399: {c: 'I'},
|
||||
0x39A: {c: 'K'},
|
||||
0x39C: {c: 'M'},
|
||||
0x39D: {c: 'N'},
|
||||
0x39F: {c: 'O'},
|
||||
0x3A1: {c: 'P'},
|
||||
0x3A4: {c: 'T'},
|
||||
0x3A7: {c: 'X'},
|
||||
0x2015: {c: '\\2014'},
|
||||
0x2017: {c: '_'},
|
||||
0x2044: {c: '/'},
|
||||
0x2206: {c: '\\394'},
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {scriptBold} from '../../../common/fonts/tex/script-bold.js';
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {script} from '../../../common/fonts/tex/script.js';
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {smallop as font} from '../../../common/fonts/tex/smallop.js';
|
||||
|
||||
export const smallop: CHTMLCharMap = AddCSS(font, {
|
||||
0x2044: {c: '/'},
|
||||
0x2329: {c: '\\27E8'},
|
||||
0x232A: {c: '\\27E9'},
|
||||
0x2758: {c: '\\2223'},
|
||||
0x2A0C: {c: '\\222C\\222C'},
|
||||
0x3008: {c: '\\27E8'},
|
||||
0x3009: {c: '\\27E9'},
|
||||
});
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {texCalligraphicBold as font} from '../../../common/fonts/tex/tex-calligraphic-bold.js';
|
||||
|
||||
export const texCalligraphicBold: CHTMLCharMap = AddCSS(font, {
|
||||
0x131: {f: 'B'},
|
||||
0x237: {f: 'B'},
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {texCalligraphic} from '../../../common/fonts/tex/tex-calligraphic.js';
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {texMathit} from '../../../common/fonts/tex/tex-mathit.js';
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {texOldstyleBold} from '../../../common/fonts/tex/tex-oldstyle-bold.js';
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
export {texOldstyle} from '../../../common/fonts/tex/tex-oldstyle.js';
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {texSize3 as font} from '../../../common/fonts/tex/tex-size3.js';
|
||||
|
||||
export const texSize3: CHTMLCharMap = AddCSS(font, {
|
||||
0x2044: {c: '/'},
|
||||
0x2329: {c: '\\27E8'},
|
||||
0x232A: {c: '\\27E9'},
|
||||
0x3008: {c: '\\27E8'},
|
||||
0x3009: {c: '\\27E9'},
|
||||
});
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {texSize4 as font} from '../../../common/fonts/tex/tex-size4.js';
|
||||
|
||||
export const texSize4: CHTMLCharMap = AddCSS(font, {
|
||||
0x2044: {c: '/'},
|
||||
0x2329: {c: '\\27E8'},
|
||||
0x232A: {c: '\\27E9'},
|
||||
0x3008: {c: '\\27E8'},
|
||||
0x3009: {c: '\\27E9'},
|
||||
0xE155: {c: '\\E153\\E152'},
|
||||
0xE156: {c: '\\E151\\E150'},
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
import {CHTMLCharMap, AddCSS} from '../../FontData.js';
|
||||
import {texVariant as font} from '../../../common/fonts/tex/tex-variant.js';
|
||||
|
||||
export const texVariant: CHTMLCharMap = AddCSS(font, {
|
||||
0x3F0: {c: '\\E009'},
|
||||
0x210F: {f: ''},
|
||||
0x2224: {c: '\\E006'},
|
||||
0x2226: {c: '\\E007'},
|
||||
0x2268: {c: '\\E00C'},
|
||||
0x2269: {c: '\\E00D'},
|
||||
0x2270: {c: '\\E011'},
|
||||
0x2271: {c: '\\E00E'},
|
||||
0x2288: {c: '\\E016'},
|
||||
0x2289: {c: '\\E018'},
|
||||
0x228A: {c: '\\E01A'},
|
||||
0x228B: {c: '\\E01B'},
|
||||
0x2A87: {c: '\\E010'},
|
||||
0x2A88: {c: '\\E00F'},
|
||||
0x2ACB: {c: '\\E017'},
|
||||
0x2ACC: {c: '\\E019'},
|
||||
});
|
||||
+829
@@ -0,0 +1,829 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the FontData class for character bbox data
|
||||
* and stretchy delimiters.
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {OptionList, defaultOptions, userOptions} from '../../util/Options.js';
|
||||
import {StyleList} from '../../util/StyleList.js';
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* The extra options allowed in a CharData array
|
||||
*/
|
||||
export interface CharOptions {
|
||||
ic?: number; // italic correction value
|
||||
sk?: number; // skew value
|
||||
dx?: number; // offset for combining characters
|
||||
unknown?: boolean; // true if not found in the given variant
|
||||
smp?: number; // Math Alphanumeric codepoint this char is mapped to
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Data about a character
|
||||
* [height, depth, width, {italic-correction, skew, options}]
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
*/
|
||||
export type CharData<C extends CharOptions> =
|
||||
[number, number, number] |
|
||||
[number, number, number, C];
|
||||
|
||||
/**
|
||||
* An object making character positions to character data
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
*/
|
||||
export type CharMap<C extends CharOptions> = {
|
||||
[n: number]: CharData<C>;
|
||||
};
|
||||
|
||||
/**
|
||||
* An object making variants to character maps
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
*/
|
||||
export type CharMapMap<C extends CharOptions> = {
|
||||
[name: string]: CharMap<C>;
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Data for a variant
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
*/
|
||||
export interface VariantData<C extends CharOptions> {
|
||||
/**
|
||||
* A list of CharMaps that must be updated when characters are
|
||||
* added to this variant
|
||||
*/
|
||||
linked: CharMap<C>[];
|
||||
/**
|
||||
* The character data for this variant
|
||||
*/
|
||||
chars: CharMap<C>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object making variants names to variant data
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
* @template V The VariantData type
|
||||
*/
|
||||
export type VariantMap<C extends CharOptions, V extends VariantData<C>> = {
|
||||
[name: string]: V;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Data to use to map unknown characters in a variant to a
|
||||
* generic CSS font:
|
||||
*
|
||||
* [fontname, italic, bold]
|
||||
*/
|
||||
export type CssFontData = [string, boolean, boolean];
|
||||
|
||||
/**
|
||||
* An object mapping a variant name to the CSS data needed for it
|
||||
*/
|
||||
export type CssFontMap = {
|
||||
[name: string]: CssFontData;
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Stretchy delimiter data
|
||||
*/
|
||||
export const enum DIRECTION {None, Vertical, Horizontal}
|
||||
export const V = DIRECTION.Vertical;
|
||||
export const H = DIRECTION.Horizontal;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Data needed for stretchy vertical and horizontal characters
|
||||
*/
|
||||
export type DelimiterData = {
|
||||
dir: DIRECTION; // vertical or horizontal direction
|
||||
sizes?: number[]; // Array of fixed sizes for this character
|
||||
variants?: number[]; // The variants in which the different sizes can be found (if not the default)
|
||||
schar?: number[]; // The character number to use for each size (if different from the default)
|
||||
stretch?: number[]; // The unicode code points for the parts of multi-character versions [beg, ext, end, mid?]
|
||||
stretchv?: number[]; // the variants to use for the stretchy characters (index into variant name array)
|
||||
HDW?: number[]; // [h, d, w] (for vertical, h and d are the normal size, w is the multi-character width,
|
||||
// for horizontal, h and d are the multi-character ones, w is for the normal size).
|
||||
min?: number; // The minimum size a multi-character version can be
|
||||
c?: number; // The character number (for aliased delimiters)
|
||||
fullExt?: [number, number] // When present, extenders must be full sized, and the first number is
|
||||
// the size of the extender, while the second is the total size of the ends
|
||||
};
|
||||
|
||||
/**
|
||||
* An object mapping character numbers to delimiter data
|
||||
*
|
||||
* @template D The DelimiterData type
|
||||
*/
|
||||
export type DelimiterMap<D extends DelimiterData> = {
|
||||
[n: number]: D;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delimiter data for a non-stretchy character
|
||||
*/
|
||||
export const NOSTRETCH: DelimiterData = {dir: DIRECTION.None};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Data for remapping characters
|
||||
*/
|
||||
export type RemapData = string;
|
||||
export type RemapMap = {
|
||||
[key: number]: RemapData;
|
||||
};
|
||||
export type RemapMapMap = {
|
||||
[key: string]: RemapMap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Character remapping data for Math Alphanumerics
|
||||
*/
|
||||
export type SmpMap = {
|
||||
[c: number]: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data for Math Alphanumeric conversion: starting positions for
|
||||
* [Alpha, alpha, Greek, greek, Numbers]
|
||||
*/
|
||||
export type SmpData = [number, number, number?, number?, number?];
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Font parameters (for TeX typesetting rules)
|
||||
*/
|
||||
export type FontParameters = {
|
||||
x_height: number,
|
||||
quad: number,
|
||||
num1: number,
|
||||
num2: number,
|
||||
num3: number,
|
||||
denom1: number,
|
||||
denom2: number,
|
||||
sup1: number,
|
||||
sup2: number,
|
||||
sup3: number,
|
||||
sub1: number,
|
||||
sub2: number,
|
||||
sup_drop: number,
|
||||
sub_drop: number,
|
||||
delim1: number,
|
||||
delim2: number,
|
||||
axis_height: number,
|
||||
rule_thickness: number,
|
||||
big_op_spacing1: number,
|
||||
big_op_spacing2: number,
|
||||
big_op_spacing3: number,
|
||||
big_op_spacing4: number,
|
||||
big_op_spacing5: number,
|
||||
|
||||
surd_height: number,
|
||||
|
||||
scriptspace: number,
|
||||
nulldelimiterspace: number,
|
||||
delimiterfactor: number,
|
||||
delimitershortfall: number,
|
||||
|
||||
min_rule_thickness: number,
|
||||
separation_factor: number,
|
||||
extra_ic: number
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
/**
|
||||
* The FontData class (for storing character bounding box data by variant,
|
||||
* and the stretchy delimiter data).
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
* @template V The VariantData type
|
||||
* @template D The DelimiterData type
|
||||
*/
|
||||
export class FontData<C extends CharOptions, V extends VariantData<C>, D extends DelimiterData> {
|
||||
|
||||
/**
|
||||
* Options for the font
|
||||
*/
|
||||
public static OPTIONS: OptionList = {
|
||||
unknownFamily: 'serif' // Should use 'monospace' with LiteAdaptor
|
||||
};
|
||||
|
||||
/**
|
||||
* The name of the output jax this font data is for (used by extensions)
|
||||
*/
|
||||
public static JAX: string = 'common';
|
||||
|
||||
/**
|
||||
* The name of the font that is being defined (used by extensions)
|
||||
*/
|
||||
public static NAME: string = '';
|
||||
|
||||
/**
|
||||
* The standard variants to define
|
||||
*/
|
||||
public static defaultVariants = [
|
||||
['normal'],
|
||||
['bold', 'normal'],
|
||||
['italic', 'normal'],
|
||||
['bold-italic', 'italic', 'bold'],
|
||||
['double-struck', 'bold'],
|
||||
['fraktur', 'normal'],
|
||||
['bold-fraktur', 'bold', 'fraktur'],
|
||||
['script', 'italic'],
|
||||
['bold-script', 'bold-italic', 'script'],
|
||||
['sans-serif', 'normal'],
|
||||
['bold-sans-serif', 'bold', 'sans-serif'],
|
||||
['sans-serif-italic', 'italic', 'sans-serif'],
|
||||
['sans-serif-bold-italic', 'bold-italic', 'bold-sans-serif'],
|
||||
['monospace', 'normal']
|
||||
];
|
||||
|
||||
/**
|
||||
* The family, style, and weight to use for each variant (for unknown characters)
|
||||
* The 'unknown' family is replaced by options.unknownFamily
|
||||
*/
|
||||
public static defaultCssFonts: CssFontMap = {
|
||||
normal: ['unknown', false, false],
|
||||
bold: ['unknown', false, true],
|
||||
italic: ['unknown', true, false],
|
||||
'bold-italic': ['unknown', true, true],
|
||||
'double-struck': ['unknown', false, true],
|
||||
fraktur: ['unknown', false, false],
|
||||
'bold-fraktur': ['unknown', false, true],
|
||||
script: ['cursive', false, false],
|
||||
'bold-script': ['cursive', false, true],
|
||||
'sans-serif': ['sans-serif', false, false],
|
||||
'bold-sans-serif': ['sans-serif', false, true],
|
||||
'sans-serif-italic': ['sans-serif', true, false],
|
||||
'sans-serif-bold-italic': ['sans-serif', true, true],
|
||||
monospace: ['monospace', false, false]
|
||||
};
|
||||
|
||||
/**
|
||||
* The default prefix for explicit font-family settings
|
||||
*/
|
||||
protected static defaultCssFamilyPrefix = '';
|
||||
|
||||
/**
|
||||
* Variant locations in the Math Alphabnumerics block:
|
||||
* [upper-alpha, lower-alpha, upper-Greek, lower-Greek, numbers]
|
||||
*/
|
||||
public static VariantSmp: {[name: string]: SmpData} = {
|
||||
bold: [0x1D400, 0x1D41A, 0x1D6A8, 0x1D6C2, 0x1D7CE],
|
||||
italic: [0x1D434, 0x1D44E, 0x1D6E2, 0x1D6FC],
|
||||
'bold-italic': [0x1D468, 0x1D482, 0x1D71C, 0x1D736],
|
||||
script: [0x1D49C, 0x1D4B6],
|
||||
'bold-script': [0x1D4D0, 0x1D4EA],
|
||||
fraktur: [0x1D504, 0x1D51E],
|
||||
'double-struck': [0x1D538, 0x1D552, , , 0x1D7D8],
|
||||
'bold-fraktur': [0x1D56C, 0x1D586],
|
||||
'sans-serif': [0x1D5A0, 0x1D5BA, , , 0x1D7E2],
|
||||
'bold-sans-serif': [0x1D5D4, 0x1D5EE, 0x1D756, 0x1D770, 0x1D7EC],
|
||||
'sans-serif-italic': [0x1D608, 0x1D622],
|
||||
'sans-serif-bold-italic': [0x1D63C, 0x1D656, 0x1D790, 0x1D7AA],
|
||||
'monospace': [0x1D670, 0x1D68A, , , 0x1D7F6]
|
||||
};
|
||||
|
||||
/**
|
||||
* Character ranges to remap into Math Alphanumerics
|
||||
*/
|
||||
public static SmpRanges = [
|
||||
[0, 0x41, 0x5A], // Upper-case alpha
|
||||
[1, 0x61, 0x7A], // Lower-case alpha
|
||||
[2, 0x391, 0x3A9], // Upper-case Greek
|
||||
[3, 0x3B1, 0x3C9], // Lower-case Greek
|
||||
[4, 0x30, 0x39] // Numbers
|
||||
];
|
||||
|
||||
/**
|
||||
* Characters to map back top other Unicode positions
|
||||
* (holes in the Math Alphanumeric ranges)
|
||||
*/
|
||||
public static SmpRemap: SmpMap = {
|
||||
0x1D455: 0x210E, // PLANCK CONSTANT
|
||||
0x1D49D: 0x212C, // SCRIPT CAPITAL B
|
||||
0x1D4A0: 0x2130, // SCRIPT CAPITAL E
|
||||
0x1D4A1: 0x2131, // SCRIPT CAPITAL F
|
||||
0x1D4A3: 0x210B, // SCRIPT CAPITAL H
|
||||
0x1D4A4: 0x2110, // SCRIPT CAPITAL I
|
||||
0x1D4A7: 0x2112, // SCRIPT CAPITAL L
|
||||
0x1D4A8: 0x2133, // SCRIPT CAPITAL M
|
||||
0x1D4AD: 0x211B, // SCRIPT CAPITAL R
|
||||
0x1D4BA: 0x212F, // SCRIPT SMALL E
|
||||
0x1D4BC: 0x210A, // SCRIPT SMALL G
|
||||
0x1D4C4: 0x2134, // SCRIPT SMALL O
|
||||
0x1D506: 0x212D, // BLACK-LETTER CAPITAL C
|
||||
0x1D50B: 0x210C, // BLACK-LETTER CAPITAL H
|
||||
0x1D50C: 0x2111, // BLACK-LETTER CAPITAL I
|
||||
0x1D515: 0x211C, // BLACK-LETTER CAPITAL R
|
||||
0x1D51D: 0x2128, // BLACK-LETTER CAPITAL Z
|
||||
0x1D53A: 0x2102, // DOUBLE-STRUCK CAPITAL C
|
||||
0x1D53F: 0x210D, // DOUBLE-STRUCK CAPITAL H
|
||||
0x1D545: 0x2115, // DOUBLE-STRUCK CAPITAL N
|
||||
0x1D547: 0x2119, // DOUBLE-STRUCK CAPITAL P
|
||||
0x1D548: 0x211A, // DOUBLE-STRUCK CAPITAL Q
|
||||
0x1D549: 0x211D, // DOUBLE-STRUCK CAPITAL R
|
||||
0x1D551: 0x2124, // DOUBLE-STRUCK CAPITAL Z
|
||||
};
|
||||
|
||||
/**
|
||||
* Greek upper-case variants
|
||||
*/
|
||||
public static SmpRemapGreekU: SmpMap = {
|
||||
0x2207: 0x19, // nabla
|
||||
0x03F4: 0x11 // theta symbol
|
||||
};
|
||||
|
||||
/**
|
||||
* Greek lower-case variants
|
||||
*/
|
||||
public static SmpRemapGreekL: SmpMap = {
|
||||
0x3D1: 0x1B, // theta symbol
|
||||
0x3D5: 0x1D, // phi symbol
|
||||
0x3D6: 0x1F, // omega symbol
|
||||
0x3F0: 0x1C, // kappa symbol
|
||||
0x3F1: 0x1E, // rho symbol
|
||||
0x3F5: 0x1A, // lunate epsilon symbol
|
||||
0x2202: 0x19 // partial differential
|
||||
};
|
||||
|
||||
/**
|
||||
* The default remappings
|
||||
*/
|
||||
protected static defaultAccentMap: RemapMap = {
|
||||
0x0300: '\u02CB', // grave accent
|
||||
0x0301: '\u02CA', // acute accent
|
||||
0x0302: '\u02C6', // curcumflex
|
||||
0x0303: '\u02DC', // tilde accent
|
||||
0x0304: '\u02C9', // macron
|
||||
0x0306: '\u02D8', // breve
|
||||
0x0307: '\u02D9', // dot
|
||||
0x0308: '\u00A8', // diaresis
|
||||
0x030A: '\u02DA', // ring above
|
||||
0x030C: '\u02C7', // caron
|
||||
0x2192: '\u20D7',
|
||||
0x2032: '\'',
|
||||
0x2033: '\'\'',
|
||||
0x2034: '\'\'\'',
|
||||
0x2035: '`',
|
||||
0x2036: '``',
|
||||
0x2037: '```',
|
||||
0x2057: '\'\'\'\'',
|
||||
0x20D0: '\u21BC', // combining left harpoon
|
||||
0x20D1: '\u21C0', // combining right harpoon
|
||||
0x20D6: '\u2190', // combining left arrow
|
||||
0x20E1: '\u2194', // combining left-right arrow
|
||||
0x20F0: '*', // combining asterisk
|
||||
0x20DB: '...', // combining three dots above
|
||||
0x20DC: '....', // combining four dots above
|
||||
0x20EC: '\u21C1', // combining low left harpoon
|
||||
0x20ED: '\u21BD', // combining low right harpoon
|
||||
0x20EE: '\u2190', // combining low left arrows
|
||||
0x20EF: '\u2192' // combining low right arrows
|
||||
};
|
||||
|
||||
/**
|
||||
* Default map for characters inside <mo>
|
||||
*/
|
||||
protected static defaultMoMap: RemapMap = {
|
||||
0x002D: '\u2212' // hyphen
|
||||
};
|
||||
|
||||
/**
|
||||
* Default map for characters inside <mn>
|
||||
*/
|
||||
protected static defaultMnMap: RemapMap = {
|
||||
0x002D: '\u2212' // hyphen
|
||||
};
|
||||
|
||||
/**
|
||||
* The default font parameters for the font
|
||||
*/
|
||||
public static defaultParams: FontParameters = {
|
||||
x_height: .442,
|
||||
quad: 1,
|
||||
num1: .676,
|
||||
num2: .394,
|
||||
num3: .444,
|
||||
denom1: .686,
|
||||
denom2: .345,
|
||||
sup1: .413,
|
||||
sup2: .363,
|
||||
sup3: .289,
|
||||
sub1: .15,
|
||||
sub2: .247,
|
||||
sup_drop: .386,
|
||||
sub_drop: .05,
|
||||
delim1: 2.39,
|
||||
delim2: 1.0,
|
||||
axis_height: .25,
|
||||
rule_thickness: .06,
|
||||
big_op_spacing1: .111,
|
||||
big_op_spacing2: .167,
|
||||
big_op_spacing3: .2,
|
||||
big_op_spacing4: .6,
|
||||
big_op_spacing5: .1,
|
||||
|
||||
surd_height: .075,
|
||||
|
||||
scriptspace: .05,
|
||||
nulldelimiterspace: .12,
|
||||
delimiterfactor: 901,
|
||||
delimitershortfall: .3,
|
||||
|
||||
min_rule_thickness: 1.25, // in pixels
|
||||
separation_factor: 1.75, // expansion factor for spacing e.g. between accents and base
|
||||
extra_ic: .033 // extra spacing for scripts (compensate for not having actual ic values)
|
||||
};
|
||||
|
||||
/**
|
||||
* The default delimiter data
|
||||
*/
|
||||
protected static defaultDelimiters: DelimiterMap<any> = {};
|
||||
|
||||
/**
|
||||
* The default character data
|
||||
*/
|
||||
protected static defaultChars: CharMapMap<any> = {};
|
||||
|
||||
/**
|
||||
* The default variants for the fixed size stretchy delimiters
|
||||
*/
|
||||
protected static defaultSizeVariants: string[] = [];
|
||||
|
||||
/**
|
||||
* The default variants for the assembly parts for stretchy delimiters
|
||||
*/
|
||||
protected static defaultStretchVariants: string[] = [];
|
||||
|
||||
/**
|
||||
* The font options
|
||||
*/
|
||||
protected options: OptionList;
|
||||
|
||||
/**
|
||||
* The actual variant information for this font
|
||||
*/
|
||||
protected variant: VariantMap<C, V> = {};
|
||||
|
||||
/**
|
||||
* The actual delimiter information for this font
|
||||
*/
|
||||
protected delimiters: DelimiterMap<D> = {};
|
||||
|
||||
/**
|
||||
* The actual size variants to use for this font
|
||||
*/
|
||||
protected sizeVariants: string[];
|
||||
|
||||
/**
|
||||
* The actual stretchy variants to use for this font
|
||||
*/
|
||||
protected stretchVariants: string[];
|
||||
|
||||
/**
|
||||
* The data to use to make variants to default fonts and css for unknown characters
|
||||
*/
|
||||
protected cssFontMap: CssFontMap = {};
|
||||
|
||||
/**
|
||||
* A prefix to use for explicit font-family CSS settings
|
||||
*/
|
||||
public cssFamilyPrefix: string;
|
||||
|
||||
/**
|
||||
* The character maps
|
||||
*/
|
||||
protected remapChars: RemapMapMap = {};
|
||||
|
||||
/**
|
||||
* The actual font parameters for this font
|
||||
*/
|
||||
public params: FontParameters;
|
||||
|
||||
/**
|
||||
* Factor by which to multiply italic correction for computation of delta in munderover
|
||||
*/
|
||||
public skewIcFactor: number = .75;
|
||||
|
||||
/**
|
||||
* Any styles needed for the font
|
||||
*/
|
||||
protected _styles: StyleList;
|
||||
|
||||
/**
|
||||
* @param {CharMap} font The font to check
|
||||
* @param {number} n The character to get options for
|
||||
* @return {CharOptions} The options for the character
|
||||
*/
|
||||
public static charOptions(font: CharMap<CharOptions>, n: number): CharOptions {
|
||||
const char = font[n];
|
||||
if (char.length === 3) {
|
||||
(char as any)[3] = {};
|
||||
}
|
||||
return char[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the data from the defaults to the instance
|
||||
*
|
||||
* @param {OptionList} options The options for this font
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
constructor(options: OptionList = null) {
|
||||
let CLASS = (this.constructor as typeof FontData);
|
||||
this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), options);
|
||||
this.params = {...CLASS.defaultParams};
|
||||
this.sizeVariants = [...CLASS.defaultSizeVariants];
|
||||
this.stretchVariants = [...CLASS.defaultStretchVariants];
|
||||
this.cssFontMap = {...CLASS.defaultCssFonts};
|
||||
for (const name of Object.keys(this.cssFontMap)) {
|
||||
if (this.cssFontMap[name][0] === 'unknown') {
|
||||
this.cssFontMap[name][0] = this.options.unknownFamily;
|
||||
}
|
||||
}
|
||||
this.cssFamilyPrefix = CLASS.defaultCssFamilyPrefix;
|
||||
this.createVariants(CLASS.defaultVariants);
|
||||
this.defineDelimiters(CLASS.defaultDelimiters);
|
||||
for (const name of Object.keys(CLASS.defaultChars)) {
|
||||
this.defineChars(name, CLASS.defaultChars[name]);
|
||||
}
|
||||
this.defineRemap('accent', CLASS.defaultAccentMap);
|
||||
this.defineRemap('mo', CLASS.defaultMoMap);
|
||||
this.defineRemap('mn', CLASS.defaultMnMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of styles needed for the font
|
||||
*/
|
||||
get styles(): StyleList {
|
||||
return this._styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets styles needed for that font.
|
||||
*/
|
||||
set styles(style: StyleList) {
|
||||
this._styles = style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the data structure for a variant -- an object with
|
||||
* prototype chain that includes a copy of the linked variant,
|
||||
* and then the inherited variant chain.
|
||||
*
|
||||
* The reason for this extra link is that for a mathvariant like
|
||||
* bold-italic, you want to inherit from both the bold and
|
||||
* italic variants, but the prototype chain can only inherit
|
||||
* from one. So for bold-italic, we make an object that has a
|
||||
* prototype consisting of a copy of the bold data, and add the
|
||||
* italic data as the prototype chain. (Since this is a copy, we
|
||||
* keep a record of this link so that if bold is changed later,
|
||||
* we can update this copy. That is not needed for the prototype
|
||||
* chain, since the prototypes are the actual objects, not
|
||||
* copies.) We then use this bold-plus-italic object as the
|
||||
* prototype chain for the bold-italic object
|
||||
*
|
||||
* That means that bold-italic will first look in its own object
|
||||
* for specifically bold-italic glyphs that are defined there,
|
||||
* then in the copy of the bold glyphs (only its top level is
|
||||
* copied, not its prototype chain), and then the specifically
|
||||
* italic glyphs, and then the prototype chain for italics,
|
||||
* which is the normal glyphs. Effectively, this means
|
||||
* bold-italic looks for bold-italic, then bold, then italic,
|
||||
* then normal glyphs in order to find the given character.
|
||||
*
|
||||
* @param {string} name The new variant to create
|
||||
* @param {string} inherit The variant to use if a character is not in this one
|
||||
* @param {string} link A variant to search before the inherit one (but only
|
||||
* its top-level object).
|
||||
*/
|
||||
public createVariant(name: string, inherit: string = null, link: string = null) {
|
||||
let variant = {
|
||||
linked: [] as CharMap<C>[],
|
||||
chars: (inherit ? Object.create(this.variant[inherit].chars) : {}) as CharMap<C>
|
||||
} as V;
|
||||
if (link && this.variant[link]) {
|
||||
Object.assign(variant.chars, this.variant[link].chars);
|
||||
this.variant[link].linked.push(variant.chars);
|
||||
variant.chars = Object.create(variant.chars);
|
||||
}
|
||||
this.remapSmpChars(variant.chars, name);
|
||||
this.variant[name] = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the mapping from Basic Latin and Greek blocks to
|
||||
* the Math Alphanumeric block for a given variant.
|
||||
*/
|
||||
protected remapSmpChars(chars: CharMap<C>, name: string) {
|
||||
const CLASS = (this.constructor as typeof FontData);
|
||||
if (CLASS.VariantSmp[name]) {
|
||||
const SmpRemap = CLASS.SmpRemap;
|
||||
const SmpGreek = [null, null, CLASS.SmpRemapGreekU, CLASS.SmpRemapGreekL];
|
||||
for (const [i, lo, hi] of CLASS.SmpRanges) {
|
||||
const base = CLASS.VariantSmp[name][i];
|
||||
if (!base) continue;
|
||||
for (let n = lo; n <= hi; n++) {
|
||||
if (n === 0x3A2) continue;
|
||||
const smp = base + n - lo;
|
||||
chars[n] = this.smpChar(SmpRemap[smp] || smp);
|
||||
}
|
||||
if (SmpGreek[i]) {
|
||||
for (const n of Object.keys(SmpGreek[i]).map((x) => parseInt(x))) {
|
||||
chars[n] = this.smpChar(base + SmpGreek[i][n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name === 'bold') {
|
||||
chars[0x3DC] = this.smpChar(0x1D7CA);
|
||||
chars[0x3DD] = this.smpChar(0x1D7CB);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n Math Alphanumerics position for this remapping
|
||||
* @return {CharData<C>} The character data for the remapping
|
||||
*/
|
||||
protected smpChar(n: number): CharData<C> {
|
||||
return [ , , , {smp: n} as C];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a collection of variants
|
||||
*
|
||||
* @param {string[][]} variants Array of [name, inherit?, link?] values for
|
||||
* the variants to define
|
||||
*/
|
||||
public createVariants(variants: string[][]) {
|
||||
for (const variant of variants) {
|
||||
this.createVariant(variant[0], variant[1], variant[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines new character data in a given variant
|
||||
* (We use Object.assign() here rather than the spread operator since
|
||||
* the character maps are objeccts with prototypes, and we don't
|
||||
* want to loose those by doing {...chars} or something similar.)
|
||||
*
|
||||
* @param {string} name The variant for these characters
|
||||
* @param {CharMap} chars The characters to define
|
||||
*/
|
||||
public defineChars(name: string, chars: CharMap<C>) {
|
||||
let variant = this.variant[name];
|
||||
Object.assign(variant.chars, chars);
|
||||
for (const link of variant.linked) {
|
||||
Object.assign(link, chars);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines stretchy delimiters
|
||||
*
|
||||
* @param {DelimiterMap} delims The delimiters to define
|
||||
*/
|
||||
public defineDelimiters(delims: DelimiterMap<D>) {
|
||||
Object.assign(this.delimiters, delims);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a character remapping map
|
||||
*
|
||||
* @param {string} name The name of the map to define or augment
|
||||
* @param {RemapMap} remap The characters to remap
|
||||
*/
|
||||
public defineRemap(name: string, remap: RemapMap) {
|
||||
if (!this.remapChars.hasOwnProperty(name)) {
|
||||
this.remapChars[name] = {};
|
||||
}
|
||||
Object.assign(this.remapChars[name], remap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The delimiter character number whose data is desired
|
||||
* @return {DelimiterData} The data for that delimiter (or undefined)
|
||||
*/
|
||||
public getDelimiter(n: number): DelimiterData {
|
||||
return this.delimiters[n];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The delimiter character number whose variant is needed
|
||||
* @param {number} i The index in the size array of the size whose variant is needed
|
||||
* @return {string} The variant of the i-th size for delimiter n
|
||||
*/
|
||||
public getSizeVariant(n: number, i: number): string {
|
||||
if (this.delimiters[n].variants) {
|
||||
i = this.delimiters[n].variants[i];
|
||||
}
|
||||
return this.sizeVariants[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n The delimiter character number whose variant is needed
|
||||
* @param {number} i The index in the stretch array of the part whose variant is needed
|
||||
* @return {string} The variant of the i-th part for delimiter n
|
||||
*/
|
||||
public getStretchVariant(n: number, i: number): string {
|
||||
return this.stretchVariants[this.delimiters[n].stretchv ? this.delimiters[n].stretchv[i] : 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name The variant whose character data is being querried
|
||||
* @param {number} n The unicode number for the character to be found
|
||||
* @return {CharData} The data for the given character (or undefined)
|
||||
*/
|
||||
public getChar(name: string, n: number): CharData<C> {
|
||||
return this.variant[name].chars[n];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the variant whose data is to be obtained
|
||||
* @return {V} The data for the requested variant (or undefined)
|
||||
*/
|
||||
public getVariant(name: string): V {
|
||||
return this.variant[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} variant The name of the variant whose data is to be obtained
|
||||
* @return {CssFontData} The CSS data for the requested variant
|
||||
*/
|
||||
public getCssFont(variant: string): CssFontData {
|
||||
return this.cssFontMap[variant] || ['serif', false, false];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} family The font camily to use
|
||||
* @return {string} The family with the css prefix
|
||||
*/
|
||||
public getFamily(family: string): string {
|
||||
return (this.cssFamilyPrefix ? this.cssFamilyPrefix + ', ' + family : family);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the map to query
|
||||
* @param {number} c The character to remap
|
||||
* @return {string} The remapped character (or the original)
|
||||
*/
|
||||
public getRemappedChar(name: string, c: number): string {
|
||||
const map = this.remapChars[name] || {} as RemapMap;
|
||||
return map[c];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The class interface for the FontData class
|
||||
*
|
||||
* @template C The CharOptions type
|
||||
* @template V The VariantData type
|
||||
* @template D The DelimiterData type
|
||||
*/
|
||||
export interface FontDataClass<C extends CharOptions, V extends VariantData<C>, D extends DelimiterData> {
|
||||
OPTIONS: OptionList;
|
||||
defaultCssFonts: CssFontMap;
|
||||
defaultVariants: string[][];
|
||||
defaultParams: FontParameters;
|
||||
/* tslint:disable-next-line:jsdoc-require */
|
||||
charOptions(font: CharMap<C>, n: number): C;
|
||||
new(...args: any[]): FontData<C, V, D>;
|
||||
}
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements utilities for notations for menclose elements
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper} from './Wrapper.js';
|
||||
import {CommonMenclose} from './Wrappers/menclose.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
export const ARROWX = 4, ARROWDX = 1, ARROWY = 2; // default relative arrowhead values
|
||||
|
||||
export const THICKNESS = .067; // default rule thickness
|
||||
export const PADDING = .2; // default padding
|
||||
|
||||
export const SOLID = THICKNESS + 'em solid'; // a solid border
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Shorthand for CommonMenclose
|
||||
*/
|
||||
export type Menclose = CommonMenclose<any, any, any>;
|
||||
|
||||
/**
|
||||
* Top, right, bottom, left padding data
|
||||
*/
|
||||
export type PaddingData = [number, number, number, number];
|
||||
|
||||
/**
|
||||
* The functions used for notation definitions
|
||||
*
|
||||
* @templare N The DOM node class
|
||||
*/
|
||||
export type Renderer<W extends AnyWrapper, N> = (node: W, child: N) => void;
|
||||
export type BBoxExtender<W extends AnyWrapper> = (node: W) => PaddingData;
|
||||
export type BBoxBorder<W extends AnyWrapper> = (node: W) => PaddingData;
|
||||
export type Initializer<W extends AnyWrapper> = (node: W) => void;
|
||||
|
||||
/**
|
||||
* The definition of a notation
|
||||
*
|
||||
* @template W The menclose wrapper class
|
||||
* @templare N The DOM node class
|
||||
*/
|
||||
export type NotationDef<W extends AnyWrapper, N> = {
|
||||
renderer: Renderer<W, N>; // renders the DOM nodes for the notation
|
||||
bbox: BBoxExtender<W>; // gives the offsets to the child bounding box: [top, right, bottom, left]
|
||||
border?: BBoxBorder<W>; // gives the amount of the bbox offset that is due to borders on the child
|
||||
renderChild?: boolean; // true if the notation is used to render the child directly (e.g., radical)
|
||||
init?: Initializer<W>; // function to be called during wrapper construction
|
||||
remove?: string; // list of notations that are suppressed by this one
|
||||
};
|
||||
|
||||
/**
|
||||
* For defining notation maps
|
||||
*
|
||||
* @template W The menclose wrapper class
|
||||
* @templare N The DOM node class
|
||||
*/
|
||||
export type DefPair<W extends AnyWrapper, N> = [string, NotationDef<W, N>];
|
||||
export type DefList<W extends AnyWrapper, N> = Map<string, NotationDef<W, N>>;
|
||||
|
||||
export type DefPairF<T, W extends AnyWrapper, N> = (name: T) => DefPair<W, N>;
|
||||
|
||||
/**
|
||||
* The list of notations for an menclose element
|
||||
*
|
||||
* @template W The menclose wrapper class
|
||||
* @templare N The DOM node class
|
||||
*/
|
||||
export type List<W extends AnyWrapper, N> = {[notation: string]: NotationDef<W, N>};
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* The names and indices of sides for borders, padding, etc.
|
||||
*/
|
||||
export const sideIndex = {top: 0, right: 1, bottom: 2, left: 3};
|
||||
export type Side = keyof typeof sideIndex;
|
||||
export const sideNames = Object.keys(sideIndex) as Side[];
|
||||
|
||||
/**
|
||||
* Common BBox and Border functions
|
||||
*/
|
||||
export const fullBBox = ((node) => new Array(4).fill(node.thickness + node.padding)) as BBoxExtender<Menclose>;
|
||||
export const fullPadding = ((node) => new Array(4).fill(node.padding)) as BBoxExtender<Menclose>;
|
||||
export const fullBorder = ((node) => new Array(4).fill(node.thickness)) as BBoxBorder<Menclose>;
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* The length of an arrowhead
|
||||
*/
|
||||
export const arrowHead = (node: Menclose) => {
|
||||
return Math.max(node.padding, node.thickness * (node.arrowhead.x + node.arrowhead.dx + 1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Adjust short bbox for tall arrow heads
|
||||
*/
|
||||
export const arrowBBoxHD = (node: Menclose, TRBL: PaddingData) => {
|
||||
if (node.childNodes[0]) {
|
||||
const {h, d} = node.childNodes[0].getBBox();
|
||||
TRBL[0] = TRBL[2] = Math.max(0, node.thickness * node.arrowhead.y - (h + d) / 2);
|
||||
}
|
||||
return TRBL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adjust thin bbox for wide arrow heads
|
||||
*/
|
||||
export const arrowBBoxW = (node: Menclose, TRBL: PaddingData) => {
|
||||
if (node.childNodes[0]) {
|
||||
const {w} = node.childNodes[0].getBBox();
|
||||
TRBL[1] = TRBL[3] = Math.max(0, node.thickness * node.arrowhead.y - w / 2);
|
||||
}
|
||||
return TRBL;
|
||||
};
|
||||
|
||||
/**
|
||||
* The data for horizontal and vertical arrow notations
|
||||
* [angle, double, isVertical, remove]
|
||||
*/
|
||||
export const arrowDef = {
|
||||
up: [-Math.PI / 2, false, true, 'verticalstrike'],
|
||||
down: [ Math.PI / 2, false, true, 'verticakstrike'],
|
||||
right: [ 0, false, false, 'horizontalstrike'],
|
||||
left: [ Math.PI, false, false, 'horizontalstrike'],
|
||||
updown: [ Math.PI / 2, true, true, 'verticalstrike uparrow downarrow'],
|
||||
leftright: [ 0, true, false, 'horizontalstrike leftarrow rightarrow']
|
||||
} as {[name: string]: [number, boolean, boolean, string]};
|
||||
|
||||
/**
|
||||
* The data for diagonal arrow notations
|
||||
* [c, pi, double, remove]
|
||||
*/
|
||||
export const diagonalArrowDef = {
|
||||
updiagonal: [-1, 0, false, 'updiagonalstrike northeastarrow'],
|
||||
northeast: [-1, 0, false, 'updiagonalstrike updiagonalarrow'],
|
||||
southeast: [ 1, 0, false, 'downdiagonalstrike'],
|
||||
northwest: [ 1, Math.PI, false, 'downdiagonalstrike'],
|
||||
southwest: [-1, Math.PI, false, 'updiagonalstrike'],
|
||||
northeastsouthwest: [-1, 0, true, 'updiagonalstrike northeastarrow updiagonalarrow southwestarrow'],
|
||||
northwestsoutheast: [ 1, 0, true, 'downdiagonalstrike northwestarrow southeastarrow']
|
||||
} as {[name: string]: [number, number, boolean, string]};
|
||||
|
||||
/**
|
||||
* The BBox functions for horizontal and vertical arrows
|
||||
*/
|
||||
export const arrowBBox = {
|
||||
up: (node) => arrowBBoxW(node, [arrowHead(node), 0, node.padding, 0]),
|
||||
down: (node) => arrowBBoxW(node, [node.padding, 0, arrowHead(node), 0]),
|
||||
right: (node) => arrowBBoxHD(node, [0, arrowHead(node), 0, node.padding]),
|
||||
left: (node) => arrowBBoxHD(node, [0, node.padding, 0, arrowHead(node)]),
|
||||
updown: (node) => arrowBBoxW(node, [arrowHead(node), 0, arrowHead(node), 0]),
|
||||
leftright: (node) => arrowBBoxHD(node, [0, arrowHead(node), 0, arrowHead(node)])
|
||||
} as {[name: string]: BBoxExtender<Menclose>};
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @param {Renderer} render The function for adding the border to the node
|
||||
* @return {string => DefPair} The function returingn the notation definition
|
||||
* for the notation having a line on the given side
|
||||
*/
|
||||
export const CommonBorder = function<W extends Menclose, N>(render: Renderer<W, N>): DefPairF<Side, W, N> {
|
||||
/**
|
||||
* @param {string} side The side on which a border should appear
|
||||
* @return {DefPair} The notation definition for the notation having a line on the given side
|
||||
*/
|
||||
return (side: Side) => {
|
||||
const i = sideIndex[side];
|
||||
return [side, {
|
||||
//
|
||||
// Add the border to the main child object
|
||||
//
|
||||
renderer: render,
|
||||
//
|
||||
// Indicate the extra space on the given side
|
||||
//
|
||||
bbox: (node) => {
|
||||
const bbox = [0, 0, 0, 0] as PaddingData;
|
||||
bbox[i] = node.thickness + node.padding;
|
||||
return bbox;
|
||||
},
|
||||
//
|
||||
// Indicate the border on the given side
|
||||
//
|
||||
border: (node) => {
|
||||
const bbox = [0, 0, 0, 0] as PaddingData;
|
||||
bbox[i] = node.thickness;
|
||||
return bbox;
|
||||
}
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Renderer} render The function for adding the borders to the node
|
||||
* @return {(sring, Side, Side) => DefPair} The function returning the notation definition
|
||||
* for the notation having lines on two sides
|
||||
*/
|
||||
export const CommonBorder2 = function<W extends Menclose, N>(render: Renderer<W, N>):
|
||||
(name: string, side1: Side, side2: Side) => DefPair<W, N> {
|
||||
/**
|
||||
* @param {string} name The name of the notation to define
|
||||
* @param {Side} side1 The first side to get a border
|
||||
* @param {Side} side2 The second side to get a border
|
||||
* @return {DefPair} The notation definition for the notation having lines on two sides
|
||||
*/
|
||||
return (name: string, side1: Side, side2: Side) => {
|
||||
const i1 = sideIndex[side1];
|
||||
const i2 = sideIndex[side2];
|
||||
return [name, {
|
||||
//
|
||||
// Add the border along the given sides
|
||||
//
|
||||
renderer: render,
|
||||
//
|
||||
// Mark the extra space along the two sides
|
||||
//
|
||||
bbox: (node) => {
|
||||
const t = node.thickness + node.padding;
|
||||
const bbox = [0, 0, 0, 0] as PaddingData;
|
||||
bbox[i1] = bbox[i2] = t;
|
||||
return bbox;
|
||||
},
|
||||
//
|
||||
// Indicate the border on the two sides
|
||||
//
|
||||
border: (node) => {
|
||||
const bbox = [0, 0, 0, 0] as PaddingData;
|
||||
bbox[i1] = bbox[i2] = node.thickness;
|
||||
return bbox;
|
||||
},
|
||||
//
|
||||
// Remove the single side notations, if present
|
||||
//
|
||||
remove: side1 + ' ' + side2
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @param {string => Renderer} render The function for adding the strike to the node
|
||||
* @return {string => DefPair} The function returning the notation definition for the diagonal strike
|
||||
*/
|
||||
export const CommonDiagonalStrike = function<W extends Menclose, N>(render: (sname: string) => Renderer<W, N>):
|
||||
DefPairF<string, W, N> {
|
||||
/**
|
||||
* @param {string} name The name of the diagonal strike to define
|
||||
* @return {DefPair} The notation definition for the diagonal strike
|
||||
*/
|
||||
return (name: string) => {
|
||||
const cname = 'mjx-' + name.charAt(0) + 'strike';
|
||||
return [name + 'diagonalstrike', {
|
||||
//
|
||||
// Find the angle and width from the bounding box size and create the diagonal line
|
||||
//
|
||||
renderer: render(cname),
|
||||
//
|
||||
// Add padding all around
|
||||
//
|
||||
bbox: fullBBox
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @param {Renderer} render The function to add the arrow to the node
|
||||
* @return {string => DefPair} The funciton returning the notation definition for the diagonal arrow
|
||||
*/
|
||||
export const CommonDiagonalArrow = function<W extends Menclose, N>(render: Renderer<W, N>): DefPairF<string, W, N> {
|
||||
/**
|
||||
* @param {string} name The name of the diagonal arrow to define
|
||||
* @return {DefPair} The notation definition for the diagonal arrow
|
||||
*/
|
||||
return (name: string) => {
|
||||
const [c, pi, double, remove] = diagonalArrowDef[name];
|
||||
return [name + 'arrow', {
|
||||
//
|
||||
// Find the angle and width from the bounding box size and create
|
||||
// the arrow from them and the other arrow data
|
||||
//
|
||||
renderer: (node, _child) => {
|
||||
const [a, W] = node.arrowAW();
|
||||
const arrow = node.arrow(W, c * (a - pi), double);
|
||||
render(node, arrow);
|
||||
},
|
||||
//
|
||||
// Add space for the arrowhead all around
|
||||
//
|
||||
bbox: (node) => {
|
||||
const {a, x, y} = node.arrowData();
|
||||
const [ax, ay, adx] = [node.arrowhead.x, node.arrowhead.y, node.arrowhead.dx];
|
||||
const [b, ar] = node.getArgMod(ax + adx, ay);
|
||||
const dy = y + (b > a ? node.thickness * ar * Math.sin(b - a) : 0);
|
||||
const dx = x + (b > Math.PI / 2 - a ? node.thickness * ar * Math.sin(b + a - Math.PI / 2) : 0);
|
||||
return [dy, dx, dy, dx];
|
||||
},
|
||||
//
|
||||
// Remove redundant notations
|
||||
//
|
||||
remove: remove
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Renderer} render The function to add the arrow to the node
|
||||
* @return {string => DefPair} The function returning the notation definition for the arrow
|
||||
*/
|
||||
export const CommonArrow = function<W extends Menclose, N>(render: Renderer<W, N>): DefPairF<string, W, N> {
|
||||
/**
|
||||
* @param {string} name The name of the horizontal or vertical arrow to define
|
||||
* @return {DefPair} The notation definition for the arrow
|
||||
*/
|
||||
return (name: string) => {
|
||||
const [angle, double, isVertical, remove] = arrowDef[name];
|
||||
return [name + 'arrow', {
|
||||
//
|
||||
// Get the arrow height and depth from the bounding box and the arrow direction
|
||||
// then create the arrow from that and the other data
|
||||
//
|
||||
renderer: (node, _child) => {
|
||||
const {w, h, d} = node.getBBox();
|
||||
const [W, offset] = (isVertical ? [h + d, 'X'] : [w, 'Y']);
|
||||
const dd = node.getOffset(offset);
|
||||
const arrow = node.arrow(W, angle, double, offset, dd);
|
||||
render(node, arrow);
|
||||
},
|
||||
//
|
||||
// Add the padding to the proper sides
|
||||
//
|
||||
bbox: arrowBBox[name],
|
||||
//
|
||||
// Remove redundant notations
|
||||
//
|
||||
remove: remove
|
||||
}];
|
||||
};
|
||||
};
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the abstract class for the CommonOutputJax
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AbstractOutputJax} from '../../core/OutputJax.js';
|
||||
import {MathDocument} from '../../core/MathDocument.js';
|
||||
import {MathItem, Metrics, STATE} from '../../core/MathItem.js';
|
||||
import {MmlNode} from '../../core/MmlTree/MmlNode.js';
|
||||
import {FontData, FontDataClass, CharOptions, DelimiterData, CssFontData} from './FontData.js';
|
||||
import {OptionList, separateOptions} from '../../util/Options.js';
|
||||
import {CommonWrapper, AnyWrapper, AnyWrapperClass} from './Wrapper.js';
|
||||
import {CommonWrapperFactory, AnyWrapperFactory} from './WrapperFactory.js';
|
||||
import {percent} from '../../util/lengths.js';
|
||||
import {StyleList, Styles} from '../../util/Styles.js';
|
||||
import {StyleList as CssStyleList, CssStyles} from '../../util/StyleList.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
export interface ExtendedMetrics extends Metrics {
|
||||
family: string; // the font family for the surrounding text
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps linking a node to the test node it contains,
|
||||
* and a map linking a node to the metrics within that node.
|
||||
*/
|
||||
export type MetricMap<N> = Map<N, ExtendedMetrics>;
|
||||
type MetricDomMap<N> = Map<N, N>;
|
||||
|
||||
/**
|
||||
* Maps for unknown characters
|
||||
*/
|
||||
export type UnknownBBox = {w: number, h: number, d: number};
|
||||
export type UnknownMap = Map<string, UnknownBBox>;
|
||||
export type UnknownVariantMap = Map<string, UnknownMap>;
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* The CommonOutputJax class on which the CHTML and SVG jax are built
|
||||
*
|
||||
* @template N The HTMLElement node class
|
||||
* @template T The Text node class
|
||||
* @template D The Document class
|
||||
* @template W The Wrapper class
|
||||
* @template F The WrapperFactory class
|
||||
* @template FD The FontData class
|
||||
* @template FC The FontDataClass object
|
||||
*/
|
||||
export abstract class CommonOutputJax<
|
||||
N, T, D,
|
||||
W extends AnyWrapper,
|
||||
F extends AnyWrapperFactory,
|
||||
FD extends FontData<any, any, any>,
|
||||
FC extends FontDataClass<any, any, any>
|
||||
> extends AbstractOutputJax<N, T, D> {
|
||||
|
||||
/**
|
||||
* The name of this output jax
|
||||
*/
|
||||
public static NAME: string = 'Common';
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static OPTIONS: OptionList = {
|
||||
...AbstractOutputJax.OPTIONS,
|
||||
scale: 1, // global scaling factor for all expressions
|
||||
minScale: .5, // smallest scaling factor to use
|
||||
mtextInheritFont: false, // true to make mtext elements use surrounding font
|
||||
merrorInheritFont: false, // true to make merror text use surrounding font
|
||||
mtextFont: '', // font to use for mtext, if not inheriting (empty means use MathJax fonts)
|
||||
merrorFont: 'serif', // font to use for merror, if not inheriting (empty means use MathJax fonts)
|
||||
mathmlSpacing: false, // true for MathML spacing rules, false for TeX rules
|
||||
skipAttributes: {}, // RFDa and other attributes NOT to copy to the output
|
||||
exFactor: .5, // default size of ex in em units
|
||||
displayAlign: 'center', // default for indentalign when set to 'auto'
|
||||
displayIndent: '0', // default for indentshift when set to 'auto'
|
||||
wrapperFactory: null, // The wrapper factory to use
|
||||
font: null, // The FontData object to use
|
||||
cssStyles: null // The CssStyles object to use
|
||||
};
|
||||
|
||||
/**
|
||||
* The default styles for the output jax
|
||||
*/
|
||||
public static commonStyles: CssStyleList = {};
|
||||
|
||||
/**
|
||||
* Used for collecting styles needed for the output jax
|
||||
*/
|
||||
public cssStyles: CssStyles;
|
||||
|
||||
/**
|
||||
* The MathDocument for the math we find
|
||||
*/
|
||||
public document: MathDocument<N, T, D>;
|
||||
|
||||
/**
|
||||
* the MathItem currently being processed
|
||||
*/
|
||||
public math: MathItem<N, T, D>;
|
||||
|
||||
/**
|
||||
* The container element for the math
|
||||
*/
|
||||
public container: N;
|
||||
|
||||
/**
|
||||
* The top-level table, if any
|
||||
*/
|
||||
public table: AnyWrapper;
|
||||
|
||||
/**
|
||||
* The pixels per em for the math item being processed
|
||||
*/
|
||||
public pxPerEm: number;
|
||||
|
||||
/**
|
||||
* The data for the font in use
|
||||
*/
|
||||
public font: FD;
|
||||
|
||||
/**
|
||||
* The wrapper factory for the MathML nodes
|
||||
*/
|
||||
public factory: F;
|
||||
|
||||
/**
|
||||
* A map from the nodes in the expression currently being processed to the
|
||||
* wrapper nodes for them (used by functions like core() to locate the wrappers
|
||||
* from the core nodes)
|
||||
*/
|
||||
public nodeMap: Map<MmlNode, W>;
|
||||
|
||||
/**
|
||||
* Node used to test for in-line metric data
|
||||
*/
|
||||
public testInline: N;
|
||||
|
||||
/**
|
||||
* Node used to test for display metric data
|
||||
*/
|
||||
public testDisplay: N;
|
||||
|
||||
/**
|
||||
* Cache of unknonw character bounding boxes for this element
|
||||
*/
|
||||
protected unknownCache: UnknownVariantMap;
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Get the WrapperFactory and connect it to this output jax
|
||||
* Get the cssStyle and font objects
|
||||
*
|
||||
* @param {OptionList} options The configuration options
|
||||
* @param {CommonWrapperFactory} defaultFactory The default wrapper factory class
|
||||
* @param {FC} defaultFont The default FontData constructor
|
||||
* @constructor
|
||||
*/
|
||||
constructor(options: OptionList = null,
|
||||
defaultFactory: typeof CommonWrapperFactory = null,
|
||||
defaultFont: FC = null) {
|
||||
const [jaxOptions, fontOptions] = separateOptions(options, defaultFont.OPTIONS);
|
||||
super(jaxOptions);
|
||||
this.factory = this.options.wrapperFactory ||
|
||||
new defaultFactory<CommonOutputJax<N, T, D, W, F, FD, FC>, W,
|
||||
AnyWrapperClass, CharOptions, DelimiterData, FD>();
|
||||
this.factory.jax = this;
|
||||
this.cssStyles = this.options.cssStyles || new CssStyles();
|
||||
this.font = this.options.font || new defaultFont(fontOptions);
|
||||
this.unknownCache = new Map();
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Save the math document
|
||||
* Create the mjx-container node
|
||||
* Create the DOM output for the root MathML math node in the container
|
||||
* Return the container node
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public typeset(math: MathItem<N, T, D>, html: MathDocument<N, T, D>) {
|
||||
this.setDocument(html);
|
||||
let node = this.createNode();
|
||||
this.toDOM(math, node, html);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {N} The container DOM node for the typeset math
|
||||
*/
|
||||
protected createNode(): N {
|
||||
const jax = (this.constructor as typeof CommonOutputJax).NAME;
|
||||
return this.html('mjx-container', {'class': 'MathJax', jax: jax});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} node The container whose scale is to be set
|
||||
*/
|
||||
protected setScale(node: N) {
|
||||
const scale = this.math.metrics.scale * this.options.scale;
|
||||
if (scale !== 1) {
|
||||
this.adaptor.setStyle(node, 'fontSize', percent(scale));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the math document, if any, and the math item
|
||||
* Set the document where HTML nodes will be created via the adaptor
|
||||
* Recursively set the TeX classes for the nodes
|
||||
* Set the scaling for the DOM node
|
||||
* Create the nodeMap (maps MathML nodes to corresponding wrappers)
|
||||
* Create the HTML output for the root MathML node in the container
|
||||
* Clear the nodeMape
|
||||
* Execute the post-filters
|
||||
*
|
||||
* @param {MathItem} math The math item to convert
|
||||
* @param {N} node The contaier to place the result into
|
||||
* @param {MathDocument} html The document containing the math
|
||||
*/
|
||||
public toDOM(math: MathItem<N, T, D>, node: N, html: MathDocument<N, T, D> = null) {
|
||||
this.setDocument(html);
|
||||
this.math = math;
|
||||
this.pxPerEm = math.metrics.ex / this.font.params.x_height;
|
||||
math.root.setTeXclass(null);
|
||||
this.setScale(node);
|
||||
this.nodeMap = new Map<MmlNode, W>();
|
||||
this.container = node;
|
||||
this.processMath(math.root, node);
|
||||
this.nodeMap = null;
|
||||
this.executeFilters(this.postFilters, math, html, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the actual typesetting function supplied by the subclass
|
||||
*
|
||||
* @param {MmlNode} math The intenral MathML node of the root math element to process
|
||||
* @param {N} node The container node where the math is to be typeset
|
||||
*/
|
||||
protected abstract processMath(math: MmlNode, node: N): void;
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @param {MathItem} math The MathItem to get the bounding box for
|
||||
* @param {MathDocument} html The MathDocument for the math
|
||||
*/
|
||||
public getBBox(math: MathItem<N, T, D>, html: MathDocument<N, T, D>) {
|
||||
this.setDocument(html);
|
||||
this.math = math;
|
||||
math.root.setTeXclass(null);
|
||||
this.nodeMap = new Map<MmlNode, W>();
|
||||
let bbox = this.factory.wrap(math.root).getOuterBBox();
|
||||
this.nodeMap = null;
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getMetrics(html: MathDocument<N, T, D>) {
|
||||
this.setDocument(html);
|
||||
const adaptor = this.adaptor;
|
||||
const maps = this.getMetricMaps(html);
|
||||
for (const math of html.math) {
|
||||
const parent = adaptor.parent(math.start.node);
|
||||
if (math.state() < STATE.METRICS && parent) {
|
||||
const map = maps[math.display ? 1 : 0];
|
||||
const {em, ex, containerWidth, lineWidth, scale, family} = map.get(parent);
|
||||
math.setMetrics(em, ex, containerWidth, lineWidth, scale);
|
||||
if (this.options.mtextInheritFont) {
|
||||
math.outputData.mtextFamily = family;
|
||||
}
|
||||
if (this.options.merrorInheritFont) {
|
||||
math.outputData.merrorFamily = family;
|
||||
}
|
||||
math.state(STATE.METRICS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} node The container node whose metrics are to be measured
|
||||
* @param {boolean} display True if the metrics are for displayed math
|
||||
* @return {Metrics} Object containing em, ex, containerWidth, etc.
|
||||
*/
|
||||
public getMetricsFor(node: N, display: boolean): ExtendedMetrics {
|
||||
const getFamily = (this.options.mtextInheritFont || this.options.merrorInheritFont);
|
||||
const test = this.getTestElement(node, display);
|
||||
const metrics = this.measureMetrics(test, getFamily);
|
||||
this.adaptor.remove(test);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MetricMap for the math list
|
||||
*
|
||||
* @param {MathDocument} html The math document whose math list is to be processed.
|
||||
* @return {MetricMap[]} The node-to-metrics maps for all the containers that have math
|
||||
*/
|
||||
protected getMetricMaps(html: MathDocument<N, T, D>): MetricMap<N>[] {
|
||||
const adaptor = this.adaptor;
|
||||
const domMaps = [new Map() as MetricDomMap<N>, new Map() as MetricDomMap<N>];
|
||||
//
|
||||
// Add the test elements all at once (so only one reflow)
|
||||
// Currently, we do one test for each container element for in-line and one for display math
|
||||
// (since we need different techniques for the two forms to avoid a WebKit bug).
|
||||
// This may need to be changed to handle floating elements better, since that has to be
|
||||
// done at the location of the math itself, not necessarily the end of the container.
|
||||
//
|
||||
for (const math of html.math) {
|
||||
const node = adaptor.parent(math.start.node);
|
||||
if (node && math.state() < STATE.METRICS) {
|
||||
const map = domMaps[math.display ? 1 : 0];
|
||||
if (!map.has(node)) {
|
||||
map.set(node, this.getTestElement(node, math.display));
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Measure the metrics for all the mapped elements
|
||||
//
|
||||
const getFamily = this.options.mtextInheritFont || this.options.merrorInheritFont;
|
||||
const maps = [new Map() as MetricMap<N>, new Map() as MetricMap<N>];
|
||||
for (const i of maps.keys()) {
|
||||
for (const node of domMaps[i].keys()) {
|
||||
maps[i].set(node, this.measureMetrics(domMaps[i].get(node), getFamily));
|
||||
}
|
||||
}
|
||||
//
|
||||
// Remove the test elements
|
||||
//
|
||||
for (const i of maps.keys()) {
|
||||
for (const node of domMaps[i].values()) {
|
||||
adaptor.remove(node);
|
||||
}
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} node The math element to be measured
|
||||
* @return {N} The test elements that were added
|
||||
*/
|
||||
protected getTestElement(node: N, display: boolean): N {
|
||||
const adaptor = this.adaptor;
|
||||
if (!this.testInline) {
|
||||
this.testInline = this.html('mjx-test', {style: {
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
'font-style': 'normal',
|
||||
'font-weight': 'normal',
|
||||
'font-size': '100%',
|
||||
'font-size-adjust': 'none',
|
||||
'text-indent': 0,
|
||||
'text-transform': 'none',
|
||||
'letter-spacing': 'normal',
|
||||
'word-spacing': 'normal',
|
||||
overflow: 'hidden',
|
||||
height: '1px',
|
||||
'margin-right': '-1px'
|
||||
}}, [
|
||||
this.html('mjx-left-box', {style: {
|
||||
display: 'inline-block',
|
||||
width: 0,
|
||||
'float': 'left'
|
||||
}}),
|
||||
this.html('mjx-ex-box', {style: {
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
width: '1px', height: '60ex'
|
||||
}}),
|
||||
this.html('mjx-right-box', {style: {
|
||||
display: 'inline-block',
|
||||
width: 0,
|
||||
'float': 'right'
|
||||
}})
|
||||
]);
|
||||
this.testDisplay = adaptor.clone(this.testInline);
|
||||
adaptor.setStyle(this.testDisplay, 'display', 'table');
|
||||
adaptor.setStyle(this.testDisplay, 'margin-right', '');
|
||||
adaptor.setStyle(adaptor.firstChild(this.testDisplay) as N, 'display', 'none');
|
||||
const right = adaptor.lastChild(this.testDisplay) as N;
|
||||
adaptor.setStyle(right, 'display', 'table-cell');
|
||||
adaptor.setStyle(right, 'width', '10000em');
|
||||
adaptor.setStyle(right, 'float', '');
|
||||
}
|
||||
return adaptor.append(node, adaptor.clone(display ? this.testDisplay : this.testInline) as N) as N;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {N} node The test node to measure
|
||||
* @param {boolean} getFamily True if font family of surroundings is to be determined
|
||||
* @return {ExtendedMetrics} The metric data for the given node
|
||||
*/
|
||||
protected measureMetrics(node: N, getFamily: boolean): ExtendedMetrics {
|
||||
const adaptor = this.adaptor;
|
||||
const family = (getFamily ? adaptor.fontFamily(node) : '');
|
||||
const em = adaptor.fontSize(node);
|
||||
const [w, h] = adaptor.nodeSize(adaptor.childNode(node, 1) as N);
|
||||
const ex = (w ? h / 60 : em * this.options.exFactor);
|
||||
const containerWidth = (!w ? 1000000 : adaptor.getStyle(node, 'display') === 'table' ?
|
||||
adaptor.nodeSize(adaptor.lastChild(node) as N)[0] - 1 :
|
||||
adaptor.nodeBBox(adaptor.lastChild(node) as N).left -
|
||||
adaptor.nodeBBox(adaptor.firstChild(node) as N).left - 2);
|
||||
const scale = Math.max(this.options.minScale,
|
||||
this.options.matchFontHeight ? ex / this.font.params.x_height / em : 1);
|
||||
const lineWidth = 1000000; // no linebreaking (otherwise would be a percentage of cwidth)
|
||||
return {em, ex, containerWidth, lineWidth, scale, family};
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public styleSheet(html: MathDocument<N, T, D>) {
|
||||
this.setDocument(html);
|
||||
//
|
||||
// Start with the common styles
|
||||
//
|
||||
this.cssStyles.clear();
|
||||
this.cssStyles.addStyles((this.constructor as typeof CommonOutputJax).commonStyles);
|
||||
//
|
||||
// Add document-specific styles
|
||||
//
|
||||
if ('getStyles' in html) {
|
||||
for (const styles of ((html as any).getStyles() as CssStyleList[])) {
|
||||
this.cssStyles.addStyles(styles);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Gather the CSS from the classes and font
|
||||
//
|
||||
this.addWrapperStyles(this.cssStyles);
|
||||
this.addFontStyles(this.cssStyles);
|
||||
//
|
||||
// Create the stylesheet for the CSS
|
||||
//
|
||||
const sheet = this.html('style', {id: 'MJX-styles'}, [this.text('\n' + this.cssStyles.cssText + '\n')]);
|
||||
return sheet as N;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CssStyles} styles The style object to add to
|
||||
*/
|
||||
protected addFontStyles(styles: CssStyles) {
|
||||
styles.addStyles(this.font.styles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CssStyles} styles The style object to add to
|
||||
*/
|
||||
protected addWrapperStyles(styles: CssStyles) {
|
||||
for (const kind of this.factory.getKinds()) {
|
||||
this.addClassStyles(this.factory.getNodeClass(kind), styles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {typeof CommonWrapper} CLASS The Wrapper class whose styles are to be added
|
||||
* @param {CssStyles} styles The style object to add to.
|
||||
*/
|
||||
protected addClassStyles(CLASS: typeof CommonWrapper, styles: CssStyles) {
|
||||
styles.addStyles(CLASS.styles);
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* @param {MathDocument} html The document to be used
|
||||
*/
|
||||
protected setDocument(html: MathDocument<N, T, D>) {
|
||||
if (html) {
|
||||
this.document = html;
|
||||
this.adaptor.document = html.document;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type The type of HTML node to create
|
||||
* @param {OptionList} def The properties to set on the HTML node
|
||||
* @param {(N|T)[]} content Array of child nodes to set for the HTML node
|
||||
* @param {string} ns The namespace for the element
|
||||
* @return {N} The newly created DOM tree
|
||||
*/
|
||||
public html(type: string, def: OptionList = {}, content: (N | T)[] = [], ns?: string): N {
|
||||
return this.adaptor.node(type, def, content, ns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text The text string for which to make a text node
|
||||
*
|
||||
* @return {T} A text node with the given text
|
||||
*/
|
||||
public text(text: string): T {
|
||||
return this.adaptor.text(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} m A number to be shown with a fixed number of digits
|
||||
* @param {number=} n The number of digits to use
|
||||
* @return {string} The formatted number
|
||||
*/
|
||||
public fixed(m: number, n: number = 3): string {
|
||||
if (Math.abs(m) < .0006) {
|
||||
return '0';
|
||||
}
|
||||
return m.toFixed(n).replace(/\.?0+$/, '');
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/*
|
||||
* Methods for handling text that is not in the current MathJax font
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a DOM node for text from a specific CSS font, or that is
|
||||
* not in the current MathJax font
|
||||
*
|
||||
* @param {string} text The text to be displayed
|
||||
* @param {string} variant The name of the variant for the text
|
||||
* @return {N} The text element containing the text
|
||||
*/
|
||||
public abstract unknownText(text: string, variant: string): N;
|
||||
|
||||
/**
|
||||
* Measure text from a specific font, or that isn't in the MathJax font
|
||||
*
|
||||
* @param {string} text The text to measure
|
||||
* @param {string} variant The variant for the text
|
||||
* @param {CssFontData} font The family, italic, and bold data for explicit fonts
|
||||
* @return {UnknownBBox} The width, height, and depth of the text (in ems)
|
||||
*/
|
||||
public measureText(text: string, variant: string, font: CssFontData = ['', false, false]): UnknownBBox {
|
||||
const node = this.unknownText(text, variant);
|
||||
if (variant === '-explicitFont') {
|
||||
const styles = this.cssFontStyles(font);
|
||||
this.adaptor.setAttributes(node, {style: styles});
|
||||
}
|
||||
return this.measureTextNodeWithCache(node, text, variant, font);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a text node, caching the result, and using
|
||||
* a cached result, if there is one.
|
||||
*
|
||||
* @param {N} text The text element to measure
|
||||
* @param {string} chars The string contained in the text node
|
||||
* @param {string} variant The variant for the text
|
||||
* @param {CssFontData} font The family, italic, and bold data for explicit fonts
|
||||
* @return {UnknownBBox} The width, height and depth for the text
|
||||
*/
|
||||
public measureTextNodeWithCache(
|
||||
text: N, chars: string, variant: string,
|
||||
font: CssFontData = ['', false, false]
|
||||
): UnknownBBox {
|
||||
if (variant === '-explicitFont') {
|
||||
variant = [font[0], font[1] ? 'T' : 'F', font[2] ? 'T' : 'F', ''].join('-');
|
||||
}
|
||||
if (!this.unknownCache.has(variant)) {
|
||||
this.unknownCache.set(variant, new Map());
|
||||
}
|
||||
const map = this.unknownCache.get(variant);
|
||||
const cached = map.get(chars);
|
||||
if (cached) return cached;
|
||||
const bbox = this.measureTextNode(text);
|
||||
map.set(chars, bbox);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure the width of a text element by placing it in the page
|
||||
* and looking up its size (fake the height and depth, since we can't measure that)
|
||||
*
|
||||
* @param {N} text The text element to measure
|
||||
* @return {UnknownBBox} The width, height and depth for the text (in ems)
|
||||
*/
|
||||
public abstract measureTextNode(text: N): UnknownBBox;
|
||||
|
||||
/**
|
||||
* Measure the width, height and depth of an annotation-xml node's content
|
||||
*
|
||||
* @param{N} xml The xml content node to be measured
|
||||
* @return {UnknownBBox} The width, height, and depth of the content
|
||||
*/
|
||||
public measureXMLnode(xml: N): UnknownBBox {
|
||||
const adaptor = this.adaptor;
|
||||
const content = this.html('mjx-xml-block', {style: {display: 'inline-block'}}, [adaptor.clone(xml)]);
|
||||
const base = this.html('mjx-baseline', {style: {display: 'inline-block', width: 0, height: 0}});
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
display: 'inline-block',
|
||||
'font-family': 'initial',
|
||||
'line-height': 'normal'
|
||||
};
|
||||
const node = this.html('mjx-measure-xml', {style}, [base, content]);
|
||||
adaptor.append(adaptor.parent(this.math.start.node), this.container);
|
||||
adaptor.append(this.container, node);
|
||||
const em = this.math.metrics.em * this.math.metrics.scale;
|
||||
const {left, right, bottom, top} = adaptor.nodeBBox(content);
|
||||
const w = (right - left) / em;
|
||||
const h = (adaptor.nodeBBox(base).top - top) / em;
|
||||
const d = (bottom - top) / em - h;
|
||||
adaptor.remove(this.container);
|
||||
adaptor.remove(node);
|
||||
return {w, h, d};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CssFontData} font The family, style, and weight for the given font
|
||||
* @param {StyleList} styles The style object to add the font data to
|
||||
* @return {StyleList} The modified (or initialized) style object
|
||||
*/
|
||||
public cssFontStyles(font: CssFontData, styles: StyleList = {}): StyleList {
|
||||
const [family, italic, bold] = font;
|
||||
styles['font-family'] = this.font.getFamily(family);
|
||||
if (italic) styles['font-style'] = 'italic';
|
||||
if (bold) styles['font-weight'] = 'bold';
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Styles} styles The style object to query
|
||||
* @return {CssFontData} The family, italic, and boolean values
|
||||
*/
|
||||
public getFontData(styles: Styles): CssFontData {
|
||||
if (!styles) {
|
||||
styles = new Styles();
|
||||
}
|
||||
return [this.font.getFamily(styles.get('font-family')),
|
||||
styles.get('font-style') === 'italic',
|
||||
styles.get('font-weight') === 'bold'] as CssFontData;
|
||||
}
|
||||
|
||||
}
|
||||
+845
@@ -0,0 +1,845 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonWrapper class
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AbstractWrapper, WrapperClass} from '../../core/Tree/Wrapper.js';
|
||||
import {PropertyList} from '../../core/Tree/Node.js';
|
||||
import {MmlNode, TextNode, AbstractMmlNode, indentAttributes} from '../../core/MmlTree/MmlNode.js';
|
||||
import {MmlMo} from '../../core/MmlTree/MmlNodes/mo.js';
|
||||
import {Property} from '../../core/Tree/Node.js';
|
||||
import {unicodeChars} from '../../util/string.js';
|
||||
import * as LENGTHS from '../../util/lengths.js';
|
||||
import {Styles} from '../../util/Styles.js';
|
||||
import {StyleList} from '../../util/StyleList.js';
|
||||
import {CommonOutputJax} from './OutputJax.js';
|
||||
import {CommonWrapperFactory} from './WrapperFactory.js';
|
||||
import {BBox} from '../../util/BBox.js';
|
||||
import {FontData, DelimiterData, CharData, CharOptions, DIRECTION, NOSTRETCH} from './FontData.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Shorthand for a dictionary object (an object of key:value pairs)
|
||||
*/
|
||||
export type StringMap = {[key: string]: string};
|
||||
|
||||
/**
|
||||
* MathML spacing rules
|
||||
*/
|
||||
/* tslint:disable-next-line:whitespace */
|
||||
const SMALLSIZE = 2/18;
|
||||
|
||||
/**
|
||||
* @param {boolean} script The scriptlevel
|
||||
* @param {number} size The space size
|
||||
* @return {number} The size clamped to SMALLSIZE when scriptlevel > 0
|
||||
*/
|
||||
function MathMLSpace(script: boolean, size: number): number {
|
||||
return (script ? size < SMALLSIZE ? 0 : SMALLSIZE : size);
|
||||
}
|
||||
|
||||
export type Constructor<T> = new(...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* Shorthands for wrappers and their constructors
|
||||
*/
|
||||
export type AnyWrapper = CommonWrapper<any, any, any, any, any, any>;
|
||||
export type AnyWrapperClass = CommonWrapperClass<any, any, any, any, any, any>;
|
||||
export type WrapperConstructor = Constructor<AnyWrapper>;
|
||||
|
||||
/*********************************************************/
|
||||
/**
|
||||
* The CommonWrapper class interface
|
||||
*
|
||||
* @template J The OutputJax type
|
||||
* @template W The Wrapper type
|
||||
* @template C The WrapperClass type
|
||||
* @template CC The CharOptions type
|
||||
* @template FD The FontData type
|
||||
*/
|
||||
export interface CommonWrapperClass<
|
||||
J extends CommonOutputJax<any, any, any, W, CommonWrapperFactory<J, W, C, CC, DD, FD>, FD, any>,
|
||||
W extends CommonWrapper<J, W, C, CC, DD, FD>,
|
||||
C extends CommonWrapperClass<J, W, C, CC, DD, FD>,
|
||||
CC extends CharOptions,
|
||||
DD extends DelimiterData,
|
||||
FD extends FontData<CC, any, DD>
|
||||
> extends WrapperClass<MmlNode, CommonWrapper<J, W, C, CC, DD, FD>> {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
new(factory: CommonWrapperFactory<J, W, C, CC, DD, FD>, node: MmlNode, ...args: any[]): W;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The base CommonWrapper class
|
||||
*
|
||||
* @template J The OutputJax type
|
||||
* @template W The Wrapper type
|
||||
* @template C The WrapperClass type
|
||||
* @template CC The CharOptions type
|
||||
* @template FD The FontData type
|
||||
*/
|
||||
export class CommonWrapper<
|
||||
J extends CommonOutputJax<any, any, any, W, CommonWrapperFactory<J, W, C, CC, DD, FD>, FD, any>,
|
||||
W extends CommonWrapper<J, W, C, CC, DD, FD>,
|
||||
C extends CommonWrapperClass<J, W, C, CC, DD, FD>,
|
||||
CC extends CharOptions,
|
||||
DD extends DelimiterData,
|
||||
FD extends FontData<CC, any, DD>
|
||||
> extends AbstractWrapper<MmlNode, CommonWrapper<J, W, C, CC, DD, FD>> {
|
||||
|
||||
/**
|
||||
* The wrapper kind
|
||||
*/
|
||||
public static kind: string = 'unknown';
|
||||
|
||||
/**
|
||||
* Any styles needed for the class
|
||||
*/
|
||||
public static styles: StyleList = {};
|
||||
|
||||
/**
|
||||
* Styles that should not be passed on from style attribute
|
||||
*/
|
||||
public static removeStyles: string[] = [
|
||||
'fontSize', 'fontFamily', 'fontWeight',
|
||||
'fontStyle', 'fontVariant', 'font'
|
||||
];
|
||||
|
||||
/**
|
||||
* Non-MathML attributes on MathML elements NOT to be copied to the
|
||||
* corresponding DOM elements. If set to false, then the attribute
|
||||
* WILL be copied. Most of these (like the font attributes) are handled
|
||||
* in other ways.
|
||||
*/
|
||||
public static skipAttributes: {[name: string]: boolean} = {
|
||||
fontfamily: true, fontsize: true, fontweight: true, fontstyle: true,
|
||||
color: true, background: true,
|
||||
'class': true, href: true, style: true,
|
||||
xmlns: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The translation of mathvariant to bold styles, or to remove
|
||||
* bold from a mathvariant.
|
||||
*/
|
||||
public static BOLDVARIANTS: {[name: string]: StringMap} = {
|
||||
bold: {
|
||||
normal: 'bold',
|
||||
italic: 'bold-italic',
|
||||
fraktur: 'bold-fraktur',
|
||||
script: 'bold-script',
|
||||
'sans-serif': 'bold-sans-serif',
|
||||
'sans-serif-italic': 'sans-serif-bold-italic'
|
||||
},
|
||||
normal: {
|
||||
bold: 'normal',
|
||||
'bold-italic': 'italic',
|
||||
'bold-fraktur': 'fraktur',
|
||||
'bold-script': 'script',
|
||||
'bold-sans-serif': 'sans-serif',
|
||||
'sans-serif-bold-italic': 'sans-serif-italic'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The translation of mathvariant to italic styles, or to remove
|
||||
* italic from a mathvariant.
|
||||
*/
|
||||
public static ITALICVARIANTS: {[name: string]: StringMap} = {
|
||||
italic: {
|
||||
normal: 'italic',
|
||||
bold: 'bold-italic',
|
||||
'sans-serif': 'sans-serif-italic',
|
||||
'bold-sans-serif': 'sans-serif-bold-italic'
|
||||
},
|
||||
normal: {
|
||||
italic: 'normal',
|
||||
'bold-italic': 'bold',
|
||||
'sans-serif-italic': 'sans-serif',
|
||||
'sans-serif-bold-italic': 'bold-sans-serif'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The factory used to create more wrappers
|
||||
*/
|
||||
protected factory: CommonWrapperFactory<J, W, C, CC, DD, FD>;
|
||||
|
||||
/**
|
||||
* The parent of this node
|
||||
*/
|
||||
public parent: W = null;
|
||||
|
||||
/**
|
||||
* The children of this node
|
||||
*/
|
||||
public childNodes: W[];
|
||||
|
||||
/**
|
||||
* Styles that must be handled directly by the wrappers (mostly having to do with fonts)
|
||||
*/
|
||||
protected removedStyles: StringMap = null;
|
||||
|
||||
/**
|
||||
* The explicit styles set by the node
|
||||
*/
|
||||
protected styles: Styles = null;
|
||||
|
||||
/**
|
||||
* The mathvariant for this node
|
||||
*/
|
||||
public variant: string = '';
|
||||
|
||||
/**
|
||||
* The bounding box for this node
|
||||
*/
|
||||
public bbox: BBox;
|
||||
/**
|
||||
* Whether the bounding box has been computed yet
|
||||
*/
|
||||
protected bboxComputed: boolean = false;
|
||||
|
||||
/**
|
||||
* Delimiter data for stretching this node (NOSTRETCH means not yet determined)
|
||||
*/
|
||||
public stretch: DD = NOSTRETCH as DD;
|
||||
|
||||
/**
|
||||
* Easy access to the font parameters
|
||||
*/
|
||||
public font: FD = null;
|
||||
|
||||
/**
|
||||
* Easy access to the output jax for this node
|
||||
*/
|
||||
get jax() {
|
||||
return this.factory.jax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Easy access to the DOMAdaptor object
|
||||
*/
|
||||
get adaptor() {
|
||||
return this.factory.jax.adaptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Easy access to the metric data for this node
|
||||
*/
|
||||
get metrics() {
|
||||
return this.factory.jax.math.metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if children with percentage widths should be resolved by this container
|
||||
*/
|
||||
get fixesPWidth() {
|
||||
return !this.node.notParent && !this.node.isToken;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
constructor(factory: CommonWrapperFactory<J, W, C, CC, DD, FD>, node: MmlNode, parent: W = null) {
|
||||
super(factory, node);
|
||||
this.parent = parent;
|
||||
this.font = factory.jax.font;
|
||||
this.bbox = BBox.zero();
|
||||
this.getStyles();
|
||||
this.getVariant();
|
||||
this.getScale();
|
||||
this.getSpace();
|
||||
this.childNodes = node.childNodes.map((child: MmlNode) => {
|
||||
const wrapped = this.wrap(child);
|
||||
if (wrapped.bbox.pwidth && (node.notParent || node.isKind('math'))) {
|
||||
this.bbox.pwidth = BBox.fullWidth;
|
||||
}
|
||||
return wrapped;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MmlNode} node The node to the wrapped
|
||||
* @param {W} parent The wrapped parent node
|
||||
* @return {W} The newly wrapped node
|
||||
*/
|
||||
public wrap(node: MmlNode, parent: W = null): W {
|
||||
const wrapped = this.factory.wrap(node, parent || this);
|
||||
if (parent) {
|
||||
parent.childNodes.push(wrapped);
|
||||
}
|
||||
this.jax.nodeMap.set(node, wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/**
|
||||
* Return the wrapped node's bounding box, either the cached one, if it exists,
|
||||
* or computed directly if not.
|
||||
*
|
||||
* @param {boolean} save Whether to cache the bbox or not (used for stretchy elements)
|
||||
* @return {BBox} The computed bounding box
|
||||
*/
|
||||
public getBBox(save: boolean = true): BBox {
|
||||
if (this.bboxComputed) {
|
||||
return this.bbox;
|
||||
}
|
||||
const bbox = (save ? this.bbox : BBox.zero());
|
||||
this.computeBBox(bbox);
|
||||
this.bboxComputed = save;
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped node's bounding box that includes borders and padding
|
||||
*
|
||||
* @param {boolean} save Whether to cache the bbox or not (used for stretchy elements)
|
||||
* @return {BBox} The computed bounding box
|
||||
*/
|
||||
public getOuterBBox(save: boolean = true): BBox {
|
||||
const bbox = this.getBBox(save);
|
||||
if (!this.styles) return bbox;
|
||||
const obox = new BBox();
|
||||
Object.assign(obox, bbox);
|
||||
for (const [name, side] of BBox.StyleAdjust) {
|
||||
const x = this.styles.get(name);
|
||||
if (x) {
|
||||
(obox as any)[side] += this.length2em(x, 1, obox.rscale);
|
||||
}
|
||||
}
|
||||
return obox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bounding box to modify (either this.bbox, or an empty one)
|
||||
* @param {boolean} recompute True if we are recomputing due to changes in children that have percentage widths
|
||||
*/
|
||||
protected computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
bbox.empty();
|
||||
for (const child of this.childNodes) {
|
||||
bbox.append(child.getOuterBBox());
|
||||
}
|
||||
bbox.clean();
|
||||
if (this.fixesPWidth && this.setChildPWidths(recompute)) {
|
||||
this.computeBBox(bbox, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively resolve any percentage widths in the child nodes using the given
|
||||
* container width (or the child width, if none was passed).
|
||||
* Overriden for mtables in order to compute the width.
|
||||
*
|
||||
* @param {boolean} recompute True if we are recomputing due to changes in children
|
||||
* @param {(number|null)=} w The width of the container (from which percentages are computed)
|
||||
* @param {boolean=} clear True if pwidth marker is to be cleared
|
||||
* @return {boolean} True if a percentage width was found
|
||||
*/
|
||||
public setChildPWidths(recompute: boolean, w: (number | null) = null, clear: boolean = true): boolean {
|
||||
if (recompute) {
|
||||
return false;
|
||||
}
|
||||
if (clear) {
|
||||
this.bbox.pwidth = '';
|
||||
}
|
||||
let changed = false;
|
||||
for (const child of this.childNodes) {
|
||||
const cbox = child.getOuterBBox();
|
||||
if (cbox.pwidth && child.setChildPWidths(recompute, w === null ? cbox.w : w, clear)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark BBox to be computed again (e.g., when an mo has stretched)
|
||||
*/
|
||||
public invalidateBBox() {
|
||||
if (this.bboxComputed) {
|
||||
this.bboxComputed = false;
|
||||
if (this.parent) {
|
||||
this.parent.invalidateBBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy child skew and italic correction
|
||||
*
|
||||
* @param {BBox} bbox The bounding box to modify
|
||||
*/
|
||||
protected copySkewIC(bbox: BBox) {
|
||||
const first = this.childNodes[0];
|
||||
if (first?.bbox.sk) {
|
||||
bbox.sk = first.bbox.sk;
|
||||
}
|
||||
if (first?.bbox.dx) {
|
||||
bbox.dx = first.bbox.dx;
|
||||
}
|
||||
const last = this.childNodes[this.childNodes.length - 1];
|
||||
if (last?.bbox.ic) {
|
||||
bbox.ic = last.bbox.ic;
|
||||
bbox.w += bbox.ic;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
/**
|
||||
* Add the style attribute, but remove any font-related styles
|
||||
* (since these are handled separately by the variant)
|
||||
*/
|
||||
protected getStyles() {
|
||||
const styleString = this.node.attributes.getExplicit('style') as string;
|
||||
if (!styleString) return;
|
||||
const style = this.styles = new Styles(styleString);
|
||||
for (let i = 0, m = CommonWrapper.removeStyles.length; i < m; i++) {
|
||||
const id = CommonWrapper.removeStyles[i];
|
||||
if (style.get(id)) {
|
||||
if (!this.removedStyles) this.removedStyles = {};
|
||||
this.removedStyles[id] = style.get(id);
|
||||
style.set(id, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mathvariant (or construct one, if needed).
|
||||
*/
|
||||
protected getVariant() {
|
||||
if (!this.node.isToken) return;
|
||||
const attributes = this.node.attributes;
|
||||
let variant = attributes.get('mathvariant') as string;
|
||||
if (!attributes.getExplicit('mathvariant')) {
|
||||
const values = attributes.getList('fontfamily', 'fontweight', 'fontstyle') as StringMap;
|
||||
if (this.removedStyles) {
|
||||
const style = this.removedStyles;
|
||||
if (style.fontFamily) values.family = style.fontFamily;
|
||||
if (style.fontWeight) values.weight = style.fontWeight;
|
||||
if (style.fontStyle) values.style = style.fontStyle;
|
||||
}
|
||||
if (values.fontfamily) values.family = values.fontfamily;
|
||||
if (values.fontweight) values.weight = values.fontweight;
|
||||
if (values.fontstyle) values.style = values.fontstyle;
|
||||
if (values.weight && values.weight.match(/^\d+$/)) {
|
||||
values.weight = (parseInt(values.weight) > 600 ? 'bold' : 'normal');
|
||||
}
|
||||
if (values.family) {
|
||||
variant = this.explicitVariant(values.family, values.weight, values.style);
|
||||
} else {
|
||||
if (this.node.getProperty('variantForm')) variant = '-tex-variant';
|
||||
variant = (CommonWrapper.BOLDVARIANTS[values.weight] || {})[variant] || variant;
|
||||
variant = (CommonWrapper.ITALICVARIANTS[values.style] || {})[variant] || variant;
|
||||
}
|
||||
}
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSS for a token element having an explicit font (rather than regular mathvariant).
|
||||
*
|
||||
* @param {string} fontFamily The font family to use
|
||||
* @param {string} fontWeight The font weight to use
|
||||
* @param {string} fontStyle The font style to use
|
||||
*/
|
||||
protected explicitVariant(fontFamily: string, fontWeight: string, fontStyle: string) {
|
||||
let style = this.styles;
|
||||
if (!style) style = this.styles = new Styles();
|
||||
style.set('fontFamily', fontFamily);
|
||||
if (fontWeight) style.set('fontWeight', fontWeight);
|
||||
if (fontStyle) style.set('fontStyle', fontStyle);
|
||||
return '-explicitFont';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the scaling factor to use for this wrapped node, and set the styles for it.
|
||||
*/
|
||||
protected getScale() {
|
||||
let scale = 1, parent = this.parent;
|
||||
let pscale = (parent ? parent.bbox.scale : 1);
|
||||
let attributes = this.node.attributes;
|
||||
let scriptlevel = Math.min(attributes.get('scriptlevel') as number, 2);
|
||||
let fontsize = attributes.get('fontsize');
|
||||
let mathsize = (this.node.isToken || this.node.isKind('mstyle') ?
|
||||
attributes.get('mathsize') : attributes.getInherited('mathsize'));
|
||||
//
|
||||
// If scriptsize is non-zero, set scale based on scriptsizemultiplier
|
||||
//
|
||||
if (scriptlevel !== 0) {
|
||||
scale = Math.pow(attributes.get('scriptsizemultiplier') as number, scriptlevel);
|
||||
let scriptminsize = this.length2em(attributes.get('scriptminsize'), .8, 1);
|
||||
if (scale < scriptminsize) scale = scriptminsize;
|
||||
}
|
||||
//
|
||||
// If there is style="font-size:...", and not fontsize attribute, use that as fontsize
|
||||
//
|
||||
if (this.removedStyles && this.removedStyles.fontSize && !fontsize) {
|
||||
fontsize = this.removedStyles.fontSize;
|
||||
}
|
||||
//
|
||||
// If there is a fontsize and no mathsize attribute, is that
|
||||
//
|
||||
if (fontsize && !attributes.getExplicit('mathsize')) {
|
||||
mathsize = fontsize;
|
||||
}
|
||||
//
|
||||
// Incorporate the mathsize, if any
|
||||
//
|
||||
if (mathsize !== '1') {
|
||||
scale *= this.length2em(mathsize, 1, 1);
|
||||
}
|
||||
//
|
||||
// Record the scaling factors and set the element's CSS
|
||||
//
|
||||
this.bbox.scale = scale;
|
||||
this.bbox.rscale = scale / pscale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spacing based on TeX or MathML algorithm
|
||||
*/
|
||||
protected getSpace() {
|
||||
const isTop = this.isTopEmbellished();
|
||||
const hasSpacing = this.node.hasSpacingAttributes();
|
||||
if (this.jax.options.mathmlSpacing || hasSpacing) {
|
||||
isTop && this.getMathMLSpacing();
|
||||
} else {
|
||||
this.getTeXSpacing(isTop, hasSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the spacing using MathML rules based on the core MO
|
||||
*/
|
||||
protected getMathMLSpacing() {
|
||||
const node = this.node.coreMO() as MmlMo;
|
||||
//
|
||||
// If the mo is not within a multi-node mrow, don't add space
|
||||
//
|
||||
const child = node.coreParent();
|
||||
const parent = child.parent;
|
||||
if (!parent || !parent.isKind('mrow') || parent.childNodes.length === 1) return;
|
||||
//
|
||||
// Get the lspace and rspace
|
||||
//
|
||||
const attributes = node.attributes;
|
||||
const isScript = (attributes.get('scriptlevel') > 0);
|
||||
this.bbox.L = (attributes.isSet('lspace') ?
|
||||
Math.max(0, this.length2em(attributes.get('lspace'))) :
|
||||
MathMLSpace(isScript, node.lspace));
|
||||
this.bbox.R = (attributes.isSet('rspace') ?
|
||||
Math.max(0, this.length2em(attributes.get('rspace'))) :
|
||||
MathMLSpace(isScript, node.rspace));
|
||||
//
|
||||
// If there are two adjacent <mo>, use enough left space to make it
|
||||
// the maximum of the rspace of the first and lspace of the second
|
||||
//
|
||||
const n = parent.childIndex(child);
|
||||
if (n === 0) return;
|
||||
const prev = parent.childNodes[n - 1] as AbstractMmlNode;
|
||||
if (!prev.isEmbellished) return;
|
||||
const bbox = this.jax.nodeMap.get(prev).getBBox();
|
||||
if (bbox.R) {
|
||||
this.bbox.L = Math.max(0, this.bbox.L - bbox.R);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the spacing using the TeX rules
|
||||
*
|
||||
* @parm {boolean} isTop True when this is a top-level embellished operator
|
||||
* @parm {boolean} hasSpacing True when there is an explicit or inherited 'form' attribute
|
||||
*/
|
||||
protected getTeXSpacing(isTop: boolean, hasSpacing: boolean) {
|
||||
if (!hasSpacing) {
|
||||
const space = this.node.texSpacing();
|
||||
if (space) {
|
||||
this.bbox.L = this.length2em(space);
|
||||
}
|
||||
}
|
||||
if (isTop || hasSpacing) {
|
||||
const attributes = this.node.coreMO().attributes;
|
||||
if (attributes.isSet('lspace')) {
|
||||
this.bbox.L = Math.max(0, this.length2em(attributes.get('lspace')));
|
||||
}
|
||||
if (attributes.isSet('rspace')) {
|
||||
this.bbox.R = Math.max(0, this.length2em(attributes.get('rspace')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} True if this is the top-most container of an embellished operator that is
|
||||
* itself an embellished operator (the maximal embellished operator for its core)
|
||||
*/
|
||||
protected isTopEmbellished(): boolean {
|
||||
return (this.node.isEmbellished &&
|
||||
!(this.node.parent && this.node.parent.isEmbellished));
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
/**
|
||||
* @return {CommonWrapper} The wrapper for this node's core node
|
||||
*/
|
||||
public core(): CommonWrapper<J, W, C, CC, DD, FD> {
|
||||
return this.jax.nodeMap.get(this.node.core());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {CommonWrapper} The wrapper for this node's core <mo> node
|
||||
*/
|
||||
public coreMO(): CommonWrapper<J, W, C, CC, DD, FD> {
|
||||
return this.jax.nodeMap.get(this.node.coreMO());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string} For a token node, the combined text content of the node's children
|
||||
*/
|
||||
public getText(): string {
|
||||
let text = '';
|
||||
if (this.node.isToken) {
|
||||
for (const child of this.node.childNodes) {
|
||||
if (child instanceof TextNode) {
|
||||
text += child.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DIRECTION} direction The direction to stretch this node
|
||||
* @return {boolean} Whether the node can stretch in that direction
|
||||
*/
|
||||
public canStretch(direction: DIRECTION): boolean {
|
||||
this.stretch = NOSTRETCH as DD;
|
||||
if (this.node.isEmbellished) {
|
||||
let core = this.core();
|
||||
if (core && core.node !== this.node) {
|
||||
if (core.canStretch(direction)) {
|
||||
this.stretch = core.stretch;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.stretch.dir !== DIRECTION.None;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {[string, number]} The alignment and indentation shift for the expression
|
||||
*/
|
||||
protected getAlignShift(): [string, number] {
|
||||
let {indentalign, indentshift, indentalignfirst, indentshiftfirst} =
|
||||
this.node.attributes.getList(...indentAttributes) as StringMap;
|
||||
if (indentalignfirst !== 'indentalign') {
|
||||
indentalign = indentalignfirst;
|
||||
}
|
||||
if (indentalign === 'auto') {
|
||||
indentalign = this.jax.options.displayAlign;
|
||||
}
|
||||
if (indentshiftfirst !== 'indentshift') {
|
||||
indentshift = indentshiftfirst;
|
||||
}
|
||||
if (indentshift === 'auto') {
|
||||
indentshift = this.jax.options.displayIndent;
|
||||
if (indentalign === 'right' && !indentshift.match(/^\s*0[a-z]*\s*$/)) {
|
||||
indentshift = ('-' + indentshift.trim()).replace(/^--/, '');
|
||||
}
|
||||
}
|
||||
const shift = this.length2em(indentshift, this.metrics.containerWidth);
|
||||
return [indentalign, shift] as [string, number];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} W The total width
|
||||
* @param {BBox} bbox The bbox to be aligned
|
||||
* @param {string} align How to align (left, center, right)
|
||||
* @return {number} The x position of the aligned width
|
||||
*/
|
||||
protected getAlignX(W: number, bbox: BBox, align: string): number {
|
||||
return (align === 'right' ? W - (bbox.w + bbox.R) * bbox.rscale :
|
||||
align === 'left' ? bbox.L * bbox.rscale :
|
||||
(W - bbox.w * bbox.rscale) / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} H The total height
|
||||
* @param {number} D The total depth
|
||||
* @param {number} h The height to be aligned
|
||||
* @param {number} d The depth to be aligned
|
||||
* @param {string} align How to align (top, bottom, center, axis, baseline)
|
||||
* @return {number} The y position of the aligned baseline
|
||||
*/
|
||||
protected getAlignY(H: number, D: number, h: number, d: number, align: string): number {
|
||||
return (align === 'top' ? H - h :
|
||||
align === 'bottom' ? d - D :
|
||||
align === 'center' ? ((H - h) - (D - d)) / 2 :
|
||||
0); // baseline and axis
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} i The index of the child element whose container is needed
|
||||
* @return {number} The inner width as a container (for percentage widths)
|
||||
*/
|
||||
public getWrapWidth(i: number): number {
|
||||
return this.childNodes[i].getBBox().w;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} i The index of the child element whose container is needed
|
||||
* @return {string} The alignment child element
|
||||
*/
|
||||
public getChildAlign(_i: number): string {
|
||||
return 'left';
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/*
|
||||
* Easy access to some utility routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {number} m A number to be shown as a percent
|
||||
* @return {string} The number m as a percent
|
||||
*/
|
||||
protected percent(m: number): string {
|
||||
return LENGTHS.percent(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} m A number to be shown in ems
|
||||
* @return {string} The number with units of ems
|
||||
*/
|
||||
protected em(m: number): string {
|
||||
return LENGTHS.em(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} m A number of em's to be shown as pixels
|
||||
* @param {number} M The minimum number of pixels to allow
|
||||
* @return {string} The number with units of px
|
||||
*/
|
||||
protected px(m: number, M: number = -LENGTHS.BIGDIMEN): string {
|
||||
return LENGTHS.px(m, M, this.metrics.em);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Property} length A dimension (giving number and units) or number to be converted to ems
|
||||
* @param {number} size The default size of the dimension (for percentage values)
|
||||
* @param {number} scale The current scaling factor (to handle absolute units)
|
||||
* @return {number} The dimension converted to ems
|
||||
*/
|
||||
protected length2em(length: Property, size: number = 1, scale: number = null): number {
|
||||
if (scale === null) {
|
||||
scale = this.bbox.scale;
|
||||
}
|
||||
return LENGTHS.length2em(length as string, size, scale, this.jax.pxPerEm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text The text to turn into unicode locations
|
||||
* @param {string} name The name of the variant for the characters
|
||||
* @return {number[]} Array of numbers represeting the string's unicode character positions
|
||||
*/
|
||||
protected unicodeChars(text: string, name: string = this.variant): number[] {
|
||||
let chars = unicodeChars(text);
|
||||
//
|
||||
// Remap to Math Alphanumerics block
|
||||
//
|
||||
const variant = this.font.getVariant(name);
|
||||
if (variant && variant.chars) {
|
||||
const map = variant.chars;
|
||||
//
|
||||
// Is map[n] doesn't exist, (map[n] || []) still gives an CharData array.
|
||||
// If the array doesn't have a CharOptions element use {} instead.
|
||||
// Then check if the options has an smp property, which gives
|
||||
// the Math Alphabet mapping for this character.
|
||||
// Otherwise use the original code point, n.
|
||||
//
|
||||
chars = chars.map((n) => ((map[n] || [])[3] || {}).smp || n);
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} chars The array of unicode character numbers to remap
|
||||
* @return {number[]} The converted array
|
||||
*/
|
||||
public remapChars(chars: number[]): number[] {
|
||||
return chars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text The text from which to create a TextNode object
|
||||
* @return {TextNode} The TextNode with the given text
|
||||
*/
|
||||
public mmlText(text: string): TextNode {
|
||||
return ((this.node as AbstractMmlNode).factory.create('text') as TextNode).setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} kind The kind of MmlNode to create
|
||||
* @param {ProperyList} properties The properties to set initially
|
||||
* @param {MmlNode[]} children The child nodes to add to the created node
|
||||
* @return {MmlNode} The newly created MmlNode
|
||||
*/
|
||||
public mmlNode(kind: string, properties: PropertyList = {}, children: MmlNode[] = []): MmlNode {
|
||||
return (this.node as AbstractMmlNode).factory.create(kind, properties, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an mo wrapper with the given text,
|
||||
* link it in, and give it the right defaults.
|
||||
*
|
||||
* @param {string} text The text for the wrapped element
|
||||
* @return {CommonWrapper} The wrapped MmlMo node
|
||||
*/
|
||||
protected createMo(text: string): CommonWrapper<J, W, C, CC, DD, FD> {
|
||||
const mmlFactory = (this.node as AbstractMmlNode).factory;
|
||||
const textNode = (mmlFactory.create('text') as TextNode).setText(text);
|
||||
const mml = mmlFactory.create('mo', {stretchy: true}, [textNode]);
|
||||
mml.inheritAttributesFrom(this.node);
|
||||
const node = this.wrap(mml);
|
||||
node.parent = this as any as W;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} variant The variant in which to look for the character
|
||||
* @param {number} n The number of the character to look up
|
||||
* @return {CharData} The full CharData object, with CharOptions guaranteed to be defined
|
||||
*/
|
||||
protected getVariantChar(variant: string, n: number): CharData<CC> {
|
||||
const char = this.font.getChar(variant, n) || [0, 0, 0, {unknown: true} as CC];
|
||||
if (char.length === 3) {
|
||||
(char as any)[3] = {};
|
||||
}
|
||||
return char as [number, number, number, CC];
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the OutputWrapperFactory class
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {CommonOutputJax} from './OutputJax.js';
|
||||
import {AbstractWrapperFactory} from '../../core/Tree/WrapperFactory.js';
|
||||
import {CommonWrapper, CommonWrapperClass} from './Wrapper.js';
|
||||
import {CharOptions, DelimiterData, FontData} from './FontData.js';
|
||||
import {MmlNode} from '../../core/MmlTree/MmlNode.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The OutputWrapperFactory class for creating OutputWrapper nodes
|
||||
*
|
||||
* @template J The OutputJax type
|
||||
* @template W The Wrapper type
|
||||
* @template C The WrapperClass type
|
||||
* @template CC The CharOptions type
|
||||
* @template FD The FontData type
|
||||
*/
|
||||
export class CommonWrapperFactory<
|
||||
J extends CommonOutputJax<any, any, any, W, CommonWrapperFactory<J, W, C, CC, DD, FD>, FD, any>,
|
||||
W extends CommonWrapper<J, W, C, CC, DD, FD>,
|
||||
C extends CommonWrapperClass<J, W, C, CC, DD, FD>,
|
||||
CC extends CharOptions,
|
||||
DD extends DelimiterData,
|
||||
FD extends FontData<CC, any, DD>
|
||||
> extends AbstractWrapperFactory<MmlNode, W, C> {
|
||||
|
||||
/**
|
||||
* The default list of wrapper nodes this factory can create
|
||||
* (filled in by subclasses)
|
||||
*/
|
||||
public static defaultNodes: {[kind: string]: CommonWrapperClass<any, any, any, any, any, any>} = {};
|
||||
|
||||
/**
|
||||
* The output jax associated with this factory
|
||||
*/
|
||||
public jax: J = null;
|
||||
|
||||
/**
|
||||
* @return {Object} The list of node-creation functions
|
||||
*/
|
||||
get Wrappers(): Object {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type AnyWrapperFactory = CommonWrapperFactory<any, any, any, any, any, any>;
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonTeXAtom wrapper mixin for the MmlTeXAtom object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {TEXCLASS} from '../../../core/MmlTree/MmlNode.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonTeXAtom interface
|
||||
*/
|
||||
export interface CommonTeXAtom extends AnyWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonTeXAtom constructor
|
||||
*/
|
||||
export type TeXAtomConstructor = Constructor<CommonTeXAtom>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonTeXAtom wrapper mixin for the TeXAtom object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonTeXAtomMixin<T extends WrapperConstructor>(Base: T): TeXAtomConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
super.computeBBox(bbox, recompute);
|
||||
if (this.childNodes[0] && this.childNodes[0].bbox.ic) {
|
||||
bbox.ic = this.childNodes[0].bbox.ic;
|
||||
}
|
||||
//
|
||||
// Center VCENTER atoms vertically
|
||||
//
|
||||
if (this.node.texClass === TEXCLASS.VCENTER) {
|
||||
const {h, d} = bbox;
|
||||
const a = this.font.params.axis_height;
|
||||
const dh = ((h + d) / 2 + a) - h; // new height minus old height
|
||||
bbox.h += dh;
|
||||
bbox.d -= dh;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonTextNode wrapper mixin for the TextNode object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {TextNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonTextNode interface
|
||||
*/
|
||||
export interface CommonTextNode extends AnyWrapper {
|
||||
/**
|
||||
* @param {string} text The text to remap
|
||||
* @param {string} variant The variant for the character
|
||||
* @return {number[]} The unicode points for the (remapped) text
|
||||
*/
|
||||
remappedText(text: string, variant: string): number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonTextNode constructor
|
||||
*/
|
||||
export type TextNodeConstructor = Constructor<CommonTextNode>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonTextNode wrapper mixin for the TextNode object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonTextNodeMixin<T extends WrapperConstructor>(Base: T): TextNodeConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, _recompute: boolean = false) {
|
||||
const variant = this.parent.variant;
|
||||
const text = (this.node as TextNode).getText();
|
||||
if (variant === '-explicitFont') {
|
||||
//
|
||||
// Measure the size of the text (using the DOM if possible)
|
||||
//
|
||||
const font = this.jax.getFontData(this.parent.styles);
|
||||
const {w, h, d} = this.jax.measureText(text, variant, font);
|
||||
bbox.h = h;
|
||||
bbox.d = d;
|
||||
bbox.w = w;
|
||||
} else {
|
||||
const chars = this.remappedText(text, variant);
|
||||
bbox.empty();
|
||||
//
|
||||
// Loop through the characters and add them in one by one
|
||||
//
|
||||
for (const char of chars) {
|
||||
let [h, d, w, data] = this.getVariantChar(variant, char);
|
||||
if (data.unknown) {
|
||||
//
|
||||
// Measure unknown characters using the DOM (if possible)
|
||||
//
|
||||
const cbox = this.jax.measureText(String.fromCodePoint(char), variant);
|
||||
w = cbox.w;
|
||||
h = cbox.h;
|
||||
d = cbox.d;
|
||||
}
|
||||
//
|
||||
// Update the bounding box
|
||||
//
|
||||
bbox.w += w;
|
||||
if (h > bbox.h) bbox.h = h;
|
||||
if (d > bbox.d) bbox.d = d;
|
||||
bbox.ic = data.ic || 0;
|
||||
bbox.sk = data.sk || 0;
|
||||
bbox.dx = data.dx || 0;
|
||||
}
|
||||
if (chars.length > 1) {
|
||||
bbox.sk = 0;
|
||||
}
|
||||
bbox.clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text The text to remap
|
||||
* @param {string} variant The variant for the character
|
||||
* @return {number[]} The unicode points for the (remapped) text
|
||||
*/
|
||||
public remappedText(text: string, variant: string): number[] {
|
||||
const c = this.parent.stretch.c;
|
||||
return (c ? [c] : this.parent.remapChars(this.unicodeChars(text, variant)));
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
/*
|
||||
* TextNodes don't need these, since these properties
|
||||
* are inherited from the parent nodes
|
||||
*/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getStyles() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getVariant() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getScale() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getSpace() {}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMaction wrapper mixin for the MmlMaction object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor, AnyWrapperClass} from '../Wrapper.js';
|
||||
import {MmlMaction} from '../../../core/MmlTree/MmlNodes/maction.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {split} from '../../../util/string.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The types needed to define the actiontypes
|
||||
*
|
||||
* @template W The maction wrapper type
|
||||
*/
|
||||
export type ActionData = {[name: string]: any};
|
||||
export type ActionHandler<W extends AnyWrapper> = (node: W, data?: ActionData) => void;
|
||||
export type ActionPair<W extends AnyWrapper> = [ActionHandler<W>, ActionData];
|
||||
export type ActionMap<W extends AnyWrapper> = Map<string, ActionPair<W>>;
|
||||
export type ActionDef<W extends AnyWrapper> = [string, [ActionHandler<W>, ActionData]];
|
||||
|
||||
export type EventHandler = (event: Event) => void;
|
||||
|
||||
/**
|
||||
* Data used for tooltip actions
|
||||
*/
|
||||
export const TooltipData = {
|
||||
dx: '.2em', // x-offset of tooltip from right side of maction bbox
|
||||
dy: '.1em', // y-offset of tooltip from bottom of maction bbox
|
||||
|
||||
postDelay: 600, // milliseconds before tooltip posts
|
||||
clearDelay: 100, // milliseconds before tooltip is removed
|
||||
|
||||
hoverTimer: new Map<any, number>(), // timers for posting tooltips
|
||||
clearTimer: new Map<any, number>(), // timers for removing tooltips
|
||||
|
||||
/*
|
||||
* clear the timers if any are active
|
||||
*/
|
||||
stopTimers: (node: any, data: ActionData) => {
|
||||
if (data.clearTimer.has(node)) {
|
||||
clearTimeout(data.clearTimer.get(node));
|
||||
data.clearTimer.delete(node);
|
||||
}
|
||||
if (data.hoverTimer.has(node)) {
|
||||
clearTimeout(data.hoverTimer.get(node));
|
||||
data.hoverTimer.delete(node);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMaction interface
|
||||
*
|
||||
* @template W The maction wrapper type
|
||||
*/
|
||||
export interface CommonMaction<W extends AnyWrapper> extends AnyWrapper {
|
||||
/**
|
||||
* The handler for the specified actiontype
|
||||
*/
|
||||
action: ActionHandler<W>;
|
||||
data: ActionData;
|
||||
|
||||
/**
|
||||
* Tooltip offsets
|
||||
*/
|
||||
dx: number;
|
||||
dy: number;
|
||||
|
||||
/**
|
||||
* The selected child wrapper
|
||||
*/
|
||||
readonly selected: W;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The CommonMaction class interface
|
||||
*
|
||||
* @template W The maction wrapper type
|
||||
*/
|
||||
export interface CommonMactionClass<W extends AnyWrapper> extends AnyWrapperClass {
|
||||
/**
|
||||
* The valid action types and their handlers
|
||||
*/
|
||||
actions: ActionMap<W>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMaction constructor
|
||||
*
|
||||
* @template W The maction wrapper type
|
||||
*/
|
||||
export type MactionConstructor<W extends AnyWrapper> = Constructor<CommonMaction<W>>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMaction wrapper mixin for the MmlMaction object
|
||||
*
|
||||
* @template W The maction wrapper type
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMactionMixin<
|
||||
W extends AnyWrapper,
|
||||
T extends WrapperConstructor
|
||||
>(Base: T): MactionConstructor<W> & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* The handler for the specified actiontype
|
||||
*/
|
||||
public action: ActionHandler<W>;
|
||||
/**
|
||||
* The data for the specified actiontype
|
||||
*/
|
||||
public data: ActionData;
|
||||
|
||||
/**
|
||||
* The x-offset for tooltips
|
||||
*/
|
||||
public dx: number;
|
||||
/**
|
||||
* The y-offset for tooltips
|
||||
*/
|
||||
public dy: number;
|
||||
|
||||
/**
|
||||
* @return {W} The selected child wrapper
|
||||
*/
|
||||
public get selected(): W {
|
||||
const selection = this.node.attributes.get('selection') as number;
|
||||
const i = Math.max(1, Math.min(this.childNodes.length, selection)) - 1;
|
||||
return this.childNodes[i] || this.wrap((this.node as MmlMaction).selected);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
const actions = (this.constructor as CommonMactionClass<W>).actions;
|
||||
const action = this.node.attributes.get('actiontype') as string;
|
||||
const [handler, data] = actions.get(action) || [((_node, _data) => {}) as ActionHandler<W>, {}];
|
||||
this.action = handler;
|
||||
this.data = data;
|
||||
this.getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up attribute parameters
|
||||
*/
|
||||
public getParameters() {
|
||||
const offsets = this.node.attributes.get('data-offsets') as string;
|
||||
let [dx, dy] = split(offsets || '');
|
||||
this.dx = this.length2em(dx || TooltipData.dx);
|
||||
this.dy = this.length2em(dy || TooltipData.dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
bbox.updateFrom(this.selected.getOuterBBox());
|
||||
this.selected.setChildPWidths(recompute);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMath wrapper mixin for the MmlMath object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMath interface
|
||||
*/
|
||||
export interface CommonMath extends AnyWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMath constructor
|
||||
*/
|
||||
export type MathConstructor = Constructor<CommonMath>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMath wrapper mixin for the MmlMath object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMathMixin<T extends WrapperConstructor>(Base: T): MathConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getWrapWidth(_i: number) {
|
||||
return (this.parent ? this.getBBox().w : this.metrics.containerWidth / this.jax.pxPerEm);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMenclose wrapper mixin for the MmlMenclose object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor, AnyWrapperClass} from '../Wrapper.js';
|
||||
import * as Notation from '../Notation.js';
|
||||
import {CommonMsqrt} from './msqrt.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {AbstractMmlNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {split} from '../../../util/string.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMenclose interface
|
||||
*
|
||||
* @template W The menclose wrapper type
|
||||
*/
|
||||
export interface CommonMenclose<W extends AnyWrapper, S extends CommonMsqrt, N> extends AnyWrapper {
|
||||
/**
|
||||
* The notations active on this menclose, and the one to use for the child, if any
|
||||
*/
|
||||
notations: Notation.List<W, N>;
|
||||
renderChild: Notation.Renderer<W, N>;
|
||||
|
||||
/**
|
||||
* fake msqrt for radial notation (if used)
|
||||
*/
|
||||
msqrt: S;
|
||||
|
||||
/**
|
||||
* The padding, thickness, and shape of the arrow head
|
||||
* (may be overridden using data-padding, data-thickness, and data-arrowhead attibutes)
|
||||
*/
|
||||
padding: number;
|
||||
thickness: number;
|
||||
arrowhead: {x: number, y: number, dx: number};
|
||||
|
||||
/**
|
||||
* The top, right, bottom, and left padding, added by notations
|
||||
*/
|
||||
TRBL: Notation.PaddingData;
|
||||
|
||||
/**
|
||||
* Look up the data-* attributes and override the default values
|
||||
*/
|
||||
getParameters(): void;
|
||||
|
||||
/**
|
||||
* Get the notations given in the notation attribute
|
||||
* and check if any are used to render the child nodes
|
||||
*/
|
||||
getNotations(): void;
|
||||
|
||||
/**
|
||||
* Remove any redundant notations
|
||||
*/
|
||||
removeRedundantNotations(): void;
|
||||
|
||||
/**
|
||||
* Run any initialization needed by notations in use
|
||||
*/
|
||||
initializeNotations(): void;
|
||||
|
||||
/**
|
||||
* @return {Notation.PaddingData} Array of the maximum extra space from the notations along each side
|
||||
*/
|
||||
getBBoxExtenders(): Notation.PaddingData;
|
||||
|
||||
/**
|
||||
* @return {Notation.PaddingData} Array of padding (i.e., BBox minus border) along each side
|
||||
*/
|
||||
getPadding(): Notation.PaddingData;
|
||||
|
||||
/**
|
||||
* Each entry in X gets replaced by the corresponding one in Y if it is larger
|
||||
*
|
||||
* @param {Notation.PaddingData} X An array of numbers
|
||||
* @param {Notation.PaddingData} Y An array of numbers that replace smaller ones in X
|
||||
*/
|
||||
maximizeEntries(X: Notation.PaddingData, Y: Notation.PaddingData): void;
|
||||
|
||||
/**
|
||||
* Get the offset amount for the given direction for vertical and horizontal centering
|
||||
*
|
||||
* @param {string} direction The direction 'X' or 'Y' for the offset
|
||||
* @return {number} The amount of offset in that direction
|
||||
*/
|
||||
getOffset(direction: string): number;
|
||||
|
||||
/**
|
||||
* @param {number} w The width of the box whose diagonal is needed
|
||||
* @param {number} h The height of the box whose diagonal is needed
|
||||
* @return {number[]} The angle and width of the diagonal of the box
|
||||
*/
|
||||
getArgMod(w: number, h: number): [number, number];
|
||||
|
||||
/**
|
||||
* Create an arrow for output
|
||||
*
|
||||
* @param {number} w The length of the arrow
|
||||
* @param {number} a The angle for the arrow
|
||||
* @param {boolean} double True if this is a double-headed arrow
|
||||
* @param {string} offset 'X' for vertical arrow, 'Y' for horizontal
|
||||
* @param {number} trans Distance to translate in the offset direction
|
||||
* @return {N} The newly created arrow
|
||||
*/
|
||||
arrow(w: number, a: number, double: boolean, offset?: string, trans?: number): N;
|
||||
|
||||
/**
|
||||
* Get the angle and width of a diagonal arrow, plus the x and y extension
|
||||
* past the content bounding box
|
||||
*/
|
||||
arrowData(): {a: number, W: number, x: number, y: number};
|
||||
|
||||
/**
|
||||
* Get the angle and width for a diagonal arrow
|
||||
*
|
||||
* @return {[number, number]} The angle and width
|
||||
*/
|
||||
arrowAW(): [number, number];
|
||||
|
||||
/**
|
||||
* Create an unattached msqrt wrapper to render the 'radical' notation.
|
||||
* We replace the inferred mrow of the msqrt with the one from the menclose
|
||||
* but without changing the parent pointer, so as not to detach it from
|
||||
* the menclose (which would desrtoy the original MathML tree).
|
||||
*
|
||||
* @param {W} child The inferred mrow that is the child of this menclose
|
||||
* @return {S} The newly created (but detached) msqrt wrapper
|
||||
*/
|
||||
createMsqrt(child: W): S;
|
||||
|
||||
/**
|
||||
* @return {number[]} The differences between the msqrt bounding box
|
||||
* and its child bounding box (i.e., the extra space
|
||||
* created by the radical symbol).
|
||||
*/
|
||||
sqrtTRBL(): number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The CommonMenclose class interface
|
||||
*
|
||||
* @template W The menclose wrapper type
|
||||
* @templare N The DOM node class
|
||||
*/
|
||||
export interface CommonMencloseClass<W extends AnyWrapper, N> extends AnyWrapperClass {
|
||||
/**
|
||||
* The definitions of the various notations
|
||||
*/
|
||||
notations: Notation.DefList<W, N>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMenclose constructor
|
||||
*
|
||||
* @template W The menclose wrapper type
|
||||
*/
|
||||
export type MencloseConstructor<W extends AnyWrapper, S extends CommonMsqrt, N> = Constructor<CommonMenclose<W, S, N>>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMenclose wrapper mixin for the MmlMenclose object
|
||||
*
|
||||
* @template W The menclose wrapper type
|
||||
* @templare N The DOM node class
|
||||
* @templare S The msqrt wrapper class
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMencloseMixin<
|
||||
W extends AnyWrapper,
|
||||
S extends CommonMsqrt,
|
||||
N,
|
||||
T extends WrapperConstructor
|
||||
>(Base: T): MencloseConstructor<W, S, N> & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* The notations active on this menclose, if any
|
||||
*/
|
||||
public notations: Notation.List<W, N> = {};
|
||||
|
||||
/**
|
||||
* The notation to use for the child, if any
|
||||
*/
|
||||
public renderChild: Notation.Renderer<W, N> = null;
|
||||
|
||||
/**
|
||||
* fake msqrt for radial notation (if used)
|
||||
*/
|
||||
public msqrt: S = null;
|
||||
|
||||
/**
|
||||
* The padding of the arrow head (may be overridden using data-padding attibute)
|
||||
*/
|
||||
public padding: number = Notation.PADDING;
|
||||
/**
|
||||
* The thickness of the arrow head (may be overridden using data-thickness attibute)
|
||||
*/
|
||||
public thickness: number = Notation.THICKNESS;
|
||||
/**
|
||||
* The shape of the arrow head (may be overridden using data-arrowhead attibutes)
|
||||
*/
|
||||
public arrowhead = {x: Notation.ARROWX, y: Notation.ARROWY, dx: Notation.ARROWDX};
|
||||
|
||||
/**
|
||||
* The top, right, bottom, and left padding (added by notations)
|
||||
*/
|
||||
public TRBL: Notation.PaddingData = [0, 0, 0, 0];
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @constructor
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.getParameters();
|
||||
this.getNotations();
|
||||
this.removeRedundantNotations();
|
||||
this.initializeNotations();
|
||||
this.TRBL = this.getBBoxExtenders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the data-* attributes and override the default values
|
||||
*/
|
||||
public getParameters() {
|
||||
const attributes = this.node.attributes;
|
||||
const padding = attributes.get('data-padding');
|
||||
if (padding !== undefined) {
|
||||
this.padding = this.length2em(padding, Notation.PADDING);
|
||||
}
|
||||
const thickness = attributes.get('data-thickness');
|
||||
if (thickness !== undefined) {
|
||||
this.thickness = this.length2em(thickness, Notation.THICKNESS);
|
||||
}
|
||||
const arrowhead = attributes.get('data-arrowhead') as string;
|
||||
if (arrowhead !== undefined) {
|
||||
let [x, y, dx] = split(arrowhead);
|
||||
this.arrowhead = {
|
||||
x: (x ? parseFloat(x) : Notation.ARROWX),
|
||||
y: (y ? parseFloat(y) : Notation.ARROWY),
|
||||
dx: (dx ? parseFloat(dx) : Notation.ARROWDX)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notations given in the notation attribute
|
||||
* and check if any are used to render the child nodes
|
||||
*/
|
||||
public getNotations() {
|
||||
const Notations = (this.constructor as CommonMencloseClass<W, N>).notations;
|
||||
for (const name of split(this.node.attributes.get('notation') as string)) {
|
||||
const notation = Notations.get(name);
|
||||
if (notation) {
|
||||
this.notations[name] = notation;
|
||||
if (notation.renderChild) {
|
||||
this.renderChild = notation.renderer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any redundant notations
|
||||
*/
|
||||
public removeRedundantNotations() {
|
||||
for (const name of Object.keys(this.notations)) {
|
||||
if (this.notations[name]) {
|
||||
const remove = this.notations[name].remove || '';
|
||||
for (const notation of remove.split(/ /)) {
|
||||
delete this.notations[notation];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run any initialization needed by notations in use
|
||||
*/
|
||||
public initializeNotations() {
|
||||
for (const name of Object.keys(this.notations)) {
|
||||
const init = this.notations[name].init;
|
||||
init && init(this as any);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
//
|
||||
// Combine the BBox from the child and add the extenders
|
||||
//
|
||||
let [T, R, B, L] = this.TRBL;
|
||||
const child = this.childNodes[0].getBBox();
|
||||
bbox.combine(child, L, 0);
|
||||
bbox.h += T;
|
||||
bbox.d += B;
|
||||
bbox.w += R;
|
||||
this.setChildPWidths(recompute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Notation.PaddingData} Array of the maximum extra space from the notations along each side
|
||||
*/
|
||||
public getBBoxExtenders(): Notation.PaddingData {
|
||||
let TRBL = [0, 0, 0, 0] as Notation.PaddingData;
|
||||
for (const name of Object.keys(this.notations)) {
|
||||
this.maximizeEntries(TRBL, this.notations[name].bbox(this as any));
|
||||
}
|
||||
return TRBL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Notation.PaddingData} Array of padding (i.e., BBox minus border) along each side
|
||||
*/
|
||||
public getPadding(): Notation.PaddingData {
|
||||
let BTRBL = [0, 0, 0, 0] as Notation.PaddingData;
|
||||
for (const name of Object.keys(this.notations)) {
|
||||
const border = this.notations[name].border;
|
||||
if (border) {
|
||||
this.maximizeEntries(BTRBL, border(this as any));
|
||||
}
|
||||
}
|
||||
return [0, 1, 2, 3].map(i => this.TRBL[i] - BTRBL[i]) as Notation.PaddingData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each entry in X gets replaced by the corresponding one in Y if it is larger
|
||||
*
|
||||
* @param {Notation.PaddingData} X An array of numbers
|
||||
* @param {Notation.PaddingData} Y An array of numbers that replace smaller ones in X
|
||||
*/
|
||||
public maximizeEntries(X: Notation.PaddingData, Y: Notation.PaddingData) {
|
||||
for (let i = 0; i < X.length; i++) {
|
||||
if (X[i] < Y[i]) {
|
||||
X[i] = Y[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* Get the offset amount for the given direction for vertical and horizontal centering
|
||||
*
|
||||
* @param {string} direction The direction 'X' or 'Y' for the offset
|
||||
* @return {number} The amount of offset in that direction
|
||||
*/
|
||||
public getOffset(direction: string): number {
|
||||
let [T, R, B, L] = this.TRBL;
|
||||
const d = (direction === 'X' ? R - L : B - T) / 2;
|
||||
return (Math.abs(d) > .001 ? d : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} w The width of the box whose diagonal is needed
|
||||
* @param {number} h The height of the box whose diagonal is needed
|
||||
* @return {number[]} The angle and width of the diagonal of the box
|
||||
*/
|
||||
public getArgMod(w: number, h: number): [number, number] {
|
||||
return [Math.atan2(h, w), Math.sqrt(w * w + h * h)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an arrow using an svg element
|
||||
*
|
||||
* @param {number} w The length of the arrow
|
||||
* @param {number} a The angle for the arrow
|
||||
* @param {boolean} double True if this is a double-headed arrow
|
||||
* @param {string} offset 'X' for vertical arrow, 'Y' for horizontal
|
||||
* @param {number} dist Distance to translate in the offset direction
|
||||
* @return {N} The newly created arrow
|
||||
*/
|
||||
public arrow(_w: number, _a: number, _double: boolean, _offset: string = '', _dist: number = 0): N {
|
||||
return null as N;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the angle and width of a diagonal arrow, plus the x and y extension
|
||||
* past the content bounding box
|
||||
*
|
||||
* @return {Object} The angle, width, and x and y extentions
|
||||
*/
|
||||
public arrowData(): {a: number, W: number, x: number, y: number} {
|
||||
const [p, t] = [this.padding, this.thickness];
|
||||
const r = t * (this.arrowhead.x + Math.max(1, this.arrowhead.dx));
|
||||
const {h, d, w} = this.childNodes[0].getBBox();
|
||||
const H = h + d;
|
||||
const R = Math.sqrt(H * H + w * w);
|
||||
const x = Math.max(p, r * w / R);
|
||||
const y = Math.max(p, r * H / R);
|
||||
const [a, W] = this.getArgMod(w + 2 * x, H + 2 * y);
|
||||
return {a, W, x, y};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the angle and width for a diagonal arrow
|
||||
*
|
||||
* @return {[number, number]} The angle and width
|
||||
*/
|
||||
public arrowAW(): [number, number] {
|
||||
const {h, d, w} = this.childNodes[0].getBBox();
|
||||
const [T, R, B, L] = this.TRBL;
|
||||
return this.getArgMod(L + w + R, T + h + d + B);
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
|
||||
/**
|
||||
* Create an unattached msqrt wrapper to render the 'radical' notation.
|
||||
* We replace the inferred mrow of the msqrt with the one from the menclose
|
||||
* but without changing the parent pointer, so as not to detach it from
|
||||
* the menclose (which would desrtoy the original MathML tree).
|
||||
*
|
||||
* @param {W} child The inferred mrow that is the child of this menclose
|
||||
* @return {S} The newly created (but detached) msqrt wrapper
|
||||
*/
|
||||
public createMsqrt(child: W): S {
|
||||
const mmlFactory = (this.node as AbstractMmlNode).factory;
|
||||
const mml = mmlFactory.create('msqrt');
|
||||
mml.inheritAttributesFrom(this.node);
|
||||
mml.childNodes[0] = child.node;
|
||||
const node = this.wrap(mml) as S;
|
||||
node.parent = this;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number[]} The differences between the msqrt bounding box
|
||||
* and its child bounding box (i.e., the extra space
|
||||
* created by the radical symbol).
|
||||
*/
|
||||
public sqrtTRBL(): [number, number, number, number] {
|
||||
const bbox = this.msqrt.getBBox();
|
||||
const cbox = this.msqrt.childNodes[0].getBBox();
|
||||
return [bbox.h - cbox.h, 0, bbox.d - cbox.d, bbox.w - cbox.w];
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMfenced wrapper mixin for the MmlMfenced object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonInferredMrow} from './mrow.js';
|
||||
import {MmlNode, AbstractMmlNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {MmlMfenced} from '../../../core/MmlTree/MmlNodes/mfenced.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMfenced interface
|
||||
*/
|
||||
export interface CommonMfenced extends AnyWrapper {
|
||||
/**
|
||||
* An mrow to use for the layout of the mfenced
|
||||
*/
|
||||
mrow: CommonInferredMrow;
|
||||
|
||||
/**
|
||||
* Creates the mrow wrapper to use for the layout
|
||||
*/
|
||||
createMrow(): void;
|
||||
|
||||
/**
|
||||
* Populate the mrow with wrapped mo elements interleaved
|
||||
* with the mfenced children (the mo's are already created
|
||||
* in the mfenced object)
|
||||
*/
|
||||
addMrowChildren(): void;
|
||||
|
||||
/**
|
||||
* Wrap an mo element and push it onto the mrow
|
||||
*
|
||||
* @param {MmlNode} node The mo element to push on the mrow
|
||||
*/
|
||||
addMo(node: MmlNode): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMfenced constructor
|
||||
*/
|
||||
export type MfencedConstructor = Constructor<CommonMfenced>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMfenced wrapper mixin for the MmlMfenced object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMfencedMixin<T extends WrapperConstructor>(Base: T): MfencedConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* An mrow to use for the layout of the mfenced
|
||||
*/
|
||||
public mrow: CommonInferredMrow = null;
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @constructor
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.createMrow();
|
||||
this.addMrowChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the mrow wrapper to use for the layout
|
||||
*/
|
||||
public createMrow() {
|
||||
const mmlFactory = (this.node as AbstractMmlNode).factory;
|
||||
const mrow = mmlFactory.create('inferredMrow');
|
||||
mrow.inheritAttributesFrom(this.node);
|
||||
this.mrow = this.wrap(mrow) as CommonInferredMrow;
|
||||
this.mrow.parent = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the mrow with wrapped mo elements interleaved
|
||||
* with the mfenced children (the mo's are already created
|
||||
* in the mfenced object)
|
||||
*/
|
||||
public addMrowChildren() {
|
||||
const mfenced = this.node as MmlMfenced;
|
||||
const mrow = this.mrow;
|
||||
this.addMo(mfenced.open);
|
||||
if (this.childNodes.length) {
|
||||
mrow.childNodes.push(this.childNodes[0]);
|
||||
}
|
||||
let i = 0;
|
||||
for (const child of this.childNodes.slice(1)) {
|
||||
this.addMo(mfenced.separators[i++]);
|
||||
mrow.childNodes.push(child);
|
||||
}
|
||||
this.addMo(mfenced.close);
|
||||
mrow.stretchChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an mo element and push it onto the mrow
|
||||
*
|
||||
* @param {MmlNode} node The mo element to push on the mrow
|
||||
*/
|
||||
public addMo(node: MmlNode) {
|
||||
if (!node) return;
|
||||
const mo = this.wrap(node);
|
||||
this.mrow.childNodes.push(mo);
|
||||
mo.parent = this.mrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
bbox.updateFrom(this.mrow.getOuterBBox());
|
||||
this.setChildPWidths(recompute);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMfrac wrapper mixin for the MmlMfrac object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonMo} from './mo.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {DIRECTION} from '../FontData.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMfrac interface
|
||||
*/
|
||||
export interface CommonMfrac extends AnyWrapper {
|
||||
/**
|
||||
* @param {BBox} bbox The buonding box to modify
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
* @param {number} t The thickness of the line
|
||||
*/
|
||||
getFractionBBox(bbox: BBox, display: boolean, t: number): void;
|
||||
|
||||
/**
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
* @param {number} t The thickness of the line
|
||||
* @return {Object} The expanded rule thickness (T), and baseline offsets
|
||||
* for numerator and denomunator (u and v)
|
||||
*/
|
||||
getTUV(display: boolean, t: number): {T: number, u: number, v: number};
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bounding box to modify
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
*/
|
||||
getAtopBBox(bbox: BBox, display: boolean): void;
|
||||
|
||||
/**
|
||||
* @param {boolean} display True for diplay-mode fractions
|
||||
* @return {Object}
|
||||
* The vertical offsets of the numerator (u), the denominator (v),
|
||||
* the separation between the two, and the bboxes themselves.
|
||||
*/
|
||||
getUVQ(display: boolean): {u: number, v: number, q: number, nbox: BBox, dbox: BBox};
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The boundng box to modify
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
*/
|
||||
getBevelledBBox(bbox: BBox, display: boolean): void;
|
||||
|
||||
/**
|
||||
* @param {boolean} display True for display-style fractions
|
||||
* @return {Object} The height (H) of the bevel, horizontal offest (delta)
|
||||
* vertical offsets (u and v) of the parts, and
|
||||
* bounding boxes of the parts.
|
||||
*/
|
||||
getBevelData(display: boolean): {H: number, delta: number, u: number, v: number, nbox: BBox, dbox: BBox};
|
||||
|
||||
/**
|
||||
* @return {boolean} True if in display mode, false otherwise
|
||||
*/
|
||||
isDisplay(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMfrac constructor
|
||||
*/
|
||||
export type MfracConstructor = Constructor<CommonMfrac>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMfrac wrapper mixin for the MmlMfrac object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMfracMixin<T extends WrapperConstructor>(Base: T): MfracConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* Wrapper for <mo> to use for bevelled fraction
|
||||
*/
|
||||
public bevel: CommonMo = null;
|
||||
|
||||
/**
|
||||
* Padding around fractions
|
||||
*/
|
||||
public pad: number;
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @constructor
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.pad = (this.node.getProperty('withDelims') as boolean ? 0 : this.font.params.nulldelimiterspace);
|
||||
//
|
||||
// create internal bevel mo element
|
||||
//
|
||||
if (this.node.attributes.get('bevelled')) {
|
||||
const {H} = this.getBevelData(this.isDisplay());
|
||||
const bevel = this.bevel = this.createMo('/') as CommonMo;
|
||||
bevel.node.attributes.set('symmetric', true);
|
||||
bevel.canStretch(DIRECTION.Vertical);
|
||||
bevel.getStretchedVariant([H], true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
bbox.empty();
|
||||
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
|
||||
const display = this.isDisplay();
|
||||
let w = null as (number | null);
|
||||
if (bevelled) {
|
||||
this.getBevelledBBox(bbox, display);
|
||||
} else {
|
||||
const thickness = this.length2em(String(linethickness), .06);
|
||||
w = -2 * this.pad;
|
||||
if (thickness === 0) {
|
||||
this.getAtopBBox(bbox, display);
|
||||
} else {
|
||||
this.getFractionBBox(bbox, display, thickness);
|
||||
w -= .2;
|
||||
}
|
||||
w += bbox.w;
|
||||
}
|
||||
bbox.clean();
|
||||
this.setChildPWidths(recompute, w);
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The buonding box to modify
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
* @param {number} t The thickness of the line
|
||||
*/
|
||||
public getFractionBBox(bbox: BBox, display: boolean, t: number) {
|
||||
const nbox = this.childNodes[0].getOuterBBox();
|
||||
const dbox = this.childNodes[1].getOuterBBox();
|
||||
const tex = this.font.params;
|
||||
const a = tex.axis_height;
|
||||
const {T, u, v} = this.getTUV(display, t);
|
||||
bbox.combine(nbox, 0, a + T + Math.max(nbox.d * nbox.rscale, u));
|
||||
bbox.combine(dbox, 0, a - T - Math.max(dbox.h * dbox.rscale, v));
|
||||
bbox.w += 2 * this.pad + .2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
* @param {number} t The thickness of the line
|
||||
* @return {Object} The expanded rule thickness (T), and baseline offsets
|
||||
* for numerator and denomunator (u and v)
|
||||
*/
|
||||
public getTUV(display: boolean, t: number): {T: number, u: number, v: number} {
|
||||
const tex = this.font.params;
|
||||
const a = tex.axis_height;
|
||||
const T = (display ? 3.5 : 1.5) * t;
|
||||
return {T: (display ? 3.5 : 1.5) * t,
|
||||
u: (display ? tex.num1 : tex.num2) - a - T,
|
||||
v: (display ? tex.denom1 : tex.denom2) + a - T};
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bounding box to modify
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
*/
|
||||
public getAtopBBox(bbox: BBox, display: boolean) {
|
||||
const {u, v, nbox, dbox} = this.getUVQ(display);
|
||||
bbox.combine(nbox, 0, u);
|
||||
bbox.combine(dbox, 0, -v);
|
||||
bbox.w += 2 * this.pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} display True for diplay-mode fractions
|
||||
* @return {Object}
|
||||
* The vertical offsets of the numerator (u), the denominator (v),
|
||||
* the separation between the two, and the bboxes themselves.
|
||||
*/
|
||||
public getUVQ(display: boolean): {u: number, v: number, q: number, nbox: BBox, dbox: BBox} {
|
||||
const nbox = this.childNodes[0].getOuterBBox();
|
||||
const dbox = this.childNodes[1].getOuterBBox();
|
||||
const tex = this.font.params;
|
||||
//
|
||||
// Initial offsets (u, v)
|
||||
// Minimum separation (p)
|
||||
// Actual separation with initial positions (q)
|
||||
//
|
||||
let [u, v] = (display ? [tex.num1, tex.denom1] : [tex.num3, tex.denom2]);
|
||||
let p = (display ? 7 : 3) * tex.rule_thickness;
|
||||
let q = (u - nbox.d * nbox.scale) - (dbox.h * dbox.scale - v);
|
||||
//
|
||||
// If actual separation is less than minimum, move them farther apart
|
||||
//
|
||||
if (q < p) {
|
||||
u += (p - q) / 2;
|
||||
v += (p - q) / 2;
|
||||
q = p;
|
||||
}
|
||||
return {u, v, q, nbox, dbox};
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The boundng box to modify
|
||||
* @param {boolean} display True for display-mode fractions
|
||||
*/
|
||||
public getBevelledBBox(bbox: BBox, display: boolean) {
|
||||
const {u, v, delta, nbox, dbox} = this.getBevelData(display);
|
||||
const lbox = this.bevel.getOuterBBox();
|
||||
bbox.combine(nbox, 0, u);
|
||||
bbox.combine(lbox, bbox.w - delta / 2, 0);
|
||||
bbox.combine(dbox, bbox.w - delta / 2, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} display True for display-style fractions
|
||||
* @return {Object} The height (H) of the bevel, horizontal offest (delta)
|
||||
* vertical offsets (u and v) of the parts, and
|
||||
* bounding boxes of the parts.
|
||||
*/
|
||||
public getBevelData(display: boolean): {
|
||||
H: number, delta: number, u: number, v: number, nbox: BBox, dbox: BBox
|
||||
} {
|
||||
const nbox = this.childNodes[0].getOuterBBox();
|
||||
const dbox = this.childNodes[1].getOuterBBox();
|
||||
const delta = (display ? .4 : .15);
|
||||
const H = Math.max(nbox.scale * (nbox.h + nbox.d), dbox.scale * (dbox.h + dbox.d)) + 2 * delta;
|
||||
const a = this.font.params.axis_height;
|
||||
const u = nbox.scale * (nbox.d - nbox.h) / 2 + a + delta;
|
||||
const v = dbox.scale * (dbox.d - dbox.h) / 2 + a - delta;
|
||||
return {H, delta, u, v, nbox, dbox};
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public canStretch(_direction: DIRECTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} True if in display mode, false otherwise
|
||||
*/
|
||||
public isDisplay(): boolean {
|
||||
const {displaystyle, scriptlevel} = this.node.attributes.getList('displaystyle', 'scriptlevel');
|
||||
return displaystyle && scriptlevel === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getWrapWidth(i: number) {
|
||||
const attributes = this.node.attributes;
|
||||
if (attributes.get('bevelled')) {
|
||||
return this.childNodes[i].getOuterBBox().w;
|
||||
}
|
||||
const w = this.getBBox().w;
|
||||
const thickness = this.length2em(attributes.get('linethickness'));
|
||||
return w - (thickness ? .2 : 0) - 2 * this.pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getChildAlign(i: number) {
|
||||
const attributes = this.node.attributes;
|
||||
return (attributes.get('bevelled') ? 'left' : attributes.get(['numalign', 'denomalign'][i]) as string);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMglyph wrapper mixin for the MmlMglyph object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {CommonTextNode} from './TextNode.js';
|
||||
import {TextNode} from '../../../core/MmlTree/MmlNode.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMglyph interface
|
||||
*/
|
||||
export interface CommonMglyph extends AnyWrapper {
|
||||
/**
|
||||
* The image's width converted to em's
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* The image's height converted to em's
|
||||
*/
|
||||
height: number;
|
||||
/*
|
||||
* The image's valign values converted to em's
|
||||
*/
|
||||
valign: number;
|
||||
|
||||
/**
|
||||
* TextNode used for deprecated fontfamily/index use case
|
||||
*/
|
||||
charWrapper: CommonTextNode;
|
||||
|
||||
/**
|
||||
* Obtain the width, height, and valign.
|
||||
* Note: Currently, the width and height must be specified explicitly, or they default to 1em
|
||||
* Since loading the image may be asynchronous, it would require a restart.
|
||||
* A future extension could implement this either by subclassing this object, or
|
||||
* perhaps as a post-filter on the MathML input jax that adds the needed dimensions
|
||||
*/
|
||||
getParameters(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMglyph constructor
|
||||
*/
|
||||
export type MglyphConstructor = Constructor<CommonMglyph>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMglyph wrapper mixin for the MmlMglyph object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMglyphMixin<T extends WrapperConstructor>(Base: T): MglyphConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public width: number;
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public height: number;
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public valign: number;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public charWrapper: CommonTextNode;
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @constructor
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getParameters() {
|
||||
const {width, height, valign, src, index} =
|
||||
this.node.attributes.getList('width', 'height', 'valign', 'src', 'index');
|
||||
if (src) {
|
||||
this.width = (width === 'auto' ? 1 : this.length2em(width));
|
||||
this.height = (height === 'auto' ? 1 : this.length2em(height));
|
||||
this.valign = this.length2em(valign || '0');
|
||||
} else {
|
||||
const text = String.fromCodePoint(parseInt(index as string));
|
||||
const mmlFactory = this.node.factory;
|
||||
this.charWrapper = this.wrap((mmlFactory.create('text') as TextNode).setText(text));
|
||||
this.charWrapper.parent = this as any as AnyWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, _recompute: boolean = false) {
|
||||
if (this.charWrapper) {
|
||||
bbox.updateFrom(this.charWrapper.getBBox());
|
||||
} else {
|
||||
bbox.w = this.width;
|
||||
bbox.h = this.height + this.valign;
|
||||
bbox.d = -this.valign;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMi wrapper mixin for the MmlMi object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMi interface
|
||||
*/
|
||||
export interface CommonMi extends AnyWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMi constructor
|
||||
*/
|
||||
export type MiConstructor = Constructor<CommonMi>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMi wrapper mixin for the MmlMi object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMiMixin<T extends WrapperConstructor>(Base: T): MiConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, _recompute: boolean = false) {
|
||||
super.computeBBox(bbox);
|
||||
this.copySkewIC(bbox);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMmultiscripts wrapper mixin for the MmlMmultiscripts object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, Constructor} from '../Wrapper.js';
|
||||
import {CommonMsubsup, MsubsupConstructor} from './msubsup.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* The data about the scripts and base
|
||||
*/
|
||||
export type ScriptData = {
|
||||
base: BBox;
|
||||
sub: BBox; // combined bbox for all subscripts
|
||||
sup: BBox; // combined bbox for all superscripts
|
||||
psub: BBox; // combined bbox for all presubscripts
|
||||
psup: BBox; // combined bbox for all presuperscripts
|
||||
numPrescripts: number;
|
||||
numScripts: number;
|
||||
};
|
||||
export type ScriptDataName = keyof ScriptData;
|
||||
|
||||
/**
|
||||
* The lists of all the individual script bboxes
|
||||
*/
|
||||
export type ScriptLists = {
|
||||
base: BBox[];
|
||||
subList: BBox[];
|
||||
supList: BBox[];
|
||||
psubList: BBox[];
|
||||
psupList: BBox[];
|
||||
};
|
||||
export type ScriptListName = keyof ScriptLists;
|
||||
|
||||
/**
|
||||
* The type of script that follows the given type
|
||||
*/
|
||||
export const NextScript: {[key: string]: ScriptListName} = {
|
||||
base: 'subList',
|
||||
subList: 'supList',
|
||||
supList: 'subList',
|
||||
psubList: 'psupList',
|
||||
psupList: 'psubList',
|
||||
};
|
||||
|
||||
/**
|
||||
* The names of the scripts (for looping)
|
||||
*/
|
||||
export const ScriptNames = ['sup', 'sup', 'psup', 'psub'] as ScriptDataName[];
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMmultiscripts interface
|
||||
*
|
||||
* @template W The child-node Wrapper class
|
||||
*/
|
||||
export interface CommonMmultiscripts<W extends AnyWrapper> extends CommonMsubsup<W> {
|
||||
|
||||
/**
|
||||
* The cached data for the various bounding boxes
|
||||
*/
|
||||
scriptData: ScriptData;
|
||||
|
||||
/**
|
||||
* The index of the child following the <mprescripts/> tag
|
||||
*/
|
||||
firstPrescript: number;
|
||||
|
||||
/**
|
||||
* @param {BBox} pre The prescript bounding box
|
||||
* @param {BBox} post The postcript bounding box
|
||||
* @return {BBox} The combined bounding box
|
||||
*/
|
||||
combinePrePost(pre: BBox, post: BBox): BBox;
|
||||
|
||||
/**
|
||||
* Compute the bounding box information about all the scripts
|
||||
*/
|
||||
getScriptData(): void;
|
||||
|
||||
/**
|
||||
* @return {ScriptLists} The bounding boxes for all the scripts divided into lists by position
|
||||
*/
|
||||
getScriptBBoxLists(): ScriptLists;
|
||||
|
||||
/**
|
||||
* Pad the second list, if it is one short
|
||||
*
|
||||
* @param {BBox[]} list1 The first list
|
||||
* @param {BBox[]} list2 The second list
|
||||
*/
|
||||
padLists(list1: BBox[], list2: BBox[]): void;
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox1 The bbox for the combined subscripts
|
||||
* @param {BBox} bbox2 The bbox for the combined superscripts
|
||||
* @param {BBox[]} list1 The list of subscripts to combine
|
||||
* @param {BBox[]} list2 The list of superscripts to combine
|
||||
*/
|
||||
combineBBoxLists(bbox1: BBox, bbox2: BBox, list1: BBox[], list2: BBox[]): void;
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bounding box from which to get the (scaled) width, height, and depth
|
||||
*/
|
||||
getScaledWHD(bbox: BBox): void;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMmultiscripts constructor
|
||||
*
|
||||
* @template W The child-node Wrapper class
|
||||
*/
|
||||
export type MmultiscriptsConstructor<W extends AnyWrapper> = Constructor<CommonMmultiscripts<W>>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMmultiscripts wrapper mixin for the MmlMmultiscripts object
|
||||
*
|
||||
* @template W The child-node Wrapper class
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMmultiscriptsMixin<
|
||||
W extends AnyWrapper,
|
||||
T extends MsubsupConstructor<W>
|
||||
>(Base: T): MmultiscriptsConstructor<W> & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* The cached data for the various bounding boxes
|
||||
*/
|
||||
public scriptData: ScriptData = null;
|
||||
|
||||
/**
|
||||
* The index of the child following the <mprescripts/> tag
|
||||
*/
|
||||
public firstPrescript = 0;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.getScriptData();
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @param {BBox} pre The prescript bounding box
|
||||
* @param {BBox} post The postcript bounding box
|
||||
* @return {BBox} The combined bounding box
|
||||
*/
|
||||
public combinePrePost(pre: BBox, post: BBox): BBox {
|
||||
const bbox = new BBox(pre);
|
||||
bbox.combine(post, 0, 0);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
//
|
||||
// Get the bounding boxes, and combine the pre- and post-scripts
|
||||
// to get a common offset for both
|
||||
//
|
||||
const scriptspace = this.font.params.scriptspace;
|
||||
const data = this.scriptData;
|
||||
const sub = this.combinePrePost(data.sub, data.psub);
|
||||
const sup = this.combinePrePost(data.sup, data.psup);
|
||||
const [u, v] = this.getUVQ(sub, sup);
|
||||
//
|
||||
// Lay out the pre-scripts, then the base, then the post-scripts
|
||||
//
|
||||
bbox.empty();
|
||||
if (data.numPrescripts) {
|
||||
bbox.combine(data.psup, scriptspace, u);
|
||||
bbox.combine(data.psub, scriptspace, v);
|
||||
}
|
||||
bbox.append(data.base);
|
||||
if (data.numScripts) {
|
||||
const w = bbox.w;
|
||||
bbox.combine(data.sup, w, u);
|
||||
bbox.combine(data.sub, w, v);
|
||||
bbox.w += scriptspace;
|
||||
}
|
||||
bbox.clean();
|
||||
this.setChildPWidths(recompute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the bounding box information about all the scripts
|
||||
*/
|
||||
public getScriptData() {
|
||||
//
|
||||
// Initialize the bounding box data
|
||||
//
|
||||
const data: ScriptData = this.scriptData = {
|
||||
base: null, sub: BBox.empty(), sup: BBox.empty(), psub: BBox.empty(), psup: BBox.empty(),
|
||||
numPrescripts: 0, numScripts: 0
|
||||
};
|
||||
//
|
||||
// Get the bboxes for all the scripts and combine them into the scriptData
|
||||
//
|
||||
const lists = this.getScriptBBoxLists();
|
||||
this.combineBBoxLists(data.sub, data.sup, lists.subList, lists.supList);
|
||||
this.combineBBoxLists(data.psub, data.psup, lists.psubList, lists.psupList);
|
||||
data.base = lists.base[0];
|
||||
//
|
||||
// Save the lengths and return the data
|
||||
//
|
||||
data.numPrescripts = lists.psubList.length;
|
||||
data.numScripts = lists.subList.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {ScriptLists} The bounding boxes for all the scripts divided into lists by position
|
||||
*/
|
||||
public getScriptBBoxLists(): ScriptLists {
|
||||
const lists: ScriptLists = {
|
||||
base: [], subList: [], supList: [], psubList: [], psupList: []
|
||||
};
|
||||
//
|
||||
// The first entry is the base, and then they altername sub- and superscripts.
|
||||
// Once we find the <mprescripts/> element, switch to presub- and presuperscript lists.
|
||||
//
|
||||
let script: ScriptListName = 'base';
|
||||
for (const child of this.childNodes) {
|
||||
if (child.node.isKind('mprescripts')) {
|
||||
script = 'psubList';
|
||||
} else {
|
||||
lists[script].push(child.getOuterBBox());
|
||||
script = NextScript[script];
|
||||
}
|
||||
}
|
||||
//
|
||||
// The index of the first prescript (skip over base, sub- and superscripts, and mprescripts)
|
||||
//
|
||||
this.firstPrescript = lists.subList.length + lists.supList.length + 2;
|
||||
//
|
||||
// Make sure the lists are the same length
|
||||
//
|
||||
this.padLists(lists.subList, lists.supList);
|
||||
this.padLists(lists.psubList, lists.psupList);
|
||||
return lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad the second list, if it is one short
|
||||
*
|
||||
* @param {BBox[]} list1 The first list
|
||||
* @param {BBox[]} list2 The second list
|
||||
*/
|
||||
public padLists(list1: BBox[], list2: BBox[]) {
|
||||
if (list1.length > list2.length) {
|
||||
list2.push(BBox.empty());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox1 The bbox for the combined subscripts
|
||||
* @param {BBox} bbox2 The bbox for the combined superscripts
|
||||
* @param {BBox[]} list1 The list of subscripts to combine
|
||||
* @param {BBox[]} list2 The list of superscripts to combine
|
||||
*/
|
||||
public combineBBoxLists(bbox1: BBox, bbox2: BBox, list1: BBox[], list2: BBox[]) {
|
||||
for (let i = 0; i < list1.length; i++) {
|
||||
const [w1, h1, d1] = this.getScaledWHD(list1[i]);
|
||||
const [w2, h2, d2] = this.getScaledWHD(list2[i]);
|
||||
const w = Math.max(w1, w2);
|
||||
bbox1.w += w;
|
||||
bbox2.w += w;
|
||||
if (h1 > bbox1.h) bbox1.h = h1;
|
||||
if (d1 > bbox1.d) bbox1.d = d1;
|
||||
if (h2 > bbox2.h) bbox2.h = h2;
|
||||
if (d2 > bbox2.d) bbox2.d = d2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bounding box from which to get the (scaled) width, height, and depth
|
||||
*/
|
||||
public getScaledWHD(bbox: BBox) {
|
||||
const {w, h, d, rscale} = bbox;
|
||||
return [w * rscale, h * rscale, d * rscale];
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getUVQ(subbox: BBox, supbox: BBox) {
|
||||
if (!this.UVQ) {
|
||||
let [u, v, q] = [0, 0, 0];
|
||||
if (subbox.h === 0 && subbox.d === 0) {
|
||||
//
|
||||
// Use placement for superscript only
|
||||
//
|
||||
u = this.getU();
|
||||
} else if (supbox.h === 0 && supbox.d === 0) {
|
||||
//
|
||||
// Use placement for subsccript only
|
||||
//
|
||||
u = -this.getV();
|
||||
} else {
|
||||
//
|
||||
// Use placement for both
|
||||
//
|
||||
[u, v, q] = super.getUVQ(subbox, supbox);
|
||||
}
|
||||
this.UVQ = [u, v, q];
|
||||
}
|
||||
return this.UVQ;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2018-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMn wrapper mixin for the MmlMn object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMn interface
|
||||
*/
|
||||
export interface CommonMn extends AnyWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMn constructor
|
||||
*/
|
||||
export type MnConstructor = Constructor<CommonMn>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMn wrapper mixin for the MmlMn object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMnMixin<T extends WrapperConstructor>(Base: T): MnConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public remapChars(chars: number[]) {
|
||||
//
|
||||
// Convert a leading hyphen to a minus
|
||||
//
|
||||
if (chars.length) {
|
||||
const text = this.font.getRemappedChar('mn', chars[0]);
|
||||
if (text) {
|
||||
const c = this.unicodeChars(text, this.variant);
|
||||
if (c.length === 1) {
|
||||
chars[0] = c[0];
|
||||
} else {
|
||||
chars = c.concat(chars.slice(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+413
@@ -0,0 +1,413 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMo wrapper mixin for the MmlMo object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {MmlMo} from '../../../core/MmlTree/MmlNodes/mo.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {unicodeChars} from '../../../util/string.js';
|
||||
import {DelimiterData} from '../FontData.js';
|
||||
import {DIRECTION, NOSTRETCH} from '../FontData.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* Convert direction to letter
|
||||
*/
|
||||
export const DirectionVH: {[n: number]: string} = {
|
||||
[DIRECTION.Vertical]: 'v',
|
||||
[DIRECTION.Horizontal]: 'h'
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMo interface
|
||||
*/
|
||||
export interface CommonMo extends AnyWrapper {
|
||||
|
||||
/**
|
||||
* The font size that a stretched operator uses.
|
||||
* If -1, then stretch arbitrarily, and bbox gives the actual height, depth, width
|
||||
*/
|
||||
size: number;
|
||||
|
||||
/**
|
||||
* True if used as an accent in an munderover construct
|
||||
*/
|
||||
isAccent: boolean;
|
||||
|
||||
/**
|
||||
* Get the (unmodified) bbox of the contents (before centering or setting accents to width 0)
|
||||
*
|
||||
* @param {BBox} bbox The bbox to fill
|
||||
*/
|
||||
protoBBox(bbox: BBox): void;
|
||||
|
||||
/**
|
||||
* @return {number} Offset to the left by half the actual width of the accent
|
||||
*/
|
||||
getAccentOffset(): number;
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bbox to center, or null to compute the bbox
|
||||
* @return {number} The offset to move the glyph to center it
|
||||
*/
|
||||
getCenterOffset(bbox?: BBox): number;
|
||||
|
||||
/**
|
||||
* Determint variant for vertically/horizontally stretched character
|
||||
*
|
||||
* @param {number[]} WH size to stretch to, either [W] or [H, D]
|
||||
* @param {boolean} exact True if not allowed to use delimiter factor and shortfall
|
||||
*/
|
||||
getStretchedVariant(WH: number[], exact?: boolean): void;
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the attribute to get
|
||||
* @param {number} value The default value to use
|
||||
* @return {number} The size in em's of the attribute (or the default value)
|
||||
*/
|
||||
getSize(name: string, value: number): number;
|
||||
|
||||
/**
|
||||
* @param {number[]} WH Either [W] for width, [H, D] for height and depth, or [] for min/max size
|
||||
* @return {number} Either the width or the total height of the character
|
||||
*/
|
||||
getWH(WH: number[]): number;
|
||||
|
||||
/**
|
||||
* @param {number[]} WHD The [W] or [H, D] being requested from the parent mrow
|
||||
* @param {number} D The full dimension (including symmetry, etc)
|
||||
* @param {DelimiterData} C The delimiter data for the stretchy character
|
||||
*/
|
||||
getStretchBBox(WHD: number[], D: number, C: DelimiterData): void;
|
||||
|
||||
/**
|
||||
* @param {number[]} WHD The [H, D] being requested from the parent mrow
|
||||
* @param {number} HD The full height (including symmetry, etc)
|
||||
* @param {DelimiterData} C The delimiter data for the stretchy character
|
||||
* @return {number[]} The height and depth for the vertically stretched delimiter
|
||||
*/
|
||||
getBaseline(WHD: number[], HD: number, C: DelimiterData): number[];
|
||||
|
||||
/**
|
||||
* Determine the size of the delimiter based on whether full extenders should be used or not.
|
||||
*
|
||||
* @param {number} D The requested size of the delimiter
|
||||
* @param {DelimiterData} C The data for the delimiter
|
||||
* @return {number} The final size of the assembly
|
||||
*/
|
||||
checkExtendedHeight(D: number, C: DelimiterData): number;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMo constructor
|
||||
*/
|
||||
export type MoConstructor = Constructor<CommonMo>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommomMo wrapper mixin for the MmlMo object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMoMixin<T extends WrapperConstructor>(Base: T): MoConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* The font size that a stretched operator uses.
|
||||
* If -1, then stretch arbitrarily, and bbox gives the actual height, depth, width
|
||||
*/
|
||||
public size: number = null;
|
||||
|
||||
/**
|
||||
* True if used as an accent in an munderover construct
|
||||
*/
|
||||
public isAccent: boolean;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this.isAccent = (this.node as MmlMo).isAccent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, _recompute: boolean = false) {
|
||||
this.protoBBox(bbox);
|
||||
if (this.node.attributes.get('symmetric') &&
|
||||
this.stretch.dir !== DIRECTION.Horizontal) {
|
||||
const d = this.getCenterOffset(bbox);
|
||||
bbox.h += d;
|
||||
bbox.d -= d;
|
||||
}
|
||||
if (this.node.getProperty('mathaccent') &&
|
||||
(this.stretch.dir === DIRECTION.None || this.size >= 0)) {
|
||||
bbox.w = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the (unmodified) bbox of the contents (before centering or setting accents to width 0)
|
||||
*
|
||||
* @param {BBox} bbox The bbox to fill
|
||||
*/
|
||||
public protoBBox(bbox: BBox) {
|
||||
const stretchy = (this.stretch.dir !== DIRECTION.None);
|
||||
if (stretchy && this.size === null) {
|
||||
this.getStretchedVariant([0]);
|
||||
}
|
||||
if (stretchy && this.size < 0) return;
|
||||
super.computeBBox(bbox);
|
||||
this.copySkewIC(bbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number} Offset to the left by half the actual width of the accent
|
||||
*/
|
||||
public getAccentOffset(): number {
|
||||
const bbox = BBox.empty();
|
||||
this.protoBBox(bbox);
|
||||
return -bbox.w / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBox} bbox The bbox to center, or null to compute the bbox
|
||||
* @return {number} The offset to move the glyph to center it
|
||||
*/
|
||||
public getCenterOffset(bbox: BBox = null): number {
|
||||
if (!bbox) {
|
||||
bbox = BBox.empty();
|
||||
super.computeBBox(bbox);
|
||||
}
|
||||
return ((bbox.h + bbox.d) / 2 + this.font.params.axis_height) - bbox.h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getVariant() {
|
||||
if (this.node.attributes.get('largeop')) {
|
||||
this.variant = (this.node.attributes.get('displaystyle') ? '-largeop' : '-smallop');
|
||||
return;
|
||||
}
|
||||
if (!this.node.attributes.getExplicit('mathvariant') &&
|
||||
this.node.getProperty('pseudoscript') === false) {
|
||||
this.variant = '-tex-variant';
|
||||
return;
|
||||
}
|
||||
super.getVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public canStretch(direction: DIRECTION) {
|
||||
if (this.stretch.dir !== DIRECTION.None) {
|
||||
return this.stretch.dir === direction;
|
||||
}
|
||||
const attributes = this.node.attributes;
|
||||
if (!attributes.get('stretchy')) return false;
|
||||
const c = this.getText();
|
||||
if (Array.from(c).length !== 1) return false;
|
||||
const delim = this.font.getDelimiter(c.codePointAt(0));
|
||||
this.stretch = (delim && delim.dir === direction ? delim : NOSTRETCH);
|
||||
return this.stretch.dir !== DIRECTION.None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determint variant for vertically/horizontally stretched character
|
||||
*
|
||||
* @param {number[]} WH size to stretch to, either [W] or [H, D]
|
||||
* @param {boolean} exact True if not allowed to use delimiter factor and shortfall
|
||||
*/
|
||||
public getStretchedVariant(WH: number[], exact: boolean = false) {
|
||||
if (this.stretch.dir !== DIRECTION.None) {
|
||||
let D = this.getWH(WH);
|
||||
const min = this.getSize('minsize', 0);
|
||||
const max = this.getSize('maxsize', Infinity);
|
||||
const mathaccent = this.node.getProperty('mathaccent');
|
||||
//
|
||||
// Clamp the dimension to the max and min
|
||||
// then get the target size via TeX rules
|
||||
//
|
||||
D = Math.max(min, Math.min(max, D));
|
||||
const df = this.font.params.delimiterfactor / 1000;
|
||||
const ds = this.font.params.delimitershortfall;
|
||||
const m = (min || exact ? D : mathaccent ? Math.min(D / df, D + ds) : Math.max(D * df, D - ds));
|
||||
//
|
||||
// Look through the delimiter sizes for one that matches
|
||||
//
|
||||
const delim = this.stretch;
|
||||
const c = delim.c || this.getText().codePointAt(0);
|
||||
let i = 0;
|
||||
if (delim.sizes) {
|
||||
for (const d of delim.sizes) {
|
||||
if (d >= m) {
|
||||
if (mathaccent && i) {
|
||||
i--;
|
||||
}
|
||||
this.variant = this.font.getSizeVariant(c, i);
|
||||
this.size = i;
|
||||
if (delim.schar && delim.schar[i]) {
|
||||
this.stretch = {...this.stretch, c: delim.schar[i]};
|
||||
}
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
//
|
||||
// No size matches, so if we can make multi-character delimiters,
|
||||
// record the data for that, otherwise, use the largest fixed size.
|
||||
//
|
||||
if (delim.stretch) {
|
||||
this.size = -1;
|
||||
this.invalidateBBox();
|
||||
this.getStretchBBox(WH, this.checkExtendedHeight(D, delim), delim);
|
||||
} else {
|
||||
this.variant = this.font.getSizeVariant(c, i - 1);
|
||||
this.size = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name The name of the attribute to get
|
||||
* @param {number} value The default value to use
|
||||
* @return {number} The size in em's of the attribute (or the default value)
|
||||
*/
|
||||
public getSize(name: string, value: number): number {
|
||||
let attributes = this.node.attributes;
|
||||
if (attributes.isSet(name)) {
|
||||
value = this.length2em(attributes.get(name), 1, 1); // FIXME: should use height of actual character
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} WH Either [W] for width, [H, D] for height and depth, or [] for min/max size
|
||||
* @return {number} Either the width or the total height of the character
|
||||
*/
|
||||
public getWH(WH: number[]): number {
|
||||
if (WH.length === 0) return 0;
|
||||
if (WH.length === 1) return WH[0];
|
||||
let [H, D] = WH;
|
||||
const a = this.font.params.axis_height;
|
||||
return (this.node.attributes.get('symmetric') ? 2 * Math.max(H - a, D + a) : H + D);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} WHD The [W] or [H, D] being requested from the parent mrow
|
||||
* @param {number} D The full dimension (including symmetry, etc)
|
||||
* @param {DelimiterData} C The delimiter data for the stretchy character
|
||||
*/
|
||||
public getStretchBBox(WHD: number[], D: number, C: DelimiterData) {
|
||||
if (C.hasOwnProperty('min') && C.min > D) {
|
||||
D = C.min;
|
||||
}
|
||||
let [h, d, w] = C.HDW;
|
||||
if (this.stretch.dir === DIRECTION.Vertical) {
|
||||
[h, d] = this.getBaseline(WHD, D, C);
|
||||
} else {
|
||||
w = D;
|
||||
}
|
||||
this.bbox.h = h;
|
||||
this.bbox.d = d;
|
||||
this.bbox.w = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} WHD The [H, D] being requested from the parent mrow
|
||||
* @param {number} HD The full height (including symmetry, etc)
|
||||
* @param {DelimiterData} C The delimiter data for the stretchy character
|
||||
* @return {[number, number]} The height and depth for the vertically stretched delimiter
|
||||
*/
|
||||
public getBaseline(WHD: number[], HD: number, C: DelimiterData): [number, number] {
|
||||
const hasWHD = (WHD.length === 2 && WHD[0] + WHD[1] === HD);
|
||||
const symmetric = this.node.attributes.get('symmetric');
|
||||
const [H, D] = (hasWHD ? WHD : [HD, 0]);
|
||||
let [h, d] = [H + D, 0];
|
||||
if (symmetric) {
|
||||
//
|
||||
// Center on the math axis
|
||||
//
|
||||
const a = this.font.params.axis_height;
|
||||
if (hasWHD) {
|
||||
h = 2 * Math.max(H - a, D + a);
|
||||
}
|
||||
d = h / 2 - a;
|
||||
} else if (hasWHD) {
|
||||
//
|
||||
// Use the given depth (from mrow)
|
||||
//
|
||||
d = D;
|
||||
} else {
|
||||
//
|
||||
// Use depth proportional to the normal-size character
|
||||
// (when stretching for minsize or maxsize by itself)
|
||||
//
|
||||
let [ch, cd] = (C.HDW || [.75, .25]);
|
||||
d = cd * (h / (ch + cd));
|
||||
}
|
||||
return [h - d, d];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public checkExtendedHeight(D: number, C: DelimiterData): number {
|
||||
if (C.fullExt) {
|
||||
const [extSize, endSize] = C.fullExt;
|
||||
const n = Math.ceil(Math.max(0, D - endSize) / extSize);
|
||||
D = endSize + n * extSize;
|
||||
}
|
||||
return D;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public remapChars(chars: number[]) {
|
||||
const primes = this.node.getProperty('primes') as string;
|
||||
if (primes) {
|
||||
return unicodeChars(primes);
|
||||
}
|
||||
if (chars.length === 1) {
|
||||
const parent = (this.node as MmlMo).coreParent().parent;
|
||||
const isAccent = this.isAccent && !parent.isKind('mrow');
|
||||
const map = (isAccent ? 'accent' : 'mo');
|
||||
const text = this.font.getRemappedChar(map, chars[0]);
|
||||
if (text) {
|
||||
chars = this.unicodeChars(text, this.variant);
|
||||
}
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMpadded wrapper mixin for the MmlMpadded object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {AnyWrapper, WrapperConstructor, Constructor} from '../Wrapper.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
import {Property} from '../../../core/Tree/Node.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMpadded interface
|
||||
*/
|
||||
export interface CommonMpadded extends AnyWrapper {
|
||||
/**
|
||||
* Get the content bounding box, and the change in size and offsets
|
||||
* as specified by the parameters
|
||||
*
|
||||
* @return {number[]} The original height, depth, width, the changes in height, depth,
|
||||
* and width, and the horizontal and vertical offsets of the content
|
||||
*/
|
||||
getDimens(): number[];
|
||||
|
||||
/**
|
||||
* Get a particular dimension, which can be relative to any of the BBox dimensions,
|
||||
* and can be an offset from the default size of the given dimension.
|
||||
*
|
||||
* @param {Property} length The value to be converted to a length in ems
|
||||
* @param {BBox} bbox The bbox of the mpadded content
|
||||
* @param {string=} d The default dimension to use for relative sizes ('w', 'h', or 'd')
|
||||
* @param {number=} m The minimum value allowed for the dimension
|
||||
* @return {number} The final dimension in ems
|
||||
*/
|
||||
dimen(length: Property, bbox: BBox, d?: string, m?: number): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMpadded constructor
|
||||
*/
|
||||
export type MpaddedConstructor = Constructor<CommonMpadded>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommomMpadded wrapper for the MmlMpadded object
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMpaddedMixin<T extends WrapperConstructor>(Base: T): MpaddedConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* Get the content bounding box, and the change in size and offsets
|
||||
* as specified by the parameters
|
||||
*
|
||||
* @return {number[]} The original height, depth, width, the changes in height, depth,
|
||||
* and width, and the horizontal and vertical offsets of the content
|
||||
*/
|
||||
public getDimens(): number[] {
|
||||
const values = this.node.attributes.getList('width', 'height', 'depth', 'lspace', 'voffset');
|
||||
const bbox = this.childNodes[0].getBBox(); // get unmodified bbox of children
|
||||
let {w, h, d} = bbox;
|
||||
let W = w, H = h, D = d, x = 0, y = 0, dx = 0;
|
||||
if (values.width !== '') w = this.dimen(values.width, bbox, 'w', 0);
|
||||
if (values.height !== '') h = this.dimen(values.height, bbox, 'h', 0);
|
||||
if (values.depth !== '') d = this.dimen(values.depth, bbox, 'd', 0);
|
||||
if (values.voffset !== '') y = this.dimen(values.voffset, bbox);
|
||||
if (values.lspace !== '') x = this.dimen(values.lspace, bbox);
|
||||
const align = this.node.attributes.get('data-align') as string;
|
||||
if (align) {
|
||||
dx = this.getAlignX(w, bbox, align);
|
||||
}
|
||||
return [H, D, W, h - H, d - D, w - W, x, y, dx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a particular dimension, which can be relative to any of the BBox dimensions,
|
||||
* and can be an offset from the default size of the given dimension.
|
||||
*
|
||||
* @param {Property} length The value to be converted to a length in ems
|
||||
* @param {BBox} bbox The bbox of the mpadded content
|
||||
* @param {string} d The default dimension to use for relative sizes ('w', 'h', or 'd')
|
||||
* @param {number} m The minimum value allowed for the dimension
|
||||
* @return {number} The final dimension in ems
|
||||
*/
|
||||
public dimen(length: Property, bbox: BBox, d: string = '', m: number = null): number {
|
||||
length = String(length);
|
||||
const match = length.match(/width|height|depth/);
|
||||
const size = (match ? bbox[match[0].charAt(0) as (keyof BBox)] :
|
||||
(d ? bbox[d as (keyof BBox)] : 0)) as number;
|
||||
let dimen = (this.length2em(length, size) || 0);
|
||||
if (length.match(/^[-+]/) && d) {
|
||||
dimen += size;
|
||||
}
|
||||
if (m != null) {
|
||||
dimen = Math.max(m, dimen);
|
||||
}
|
||||
return dimen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public computeBBox(bbox: BBox, recompute: boolean = false) {
|
||||
const [H, D, W, dh, dd, dw] = this.getDimens();
|
||||
bbox.w = W + dw;
|
||||
bbox.h = H + dh;
|
||||
bbox.d = D + dd;
|
||||
this.setChildPWidths(recompute, bbox.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getWrapWidth(_i: number) {
|
||||
return this.getBBox().w;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getChildAlign(_i: number) {
|
||||
return this.node.attributes.get('data-align') as string || 'left';
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Implements the CommonMroot wrapper mixin for the MmlMroot object
|
||||
*
|
||||
* @author dpvc@mathjax.org (Davide Cervone)
|
||||
*/
|
||||
|
||||
import {Constructor} from '../../common/Wrapper.js';
|
||||
import {CommonMsqrt, MsqrtConstructor} from './msqrt.js';
|
||||
import {CommonMo} from './mo.js';
|
||||
import {BBox} from '../../../util/BBox.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMroot interface
|
||||
*/
|
||||
export interface CommonMroot extends CommonMsqrt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for the CommonMroot constructor
|
||||
*/
|
||||
export type MrootConstructor = Constructor<CommonMroot>;
|
||||
|
||||
/*****************************************************************/
|
||||
/**
|
||||
* The CommonMroot wrapper mixin for the MmlMroot object (extends CommonMsqrt)
|
||||
*
|
||||
* @template T The Wrapper class constructor type
|
||||
*/
|
||||
export function CommonMrootMixin<T extends MsqrtConstructor>(Base: T): MrootConstructor & T {
|
||||
|
||||
return class extends Base {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get surd() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get root(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public combineRootBBox(BBOX: BBox, sbox: BBox, H: number) {
|
||||
const bbox = this.childNodes[this.root].getOuterBBox();
|
||||
const h = this.getRootDimens(sbox, H)[1];
|
||||
BBOX.combine(bbox, 0, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getRootDimens(sbox: BBox, H: number) {
|
||||
const surd = this.childNodes[this.surd] as CommonMo;
|
||||
const bbox = this.childNodes[this.root].getOuterBBox();
|
||||
const offset = (surd.size < 0 ? .5 : .6) * sbox.w;
|
||||
const {w, rscale} = bbox;
|
||||
const W = Math.max(w, offset / rscale);
|
||||
const dx = Math.max(0, W - w);
|
||||
const h = this.rootHeight(bbox, sbox, surd.size, H);
|
||||
const x = W * rscale - offset;
|
||||
return [x, h, dx];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BBox} rbox The bbox of the root
|
||||
* @param {BBox} sbox The bbox of the surd
|
||||
* @param {number} size The size of the surd
|
||||
* @param {number} H The height of the root as a whole
|
||||
* @return {number} The height of the root within the surd
|
||||
*/
|
||||
public rootHeight(rbox: BBox, sbox: BBox, size: number, H: number): number {
|
||||
const h = sbox.h + sbox.d;
|
||||
const b = (size < 0 ? 1.9 : .55 * h) - (h - H);
|
||||
return b + Math.max(0, rbox.d * rbox.rscale);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user