@maplibre/maplibre-gl-style-spec
Version:
a specification for maplibre styles
1 lines • 994 kB
Source Map (JSON)
{"version":3,"file":"gl-style-migrate.cjs","sources":["../node_modules/minimist/index.js","../node_modules/json-stringify-pretty-compact/index.js","../src/format.ts","../src/visit.ts","../src/migrate/v8.ts","../src/expression/parsing_error.ts","../src/expression/scope.ts","../src/expression/types.ts","../src/expression/types/color_spaces.ts","../src/expression/types/parse_css_color.ts","../src/util/interpolate-primitives.ts","../src/expression/types/color.ts","../src/expression/types/collator.ts","../src/expression/types/formatted.ts","../src/expression/types/padding.ts","../src/expression/runtime_error.ts","../src/expression/types/variable_anchor_offset_collection.ts","../src/expression/types/resolved_image.ts","../src/expression/types/projection_definition.ts","../src/expression/values.ts","../src/expression/definitions/literal.ts","../src/expression/definitions/assertion.ts","../src/expression/definitions/coercion.ts","../src/expression/evaluation_context.ts","../src/expression/parsing_context.ts","../src/expression/definitions/let.ts","../src/expression/definitions/var.ts","../src/expression/definitions/at.ts","../src/expression/definitions/in.ts","../src/expression/definitions/index_of.ts","../src/expression/definitions/match.ts","../src/expression/definitions/case.ts","../src/expression/definitions/slice.ts","../src/expression/stops.ts","../src/expression/definitions/step.ts","../node_modules/@mapbox/unitbezier/index.js","../src/expression/definitions/interpolate.ts","../src/expression/definitions/coalesce.ts","../src/expression/definitions/comparison.ts","../src/expression/definitions/collator.ts","../src/expression/definitions/number_format.ts","../src/expression/definitions/format.ts","../src/expression/definitions/image.ts","../src/expression/definitions/length.ts","../src/util/geometry_util.ts","../src/expression/definitions/within.ts","../node_modules/tinyqueue/index.js","../src/util/classify_rings.ts","../src/util/cheap_ruler.ts","../src/expression/definitions/distance.ts","../src/expression/definitions/index.ts","../src/expression/compound_expression.ts","../src/expression/index.ts","../src/function/convert.ts","../src/feature_filter/index.ts","../src/feature_filter/convert.ts","../src/migrate/expressions.ts","../src/migrate/migrate_colors.ts","../src/migrate.ts","../bin/gl-style-migrate.ts"],"sourcesContent":["'use strict';\n\nfunction hasKey(obj, keys) {\n\tvar o = obj;\n\tkeys.slice(0, -1).forEach(function (key) {\n\t\to = o[key] || {};\n\t});\n\n\tvar key = keys[keys.length - 1];\n\treturn key in o;\n}\n\nfunction isNumber(x) {\n\tif (typeof x === 'number') { return true; }\n\tif ((/^0x[0-9a-f]+$/i).test(x)) { return true; }\n\treturn (/^[-+]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(e[-+]?\\d+)?$/).test(x);\n}\n\nfunction isConstructorOrProto(obj, key) {\n\treturn (key === 'constructor' && typeof obj[key] === 'function') || key === '__proto__';\n}\n\nmodule.exports = function (args, opts) {\n\tif (!opts) { opts = {}; }\n\n\tvar flags = {\n\t\tbools: {},\n\t\tstrings: {},\n\t\tunknownFn: null,\n\t};\n\n\tif (typeof opts.unknown === 'function') {\n\t\tflags.unknownFn = opts.unknown;\n\t}\n\n\tif (typeof opts.boolean === 'boolean' && opts.boolean) {\n\t\tflags.allBools = true;\n\t} else {\n\t\t[].concat(opts.boolean).filter(Boolean).forEach(function (key) {\n\t\t\tflags.bools[key] = true;\n\t\t});\n\t}\n\n\tvar aliases = {};\n\n\tfunction aliasIsBoolean(key) {\n\t\treturn aliases[key].some(function (x) {\n\t\t\treturn flags.bools[x];\n\t\t});\n\t}\n\n\tObject.keys(opts.alias || {}).forEach(function (key) {\n\t\taliases[key] = [].concat(opts.alias[key]);\n\t\taliases[key].forEach(function (x) {\n\t\t\taliases[x] = [key].concat(aliases[key].filter(function (y) {\n\t\t\t\treturn x !== y;\n\t\t\t}));\n\t\t});\n\t});\n\n\t[].concat(opts.string).filter(Boolean).forEach(function (key) {\n\t\tflags.strings[key] = true;\n\t\tif (aliases[key]) {\n\t\t\t[].concat(aliases[key]).forEach(function (k) {\n\t\t\t\tflags.strings[k] = true;\n\t\t\t});\n\t\t}\n\t});\n\n\tvar defaults = opts.default || {};\n\n\tvar argv = { _: [] };\n\n\tfunction argDefined(key, arg) {\n\t\treturn (flags.allBools && (/^--[^=]+$/).test(arg))\n\t\t\t|| flags.strings[key]\n\t\t\t|| flags.bools[key]\n\t\t\t|| aliases[key];\n\t}\n\n\tfunction setKey(obj, keys, value) {\n\t\tvar o = obj;\n\t\tfor (var i = 0; i < keys.length - 1; i++) {\n\t\t\tvar key = keys[i];\n\t\t\tif (isConstructorOrProto(o, key)) { return; }\n\t\t\tif (o[key] === undefined) { o[key] = {}; }\n\t\t\tif (\n\t\t\t\to[key] === Object.prototype\n\t\t\t\t|| o[key] === Number.prototype\n\t\t\t\t|| o[key] === String.prototype\n\t\t\t) {\n\t\t\t\to[key] = {};\n\t\t\t}\n\t\t\tif (o[key] === Array.prototype) { o[key] = []; }\n\t\t\to = o[key];\n\t\t}\n\n\t\tvar lastKey = keys[keys.length - 1];\n\t\tif (isConstructorOrProto(o, lastKey)) { return; }\n\t\tif (\n\t\t\to === Object.prototype\n\t\t\t|| o === Number.prototype\n\t\t\t|| o === String.prototype\n\t\t) {\n\t\t\to = {};\n\t\t}\n\t\tif (o === Array.prototype) { o = []; }\n\t\tif (o[lastKey] === undefined || flags.bools[lastKey] || typeof o[lastKey] === 'boolean') {\n\t\t\to[lastKey] = value;\n\t\t} else if (Array.isArray(o[lastKey])) {\n\t\t\to[lastKey].push(value);\n\t\t} else {\n\t\t\to[lastKey] = [o[lastKey], value];\n\t\t}\n\t}\n\n\tfunction setArg(key, val, arg) {\n\t\tif (arg && flags.unknownFn && !argDefined(key, arg)) {\n\t\t\tif (flags.unknownFn(arg) === false) { return; }\n\t\t}\n\n\t\tvar value = !flags.strings[key] && isNumber(val)\n\t\t\t? Number(val)\n\t\t\t: val;\n\t\tsetKey(argv, key.split('.'), value);\n\n\t\t(aliases[key] || []).forEach(function (x) {\n\t\t\tsetKey(argv, x.split('.'), value);\n\t\t});\n\t}\n\n\tObject.keys(flags.bools).forEach(function (key) {\n\t\tsetArg(key, defaults[key] === undefined ? false : defaults[key]);\n\t});\n\n\tvar notFlags = [];\n\n\tif (args.indexOf('--') !== -1) {\n\t\tnotFlags = args.slice(args.indexOf('--') + 1);\n\t\targs = args.slice(0, args.indexOf('--'));\n\t}\n\n\tfor (var i = 0; i < args.length; i++) {\n\t\tvar arg = args[i];\n\t\tvar key;\n\t\tvar next;\n\n\t\tif ((/^--.+=/).test(arg)) {\n\t\t\t// Using [\\s\\S] instead of . because js doesn't support the\n\t\t\t// 'dotall' regex modifier. See:\n\t\t\t// http://stackoverflow.com/a/1068308/13216\n\t\t\tvar m = arg.match(/^--([^=]+)=([\\s\\S]*)$/);\n\t\t\tkey = m[1];\n\t\t\tvar value = m[2];\n\t\t\tif (flags.bools[key]) {\n\t\t\t\tvalue = value !== 'false';\n\t\t\t}\n\t\t\tsetArg(key, value, arg);\n\t\t} else if ((/^--no-.+/).test(arg)) {\n\t\t\tkey = arg.match(/^--no-(.+)/)[1];\n\t\t\tsetArg(key, false, arg);\n\t\t} else if ((/^--.+/).test(arg)) {\n\t\t\tkey = arg.match(/^--(.+)/)[1];\n\t\t\tnext = args[i + 1];\n\t\t\tif (\n\t\t\t\tnext !== undefined\n\t\t\t\t&& !(/^(-|--)[^-]/).test(next)\n\t\t\t\t&& !flags.bools[key]\n\t\t\t\t&& !flags.allBools\n\t\t\t\t&& (aliases[key] ? !aliasIsBoolean(key) : true)\n\t\t\t) {\n\t\t\t\tsetArg(key, next, arg);\n\t\t\t\ti += 1;\n\t\t\t} else if ((/^(true|false)$/).test(next)) {\n\t\t\t\tsetArg(key, next === 'true', arg);\n\t\t\t\ti += 1;\n\t\t\t} else {\n\t\t\t\tsetArg(key, flags.strings[key] ? '' : true, arg);\n\t\t\t}\n\t\t} else if ((/^-[^-]+/).test(arg)) {\n\t\t\tvar letters = arg.slice(1, -1).split('');\n\n\t\t\tvar broken = false;\n\t\t\tfor (var j = 0; j < letters.length; j++) {\n\t\t\t\tnext = arg.slice(j + 2);\n\n\t\t\t\tif (next === '-') {\n\t\t\t\t\tsetArg(letters[j], next, arg);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ((/[A-Za-z]/).test(letters[j]) && next[0] === '=') {\n\t\t\t\t\tsetArg(letters[j], next.slice(1), arg);\n\t\t\t\t\tbroken = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t(/[A-Za-z]/).test(letters[j])\n\t\t\t\t\t&& (/-?\\d+(\\.\\d*)?(e-?\\d+)?$/).test(next)\n\t\t\t\t) {\n\t\t\t\t\tsetArg(letters[j], next, arg);\n\t\t\t\t\tbroken = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (letters[j + 1] && letters[j + 1].match(/\\W/)) {\n\t\t\t\t\tsetArg(letters[j], arg.slice(j + 2), arg);\n\t\t\t\t\tbroken = true;\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tsetArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tkey = arg.slice(-1)[0];\n\t\t\tif (!broken && key !== '-') {\n\t\t\t\tif (\n\t\t\t\t\targs[i + 1]\n\t\t\t\t\t&& !(/^(-|--)[^-]/).test(args[i + 1])\n\t\t\t\t\t&& !flags.bools[key]\n\t\t\t\t\t&& (aliases[key] ? !aliasIsBoolean(key) : true)\n\t\t\t\t) {\n\t\t\t\t\tsetArg(key, args[i + 1], arg);\n\t\t\t\t\ti += 1;\n\t\t\t\t} else if (args[i + 1] && (/^(true|false)$/).test(args[i + 1])) {\n\t\t\t\t\tsetArg(key, args[i + 1] === 'true', arg);\n\t\t\t\t\ti += 1;\n\t\t\t\t} else {\n\t\t\t\t\tsetArg(key, flags.strings[key] ? '' : true, arg);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (!flags.unknownFn || flags.unknownFn(arg) !== false) {\n\t\t\t\targv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg));\n\t\t\t}\n\t\t\tif (opts.stopEarly) {\n\t\t\t\targv._.push.apply(argv._, args.slice(i + 1));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tObject.keys(defaults).forEach(function (k) {\n\t\tif (!hasKey(argv, k.split('.'))) {\n\t\t\tsetKey(argv, k.split('.'), defaults[k]);\n\n\t\t\t(aliases[k] || []).forEach(function (x) {\n\t\t\t\tsetKey(argv, x.split('.'), defaults[k]);\n\t\t\t});\n\t\t}\n\t});\n\n\tif (opts['--']) {\n\t\targv['--'] = notFlags.slice();\n\t} else {\n\t\tnotFlags.forEach(function (k) {\n\t\t\targv._.push(k);\n\t\t});\n\t}\n\n\treturn argv;\n};\n","// Note: This regex matches even invalid JSON strings, but since we’re\n// working on the output of `JSON.stringify` we know that only valid strings\n// are present (unless the user supplied a weird `options.indent` but in\n// that case we don’t care since the output would be invalid anyway).\nconst stringOrChar = /(\"(?:[^\\\\\"]|\\\\.)*\")|[:,]/g;\n\nexport default function stringify(passedObj, options = {}) {\n const indent = JSON.stringify(\n [1],\n undefined,\n options.indent === undefined ? 2 : options.indent\n ).slice(2, -3);\n\n const maxLength =\n indent === \"\"\n ? Infinity\n : options.maxLength === undefined\n ? 80\n : options.maxLength;\n\n let { replacer } = options;\n\n return (function _stringify(obj, currentIndent, reserved) {\n if (obj && typeof obj.toJSON === \"function\") {\n obj = obj.toJSON();\n }\n\n const string = JSON.stringify(obj, replacer);\n\n if (string === undefined) {\n return string;\n }\n\n const length = maxLength - currentIndent.length - reserved;\n\n if (string.length <= length) {\n const prettified = string.replace(\n stringOrChar,\n (match, stringLiteral) => {\n return stringLiteral || `${match} `;\n }\n );\n if (prettified.length <= length) {\n return prettified;\n }\n }\n\n if (replacer != null) {\n obj = JSON.parse(string);\n replacer = undefined;\n }\n\n if (typeof obj === \"object\" && obj !== null) {\n const nextIndent = currentIndent + indent;\n const items = [];\n let index = 0;\n let start;\n let end;\n\n if (Array.isArray(obj)) {\n start = \"[\";\n end = \"]\";\n const { length } = obj;\n for (; index < length; index++) {\n items.push(\n _stringify(obj[index], nextIndent, index === length - 1 ? 0 : 1) ||\n \"null\"\n );\n }\n } else {\n start = \"{\";\n end = \"}\";\n const keys = Object.keys(obj);\n const { length } = keys;\n for (; index < length; index++) {\n const key = keys[index];\n const keyPart = `${JSON.stringify(key)}: `;\n const value = _stringify(\n obj[key],\n nextIndent,\n keyPart.length + (index === length - 1 ? 0 : 1)\n );\n if (value !== undefined) {\n items.push(keyPart + value);\n }\n }\n }\n\n if (items.length > 0) {\n return [start, indent + items.join(`,\\n${nextIndent}`), end].join(\n `\\n${currentIndent}`\n );\n }\n }\n\n return string;\n })(passedObj, \"\", 0);\n}\n","\nimport {latest} from './reference/latest';\nimport stringifyPretty from 'json-stringify-pretty-compact';\n\nfunction sortKeysBy(obj, reference) {\n const result = {};\n for (const key in reference) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n for (const key in obj) {\n if (result[key] === undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Format a MapLibre Style. Returns a stringified style with its keys\n * sorted in the same order as the reference style.\n *\n * The optional `space` argument is passed to\n * [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)\n * to generate formatted output.\n *\n * If `space` is unspecified, a default of `2` spaces will be used.\n *\n * @private\n * @param {Object} style a MapLibre Style\n * @param {number} [space] space argument to pass to `JSON.stringify`\n * @returns {string} stringified formatted JSON\n * @example\n * var fs = require('fs');\n * var format = require('maplibre-gl-style-spec').format;\n * var style = fs.readFileSync('./source.json', 'utf8');\n * fs.writeFileSync('./dest.json', format(style));\n * fs.writeFileSync('./dest.min.json', format(style, 0));\n */\nexport function format(style, space = 2) {\n style = sortKeysBy(style, latest.$root);\n\n if (style.layers) {\n style.layers = style.layers.map((layer) => sortKeysBy(layer, latest.layer));\n }\n\n return stringifyPretty(style, {indent: space});\n}\n\n","import Reference from './reference/v8.json' with {type: 'json'};\nimport type {StylePropertySpecification} from '.';\nimport type {\n StyleSpecification,\n SourceSpecification,\n LayerSpecification,\n PropertyValueSpecification,\n DataDrivenPropertyValueSpecification\n} from './types.g';\n\nfunction getPropertyReference(propertyName): StylePropertySpecification {\n for (let i = 0; i < Reference.layout.length; i++) {\n for (const key in Reference[Reference.layout[i]]) {\n if (key === propertyName) return Reference[Reference.layout[i]][key] as any;\n }\n }\n for (let i = 0; i < Reference.paint.length; i++) {\n for (const key in Reference[Reference.paint[i]]) {\n if (key === propertyName) return Reference[Reference.paint[i]][key] as any;\n }\n }\n\n return null;\n}\n\nexport function eachSource(style: StyleSpecification, callback: (_: SourceSpecification) => void) {\n for (const k in style.sources) {\n callback(style.sources[k]);\n }\n}\n\nexport function eachLayer(style: StyleSpecification, callback: (_: LayerSpecification) => void) {\n for (const layer of style.layers) {\n callback(layer);\n }\n}\n\ntype PropertyCallback = (\n a: {\n path: [string, 'paint' | 'layout', string]; // [layerid, paint/layout, property key],\n key: string;\n value: PropertyValueSpecification<unknown> | DataDrivenPropertyValueSpecification<unknown>;\n reference: StylePropertySpecification;\n set: (\n a: PropertyValueSpecification<unknown> | DataDrivenPropertyValueSpecification<unknown>\n ) => void;\n }\n) => void;\n\nexport function eachProperty(\n style: StyleSpecification,\n options: {\n paint?: boolean;\n layout?: boolean;\n },\n callback: PropertyCallback\n) {\n function inner(layer, propertyType: 'paint' | 'layout') {\n const properties = (layer[propertyType] as any);\n if (!properties) return;\n Object.keys(properties).forEach((key) => {\n callback({\n path: [layer.id, propertyType, key],\n key,\n value: properties[key],\n reference: getPropertyReference(key),\n set(x) {\n properties[key] = x;\n }\n });\n });\n }\n\n eachLayer(style, (layer) => {\n if (options.paint) {\n inner(layer, 'paint');\n }\n if (options.layout) {\n inner(layer, 'layout');\n }\n });\n}\n","\nimport {eachSource, eachLayer, eachProperty} from '../visit';\nimport type {LayerSpecification, StyleSpecification} from '../types.g';\n\nfunction eachLayout(layer: LayerSpecification, callback: (_: LayerSpecification['layout'], __: string) => void) {\n for (const k in layer) {\n if (k.indexOf('layout') === 0) {\n callback(layer[k], k);\n }\n }\n}\n\nfunction eachPaint(layer: LayerSpecification, callback: (_: LayerSpecification['paint'], __: string) => void) {\n for (const k in layer) {\n if (k.indexOf('paint') === 0) {\n callback(layer[k], k);\n }\n }\n}\n\nfunction resolveConstant(style: StyleSpecification, value: any) {\n if (typeof value === 'string' && value[0] === '@') {\n return resolveConstant(style, (style as any).constants[value]);\n } else {\n return value;\n }\n}\n\nfunction isFunction(value) {\n return Array.isArray(value.stops);\n}\n\nfunction renameProperty(obj: Object, from: string, to: string) {\n obj[to] = obj[from]; delete obj[from];\n}\n\nexport function migrateV8(style: StyleSpecification) {\n style.version = 8;\n\n // Rename properties, reverse coordinates in source and layers\n eachSource(style, (source) => {\n if (source.type === 'video' && source['url'] !== undefined) {\n renameProperty(source, 'url', 'urls');\n }\n if (source.type === 'video') {\n source.coordinates.forEach((coord) => {\n return coord.reverse();\n });\n }\n });\n\n eachLayer(style, (layer) => {\n eachLayout(layer, (layout) => {\n if (layout['symbol-min-distance'] !== undefined) {\n renameProperty(layout, 'symbol-min-distance', 'symbol-spacing');\n }\n });\n\n eachPaint(layer, (paint) => {\n if (paint['background-image'] !== undefined) {\n renameProperty(paint, 'background-image', 'background-pattern');\n }\n if (paint['line-image'] !== undefined) {\n renameProperty(paint, 'line-image', 'line-pattern');\n }\n if (paint['fill-image'] !== undefined) {\n renameProperty(paint, 'fill-image', 'fill-pattern');\n }\n });\n });\n\n // Inline Constants\n eachProperty(style, {paint: true, layout: true}, (property) => {\n const value = resolveConstant(style, property.value);\n\n if (isFunction(value)) {\n value.stops.forEach((stop) => {\n stop[1] = resolveConstant(style, stop[1]);\n });\n }\n\n property.set(value);\n });\n delete style['constants'];\n\n eachLayer(style, (layer) => {\n // get rid of text-max-size, icon-max-size\n // turn text-size, icon-size into layout properties\n // https://github.com/mapbox/mapbox-gl-style-spec/issues/255\n\n eachLayout(layer, (layout) => {\n delete layout['text-max-size'];\n delete layout['icon-max-size'];\n });\n\n eachPaint(layer, (paint) => {\n if (paint['text-size']) {\n if (!layer.layout) layer.layout = {};\n layer.layout['text-size'] = paint['text-size'];\n delete paint['text-size'];\n }\n\n if (paint['icon-size']) {\n if (!layer.layout) layer.layout = {};\n layer.layout['icon-size'] = paint['icon-size'];\n delete paint['icon-size'];\n }\n });\n });\n\n function migrateFontStack(font) {\n function splitAndTrim(string) {\n return string.split(',').map((s) => {\n return s.trim();\n });\n }\n\n if (Array.isArray(font)) {\n // Assume it's a previously migrated font-array.\n return font;\n\n } else if (typeof font === 'string') {\n return splitAndTrim(font);\n\n } else if (typeof font === 'object') {\n font.stops.forEach((stop) => {\n stop[1] = splitAndTrim(stop[1]);\n });\n return font;\n\n } else {\n throw new Error('unexpected font value');\n }\n }\n\n eachLayer(style, (layer) => {\n eachLayout(layer, (layout) => {\n if (layout['text-font']) {\n layout['text-font'] = migrateFontStack(layout['text-font']);\n }\n });\n });\n\n // Reverse order of symbol layers. This is an imperfect migration.\n //\n // The order of a symbol layer in the layers list affects two things:\n // - how it is drawn relative to other layers (like oneway arrows below bridges)\n // - the placement priority compared to other layers\n //\n // It's impossible to reverse the placement priority without breaking the draw order\n // in some cases. This migration only reverses the order of symbol layers that\n // are above all other types of layers.\n //\n // Symbol layers that are at the top of the map preserve their priority.\n // Symbol layers that are below another type (line, fill) of layer preserve their draw order.\n\n let firstSymbolLayer = 0;\n for (let i = style.layers.length - 1; i >= 0; i--) {\n const layer = style.layers[i];\n if (layer.type !== 'symbol') {\n firstSymbolLayer = i + 1;\n break;\n }\n }\n\n const symbolLayers = style.layers.splice(firstSymbolLayer);\n symbolLayers.reverse();\n style.layers = style.layers.concat(symbolLayers);\n\n return style;\n}\n","export class ExpressionParsingError extends Error {\n key: string;\n message: string;\n constructor(key: string, message: string) {\n super(message);\n this.message = message;\n this.key = key;\n }\n}\n","import type {Expression} from './expression';\n\n/**\n * Tracks `let` bindings during expression parsing.\n * @private\n */\nexport class Scope {\n parent: Scope;\n bindings: {[_: string]: Expression};\n constructor(parent?: Scope, bindings: Array<[string, Expression]> = []) {\n this.parent = parent;\n this.bindings = {};\n for (const [name, expression] of bindings) {\n this.bindings[name] = expression;\n }\n }\n\n concat(bindings: Array<[string, Expression]>) {\n return new Scope(this, bindings);\n }\n\n get(name: string): Expression {\n if (this.bindings[name]) { return this.bindings[name]; }\n if (this.parent) { return this.parent.get(name); }\n throw new Error(`${name} not found in scope.`);\n }\n\n has(name: string): boolean {\n if (this.bindings[name]) return true;\n return this.parent ? this.parent.has(name) : false;\n }\n}\n","export type NullTypeT = {\n kind: 'null';\n};\nexport type NumberTypeT = {\n kind: 'number';\n};\nexport type StringTypeT = {\n kind: 'string';\n};\nexport type BooleanTypeT = {\n kind: 'boolean';\n};\nexport type ColorTypeT = {\n kind: 'color';\n};\nexport type ProjectionDefinitionTypeT = {\n kind: 'projectionDefinition';\n};\nexport type ObjectTypeT = {\n kind: 'object';\n};\nexport type ValueTypeT = {\n kind: 'value';\n};\nexport type ErrorTypeT = {\n kind: 'error';\n};\nexport type CollatorTypeT = {\n kind: 'collator';\n};\nexport type FormattedTypeT = {\n kind: 'formatted';\n};\nexport type PaddingTypeT = {\n kind: 'padding';\n};\nexport type ResolvedImageTypeT = {\n kind: 'resolvedImage';\n};\nexport type VariableAnchorOffsetCollectionTypeT = {\n kind: 'variableAnchorOffsetCollection';\n};\n\nexport type EvaluationKind = 'constant' | 'source' | 'camera' | 'composite';\n\nexport type Type = NullTypeT | NumberTypeT | StringTypeT | BooleanTypeT | ColorTypeT | ProjectionDefinitionTypeT | ObjectTypeT | ValueTypeT |\nArrayType | ErrorTypeT | CollatorTypeT | FormattedTypeT | PaddingTypeT | ResolvedImageTypeT | VariableAnchorOffsetCollectionTypeT;\n\nexport interface ArrayType<T extends Type = Type> {\n kind: 'array';\n itemType: T;\n N: number;\n}\n\nexport type NativeType = 'number' | 'string' | 'boolean' | 'null' | 'array' | 'object';\n\nexport const NullType = {kind: 'null'} as NullTypeT;\nexport const NumberType = {kind: 'number'} as NumberTypeT;\nexport const StringType = {kind: 'string'} as StringTypeT;\nexport const BooleanType = {kind: 'boolean'} as BooleanTypeT;\nexport const ColorType = {kind: 'color'} as ColorTypeT;\nexport const ProjectionDefinitionType = {kind: 'projectionDefinition'} as ProjectionDefinitionTypeT;\nexport const ObjectType = {kind: 'object'} as ObjectTypeT;\nexport const ValueType = {kind: 'value'} as ValueTypeT;\nexport const ErrorType = {kind: 'error'} as ErrorTypeT;\nexport const CollatorType = {kind: 'collator'} as CollatorTypeT;\nexport const FormattedType = {kind: 'formatted'} as FormattedTypeT;\nexport const PaddingType = {kind: 'padding'} as PaddingTypeT;\nexport const ResolvedImageType = {kind: 'resolvedImage'} as ResolvedImageTypeT;\nexport const VariableAnchorOffsetCollectionType = {kind: 'variableAnchorOffsetCollection'} as VariableAnchorOffsetCollectionTypeT;\n\nexport function array<T extends Type>(itemType: T, N?: number | null): ArrayType<T> {\n return {\n kind: 'array',\n itemType,\n N\n };\n}\n\nexport function typeToString(type: Type): string {\n if (type.kind === 'array') {\n const itemType = typeToString(type.itemType);\n return typeof type.N === 'number' ?\n `array<${itemType}, ${type.N}>` :\n type.itemType.kind === 'value' ? 'array' : `array<${itemType}>`;\n } else {\n return type.kind;\n }\n}\n\nconst valueMemberTypes = [\n NullType,\n NumberType,\n StringType,\n BooleanType,\n ColorType,\n ProjectionDefinitionType,\n FormattedType,\n ObjectType,\n array(ValueType),\n PaddingType,\n ResolvedImageType,\n VariableAnchorOffsetCollectionType\n];\n\n/**\n * Returns null if `t` is a subtype of `expected`; otherwise returns an\n * error message.\n * @private\n */\nexport function checkSubtype(expected: Type, t: Type): string {\n if (t.kind === 'error') {\n // Error is a subtype of every type\n return null;\n } else if (expected.kind === 'array') {\n if (t.kind === 'array' &&\n ((t.N === 0 && t.itemType.kind === 'value') || !checkSubtype(expected.itemType, t.itemType)) &&\n (typeof expected.N !== 'number' || expected.N === t.N)) {\n return null;\n }\n } else if (expected.kind === t.kind) {\n return null;\n } else if (expected.kind === 'value') {\n for (const memberType of valueMemberTypes) {\n if (!checkSubtype(memberType, t)) {\n return null;\n }\n }\n }\n\n return `Expected ${typeToString(expected)} but found ${typeToString(t)} instead.`;\n}\n\nexport function isValidType(provided: Type, allowedTypes: Array<Type>): boolean {\n return allowedTypes.some(t => t.kind === provided.kind);\n}\n\nexport function isValidNativeType(provided: any, allowedTypes: Array<NativeType>): boolean {\n return allowedTypes.some(t => {\n if (t === 'null') {\n return provided === null;\n } else if (t === 'array') {\n return Array.isArray(provided);\n } else if (t === 'object') {\n return provided && !Array.isArray(provided) && typeof provided === 'object';\n } else {\n return t === typeof provided;\n }\n });\n}\n\n/**\n * Verify whether the specified type is of the same type as the specified sample.\n *\n * @param provided Type to verify\n * @param sample Sample type to reference\n * @returns `true` if both objects are of the same type, `false` otherwise\n * @example basic types\n * if (verifyType(outputType, ValueType)) {\n * // type narrowed to:\n * outputType.kind; // 'value'\n * }\n * @example array types\n * if (verifyType(outputType, array(NumberType))) {\n * // type narrowed to:\n * outputType.kind; // 'array'\n * outputType.itemType; // NumberTypeT\n * outputType.itemType.kind; // 'number'\n * }\n */\nexport function verifyType<T extends Type>(provided: Type, sample: T): provided is T {\n if (provided.kind === 'array' && sample.kind === 'array') {\n return provided.itemType.kind === sample.itemType.kind && typeof provided.N === 'number';\n }\n return provided.kind === sample.kind;\n}\n","/**\n * @param r Red component 0..1\n * @param g Green component 0..1\n * @param b Blue component 0..1\n * @param alpha Alpha component 0..1\n */\nexport type RGBColor = [r: number, g: number, b: number, alpha: number];\n\n/**\n * @param h Hue as degrees 0..360\n * @param s Saturation as percentage 0..100\n * @param l Lightness as percentage 0..100\n * @param alpha Alpha component 0..1\n */\nexport type HSLColor = [h: number, s: number, l: number, alpha: number];\n\n/**\n * @param h Hue as degrees 0..360\n * @param c Chroma 0..~230\n * @param l Lightness as percentage 0..100\n * @param alpha Alpha component 0..1\n */\nexport type HCLColor = [h: number, c: number, l: number, alpha: number];\n\n/**\n * @param l Lightness as percentage 0..100\n * @param a A axis value -125..125\n * @param b B axis value -125..125\n * @param alpha Alpha component 0..1\n */\nexport type LABColor = [l: number, a: number, b: number, alpha: number];\n\n// See https://observablehq.com/@mbostock/lab-and-rgb\nconst Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1,\n deg2rad = Math.PI / 180,\n rad2deg = 180 / Math.PI;\n\nfunction constrainAngle(angle: number): number {\n angle = angle % 360;\n if (angle < 0) {\n angle += 360;\n }\n return angle;\n}\n\nexport function rgbToLab([r, g, b, alpha]: RGBColor): LABColor {\n r = rgb2xyz(r);\n g = rgb2xyz(g);\n b = rgb2xyz(b);\n let x, z;\n const y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn);\n if (r === g && g === b) {\n x = z = y;\n } else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n\n const l = 116 * y - 16;\n return [(l < 0) ? 0 : l, 500 * (x - y), 200 * (y - z), alpha];\n}\n\nfunction rgb2xyz(x: number): number {\n return (x <= 0.04045) ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction xyz2lab(t: number): number {\n return (t > t3) ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nexport function labToRgb([l, a, b, alpha]: LABColor): RGBColor {\n let y = (l + 16) / 116,\n x = isNaN(a) ? y : y + a / 500,\n z = isNaN(b) ? y : y - b / 200;\n\n y = Yn * lab2xyz(y);\n x = Xn * lab2xyz(x);\n z = Zn * lab2xyz(z);\n\n return [\n xyz2rgb(3.1338561 * x - 1.6168667 * y - 0.4906146 * z), // D50 -> sRGB\n xyz2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n xyz2rgb(0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n alpha,\n ];\n}\n\nfunction xyz2rgb(x: number): number {\n x = (x <= 0.00304) ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055;\n return (x < 0) ? 0 : (x > 1) ? 1 : x; // clip to 0..1 range\n}\n\nfunction lab2xyz(t: number): number {\n return (t > t1) ? t * t * t : t2 * (t - t0);\n}\n\nexport function rgbToHcl(rgbColor: RGBColor): HCLColor {\n const [l, a, b, alpha] = rgbToLab(rgbColor);\n const c = Math.sqrt(a * a + b * b);\n const h = Math.round(c * 10000) ? constrainAngle(Math.atan2(b, a) * rad2deg) : NaN;\n return [h, c, l, alpha];\n}\n\nexport function hclToRgb([h, c, l, alpha]: HCLColor): RGBColor {\n h = isNaN(h) ? 0 : h * deg2rad;\n return labToRgb([l, Math.cos(h) * c, Math.sin(h) * c, alpha]);\n}\n\n// https://drafts.csswg.org/css-color-4/#hsl-to-rgb\nexport function hslToRgb([h, s, l, alpha]: HSLColor): RGBColor {\n h = constrainAngle(h);\n s /= 100;\n l /= 100;\n\n function f(n) {\n const k = (n + h / 30) % 12;\n const a = s * Math.min(l, 1 - l);\n return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));\n }\n\n return [f(0), f(8), f(4), alpha];\n}\n","import {HSLColor, hslToRgb, RGBColor} from './color_spaces';\n\n/**\n * CSS color parser compliant with CSS Color 4 Specification.\n * Supports: named colors, `transparent` keyword, all rgb hex notations,\n * rgb(), rgba(), hsl() and hsla() functions.\n * Does not round the parsed values to integers from the range 0..255.\n *\n * Syntax:\n *\n * <alpha-value> = <number> | <percentage>\n * <hue> = <number> | <angle>\n *\n * rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) | rgb( <number>{3} [ / <alpha-value> ]? )\n * rgb() = rgb( <percentage>#{3} , <alpha-value>? ) | rgb( <number>#{3} , <alpha-value>? )\n *\n * hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? )\n * hsl() = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )\n *\n * Caveats:\n * - <angle> - <number> with optional `deg` suffix; `grad`, `rad`, `turn` are not supported\n * - `none` keyword is not supported\n * - comments inside rgb()/hsl() are not supported\n * - legacy color syntax rgba() is supported with an identical grammar and behavior to rgb()\n * - legacy color syntax hsla() is supported with an identical grammar and behavior to hsl()\n *\n * @param input CSS color string to parse.\n * @returns Color in sRGB color space, with `red`, `green`, `blue`\n * and `alpha` channels normalized to the range 0..1,\n * or `undefined` if the input is not a valid color string.\n */\nexport function parseCssColor(input: string): RGBColor | undefined {\n input = input.toLowerCase().trim();\n\n if (input === 'transparent') {\n return [0, 0, 0, 0];\n }\n\n // 'white', 'black', 'blue'\n const namedColorsMatch = namedColors[input];\n if (namedColorsMatch) {\n const [r, g, b] = namedColorsMatch;\n return [r / 255, g / 255, b / 255, 1];\n }\n\n // #f0c, #f0cf, #ff00cc, #ff00ccff\n if (input.startsWith('#')) {\n const hexRegexp = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/;\n if (hexRegexp.test(input)) {\n const step = input.length < 6 ? 1 : 2;\n let i = 1;\n return [\n parseHex(input.slice(i, i += step)),\n parseHex(input.slice(i, i += step)),\n parseHex(input.slice(i, i += step)),\n parseHex(input.slice(i, i + step) || 'ff'),\n ];\n }\n }\n\n // rgb(128 0 0), rgb(50% 0% 0%), rgba(255,0,255,0.6), rgb(255 0 255 / 60%), rgb(100% 0% 100% /.6)\n if (input.startsWith('rgb')) {\n const rgbRegExp = /^rgba?\\(\\s*([\\de.+-]+)(%)?(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)(%)?(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)(%)?(?:\\s*([,\\/])\\s*([\\de.+-]+)(%)?)?\\s*\\)$/;\n const rgbMatch = input.match(rgbRegExp);\n if (rgbMatch) {\n const [\n _, // eslint-disable-line @typescript-eslint/no-unused-vars\n r, // <numeric>\n rp, // % (optional)\n f1, // , (optional)\n g, // <numeric>\n gp, // % (optional)\n f2, // , (optional)\n b, // <numeric>\n bp, // % (optional)\n f3, // ,|/ (optional)\n a, // <numeric> (optional)\n ap, // % (optional)\n ] = rgbMatch;\n\n const argFormat = [f1 || ' ', f2 || ' ', f3].join('');\n if (\n argFormat === ' ' ||\n argFormat === ' /' ||\n argFormat === ',,' ||\n argFormat === ',,,'\n ) {\n const valFormat = [rp, gp, bp].join('');\n const maxValue =\n (valFormat === '%%%') ? 100 :\n (valFormat === '') ? 255 : 0;\n if (maxValue) {\n const rgba: RGBColor = [\n clamp(+r / maxValue, 0, 1),\n clamp(+g / maxValue, 0, 1),\n clamp(+b / maxValue, 0, 1),\n a ? parseAlpha(+a, ap) : 1,\n ];\n if (validateNumbers(rgba)) {\n return rgba;\n }\n // invalid numbers\n }\n // values must be all numbers or all percentages\n }\n return; // comma optional syntax requires no commas at all\n }\n }\n\n // hsl(120 50% 80%), hsla(120deg,50%,80%,.9), hsl(12e1 50% 80% / 90%)\n const hslRegExp = /^hsla?\\(\\s*([\\de.+-]+)(?:deg)?(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)%(?:\\s+|\\s*(,)\\s*)([\\de.+-]+)%(?:\\s*([,\\/])\\s*([\\de.+-]+)(%)?)?\\s*\\)$/;\n const hslMatch = input.match(hslRegExp);\n if (hslMatch) {\n const [\n _, // eslint-disable-line @typescript-eslint/no-unused-vars\n h, // <numeric>\n f1, // , (optional)\n s, // <numeric>\n f2, // , (optional)\n l, // <numeric>\n f3, // ,|/ (optional)\n a, // <numeric> (optional)\n ap, // % (optional)\n ] = hslMatch;\n\n const argFormat = [f1 || ' ', f2 || ' ', f3].join('');\n if (\n argFormat === ' ' ||\n argFormat === ' /' ||\n argFormat === ',,' ||\n argFormat === ',,,'\n ) {\n const hsla: HSLColor = [\n +h,\n clamp(+s, 0, 100),\n clamp(+l, 0, 100),\n a ? parseAlpha(+a, ap) : 1,\n ];\n if (validateNumbers(hsla)) {\n return hslToRgb(hsla);\n }\n // invalid numbers\n }\n // comma optional syntax requires no commas at all\n }\n}\n\nfunction parseHex(hex: string): number {\n return parseInt(hex.padEnd(2, hex), 16) / 255;\n}\n\nfunction parseAlpha(a: number, asPercentage: string | undefined): number {\n return clamp(asPercentage ? (a / 100) : a, 0, 1);\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(Math.max(min, n), max);\n}\n\n/**\n * The regular expression for numeric values is not super specific, and it may\n * happen that it will accept a value that is not a valid number. In order to\n * detect and eliminate such values this function exists.\n *\n * @param array Array of uncertain numbers.\n * @returns `true` if the specified array contains only valid numbers, `false` otherwise.\n */\nfunction validateNumbers(array: number[]): boolean {\n return !array.some(Number.isNaN);\n}\n\n/**\n * To generate:\n * - visit {@link https://www.w3.org/TR/css-color-4/#named-colors}\n * - run in the console:\n * @example\n * copy(`{\\n${[...document.querySelector('.named-color-table tbody').children].map((tr) => `${tr.cells[2].textContent.trim()}: [${tr.cells[4].textContent.trim().split(/\\s+/).join(', ')}],`).join('\\n')}\\n}`);\n */\nconst namedColors: Record<string, [number, number, number]> = {\n aliceblue: [240, 248, 255],\n antiquewhite: [250, 235, 215],\n aqua: [0, 255, 255],\n aquamarine: [127, 255, 212],\n azure: [240, 255, 255],\n beige: [245, 245, 220],\n bisque: [255, 228, 196],\n black: [0, 0, 0],\n blanchedalmond: [255, 235, 205],\n blue: [0, 0, 255],\n blueviolet: [138, 43, 226],\n brown: [165, 42, 42],\n burlywood: [222, 184, 135],\n cadetblue: [95, 158, 160],\n chartreuse: [127, 255, 0],\n chocolate: [210, 105, 30],\n coral: [255, 127, 80],\n cornflowerblue: [100, 149, 237],\n cornsilk: [255, 248, 220],\n crimson: [220, 20, 60],\n cyan: [0, 255, 255],\n darkblue: [0, 0, 139],\n darkcyan: [0, 139, 139],\n darkgoldenrod: [184, 134, 11],\n darkgray: [169, 169, 169],\n darkgreen: [0, 100, 0],\n darkgrey: [169, 169, 169],\n darkkhaki: [189, 183, 107],\n darkmagenta: [139, 0, 139],\n darkolivegreen: [85, 107, 47],\n darkorange: [255, 140, 0],\n darkorchid: [153, 50, 204],\n darkred: [139, 0, 0],\n darksalmon: [233, 150, 122],\n darkseagreen: [143, 188, 143],\n darkslateblue: [72, 61, 139],\n darkslategray: [47, 79, 79],\n darkslategrey: [47, 79, 79],\n darkturquoise: [0, 206, 209],\n darkviolet: [148, 0, 211],\n deeppink: [255, 20, 147],\n deepskyblue: [0, 191, 255],\n dimgray: [105, 105, 105],\n dimgrey: [105, 105, 105],\n dodgerblue: [30, 144, 255],\n firebrick: [178, 34, 34],\n floralwhite: [255, 250, 240],\n forestgreen: [34, 139, 34],\n fuchsia: [255, 0, 255],\n gainsboro: [220, 220, 220],\n ghostwhite: [248, 248, 255],\n gold: [255, 215, 0],\n goldenrod: [218, 165, 32],\n gray: [128, 128, 128],\n green: [0, 128, 0],\n greenyellow: [173, 255, 47],\n grey: [128, 128, 128],\n honeydew: [240, 255, 240],\n hotpink: [255, 105, 180],\n indianred: [205, 92, 92],\n indigo: [75, 0, 130],\n ivory: [255, 255, 240],\n khaki: [240, 230, 140],\n lavender: [230, 230, 250],\n lavenderblush: [255, 240, 245],\n lawngreen: [124, 252, 0],\n lemonchiffon: [255, 250, 205],\n lightblue: [173, 216, 230],\n lightcoral: [240, 128, 128],\n lightcyan: [224, 255, 255],\n lightgoldenrodyellow: [250, 250, 210],\n lightgray: [211, 211, 211],\n lightgreen: [144, 238, 144],\n lightgrey: [211, 211, 211],\n lightpink: [255, 182, 193],\n lightsalmon: [255, 160, 122],\n lightseagreen: [32, 178, 170],\n lightskyblue: [135, 206, 250],\n lightslategray: [119, 136, 153],\n lightslategrey: [119, 136, 153],\n lightsteelblue: [176, 196, 222],\n lightyellow: [255, 255, 224],\n lime: [0, 255, 0],\n limegreen: [50, 205, 50],\n linen: [250, 240, 230],\n magenta: [255, 0, 255],\n maroon: [128, 0, 0],\n mediumaquamarine: [102, 205, 170],\n mediumblue: [0, 0, 205],\n mediumorchid: [186, 85, 211],\n mediumpurple: [147, 112, 219],\n mediumseagreen: [60, 179, 113],\n mediumslateblue: [123, 104, 238],\n mediumspringgreen: [0, 250, 154],\n mediumturquoise: [72, 209, 204],\n mediumvioletred: [199, 21, 133],\n midnightblue: [25, 25, 112],\n mintcream: [245, 255, 250],\n mistyrose: [255, 228, 225],\n moccasin: [255, 228, 181],\n navajowhite: [255, 222, 173],\n navy: [0, 0, 128],\n oldlace: [253, 245, 230],\n olive: [128, 128, 0],\n olivedrab: [107, 142, 35],\n orange: [255, 165, 0],\n orangered: [255, 69, 0],\n orchid: [218, 112, 214],\n palegoldenrod: [238, 232, 170],\n palegreen: [152, 251, 152],\n paleturquoise: [175, 238, 238],\n palevioletred: [219, 112, 147],\n papayawhip: [255, 239, 213],\n peachpuff: [255, 218, 185],\n peru: [205, 133, 63],\n pink: [255, 192, 203],\n plum: [221, 160, 221],\n powderblue: [176, 224, 230],\n purple: [128, 0, 128],\n rebeccapurple: [102, 51, 153],\n red: [255, 0, 0],\n rosybrown: [188, 143, 143],\n royalblue: [65, 105, 225],\n saddlebrown: [139, 69, 19],\n salmon: [250, 128, 114],\n sandybrown: [244, 164, 96],\n seagreen: [46, 139, 87],\n seashell: [255, 245, 238],\n sienna: [160, 82, 45],\n silver: [192, 192, 192],\n skyblue: [135, 206, 235],\n slateblue: [106, 90, 205],\n slategray: [112, 128, 144],\n slategrey: [112, 128, 144],\n snow: [255, 250, 250],\n springgreen: [0, 255, 127],\n steelblue: [70, 130, 180],\n tan: [210, 180, 140],\n teal: [0, 128, 128],\n thistle: [216, 191, 216],\n tomato: [255, 99, 71],\n turquoise: [64, 224, 208],\n violet: [238, 130, 238],\n wheat: [245, 222, 179],\n white: [255, 255, 255],\n whitesmoke: [245, 245, 245],\n yellow: [255, 255, 0],\n yellowgreen: [154, 205, 50],\n};\n","export function interpolateNumber(from: number, to: number, t: number): number {\n return from + t * (to - from);\n}\n\nexport function interpolateArray<T extends number[]>(from: T, to: T, t: number): T {\n return from.map((d, i) => {\n return interpolateNumber(d, to[i], t);\n }) as T;\n}\n","import {HCLColor, hclToRgb, LABColor, labToRgb, RGBColor, rgbToHcl, rgbToLab} from './color_spaces';\nimport {parseCssColor} from './parse_css_color';\nimport {interpolateArray, interpolateNumber} from '../../util/interpolate-primitives';\n\nexport type InterpolationColorSpace = 'rgb' | 'hcl' | 'lab';\n\n/**\n * Checks whether the specified color space is one of the supported interpolation color spaces.\n *\n * @param colorSpace Color space key to verify.\n * @returns `true` if the specified color space is one of the supported\n * interpolation color spaces, `false` otherwise\n */\nexport function isSupportedInterpolationColorSpace(colorSpace: string): colorSpace is InterpolationColorSpace {\n return colorSpace === 'rgb' || colorSpace === 'hcl' || colorSpace === 'lab';\n}\n\n/**\n * Color representation used by WebGL.\n * Defined in sRGB color space and pre-blended with alpha.\n * @private\n */\nexport class Color {\n\n readonly r: number;\n readonly g: number;\n readonly b: number;\n readonly a: number;\n\n /**\n * @param r Red component premultiplied by `alpha` 0..1\n * @param g Green component premultiplied by `alpha` 0..1\n * @param b Blue component premultiplied by `alpha` 0..1\n * @param [alpha=1] Alpha component 0..1\n * @param [premultiplied=true] Whether the `r`, `g` and `b` values have already\n * been multiplied by alpha. If `true` nothing happens if `false` then they will\n * be multiplied automatically.\n */\n constructor(r: number, g: number, b: number, alpha = 1, premultiplied = true) {\n this.r = r;\n this.g = g;\n this.b = b;\n this.a = alpha;\n\n if (!premultiplied) {\n this.r *= alpha;\n this.g *= alpha;\n this.b *= alpha;\n\n if (!alpha) {\n // alpha = 0 erases completely rgb channels. This behavior is not desirable\n // if this particular color is later used in color interpolation.\n // Because of that, a reference to original color is saved.\n this.overwriteGetter('rgb', [r, g, b, alpha]);\n }\n }\n }\n\n static black = new Color(0, 0, 0, 1);\n static white = new Color(1, 1, 1, 1);\n static transparent = new Color(0, 0, 0, 0);\n static red = new Color(1, 0, 0, 1);\n\n /**\n * Parses CSS color strings and converts colors to sRGB color space if needed.\n * Officially supported color formats:\n * - keyword, e.g. 'aquamarine' or 'steelblue'\n * - hex (with 3, 4, 6 or 8 digits), e.g. '#f0f' or '#e9bebea9'\n * - rgb and rgba, e.g. 'rgb(0,240,120)' or 'rgba(0%,94%,47%,0.1)' or 'rgb(0 240 120 / .3)'\n * - hsl and hsla, e.g. 'hsl(0,0%,83%)' or 'hsla(0,0%,83%,.5)' or 'hsl(0 0% 83% / 20%)'\n *\n * @param input CSS color string to parse.\n * @returns A `Color` instance, or `undefined` if the input is not a valid color string.\n */\n static parse(input: Color | string | undefined | null): Color | undefined {\n // in zoom-and-property function input could be an instance of Color class\n if (input instanceof Color) {\n return input;\n }\n\n if (typeof input !== 'string') {\n return;\n }\n\n const rgba = parseCssColor(input);\n if (rgba) {\n return new Color(...rgba, false);\n }\n }\n\n /**\n * Used in color interpolation and by 'to-rgba' expression.\n *\n * @returns Gien color, with reversed alpha blending, in sRGB color space.\n */\n get rgb(): RGBColor {\n const {r, g, b, a} = this;\n const f = a || Infinity; // reverse alpha blending factor\n return this.overwriteGetter('rgb', [r / f, g / f, b / f, a]);\n }\n\n /**\n * Used in color interpolation.\n *\n * @returns Gien color, with reversed alpha blending, in HCL color space.\n */\n get hcl(): HCLColor {\n return this.overwriteGetter('hcl', rgbToHcl(this.rgb));\n }\n\n /**\n * Used in color interpolation.\n *\n * @returns Gien color, with reversed alpha blending, in LAB color space.\n */\n get lab(): LABColor {\n return this.overwriteGetter('lab', rgbToLab(this.rgb));\n }\n\n /**\n * Lazy getter pattern. When getter is called for the first time lazy value\n * is calculated and then overwrites getter function in given object instance.\n *\n * @example:\n * const redColor = Color.parse('red');\n * let x = redColor.hcl; // this will invoke `get hcl()`, which will calculate\n * // the value of red in HCL space and invoke this `overwriteGetter` function\n * // which in turn will set a field with a key 'hcl' in the `redColor` object.\n * // In other words it will override `get hcl()` from its `Color` prototype\n * // with its own property: hcl = [calculated red value in hcl].\n * let y = redColor.hcl; // next call will no longer invoke getter but simply\n * // return the previously calculated value\n * x === y; // true - `x` is exactly the same object as `y`\n *\n * @param getterKey Getter key\n * @param lazyValue Lazily