Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
"use strict";
module.exports = {
/**
* Given a CSS unit type, get the amount by which to increment a numeric value.
* Used as the step attribute in inputs of type "range" or "number".
*
* @param {String} unit
* CSS unit type (px, %, em, rem, vh, vw, ...)
* @return {Number}
* Amount by which to increment.
*/
getStepForUnit(unit) {
let step;
switch (unit) {
case "":
case "em":
case "rem":
case "vw":
case "vh":
case "vmin":
case "vmax":
step = 0.1;
break;
default:
step = 1;
}
return step;
},
/**
* Get the unit type from the end of a CSS value string.
* Returns null for non-string input or unitless values.
*
* @param {String} value
* CSS value string.
* @return {String|null}
* CSS unit type, like "px", "em", "rem", etc or null.
*/
getUnitFromValue(value) {
if (typeof value !== "string" || isNaN(parseFloat(value))) {
return null;
}
const match = value.match(/\D+?$/);
return match?.length ? match[0] : null;
},
/**
* Parse the string value of CSS font-variation-settings into an object with
* axis tag names and corresponding values. If the string is a keyword or does not
* contain axes, return an empty object.
*
* @param {String} string
* Value of font-variation-settings property coming from node's computed style.
* Its contents are expected to be stable having been already parsed by the
* browser.
* @return {Object}
*/
parseFontVariationAxes(string) {
let axes = {};
const keywords = ["initial", "normal", "inherit", "unset"];
if (!string || keywords.includes(string.trim())) {
return axes;
}
// Parse font-variation-settings CSS declaration into an object
// with axis tags as keys and axis values as values.
axes = string.split(",").reduce((acc, pair) => {
// Tags are always in quotes. Split by quote and filter excessive whitespace.
pair = pair.split(/["']/).filter(part => part.trim() !== "");
// Guard against malformed input that may have slipped through.
if (pair.length === 0) {
return acc;
}
const tag = pair[0];
const value = pair[1].trim();
// Axis tags shorter or longer than 4 characters are invalid. Whitespace is valid.
if (tag.length === 4) {
acc[tag] = parseFloat(value);
}
return acc;
}, {});
return axes;
},
/**
* Limit the decimal count of a number. Unlike Number.toFixed(),
* this function does not pad with extra zeros. If the input is not a number,
* the function throws an error.
*
* @param {Number} number
* @param {Number} decimals
* Decimal count in the output number. Default to one decimal.
* @return {Number}
*/
toFixed(number, decimals = 1) {
if (typeof number !== "number") {
throw new Error(`Input: "${number}" is not a number.`);
}
return Math.floor(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
},
};