/************************************************************* * * Copyright (c) 2017-2025 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. */ /** * @file Implements Attribute class for MmlNodes * * @author dpvc@mathjax.org (Davide Cervone) */ import { PropertyList, Property } from '../Tree/Node.js'; /** * A constant for when a property should be inherited from the global defaults lists */ export const INHERIT = '_inherit_'; /******************************************************************/ /** * Implements the Attributes class for MmlNodes * (These can be set explicitly, inherited from parent nodes, * taken from a default list of values, or taken from global * defaults.) */ export class Attributes { /** * The attributes explicitly set on a node */ protected attributes: PropertyList; /** * The attributes inherited from parent nodes */ protected inherited: PropertyList; /** * The default attributes for the node type */ protected defaults: PropertyList; /** * Global attributes from the math node itself */ protected global: PropertyList; /** * @param {PropertyList} defaults The defaults for this node type * @param {PropertyList} global The global properties (from the math node) * * @class */ constructor(defaults: PropertyList, global: PropertyList) { this.global = global; this.defaults = Object.create(global); this.inherited = Object.create(this.defaults); this.attributes = Object.create(this.inherited); Object.assign(this.defaults, defaults); } /** * @param {string} name The name of the attribute to set * @param {Property} value The value to give the named attribute */ public set(name: string, value: Property) { this.attributes[name] = value; } /** * @param {PropertyList} list An object containing the properties to set */ public setList(list: PropertyList) { Object.assign(this.attributes, list); } /** * @param {string} name The name of the attribute to remove */ public unset(name: string) { delete this.attributes[name]; } /** * @param {string} name The name of the attribute whose value is to be returned * @returns {Property} The value of the named attribute (including inheritance and defaults) */ public get(name: string): Property { let value = this.attributes[name]; if (value === INHERIT) { value = this.global[name]; } return value; } /** * @param {string} name The value of the attribute whose value is to be returned * @returns {Property} The attribute whose name was given if it is explicit on the * node (not inherited or defaulted), null otherwise */ public getExplicit(name: string): Property { return this.hasExplicit(name) ? this.attributes[name] : undefined; } /** * @param {string} name The value of the attribute whose presence is to be checked * @returns {boolean} True if the attribute is explicitly given on this node */ public hasExplicit(name: string): boolean { return Object.hasOwn(this.attributes, name); } /** * @param {string[]} names The attribute names to look for. * @returns {boolean} True if one of the names is an explicit attribute, false otherwise */ public hasOneOf(names: string[]): boolean { for (const name of names) { if (this.hasExplicit(name)) { return true; } } return false; } /** * @param {string[]} names The names of attributes whose values are to be returned * @returns {PropertyList} An object containing the attributes and their values */ public getList(...names: string[]): PropertyList { const values: PropertyList = {}; for (const name of names) { values[name] = this.get(name); } return values; } /** * @param {string} name The name of an inherited attribute to be set * @param {Property} value The value to assign to the named attribute */ public setInherited(name: string, value: Property) { this.inherited[name] = value; } /** * @param {string} name The name of an inherited attribute whose value is to be returned * @returns {Property} The value of the named attribute if it is inherited, null otherwise */ public getInherited(name: string): Property { return this.inherited[name]; } /** * @param {string} name The name of a default attribute whose value is to be returned * @returns {Property} The value of the named attribute if a default exists for it, null otherwise */ public getDefault(name: string): Property { return this.defaults[name]; } /** * @param {string} name The name of a attribute to check * @returns {boolean} True if attribute is set explicitly or inherited * from an explicit mstyle or math attribute */ public isSet(name: string): boolean { return ( Object.hasOwn(this.attributes, name) || Object.hasOwn(this.inherited, name) ); } /** * @param {string} name The name of an attribute to test for the existence of a default * @returns {boolean} True of there is a default for the named attribute, false otherwise */ public hasDefault(name: string): boolean { return name in this.defaults; } /** * @returns {string[]} The names of all the attributes explicitly set on the node */ public getExplicitNames(): string[] { return Object.keys(this.attributes); } /** * @returns {string[]} The names of all the inherited attributes for the node */ public getInheritedNames(): string[] { return Object.keys(this.inherited); } /** * @returns {string[]} The names of all the default attributes for the node */ public getDefaultNames(): string[] { return Object.keys(this.defaults); } /** * @returns {string[]} The names of all the global attributes */ public getGlobalNames(): string[] { return Object.keys(this.global); } /** * @returns {PropertyList} The attribute object */ public getAllAttributes(): PropertyList { return this.attributes; } /** * @returns {PropertyList} The inherited object */ public getAllInherited(): PropertyList { return this.inherited; } /** * @returns {PropertyList} The defaults object */ public getAllDefaults(): PropertyList { return this.defaults; } /** * @returns {PropertyList} The global object */ public getAllGlobals(): PropertyList { return this.global; } }