123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- /*************************************************************
- *
- * 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);
- }
- }
|