UNPKG

svgdom

Version:

Straightforward DOM implementation for SVG, HTML and XML

152 lines (133 loc) 4.41 kB
// @ts-check // @ts-ignore import { extendStatic } from '../../utils/objectCreationUtils.js' const unitTypes = { SVG_LENGTHTYPE_UNKNOWN: 0, SVG_LENGTHTYPE_NUMBER: 1, SVG_LENGTHTYPE_PERCENTAGE: 2, SVG_LENGTHTYPE_EMS: 3, SVG_LENGTHTYPE_EXS: 4, SVG_LENGTHTYPE_PX: 5, SVG_LENGTHTYPE_CM: 6, SVG_LENGTHTYPE_MM: 7, SVG_LENGTHTYPE_IN: 8, SVG_LENGTHTYPE_PT: 9, SVG_LENGTHTYPE_PC: 10, } const unitByString = { ['']: unitTypes.SVG_LENGTHTYPE_NUMBER, ['%']: unitTypes.SVG_LENGTHTYPE_PERCENTAGE, ['em']: unitTypes.SVG_LENGTHTYPE_EMS, ['ex']: unitTypes.SVG_LENGTHTYPE_EXS, ['px']: unitTypes.SVG_LENGTHTYPE_PX, ['cm']: unitTypes.SVG_LENGTHTYPE_CM, ['mm']: unitTypes.SVG_LENGTHTYPE_MM, ['in']: unitTypes.SVG_LENGTHTYPE_IN, ['pt']: unitTypes.SVG_LENGTHTYPE_PT, ['pc']: unitTypes.SVG_LENGTHTYPE_PC, } const unitStringByConstant = new Map( Object.entries(unitByString).map(([unitString, unitConstant]) => [ unitConstant, unitString, ]) ) const unitFactors = new Map([ [unitTypes.SVG_LENGTHTYPE_NUMBER, 1], [unitTypes.SVG_LENGTHTYPE_PERCENTAGE, NaN], [unitTypes.SVG_LENGTHTYPE_EMS, NaN], [unitTypes.SVG_LENGTHTYPE_EXS, NaN], [unitTypes.SVG_LENGTHTYPE_PX, 1], [unitTypes.SVG_LENGTHTYPE_CM, 6], [unitTypes.SVG_LENGTHTYPE_MM, 96 / 25.4], [unitTypes.SVG_LENGTHTYPE_IN, 96], [unitTypes.SVG_LENGTHTYPE_PT, 4 / 3], [unitTypes.SVG_LENGTHTYPE_PC, 16], ]) const valuePattern = /^\s*([+-]?[0-9]*[.]?[0-9]+(?:e[+-]?[0-9]+)?)(em|ex|px|in|cm|mm|pt|pc|%)?\s*$/i; export class SVGLength { element attributeName /** * @param {Element} element * @param {string} attributeName */ constructor(element, attributeName) { this.element = element this.attributeName = attributeName } get unitType() { return parseValue(this.element.getAttribute(this.attributeName))[1] } get value() { const [value, unit] = parseValue( this.element.getAttribute(this.attributeName) ) return value * getUnitFactor(unit) } set value(value) { const unitFactor = getUnitFactor(this.unitType) this.element.setAttribute( this.attributeName, value / unitFactor + unitString(this) ) } get valueInSpecifiedUnits() { return parseValue(this.element.getAttribute(this.attributeName))[0] } set valueInSpecifiedUnits(value) { this.element.setAttribute(this.attributeName, value + unitString(this)) } get valueAsString() { // Do not simply use getAttribute() as this function has to return a string // that is a valid representation of the used value. return this.valueInSpecifiedUnits + unitString(this) } set valueAsString(valueString) { const [value, unit] = parseValue(valueString, false) const unitString = unitStringByConstant.get(unit) || '' this.element.setAttribute(this.attributeName, value + unitString) } } /** * @param {string|null} valueString * @param {boolean} fallback If set to `false` causes an error to be thrown if * `valueString` can not be parsed properly. Otherwise the returned value falls * back to 0 and the unit falls back to `SVG_LENGTHTYPE_NUMBER`. * @return {[number, number]} Value and unit. For unknown units, if the * attribute is not of the correct format or if the attribute is not present on * the element, value 0 and unit SVG_LENGTHTYPE_NUMBER are returned. */ function parseValue(valueString, fallback = true) { const [, rawValue, rawUnit] = (valueString || '').match(valuePattern) || [] const unit = unitByString[(rawUnit || '').toLowerCase()] if (rawValue !== undefined && unit !== undefined) { return [parseFloat(rawValue), unit] } if (fallback) { // For unknown units or unparsable attributes, browsers fall back to value 0 return [0, unitTypes.SVG_LENGTHTYPE_NUMBER] } throw new Error('An invalid or illegal string was specified') } /** * @param {number} unit Unit constant */ function getUnitFactor(unit) { const unitFactor = unitFactors.get(unit) if (unitFactor === undefined) { throw new Error(unitFactor + ' is not a known unit constant') } if (isNaN(unitFactor)) { throw new Error(`Unit ${unitStringByConstant.get(unit)} is not supported`) } return unitFactor } /** * @param {SVGLength} svgLength * @return {string} */ function unitString(svgLength) { return unitStringByConstant.get(svgLength.unitType) || '' } extendStatic(SVGLength, unitTypes)