[
  {
    "path": ".changeset/README.md",
    "content": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works\nwith multi-package repos, or single-package repos to help you version and publish your code. You can\nfind the full documentation for it [in our repository](https://github.com/changesets/changesets)\n\nWe have a quick list of common questions to get you started engaging with this project in\n[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)\n"
  },
  {
    "path": ".changeset/config.json",
    "content": "{\n  \"$schema\": \"https://unpkg.com/@changesets/config@3.1.2/schema.json\",\n  \"changelog\": \"@changesets/cli/changelog\",\n  \"commit\": false,\n  \"fixed\": [],\n  \"linked\": [],\n  \"access\": \"restricted\",\n  \"baseBranch\": \"main\",\n  \"updateInternalDependencies\": \"patch\",\n  \"ignore\": []\n}\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @gka\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: gka\npatreon: # Replace with a single Patreon username\nopen_collective: gka\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: gh/gka\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - main\n\njobs:\n  lint-and-test:\n    runs-on: ubuntu-latest\n    env:\n      CI: true\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Set up pnpm\n        uses: pnpm/action-setup@v4\n        with:\n          version: 10.24.0\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: pnpm\n\n      - name: Install dependencies\n        run: pnpm install --frozen-lockfile\n\n      - name: Lint\n        run: pnpm lint\n\n      - name: Test\n        run: pnpm test -- --run\n"
  },
  {
    "path": ".gitignore",
    "content": "readme (Autosaved).md\nm.txt\n.DS_Store\nlicense.coffee\nnode_modules\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "pnpm run lint\npnpm test -- --run\n"
  },
  {
    "path": ".npmignore",
    "content": "# .gitignore equivelant\n.git*\nreadme (Autosaved).md\nm.txt\n.DS_Store\nlicense.coffee\nnode_modules\nyarn.lock\n\n# Dev content\ntest/\ndocs/\n.eslintrc.js\n.travis.yml\nGruntfile.js\nrollup.config.js\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"20\"\nbefore_script: \"npm install\"\nscript: \"npm test\"\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## Changelog\n\n### 3.2.0\n\n- scale.domain now returns the original domain array when called with no arguments\n\n### 3.1.3\n\n- updated dependencies\n\n### 3.1.2\n\n- fixed a bug in Lch interpolation of hue-less colors\n\n### 3.1.1\n\n- fix: allow deep-imports in vite projects\n\n### 3.1.0\n\n- feat: parse `'transparent'` as black with 0% opacity - resolves [#280](https://github.com/gka/chroma.js/issues/280)\n- make it easier to access colorbrewer palette names - resolves [#314](https://github.com/gka/chroma.js/issues/314)\n- docs: explain differences to official colorbrewer scales - resolves [#316](https://github.com/gka/chroma.js/issues/316)\n- fix: correct parsing of modern css colors with percentage alpha - resolves [#297](https://github.com/gka/chroma.js/issues/297)\n- fix: css output for hue-less colors in lch() and oklch() - resolves [#357](https://github.com/gka/chroma.js/issues/357)\n\n### 3.0.0\n\n- 🎉 NEW: Add support for modern CSS color spaces. This means you can now export and parse CSS colors in `lab()`, `lch()`, `oklab()`, `oklch()` space.\n- 🎉 NEW: you can now control the standard white reference point for the CIE Lab and CIE Lch color spaces via `setLabWhitePoint`.\n- Breaking: `color.css()` will no longer return [legacy CSS colors](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb#legacy_syntax_comma-separated_values) like `rgb(255, 255, 0)` but use modern CSS colors like `rgb(255 255 0)` instead.\n- fix: you can now use chroma.js both via the default export as well as named exports in ES6.\n- fix: switch to W3C implementation of OKLab color space\n\n### 2.6.0\n\n- 🎉 NEW: add [`color.shade()`](#color-shade), [`color.tint()`](#color-shade).\n- fix: remove false w3c color cornflower\n\n### 2.5.0\n\n- refactored code base to ES6 modules\n\n### 2.4.0\n\n- add support for Oklab and Oklch color spaces\n\n### 2.3.0\n\n- use binom of degree n in chroma.bezier\n\n### 2.2.0\n\n- use Delta e2000 for chroma.deltaE #269\n\n### 2.0.3\n\n- hsl2rgb will, like other x2rgb conversions now set the default alpha to 1\n\n### 2.0.2\n\n- use a more mangle-safe check for Color class constructor to fix issues with uglifyjs and terser\n\n### 2.0.1\n\n- added `chroma.valid()` for checking if a color can be parsed by chroma.js\n\n### 2.0.0\n\n- chroma.js has been ported from CoffeeScript to ES6! This means you can now import parts of chroma in your projects!\n- changed HCG input space from [0..360,0..100,0..100] to [0..360,0..1,0..1] (to be in line with HSL)\n- added new object unpacking (e.g. `hsl2rgb({h,s,l})`)\n- changed default interpolation to `lrgb` in mix/interpolate and average.\n- if colors can't be parsed correctly, chroma will now throw Errors instead of silently failing with console.errors\n\n### 1.4.1\n\n- chroma.scale() now interprets `null` as NaN and returns the fallback color. Before it had interpreted `null` as `0`\n- added `scale.nodata()` to allow customizing the previously hard-coded fallback (aka \"no data\") color #cccccc\n\n### 1.4.0\n\n- color.hex() now automatically sets the mode to 'rgba' if the colors alpha channel is < 1. so `chroma('rgba(255,0,0,.5)').hex()` will now return `\"#ff000080\"` instead of `\"#ff0000\"`. if this is not what you want, you must explicitly set the mode to `rgb` using `.hex(\"rgb\")`.\n- bugfix in chroma.average in LRGB mode ([#187](https://github.com/gka/chroma.js/issues/187))\n- chroma.scale now also works with just one color ([#180](https://github.com/gka/chroma.js/issues/180))\n\n### 1.3.5\n\n- added LRGB interpolation\n\n### 1.3.4\n\n- passing _null_ as mode in scale.colors will return chroma objects\n\n### 1.3.3\n\n- added [color.clipped](https://gka.github.io/chroma.js/#color-clipped)\n- added [chroma.distance](https://gka.github.io/chroma.js/#chroma-distance)\n- added [chroma.deltaE](https://gka.github.io/chroma.js/#chroma-deltae)\n- [color.set](https://gka.github.io/chroma.js/#color-set) now returns a new chroma instance\n- chroma.scale now allows [disabling of internal cache](https://gka.github.io/chroma.js/#scale-cache)\n- [chroma.average](https://gka.github.io/chroma.js/#chroma-average) now works with any color mode\n- added unit tests for color conversions\n- use hex colors as default string representation\n- RGB channels are now stored as floats internally for higher precision\n- bugfix with cubehelix and constant lightness\n- bugfix in chroma.limits quantiles\n- bugfix when running scale.colors(1)\n- bugfix in hsi2rgb color conversion\n\n### 1.2.2\n\n- scale.colors() now returns the original colors instead of just min/max range\n\n### 1.2.0\n\n- added chroma.average for averaging colors\n\n### 1.1.0\n\n- refactored chroma.scale\n- changed behaviour of scale.domain\n- added scale.classes\n- added scale.padding\n\n### 1.0.2\n\n- standardized alpha channel construction\n- chroma.bezier automatically returns chroma.scale\n\n### 1.0.1\n\n- added simple color output to chroma.scale().colors()\n\n### 1.0.0\n\n- numeric interpolation does what it should\n- refactored and modularized code base\n- changed argument order of Color::interpolate\n"
  },
  {
    "path": "LICENSE",
    "content": "chroma.js - JavaScript library for color conversions\n\nCopyright (c) 2011-2025, Gregor Aisch\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. The name Gregor Aisch may not be used to endorse or promote products\n   derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\nOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-------------------------------------------------------\n\nchroma.js includes colors from colorbrewer2.org, which are released under\nthe following license:\n\nCopyright (c) 2002 Cynthia Brewer, Mark Harrower,\nand The Pennsylvania State University.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing,\nsoftware distributed under the License is distributed on an\n\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\neither express or implied. See the License for the specific\nlanguage governing permissions and limitations under the License.\n\n------------------------------------------------------\n\nNamed colors are taken from X11 Color Names.\nhttp://www.w3.org/TR/css3-color/#svg-color\n\n@preserve\n"
  },
  {
    "path": "dist/chroma-light.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.chroma = factory());\n})(this, (function () { 'use strict';\n\n    var min$1 = Math.min;\n    var max$1 = Math.max;\n\n    function limit (x, low, high) {\n        if ( high === void 0 ) high = 1;\n\n        return min$1(max$1(low, x), high);\n    }\n\n    function clip_rgb (rgb) {\n        rgb._clipped = false;\n        rgb._unclipped = rgb.slice(0);\n        for (var i = 0; i <= 3; i++) {\n            if (i < 3) {\n                if (rgb[i] < 0 || rgb[i] > 255) { rgb._clipped = true; }\n                rgb[i] = limit(rgb[i], 0, 255);\n            } else if (i === 3) {\n                rgb[i] = limit(rgb[i], 0, 1);\n            }\n        }\n        return rgb;\n    }\n\n    // ported from jQuery's $.type\n    var classToType = {};\n    for (var i = 0, list = [\n        'Boolean',\n        'Number',\n        'String',\n        'Function',\n        'Array',\n        'Date',\n        'RegExp',\n        'Undefined',\n        'Null'\n    ]; i < list.length; i += 1) {\n        var name = list[i];\n\n        classToType[(\"[object \" + name + \"]\")] = name.toLowerCase();\n    }\n    function type (obj) {\n        return classToType[Object.prototype.toString.call(obj)] || 'object';\n    }\n\n    function unpack (args, keyOrder) {\n        if ( keyOrder === void 0 ) keyOrder = null;\n\n        // if called with more than 3 arguments, we return the arguments\n        if (args.length >= 3) { return Array.prototype.slice.call(args); }\n        // with less than 3 args we check if first arg is object\n        // and use the keyOrder string to extract and sort properties\n        if (type(args[0]) == 'object' && keyOrder) {\n            return keyOrder\n                .split('')\n                .filter(function (k) { return args[0][k] !== undefined; })\n                .map(function (k) { return args[0][k]; });\n        }\n        // otherwise we just return the first argument\n        // (which we suppose is an array of args)\n        return args[0].slice(0);\n    }\n\n    function last (args) {\n        if (args.length < 2) { return null; }\n        var l = args.length - 1;\n        if (type(args[l]) == 'string') { return args[l].toLowerCase(); }\n        return null;\n    }\n\n    var PI = Math.PI;\n    var min = Math.min;\n    var max = Math.max;\n\n    var rnd2 = function (a) { return Math.round(a * 100) / 100; };\n    var rnd3 = function (a) { return Math.round(a * 100) / 100; };\n    var DEG2RAD = PI / 180;\n    var RAD2DEG = 180 / PI;\n\n    var input = {\n        format: {},\n        autodetect: []\n    };\n\n    var Color = function Color() {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var me = this;\n        if (\n            type(args[0]) === 'object' &&\n            args[0].constructor &&\n            args[0].constructor === this.constructor\n        ) {\n            // the argument is already a Color instance\n            return args[0];\n        }\n        // last argument could be the mode\n        var mode = last(args);\n        var autodetect = false;\n        if (!mode) {\n            autodetect = true;\n\n            if (!input.sorted) {\n                input.autodetect = input.autodetect.sort(function (a, b) { return b.p - a.p; });\n                input.sorted = true;\n            }\n\n            // auto-detect format\n            for (var i = 0, list = input.autodetect; i < list.length; i += 1) {\n                var chk = list[i];\n\n                mode = chk.test.apply(chk, args);\n                if (mode) { break; }\n            }\n        }\n        if (input.format[mode]) {\n            var rgb = input.format[mode].apply(\n                null,\n                autodetect ? args : args.slice(0, -1)\n            );\n            me._rgb = clip_rgb(rgb);\n        } else {\n            throw new Error('unknown format: ' + args);\n        }\n        // add alpha channel\n        if (me._rgb.length === 3) { me._rgb.push(1); }\n    };\n    Color.prototype.toString = function toString () {\n        if (type(this.hex) == 'function') { return this.hex(); }\n        return (\"[\" + (this._rgb.join(',')) + \"]\");\n    };\n\n    // this gets updated automatically\n    var version = '3.2.0';\n\n    var chroma = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n    };\n\n    chroma.version = version;\n\n    /*\n     * supported arguments:\n     * - hsl2css(h,s,l)\n     * - hsl2css(h,s,l,a)\n     * - hsl2css([h,s,l], mode)\n     * - hsl2css([h,s,l,a], mode)\n     * - hsl2css({h,s,l,a}, mode)\n     */\n    var hsl2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var hsla = unpack(args, 'hsla');\n        var mode = last(args) || 'lsa';\n        hsla[0] = rnd2(hsla[0] || 0) + 'deg';\n        hsla[1] = rnd2(hsla[1] * 100) + '%';\n        hsla[2] = rnd2(hsla[2] * 100) + '%';\n        if (mode === 'hsla' || (hsla.length > 3 && hsla[3] < 1)) {\n            hsla[3] = '/ ' + (hsla.length > 3 ? hsla[3] : 1);\n            mode = 'hsla';\n        } else {\n            hsla.length = 3;\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (hsla.join(' ')) + \")\");\n    };\n\n    /*\n     * supported arguments:\n     * - rgb2hsl(r,g,b)\n     * - rgb2hsl(r,g,b,a)\n     * - rgb2hsl([r,g,b])\n     * - rgb2hsl([r,g,b,a])\n     * - rgb2hsl({r,g,b,a})\n     */\n    var rgb2hsl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'rgba');\n        var r = args[0];\n        var g = args[1];\n        var b = args[2];\n\n        r /= 255;\n        g /= 255;\n        b /= 255;\n\n        var minRgb = min(r, g, b);\n        var maxRgb = max(r, g, b);\n\n        var l = (maxRgb + minRgb) / 2;\n        var s, h;\n\n        if (maxRgb === minRgb) {\n            s = 0;\n            h = Number.NaN;\n        } else {\n            s =\n                l < 0.5\n                    ? (maxRgb - minRgb) / (maxRgb + minRgb)\n                    : (maxRgb - minRgb) / (2 - maxRgb - minRgb);\n        }\n\n        if (r == maxRgb) { h = (g - b) / (maxRgb - minRgb); }\n        else if (g == maxRgb) { h = 2 + (b - r) / (maxRgb - minRgb); }\n        else if (b == maxRgb) { h = 4 + (r - g) / (maxRgb - minRgb); }\n\n        h *= 60;\n        if (h < 0) { h += 360; }\n        if (args.length > 3 && args[3] !== undefined) { return [h, s, l, args[3]]; }\n        return [h, s, l];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        var mode = last(args) || 'lab';\n        laba[0] = rnd2(laba[0]) + '%';\n        laba[1] = rnd2(laba[1]);\n        laba[2] = rnd2(laba[2]);\n        if (mode === 'laba' || (laba.length > 3 && laba[3] < 1)) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"lab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var labConstants = {\n        // Corresponds roughly to RGB brighter/darker\n        Kn: 18,\n\n        // D65 standard referent\n        labWhitePoint: 'd65',\n        Xn: 0.95047,\n        Yn: 1,\n        Zn: 1.08883,\n\n        kE: 216.0 / 24389.0,\n        kKE: 8.0,\n        kK: 24389.0 / 27.0,\n\n        RefWhiteRGB: {\n            // sRGB\n            X: 0.95047,\n            Y: 1,\n            Z: 1.08883\n        },\n\n        MtxRGB2XYZ: {\n            m00: 0.4124564390896922,\n            m01: 0.21267285140562253,\n            m02: 0.0193338955823293,\n            m10: 0.357576077643909,\n            m11: 0.715152155287818,\n            m12: 0.11919202588130297,\n            m20: 0.18043748326639894,\n            m21: 0.07217499330655958,\n            m22: 0.9503040785363679\n        },\n\n        MtxXYZ2RGB: {\n            m00: 3.2404541621141045,\n            m01: -0.9692660305051868,\n            m02: 0.055643430959114726,\n            m10: -1.5371385127977166,\n            m11: 1.8760108454466942,\n            m12: -0.2040259135167538,\n            m20: -0.498531409556016,\n            m21: 0.041556017530349834,\n            m22: 1.0572251882231791\n        },\n\n        // used in rgb2xyz\n        As: 0.9414285350000001,\n        Bs: 1.040417467,\n        Cs: 1.089532651,\n\n        MtxAdaptMa: {\n            m00: 0.8951,\n            m01: -0.7502,\n            m02: 0.0389,\n            m10: 0.2664,\n            m11: 1.7135,\n            m12: -0.0685,\n            m20: -0.1614,\n            m21: 0.0367,\n            m22: 1.0296\n        },\n\n        MtxAdaptMaI: {\n            m00: 0.9869929054667123,\n            m01: 0.43230526972339456,\n            m02: -0.008528664575177328,\n            m10: -0.14705425642099013,\n            m11: 0.5183602715367776,\n            m12: 0.04004282165408487,\n            m20: 0.15996265166373125,\n            m21: 0.0492912282128556,\n            m22: 0.9684866957875502\n        }\n    };\n\n    // taken from https://de.mathworks.com/help/images/ref/whitepoint.html\n    var ILLUMINANTS = new Map([\n        // ASTM E308-01\n        ['a', [1.0985, 0.35585]],\n        // Wyszecki & Stiles, p. 769\n        ['b', [1.0985, 0.35585]],\n        // C ASTM E308-01\n        ['c', [0.98074, 1.18232]],\n        // D50 (ASTM E308-01)\n        ['d50', [0.96422, 0.82521]],\n        // D55 (ASTM E308-01)\n        ['d55', [0.95682, 0.92149]],\n        // D65 (ASTM E308-01)\n        ['d65', [0.95047, 1.08883]],\n        // E (ASTM E308-01)\n        ['e', [1, 1, 1]],\n        // F2 (ASTM E308-01)\n        ['f2', [0.99186, 0.67393]],\n        // F7 (ASTM E308-01)\n        ['f7', [0.95041, 1.08747]],\n        // F11 (ASTM E308-01)\n        ['f11', [1.00962, 0.6435]],\n        ['icc', [0.96422, 0.82521]]\n    ]);\n\n    function setLabWhitePoint(name) {\n        var ill = ILLUMINANTS.get(String(name).toLowerCase());\n        if (!ill) {\n            throw new Error('unknown Lab illuminant ' + name);\n        }\n        labConstants.labWhitePoint = name;\n        labConstants.Xn = ill[0];\n        labConstants.Zn = ill[1];\n    }\n\n    function getLabWhitePoint() {\n        return labConstants.labWhitePoint;\n    }\n\n    var rgb2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2xyz(r, g, b);\n        var x = ref$1[0];\n        var y = ref$1[1];\n        var z = ref$1[2];\n        var ref$2 = xyz2lab(x, y, z);\n        var L = ref$2[0];\n        var a = ref$2[1];\n        var b_ = ref$2[2];\n        return [L, a, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    function xyz2lab(x, y, z) {\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var xr = x / Xn;\n        var yr = y / Yn;\n        var zr = z / Zn;\n\n        var fx = xr > kE ? Math.pow(xr, 1.0 / 3.0) : (kK * xr + 16.0) / 116.0;\n        var fy = yr > kE ? Math.pow(yr, 1.0 / 3.0) : (kK * yr + 16.0) / 116.0;\n        var fz = zr > kE ? Math.pow(zr, 1.0 / 3.0) : (kK * zr + 16.0) / 116.0;\n\n        return [116.0 * fy - 16.0, 500.0 * (fx - fy), 200.0 * (fy - fz)];\n    }\n\n    function gammaAdjustSRGB(companded) {\n        var sign = Math.sign(companded);\n        companded = Math.abs(companded);\n        var linear =\n            companded <= 0.04045\n                ? companded / 12.92\n                : Math.pow((companded + 0.055) / 1.055, 2.4);\n        return linear * sign;\n    }\n\n    var rgb2xyz = function (r, g, b) {\n        // normalize and gamma adjust\n        r = gammaAdjustSRGB(r / 255);\n        g = gammaAdjustSRGB(g / 255);\n        b = gammaAdjustSRGB(b / 255);\n\n        var MtxRGB2XYZ = labConstants.MtxRGB2XYZ;\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var As = labConstants.As;\n        var Bs = labConstants.Bs;\n        var Cs = labConstants.Cs;\n\n        var x = r * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;\n        var y = r * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;\n        var z = r * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;\n\n        var Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20;\n        var Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21;\n        var Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22;\n\n        X *= Ad / As;\n        Y *= Bd / Bs;\n        Z *= Cd / Cs;\n\n        x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;\n        y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;\n        z = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;\n\n        return [x, y, z];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        var mode = last(args) || 'lab';\n        lcha[0] = rnd2(lcha[0]) + '%';\n        lcha[1] = rnd2(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (mode === 'lcha' || (lcha.length > 3 && lcha[3] < 1)) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"lch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var sqrt$1 = Math.sqrt;\n    var atan2 = Math.atan2;\n    var round$4 = Math.round;\n\n    var lab2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'lab');\n        var l = ref[0];\n        var a = ref[1];\n        var b = ref[2];\n        var c = sqrt$1(a * a + b * b);\n        var h = (atan2(b, a) * RAD2DEG + 360) % 360;\n        if (round$4(c * 10000) === 0) { h = Number.NaN; }\n        return [l, c, h];\n    };\n\n    var rgb2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2lab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/multiply-matrices.js\n    function multiplyMatrices(A, B) {\n        var m = A.length;\n\n        if (!Array.isArray(A[0])) {\n            // A is vector, convert to [[a, b, c, ...]]\n            A = [A];\n        }\n\n        if (!Array.isArray(B[0])) {\n            // B is vector, convert to [[a], [b], [c], ...]]\n            B = B.map(function (x) { return [x]; });\n        }\n\n        var p = B[0].length;\n        var B_cols = B[0].map(function (_, i) { return B.map(function (x) { return x[i]; }); }); // transpose B\n        var product = A.map(function (row) { return B_cols.map(function (col) {\n                if (!Array.isArray(row)) {\n                    return col.reduce(function (a, c) { return a + c * row; }, 0);\n                }\n\n                return row.reduce(function (a, c, i) { return a + c * (col[i] || 0); }, 0);\n            }); }\n        );\n\n        if (m === 1) {\n            product = product[0]; // Avoid [[a, b, c, ...]]\n        }\n\n        if (p === 1) {\n            return product.map(function (x) { return x[0]; }); // Avoid [[a], [b], [c], ...]]\n        }\n\n        return product;\n    }\n\n    var rgb2oklab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var xyz = rgb2xyz(r, g, b);\n        var oklab = XYZ_to_OKLab(xyz);\n        return oklab.concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function XYZ_to_OKLab(XYZ) {\n        // Given XYZ relative to D65, convert to OKLab\n        var XYZtoLMS = [\n            [0.819022437996703, 0.3619062600528904, -0.1288737815209879],\n            [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],\n            [0.0481771893596242, 0.2642395317527308, 0.6335478284694309]\n        ];\n        var LMStoOKLab = [\n            [0.210454268309314, 0.7936177747023054, -0.0040720430116193],\n            [1.9779985324311684, -2.42859224204858, 0.450593709617411],\n            [0.0259040424655478, 0.7827717124575296, -0.8086757549230774]\n        ];\n\n        var LMS = multiplyMatrices(XYZtoLMS, XYZ);\n        // JavaScript Math.cbrt returns a sign-matched cube root\n        // beware if porting to other languages\n        // especially if tempted to use a general power function\n        return multiplyMatrices(\n            LMStoOKLab,\n            LMS.map(function (c) { return Math.cbrt(c); })\n        );\n        // L in range [0,1]. For use in CSS, multiply by 100 and add a percent\n    }\n\n    var oklab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        laba[0] = rnd2(laba[0] * 100) + '%';\n        laba[1] = rnd3(laba[1]);\n        laba[2] = rnd3(laba[2]);\n        if (laba.length > 3 && laba[3] < 1) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"oklab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var rgb2oklch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2oklab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var oklch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        lcha[0] = rnd2(lcha[0] * 100) + '%';\n        lcha[1] = rnd3(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (lcha.length > 3 && lcha[3] < 1) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"oklch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var round$3 = Math.round;\n\n    /*\n     * supported arguments:\n     * - rgb2css(r,g,b)\n     * - rgb2css(r,g,b,a)\n     * - rgb2css([r,g,b], mode)\n     * - rgb2css([r,g,b,a], mode)\n     * - rgb2css({r,g,b,a}, mode)\n     */\n    var rgb2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        var mode = last(args) || 'rgb';\n        if (mode.substr(0, 3) === 'hsl') {\n            return hsl2css(rgb2hsl(rgba), mode);\n        }\n        if (mode.substr(0, 3) === 'lab') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor = lab2css(rgb2lab(rgba), mode);\n            setLabWhitePoint(prevWhitePoint);\n            return cssColor;\n        }\n        if (mode.substr(0, 3) === 'lch') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor$1 = lch2css(rgb2lch(rgba), mode);\n            setLabWhitePoint(prevWhitePoint$1);\n            return cssColor$1;\n        }\n        if (mode.substr(0, 5) === 'oklab') {\n            return oklab2css(rgb2oklab(rgba));\n        }\n        if (mode.substr(0, 5) === 'oklch') {\n            return oklch2css(rgb2oklch(rgba));\n        }\n        rgba[0] = round$3(rgba[0]);\n        rgba[1] = round$3(rgba[1]);\n        rgba[2] = round$3(rgba[2]);\n        if (mode === 'rgba' || (rgba.length > 3 && rgba[3] < 1)) {\n            rgba[3] = '/ ' + (rgba.length > 3 ? rgba[3] : 1);\n            mode = 'rgba';\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (rgba.slice(0, mode === 'rgb' ? 3 : 4).join(' ')) + \")\");\n    };\n\n    var hsl2rgb = function () {\n        var assign;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hsl');\n        var h = args[0];\n        var s = args[1];\n        var l = args[2];\n        var r, g, b;\n        if (s === 0) {\n            r = g = b = l * 255;\n        } else {\n            var t3 = [0, 0, 0];\n            var c = [0, 0, 0];\n            var t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;\n            var t1 = 2 * l - t2;\n            var h_ = h / 360;\n            t3[0] = h_ + 1 / 3;\n            t3[1] = h_;\n            t3[2] = h_ - 1 / 3;\n            for (var i = 0; i < 3; i++) {\n                if (t3[i] < 0) { t3[i] += 1; }\n                if (t3[i] > 1) { t3[i] -= 1; }\n                if (6 * t3[i] < 1) { c[i] = t1 + (t2 - t1) * 6 * t3[i]; }\n                else if (2 * t3[i] < 1) { c[i] = t2; }\n                else if (3 * t3[i] < 2) { c[i] = t1 + (t2 - t1) * (2 / 3 - t3[i]) * 6; }\n                else { c[i] = t1; }\n            }\n            (assign = [c[0] * 255, c[1] * 255, c[2] * 255], r = assign[0], g = assign[1], b = assign[2]);\n        }\n        if (args.length > 3) {\n            // keep alpha channel\n            return [r, g, b, args[3]];\n        }\n        return [r, g, b, 1];\n    };\n\n    /*\n     * L* [0..100]\n     * a [-100..100]\n     * b [-100..100]\n     */\n    var lab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var ref = lab2xyz(L, a, b);\n        var x = ref[0];\n        var y = ref[1];\n        var z = ref[2];\n        var ref$1 = xyz2rgb(x, y, z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_, args.length > 3 ? args[3] : 1];\n    };\n\n    var lab2xyz = function (L, a, b) {\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var kKE = labConstants.kKE;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var fy = (L + 16.0) / 116.0;\n        var fx = 0.002 * a + fy;\n        var fz = fy - 0.005 * b;\n\n        var fx3 = fx * fx * fx;\n        var fz3 = fz * fz * fz;\n\n        var xr = fx3 > kE ? fx3 : (116.0 * fx - 16.0) / kK;\n        var yr = L > kKE ? Math.pow((L + 16.0) / 116.0, 3.0) : L / kK;\n        var zr = fz3 > kE ? fz3 : (116.0 * fz - 16.0) / kK;\n\n        var x = xr * Xn;\n        var y = yr * Yn;\n        var z = zr * Zn;\n\n        return [x, y, z];\n    };\n\n    var compand = function (linear) {\n        /* sRGB */\n        var sign = Math.sign(linear);\n        linear = Math.abs(linear);\n        return (\n            (linear <= 0.0031308\n                ? linear * 12.92\n                : 1.055 * Math.pow(linear, 1.0 / 2.4) - 0.055) * sign\n        );\n    };\n\n    var xyz2rgb = function (x, y, z) {\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var MtxXYZ2RGB = labConstants.MtxXYZ2RGB;\n        var RefWhiteRGB = labConstants.RefWhiteRGB;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cs = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var Ad =\n            RefWhiteRGB.X * MtxAdaptMa.m00 +\n            RefWhiteRGB.Y * MtxAdaptMa.m10 +\n            RefWhiteRGB.Z * MtxAdaptMa.m20;\n        var Bd =\n            RefWhiteRGB.X * MtxAdaptMa.m01 +\n            RefWhiteRGB.Y * MtxAdaptMa.m11 +\n            RefWhiteRGB.Z * MtxAdaptMa.m21;\n        var Cd =\n            RefWhiteRGB.X * MtxAdaptMa.m02 +\n            RefWhiteRGB.Y * MtxAdaptMa.m12 +\n            RefWhiteRGB.Z * MtxAdaptMa.m22;\n\n        var X1 =\n            (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20) *\n            (Ad / As);\n        var Y1 =\n            (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21) *\n            (Bd / Bs);\n        var Z1 =\n            (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22) *\n            (Cd / Cs);\n\n        var X2 =\n            X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;\n        var Y2 =\n            X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;\n        var Z2 =\n            X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;\n\n        var r = compand(\n            X2 * MtxXYZ2RGB.m00 + Y2 * MtxXYZ2RGB.m10 + Z2 * MtxXYZ2RGB.m20\n        );\n        var g = compand(\n            X2 * MtxXYZ2RGB.m01 + Y2 * MtxXYZ2RGB.m11 + Z2 * MtxXYZ2RGB.m21\n        );\n        var b = compand(\n            X2 * MtxXYZ2RGB.m02 + Y2 * MtxXYZ2RGB.m12 + Z2 * MtxXYZ2RGB.m22\n        );\n\n        return [r * 255, g * 255, b * 255];\n    };\n\n    var sin = Math.sin;\n    var cos = Math.cos;\n\n    var lch2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n        These formulas were invented by David Dalrymple to obtain maximum contrast without going\n        out of gamut if the parameters are in the range 0-1.\n\n        A saturation multiplier was added by Gregor Aisch\n        */\n        var ref = unpack(args, 'lch');\n        var l = ref[0];\n        var c = ref[1];\n        var h = ref[2];\n        if (isNaN(h)) { h = 0; }\n        h = h * DEG2RAD;\n        return [l, cos(h) * c, sin(h) * c];\n    };\n\n    var lch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = lab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var oklab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var rest = args.slice(3);\n        var ref = OKLab_to_XYZ([L, a, b]);\n        var X = ref[0];\n        var Y = ref[1];\n        var Z = ref[2];\n        var ref$1 = xyz2rgb(X, Y, Z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function OKLab_to_XYZ(OKLab) {\n        // Given OKLab, convert to XYZ relative to D65\n        var LMStoXYZ = [\n            [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],\n            [-0.0405757452148008, 1.112286803280317, -0.0717110580655164],\n            [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]\n        ];\n        var OKLabtoLMS = [\n            [1.0, 0.3963377773761749, 0.2158037573099136],\n            [1.0, -0.1055613458156586, -0.0638541728258133],\n            [1.0, -0.0894841775298119, -1.2914855480194092]\n        ];\n\n        var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab);\n        return multiplyMatrices(\n            LMStoXYZ,\n            LMSnl.map(function (c) { return Math.pow( c, 3 ); })\n        );\n    }\n\n    var oklch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var rest = args.slice(3);\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = oklab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var INT_OR_PCT = /((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source;\n    var FLOAT_OR_PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source;\n    var PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source;\n    var RE_S = /\\s*/.source;\n    var SEP = /\\s+/.source;\n    var COMMA = /\\s*,\\s*/.source;\n    var ANLGE = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source;\n    var ALPHA = /\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source;\n\n    // e.g. rgb(250 20 0), rgb(100% 50% 20%), rgb(100% 50% 20% / 0.5)\n    var RE_RGB = new RegExp(\n        '^rgba?\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_RGB_LEGACY = new RegExp(\n        '^rgb\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n    var RE_RGBA_LEGACY = new RegExp(\n        '^rgba\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT, FLOAT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n\n    var RE_HSL = new RegExp(\n        '^hsla?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(SEP) + ALPHA + '\\\\)$'\n    );\n    var RE_HSL_LEGACY = new RegExp(\n        '^hsl?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(COMMA) + RE_S + '\\\\)$'\n    );\n    var RE_HSLA_LEGACY =\n        /^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/;\n\n    var RE_LAB = new RegExp(\n        '^lab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_LCH = new RegExp(\n        '^lch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLAB = new RegExp(\n        '^oklab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLCH = new RegExp(\n        '^oklch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n\n    var round$2 = Math.round;\n\n    var roundRGB = function (rgb) {\n        return rgb.map(function (v, i) { return (i <= 2 ? limit(round$2(v), 0, 255) : v); });\n    };\n\n    var percentToAbsolute = function (pct, min, max, signed) {\n        if ( min === void 0 ) min = 0;\n        if ( max === void 0 ) max = 100;\n        if ( signed === void 0 ) signed = false;\n\n        if (typeof pct === 'string' && pct.endsWith('%')) {\n            pct = parseFloat(pct.substring(0, pct.length - 1)) / 100;\n            if (signed) {\n                // signed percentages are in the range -100% to 100%\n                pct = min + (pct + 1) * 0.5 * (max - min);\n            } else {\n                pct = min + pct * (max - min);\n            }\n        }\n        return +pct;\n    };\n\n    var noneToValue = function (v, noneValue) {\n        return v === 'none' ? noneValue : v;\n    };\n\n    var css2rgb = function (css) {\n        css = css.toLowerCase().trim();\n\n        if (css === 'transparent') {\n            return [0, 0, 0, 0];\n        }\n\n        var m;\n\n        if (input.format.named) {\n            try {\n                return input.format.named(css);\n                // eslint-disable-next-line\n            } catch (e) {}\n        }\n\n        // rgb(250 20 0) or rgb(250,20,0)\n        if ((m = css.match(RE_RGB)) || (m = css.match(RE_RGB_LEGACY))) {\n            var rgb = m.slice(1, 4);\n            for (var i = 0; i < 3; i++) {\n                rgb[i] = +percentToAbsolute(noneToValue(rgb[i], 0), 0, 255);\n            }\n            rgb = roundRGB(rgb);\n            var alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb[3] = alpha; // default alpha\n            return rgb;\n        }\n\n        // rgba(250,20,0,0.4)\n        if ((m = css.match(RE_RGBA_LEGACY))) {\n            var rgb$1 = m.slice(1, 5);\n            for (var i$1 = 0; i$1 < 4; i$1++) {\n                rgb$1[i$1] = +percentToAbsolute(rgb$1[i$1], 0, 255);\n            }\n            return rgb$1;\n        }\n\n        // hsl(0,100%,50%)\n        if ((m = css.match(RE_HSL)) || (m = css.match(RE_HSL_LEGACY))) {\n            var hsl = m.slice(1, 4);\n            hsl[0] = +noneToValue(hsl[0].replace('deg', ''), 0);\n            hsl[1] = +percentToAbsolute(noneToValue(hsl[1], 0), 0, 100) * 0.01;\n            hsl[2] = +percentToAbsolute(noneToValue(hsl[2], 0), 0, 100) * 0.01;\n            var rgb$2 = roundRGB(hsl2rgb(hsl));\n            var alpha$1 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$2[3] = alpha$1;\n            return rgb$2;\n        }\n\n        // hsla(0,100%,50%,0.5)\n        if ((m = css.match(RE_HSLA_LEGACY))) {\n            var hsl$1 = m.slice(1, 4);\n            hsl$1[1] *= 0.01;\n            hsl$1[2] *= 0.01;\n            var rgb$3 = hsl2rgb(hsl$1);\n            for (var i$2 = 0; i$2 < 3; i$2++) {\n                rgb$3[i$2] = round$2(rgb$3[i$2]);\n            }\n            rgb$3[3] = +m[4]; // default alpha = 1\n            return rgb$3;\n        }\n\n        if ((m = css.match(RE_LAB))) {\n            var lab = m.slice(1, 4);\n            lab[0] = percentToAbsolute(noneToValue(lab[0], 0), 0, 100);\n            lab[1] = percentToAbsolute(noneToValue(lab[1], 0), -125, 125, true);\n            lab[2] = percentToAbsolute(noneToValue(lab[2], 0), -125, 125, true);\n            // convert to D50 Lab whitepoint\n            var wp = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$4 = roundRGB(lab2rgb(lab));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp);\n            var alpha$2 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$4[3] = alpha$2;\n            return rgb$4;\n        }\n\n        if ((m = css.match(RE_LCH))) {\n            var lch = m.slice(1, 4);\n            lch[0] = percentToAbsolute(lch[0], 0, 100);\n            lch[1] = percentToAbsolute(noneToValue(lch[1], 0), 0, 150, false);\n            lch[2] = +noneToValue(lch[2].replace('deg', ''), 0);\n            // convert to D50 Lab whitepoint\n            var wp$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$5 = roundRGB(lch2rgb(lch));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp$1);\n            var alpha$3 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$5[3] = alpha$3;\n            return rgb$5;\n        }\n\n        if ((m = css.match(RE_OKLAB))) {\n            var oklab = m.slice(1, 4);\n            oklab[0] = percentToAbsolute(noneToValue(oklab[0], 0), 0, 1);\n            oklab[1] = percentToAbsolute(noneToValue(oklab[1], 0), -0.4, 0.4, true);\n            oklab[2] = percentToAbsolute(noneToValue(oklab[2], 0), -0.4, 0.4, true);\n            var rgb$6 = roundRGB(oklab2rgb(oklab));\n            var alpha$4 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$6[3] = alpha$4;\n            return rgb$6;\n        }\n\n        if ((m = css.match(RE_OKLCH))) {\n            var oklch = m.slice(1, 4);\n            oklch[0] = percentToAbsolute(noneToValue(oklch[0], 0), 0, 1);\n            oklch[1] = percentToAbsolute(noneToValue(oklch[1], 0), 0, 0.4, false);\n            oklch[2] = +noneToValue(oklch[2].replace('deg', ''), 0);\n            var rgb$7 = roundRGB(oklch2rgb(oklch));\n            var alpha$5 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$7[3] = alpha$5;\n            return rgb$7;\n        }\n    };\n\n    css2rgb.test = function (s) {\n        return (\n            // modern\n            RE_RGB.test(s) ||\n            RE_HSL.test(s) ||\n            RE_LAB.test(s) ||\n            RE_LCH.test(s) ||\n            RE_OKLAB.test(s) ||\n            RE_OKLCH.test(s) ||\n            // legacy\n            RE_RGB_LEGACY.test(s) ||\n            RE_RGBA_LEGACY.test(s) ||\n            RE_HSL_LEGACY.test(s) ||\n            RE_HSLA_LEGACY.test(s) ||\n            s === 'transparent'\n        );\n    };\n\n    Color.prototype.css = function (mode) {\n        return rgb2css(this._rgb, mode);\n    };\n\n    var css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['css']) ));\n    };\n    chroma.css = css;\n\n    input.format.css = css2rgb;\n\n    input.autodetect.push({\n        p: 5,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (!rest.length && type(h) === 'string' && css2rgb.test(h)) {\n                return 'css';\n            }\n        }\n    });\n\n    var RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\n    var RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/;\n\n    var hex2rgb = function (hex) {\n        if (hex.match(RE_HEX)) {\n            // remove optional leading #\n            if (hex.length === 4 || hex.length === 7) {\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full six-digit\n            if (hex.length === 3) {\n                hex = hex.split('');\n                hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n            }\n            var u = parseInt(hex, 16);\n            var r = u >> 16;\n            var g = (u >> 8) & 0xff;\n            var b = u & 0xff;\n            return [r, g, b, 1];\n        }\n\n        // match rgba hex format, eg #FF000077\n        if (hex.match(RE_HEXA)) {\n            if (hex.length === 5 || hex.length === 9) {\n                // remove optional leading #\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full eight-digit\n            if (hex.length === 4) {\n                hex = hex.split('');\n                hex =\n                    hex[0] +\n                    hex[0] +\n                    hex[1] +\n                    hex[1] +\n                    hex[2] +\n                    hex[2] +\n                    hex[3] +\n                    hex[3];\n            }\n            var u$1 = parseInt(hex, 16);\n            var r$1 = (u$1 >> 24) & 0xff;\n            var g$1 = (u$1 >> 16) & 0xff;\n            var b$1 = (u$1 >> 8) & 0xff;\n            var a = Math.round(((u$1 & 0xff) / 0xff) * 100) / 100;\n            return [r$1, g$1, b$1, a];\n        }\n\n        // we used to check for css colors here\n        // if _input.css? and rgb = _input.css hex\n        //     return rgb\n\n        throw new Error((\"unknown hex color: \" + hex));\n    };\n\n    var round$1 = Math.round;\n\n    var rgb2hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgba');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var a = ref[3];\n        var mode = last(args) || 'auto';\n        if (a === undefined) { a = 1; }\n        if (mode === 'auto') {\n            mode = a < 1 ? 'rgba' : 'rgb';\n        }\n        r = round$1(r);\n        g = round$1(g);\n        b = round$1(b);\n        var u = (r << 16) | (g << 8) | b;\n        var str = '000000' + u.toString(16); //#.toUpperCase();\n        str = str.substr(str.length - 6);\n        var hxa = '0' + round$1(a * 255).toString(16);\n        hxa = hxa.substr(hxa.length - 2);\n        switch (mode.toLowerCase()) {\n            case 'rgba':\n                return (\"#\" + str + hxa);\n            case 'argb':\n                return (\"#\" + hxa + str);\n            default:\n                return (\"#\" + str);\n        }\n    };\n\n    Color.prototype.hex = function (mode) {\n        return rgb2hex(this._rgb, mode);\n    };\n\n    var hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hex']) ));\n    };\n    chroma.hex = hex;\n\n    input.format.hex = hex2rgb;\n    input.autodetect.push({\n        p: 4,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (\n                !rest.length &&\n                type(h) === 'string' &&\n                [3, 4, 5, 6, 7, 8, 9].indexOf(h.length) >= 0\n            ) {\n                return 'hex';\n            }\n        }\n    });\n\n    Color.prototype.hsl = function () {\n        return rgb2hsl(this._rgb);\n    };\n\n    var hsl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsl']) ));\n    };\n    chroma.hsl = hsl;\n\n    input.format.hsl = hsl2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsl');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsl';\n            }\n        }\n    });\n\n    Color.prototype.lab = function () {\n        return rgb2lab(this._rgb);\n    };\n\n    var lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['lab']) ));\n    };\n    Object.assign(chroma, { lab: lab, getLabWhitePoint: getLabWhitePoint, setLabWhitePoint: setLabWhitePoint });\n\n    input.format.lab = lab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'lab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'lab';\n            }\n        }\n    });\n\n    Color.prototype.oklab = function () {\n        return rgb2oklab(this._rgb);\n    };\n\n    var oklab$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['oklab']) ));\n    };\n    Object.assign(chroma, { oklab: oklab$1 });\n\n    input.format.oklab = oklab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'oklab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'oklab';\n            }\n        }\n    });\n\n    var round = Math.round;\n\n    Color.prototype.rgb = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        if (rnd === false) { return this._rgb.slice(0, 3); }\n        return this._rgb.slice(0, 3).map(round);\n    };\n\n    Color.prototype.rgba = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        return this._rgb.slice(0, 4).map(function (v, i) {\n            return i < 3 ? (rnd === false ? v : round(v)) : v;\n        });\n    };\n\n    var rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['rgb']) ));\n    };\n    Object.assign(chroma, { rgb: rgb });\n\n    input.format.rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        if (rgba[3] === undefined) { rgba[3] = 1; }\n        return rgba;\n    };\n\n    input.autodetect.push({\n        p: 3,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'rgba');\n            if (\n                type(args) === 'array' &&\n                (args.length === 3 ||\n                    (args.length === 4 &&\n                        type(args[3]) == 'number' &&\n                        args[3] >= 0 &&\n                        args[3] <= 1))\n            ) {\n                return 'rgb';\n            }\n        }\n    });\n\n    Color.prototype.alpha = function (a, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        if (a !== undefined && type(a) === 'number') {\n            if (mutate) {\n                this._rgb[3] = a;\n                return this;\n            }\n            return new Color([this._rgb[0], this._rgb[1], this._rgb[2], a], 'rgb');\n        }\n        return this._rgb[3];\n    };\n\n    Color.prototype.darken = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        var me = this;\n        var lab = me.lab();\n        lab[0] -= labConstants.Kn * amount;\n        return new Color(lab, 'lab').alpha(me.alpha(), true);\n    };\n\n    Color.prototype.brighten = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        return this.darken(-amount);\n    };\n\n    Color.prototype.darker = Color.prototype.darken;\n    Color.prototype.brighter = Color.prototype.brighten;\n\n    Color.prototype.get = function (mc) {\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) { return src[i]; }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    var index = {};\n\n    function mix (col1, col2, f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 3;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 3 ];\n\n        var mode = rest[0] || 'lrgb';\n        if (!index[mode] && !rest.length) {\n            // fall back to the first supported mode\n            mode = Object.keys(index)[0];\n        }\n        if (!index[mode]) {\n            throw new Error((\"interpolation mode \" + mode + \" is not defined\"));\n        }\n        if (type(col1) !== 'object') { col1 = new Color(col1); }\n        if (type(col2) !== 'object') { col2 = new Color(col2); }\n        return index[mode](col1, col2, f).alpha(\n            col1.alpha() + f * (col2.alpha() - col1.alpha())\n        );\n    }\n\n    Color.prototype.mix = Color.prototype.interpolate = function (\n        col2,\n        f\n    ) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 2;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 2 ];\n\n        return mix.apply(void 0, [ this, col2, f ].concat( rest ));\n    };\n\n    Color.prototype.set = function (mc, value, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) {\n                if (type(value) == 'string') {\n                    switch (value.charAt(0)) {\n                        case '+':\n                            src[i] += +value;\n                            break;\n                        case '-':\n                            src[i] += +value;\n                            break;\n                        case '*':\n                            src[i] *= +value.substr(1);\n                            break;\n                        case '/':\n                            src[i] /= +value.substr(1);\n                            break;\n                        default:\n                            src[i] = +value;\n                    }\n                } else if (type(value) === 'number') {\n                    src[i] = value;\n                } else {\n                    throw new Error(\"unsupported value for Color.set\");\n                }\n                var out = new Color(src, mode);\n                if (mutate) {\n                    this._rgb = out._rgb;\n                    return this;\n                }\n                return out;\n            }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    Color.prototype.tint = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'white', f ].concat( rest ));\n    };\n\n    Color.prototype.shade = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'black', f ].concat( rest ));\n    };\n\n    var sqrt = Math.sqrt;\n    var pow = Math.pow;\n\n    var lrgb = function (col1, col2, f) {\n        var ref = col1._rgb;\n        var x1 = ref[0];\n        var y1 = ref[1];\n        var z1 = ref[2];\n        var ref$1 = col2._rgb;\n        var x2 = ref$1[0];\n        var y2 = ref$1[1];\n        var z2 = ref$1[2];\n        return new Color(\n            sqrt(pow(x1, 2) * (1 - f) + pow(x2, 2) * f),\n            sqrt(pow(y1, 2) * (1 - f) + pow(y2, 2) * f),\n            sqrt(pow(z1, 2) * (1 - f) + pow(z2, 2) * f),\n            'rgb'\n        );\n    };\n\n    // register interpolator\n    index.lrgb = lrgb;\n\n    var oklab = function (col1, col2, f) {\n        var xyz0 = col1.oklab();\n        var xyz1 = col2.oklab();\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'oklab'\n        );\n    };\n\n    // register interpolator\n    index.oklab = oklab;\n\n    function valid () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        try {\n            new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n            return true;\n            // eslint-disable-next-line\n        } catch (e) {\n            return false;\n        }\n    }\n\n    Object.assign(chroma, {\n        Color: Color,\n        valid: valid,\n        css: css,\n        hex: hex,\n        hsl: hsl,\n        lab: lab,\n        oklab: oklab$1,\n        rgb: rgb,\n        mix: mix,\n        interpolate: mix\n    });\n\n    return chroma;\n\n}));\n"
  },
  {
    "path": "dist/chroma-light.min.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n!function(t,r){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=r():\"function\"==typeof define&&define.amd?define(r):(t=\"undefined\"!=typeof globalThis?globalThis:t||self).chroma=r()}(this,(function(){\"use strict\";var t=Math.min,r=Math.max;function n(n,e,o){return void 0===o&&(o=1),t(r(e,n),o)}for(var e={},o=0,a=[\"Boolean\",\"Number\",\"String\",\"Function\",\"Array\",\"Date\",\"RegExp\",\"Undefined\",\"Null\"];o<a.length;o+=1){var i=a[o];e[\"[object \"+i+\"]\"]=i.toLowerCase()}function u(t){return e[Object.prototype.toString.call(t)]||\"object\"}function l(t,r){return void 0===r&&(r=null),t.length>=3?Array.prototype.slice.call(t):\"object\"==u(t[0])&&r?r.split(\"\").filter((function(r){return void 0!==t[0][r]})).map((function(r){return t[0][r]})):t[0].slice(0)}function c(t){if(t.length<2)return null;var r=t.length-1;return\"string\"==u(t[r])?t[r].toLowerCase():null}var h=Math.PI,s=Math.min,f=Math.max,g=function(t){return Math.round(100*t)/100},p=function(t){return Math.round(100*t)/100},m=h/180,v=180/h,b={format:{},autodetect:[]},d=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var e=this;if(\"object\"===u(t[0])&&t[0].constructor&&t[0].constructor===this.constructor)return t[0];var o=c(t),a=!1;if(!o){a=!0,b.sorted||(b.autodetect=b.autodetect.sort((function(t,r){return r.p-t.p})),b.sorted=!0);for(var i=0,l=b.autodetect;i<l.length;i+=1){var h=l[i];if(o=h.test.apply(h,t))break}}if(!b.format[o])throw new Error(\"unknown format: \"+t);var s=b.format[o].apply(null,a?t:t.slice(0,-1));e._rgb=function(t){t._clipped=!1,t._unclipped=t.slice(0);for(var r=0;r<=3;r++)r<3?((t[r]<0||t[r]>255)&&(t._clipped=!0),t[r]=n(t[r],0,255)):3===r&&(t[r]=n(t[r],0,1));return t}(s),3===e._rgb.length&&e._rgb.push(1)};d.prototype.toString=function(){return\"function\"==u(this.hex)?this.hex():\"[\"+this._rgb.join(\",\")+\"]\"};var y=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t)))};y.version=\"3.2.0\";var w=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n,e,o=(t=l(t,\"rgba\"))[0],a=t[1],i=t[2],u=s(o/=255,a/=255,i/=255),c=f(o,a,i),h=(c+u)/2;return c===u?(n=0,e=Number.NaN):n=h<.5?(c-u)/(c+u):(c-u)/(2-c-u),o==c?e=(a-i)/(c-u):a==c?e=2+(i-o)/(c-u):i==c&&(e=4+(o-a)/(c-u)),(e*=60)<0&&(e+=360),t.length>3&&void 0!==t[3]?[e,n,h,t[3]]:[e,n,h]},M={Kn:18,labWhitePoint:\"d65\",Xn:.95047,Yn:1,Zn:1.08883,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},k=new Map([[\"a\",[1.0985,.35585]],[\"b\",[1.0985,.35585]],[\"c\",[.98074,1.18232]],[\"d50\",[.96422,.82521]],[\"d55\",[.95682,.92149]],[\"d65\",[.95047,1.08883]],[\"e\",[1,1,1]],[\"f2\",[.99186,.67393]],[\"f7\",[.95041,1.08747]],[\"f11\",[1.00962,.6435]],[\"icc\",[.96422,.82521]]]);function x(t){var r=k.get(String(t).toLowerCase());if(!r)throw new Error(\"unknown Lab illuminant \"+t);M.labWhitePoint=t,M.Xn=r[0],M.Zn=r[1]}function j(){return M.labWhitePoint}var _=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgb\"),e=n[0],o=n[1],a=n[2],i=n.slice(3),u=A(e,o,a),c=function(t,r,n){var e=M.Xn,o=M.Yn,a=M.Zn,i=M.kE,u=M.kK,l=t/e,c=r/o,h=n/a,s=l>i?Math.pow(l,1/3):(u*l+16)/116,f=c>i?Math.pow(c,1/3):(u*c+16)/116,g=h>i?Math.pow(h,1/3):(u*h+16)/116;return[116*f-16,500*(s-f),200*(f-g)]}(u[0],u[1],u[2]);return[c[0],c[1],c[2]].concat(i.length>0&&i[0]<1?[i[0]]:[])};function E(t){var r=Math.sign(t);return((t=Math.abs(t))<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4))*r}var A=function(t,r,n){t=E(t/255),r=E(r/255),n=E(n/255);var e=M.MtxRGB2XYZ,o=M.MtxAdaptMa,a=M.MtxAdaptMaI,i=M.Xn,u=M.Yn,l=M.Zn,c=M.As,h=M.Bs,s=M.Cs,f=t*e.m00+r*e.m10+n*e.m20,g=t*e.m01+r*e.m11+n*e.m21,p=t*e.m02+r*e.m12+n*e.m22,m=i*o.m00+u*o.m10+l*o.m20,v=i*o.m01+u*o.m11+l*o.m21,b=i*o.m02+u*o.m12+l*o.m22,d=f*o.m00+g*o.m10+p*o.m20,y=f*o.m01+g*o.m11+p*o.m21,w=f*o.m02+g*o.m12+p*o.m22;return y*=v/h,w*=b/s,[f=(d*=m/c)*a.m00+y*a.m10+w*a.m20,g=d*a.m01+y*a.m11+w*a.m21,p=d*a.m02+y*a.m12+w*a.m22]},R=Math.sqrt,F=Math.atan2,N=Math.round,X=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lab\"),e=n[0],o=n[1],a=n[2],i=R(o*o+a*a),u=(F(a,o)*v+360)%360;return 0===N(1e4*i)&&(u=Number.NaN),[e,i,u]};function Z(t,r){var n=t.length;Array.isArray(t[0])||(t=[t]),Array.isArray(r[0])||(r=r.map((function(t){return[t]})));var e=r[0].length,o=r[0].map((function(t,n){return r.map((function(t){return t[n]}))})),a=t.map((function(t){return o.map((function(r){return Array.isArray(t)?t.reduce((function(t,n,e){return t+n*(r[e]||0)}),0):r.reduce((function(r,n){return r+n*t}),0)}))}));return 1===n&&(a=a[0]),1===e?a.map((function(t){return t[0]})):a}var Y=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n,e,o=l(t,\"rgb\"),a=o[0],i=o[1],u=o[2],c=o.slice(3),h=A(a,i,u);return(n=[[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],e=Z([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],h),Z(n,e.map((function(t){return Math.cbrt(t)})))).concat(c.length>0&&c[0]<1?[c[0]]:[])};var $=Math.round,B=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgba\"),e=c(t)||\"rgb\";if(\"hsl\"===e.substr(0,3))return function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"hsla\"),e=c(t)||\"lsa\";return n[0]=g(n[0]||0)+\"deg\",n[1]=g(100*n[1])+\"%\",n[2]=g(100*n[2])+\"%\",\"hsla\"===e||n.length>3&&n[3]<1?(n[3]=\"/ \"+(n.length>3?n[3]:1),e=\"hsla\"):n.length=3,e.substr(0,3)+\"(\"+n.join(\" \")+\")\"}(w(n),e);if(\"lab\"===e.substr(0,3)){var o=j();x(\"d50\");var a=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lab\"),e=c(t)||\"lab\";return n[0]=g(n[0])+\"%\",n[1]=g(n[1]),n[2]=g(n[2]),\"laba\"===e||n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"lab(\"+n.join(\" \")+\")\"}(_(n),e);return x(o),a}if(\"lch\"===e.substr(0,3)){var i=j();x(\"d50\");var u=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lch\"),e=c(t)||\"lab\";return n[0]=g(n[0])+\"%\",n[1]=g(n[1]),n[2]=isNaN(n[2])?\"none\":g(n[2])+\"deg\",\"lcha\"===e||n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"lch(\"+n.join(\" \")+\")\"}(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgb\"),e=n[0],o=n[1],a=n[2],i=n.slice(3),u=_(e,o,a),c=u[0],h=u[1],s=u[2],f=X(c,h,s);return[f[0],f[1],f[2]].concat(i.length>0&&i[0]<1?[i[0]]:[])}(n),e);return x(i),u}return\"oklab\"===e.substr(0,5)?function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lab\");return n[0]=g(100*n[0])+\"%\",n[1]=p(n[1]),n[2]=p(n[2]),n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"oklab(\"+n.join(\" \")+\")\"}(Y(n)):\"oklch\"===e.substr(0,5)?function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lch\");return n[0]=g(100*n[0])+\"%\",n[1]=p(n[1]),n[2]=isNaN(n[2])?\"none\":g(n[2])+\"deg\",n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"oklch(\"+n.join(\" \")+\")\"}(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgb\"),e=n[0],o=n[1],a=n[2],i=n.slice(3),u=Y(e,o,a),c=u[0],h=u[1],s=u[2],f=X(c,h,s);return[f[0],f[1],f[2]].concat(i.length>0&&i[0]<1?[i[0]]:[])}(n)):(n[0]=$(n[0]),n[1]=$(n[1]),n[2]=$(n[2]),(\"rgba\"===e||n.length>3&&n[3]<1)&&(n[3]=\"/ \"+(n.length>3?n[3]:1),e=\"rgba\"),e.substr(0,3)+\"(\"+n.slice(0,\"rgb\"===e?3:4).join(\" \")+\")\")},C=function(){for(var t,r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,o,a,i=(r=l(r,\"hsl\"))[0],u=r[1],c=r[2];if(0===u)e=o=a=255*c;else{var h=[0,0,0],s=[0,0,0],f=c<.5?c*(1+u):c+u-c*u,g=2*c-f,p=i/360;h[0]=p+1/3,h[1]=p,h[2]=p-1/3;for(var m=0;m<3;m++)h[m]<0&&(h[m]+=1),h[m]>1&&(h[m]-=1),6*h[m]<1?s[m]=g+6*(f-g)*h[m]:2*h[m]<1?s[m]=f:3*h[m]<2?s[m]=g+(f-g)*(2/3-h[m])*6:s[m]=g;e=(t=[255*s[0],255*s[1],255*s[2]])[0],o=t[1],a=t[2]}return r.length>3?[e,o,a,r[3]]:[e,o,a,1]},O=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=(t=l(t,\"lab\"))[0],e=t[1],o=t[2],a=L(n,e,o),i=a[0],u=a[1],c=a[2],h=K(i,u,c);return[h[0],h[1],h[2],t.length>3?t[3]:1]},L=function(t,r,n){var e=M.kE,o=M.kK,a=M.kKE,i=M.Xn,u=M.Yn,l=M.Zn,c=(t+16)/116,h=.002*r+c,s=c-.005*n,f=h*h*h,g=s*s*s;return[(f>e?f:(116*h-16)/o)*i,(t>a?Math.pow((t+16)/116,3):t/o)*u,(g>e?g:(116*s-16)/o)*l]},W=function(t){var r=Math.sign(t);return((t=Math.abs(t))<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)*r},K=function(t,r,n){var e=M.MtxAdaptMa,o=M.MtxAdaptMaI,a=M.MtxXYZ2RGB,i=M.RefWhiteRGB,u=M.Xn,l=M.Yn,c=M.Zn,h=u*e.m00+l*e.m10+c*e.m20,s=u*e.m01+l*e.m11+c*e.m21,f=u*e.m02+l*e.m12+c*e.m22,g=i.X*e.m00+i.Y*e.m10+i.Z*e.m20,p=i.X*e.m01+i.Y*e.m11+i.Z*e.m21,m=i.X*e.m02+i.Y*e.m12+i.Z*e.m22,v=(t*e.m00+r*e.m10+n*e.m20)*(g/h),b=(t*e.m01+r*e.m11+n*e.m21)*(p/s),d=(t*e.m02+r*e.m12+n*e.m22)*(m/f),y=v*o.m00+b*o.m10+d*o.m20,w=v*o.m01+b*o.m11+d*o.m21,k=v*o.m02+b*o.m12+d*o.m22;return[255*W(y*a.m00+w*a.m10+k*a.m20),255*W(y*a.m01+w*a.m11+k*a.m21),255*W(y*a.m02+w*a.m12+k*a.m22)]},G=Math.sin,I=Math.cos,P=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lch\"),e=n[0],o=n[1],a=n[2];return isNaN(a)&&(a=0),[e,I(a*=m)*o,G(a)*o]},S=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n,e,o=(t=l(t,\"lab\"))[0],a=t[1],i=t[2],u=t.slice(3),c=(n=[[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],e=Z([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],[o,a,i]),Z(n,e.map((function(t){return Math.pow(t,3)})))),h=c[0],s=c[1],f=c[2],g=K(h,s,f);return[g[0],g[1],g[2]].concat(u.length>0&&u[0]<1?[u[0]]:[])};var q=/((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source,T=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source,D=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source,U=/\\s*/.source,z=/\\s+/.source,H=/\\s*,\\s*/.source,J=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source,Q=/\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source,V=new RegExp(\"^rgba?\\\\(\"+U+[q,q,q].join(z)+Q+\"\\\\)$\"),tt=new RegExp(\"^rgb\\\\(\"+U+[q,q,q].join(H)+U+\"\\\\)$\"),rt=new RegExp(\"^rgba\\\\(\"+U+[q,q,q,T].join(H)+U+\"\\\\)$\"),nt=new RegExp(\"^hsla?\\\\(\"+U+[J,D,D].join(z)+Q+\"\\\\)$\"),et=new RegExp(\"^hsl?\\\\(\"+U+[J,D,D].join(H)+U+\"\\\\)$\"),ot=/^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/,at=new RegExp(\"^lab\\\\(\"+U+[T,T,T].join(z)+Q+\"\\\\)$\"),it=new RegExp(\"^lch\\\\(\"+U+[T,T,J].join(z)+Q+\"\\\\)$\"),ut=new RegExp(\"^oklab\\\\(\"+U+[T,T,T].join(z)+Q+\"\\\\)$\"),lt=new RegExp(\"^oklch\\\\(\"+U+[T,T,J].join(z)+Q+\"\\\\)$\"),ct=Math.round,ht=function(t){return t.map((function(t,r){return r<=2?n(ct(t),0,255):t}))},st=function(t,r,n,e){return void 0===r&&(r=0),void 0===n&&(n=100),void 0===e&&(e=!1),\"string\"==typeof t&&t.endsWith(\"%\")&&(t=parseFloat(t.substring(0,t.length-1))/100,t=e?r+.5*(t+1)*(n-r):r+t*(n-r)),+t},ft=function(t,r){return\"none\"===t?r:t},gt=function(t){if(\"transparent\"===(t=t.toLowerCase().trim()))return[0,0,0,0];var r;if(b.format.named)try{return b.format.named(t)}catch(t){}if((r=t.match(V))||(r=t.match(tt))){for(var n=r.slice(1,4),e=0;e<3;e++)n[e]=+st(ft(n[e],0),0,255);n=ht(n);var o=void 0!==r[4]?+st(r[4],0,1):1;return n[3]=o,n}if(r=t.match(rt)){for(var a=r.slice(1,5),i=0;i<4;i++)a[i]=+st(a[i],0,255);return a}if((r=t.match(nt))||(r=t.match(et))){var u=r.slice(1,4);u[0]=+ft(u[0].replace(\"deg\",\"\"),0),u[1]=.01*+st(ft(u[1],0),0,100),u[2]=.01*+st(ft(u[2],0),0,100);var c=ht(C(u)),h=void 0!==r[4]?+st(r[4],0,1):1;return c[3]=h,c}if(r=t.match(ot)){var s=r.slice(1,4);s[1]*=.01,s[2]*=.01;for(var f=C(s),g=0;g<3;g++)f[g]=ct(f[g]);return f[3]=+r[4],f}if(r=t.match(at)){var p=r.slice(1,4);p[0]=st(ft(p[0],0),0,100),p[1]=st(ft(p[1],0),-125,125,!0),p[2]=st(ft(p[2],0),-125,125,!0);var m=j();x(\"d50\");var v=ht(O(p));x(m);var d=void 0!==r[4]?+st(r[4],0,1):1;return v[3]=d,v}if(r=t.match(it)){var y=r.slice(1,4);y[0]=st(y[0],0,100),y[1]=st(ft(y[1],0),0,150,!1),y[2]=+ft(y[2].replace(\"deg\",\"\"),0);var w=j();x(\"d50\");var M=ht(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=(t=l(t,\"lch\"))[0],e=t[1],o=t[2],a=P(n,e,o),i=a[0],u=a[1],c=a[2],h=O(i,u,c);return[h[0],h[1],h[2],t.length>3?t[3]:1]}(y));x(w);var k=void 0!==r[4]?+st(r[4],0,1):1;return M[3]=k,M}if(r=t.match(ut)){var _=r.slice(1,4);_[0]=st(ft(_[0],0),0,1),_[1]=st(ft(_[1],0),-.4,.4,!0),_[2]=st(ft(_[2],0),-.4,.4,!0);var E=ht(S(_)),A=void 0!==r[4]?+st(r[4],0,1):1;return E[3]=A,E}if(r=t.match(lt)){var R=r.slice(1,4);R[0]=st(ft(R[0],0),0,1),R[1]=st(ft(R[1],0),0,.4,!1),R[2]=+ft(R[2].replace(\"deg\",\"\"),0);var F=ht(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=(t=l(t,\"lch\"))[0],e=t[1],o=t[2],a=t.slice(3),i=P(n,e,o),u=i[0],c=i[1],h=i[2],s=S(u,c,h);return[s[0],s[1],s[2]].concat(a.length>0&&a[0]<1?[a[0]]:[])}(R)),N=void 0!==r[4]?+st(r[4],0,1):1;return F[3]=N,F}};gt.test=function(t){return V.test(t)||nt.test(t)||at.test(t)||it.test(t)||ut.test(t)||lt.test(t)||tt.test(t)||rt.test(t)||et.test(t)||ot.test(t)||\"transparent\"===t},d.prototype.css=function(t){return B(this._rgb,t)};var pt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"css\"])))};y.css=pt,b.format.css=gt,b.autodetect.push({p:5,test:function(t){for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];if(!r.length&&\"string\"===u(t)&&gt.test(t))return\"css\"}});var mt=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,vt=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,bt=Math.round;d.prototype.hex=function(t){return function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgba\"),e=n[0],o=n[1],a=n[2],i=n[3],u=c(t)||\"auto\";void 0===i&&(i=1),\"auto\"===u&&(u=i<1?\"rgba\":\"rgb\");var h=\"000000\"+((e=bt(e))<<16|(o=bt(o))<<8|(a=bt(a))).toString(16);h=h.substr(h.length-6);var s=\"0\"+bt(255*i).toString(16);switch(s=s.substr(s.length-2),u.toLowerCase()){case\"rgba\":return\"#\"+h+s;case\"argb\":return\"#\"+s+h;default:return\"#\"+h}}(this._rgb,t)};var dt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"hex\"])))};y.hex=dt,b.format.hex=function(t){if(t.match(mt)){4!==t.length&&7!==t.length||(t=t.substr(1)),3===t.length&&(t=(t=t.split(\"\"))[0]+t[0]+t[1]+t[1]+t[2]+t[2]);var r=parseInt(t,16);return[r>>16,r>>8&255,255&r,1]}if(t.match(vt)){5!==t.length&&9!==t.length||(t=t.substr(1)),4===t.length&&(t=(t=t.split(\"\"))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);var n=parseInt(t,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error(\"unknown hex color: \"+t)},b.autodetect.push({p:4,test:function(t){for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];if(!r.length&&\"string\"===u(t)&&[3,4,5,6,7,8,9].indexOf(t.length)>=0)return\"hex\"}}),d.prototype.hsl=function(){return w(this._rgb)};var yt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"hsl\"])))};y.hsl=yt,b.format.hsl=C,b.autodetect.push({p:2,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"hsl\"))&&3===t.length)return\"hsl\"}}),d.prototype.lab=function(){return _(this._rgb)};var wt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"lab\"])))};Object.assign(y,{lab:wt,getLabWhitePoint:j,setLabWhitePoint:x}),b.format.lab=O,b.autodetect.push({p:2,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"lab\"))&&3===t.length)return\"lab\"}}),d.prototype.oklab=function(){return Y(this._rgb)};var Mt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"oklab\"])))};Object.assign(y,{oklab:Mt}),b.format.oklab=S,b.autodetect.push({p:2,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"oklab\"))&&3===t.length)return\"oklab\"}});var kt=Math.round;d.prototype.rgb=function(t){return void 0===t&&(t=!0),!1===t?this._rgb.slice(0,3):this._rgb.slice(0,3).map(kt)},d.prototype.rgba=function(t){return void 0===t&&(t=!0),this._rgb.slice(0,4).map((function(r,n){return n<3?!1===t?r:kt(r):r}))};var xt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"rgb\"])))};Object.assign(y,{rgb:xt}),b.format.rgb=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgba\");return void 0===n[3]&&(n[3]=1),n},b.autodetect.push({p:3,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"rgba\"))&&(3===t.length||4===t.length&&\"number\"==u(t[3])&&t[3]>=0&&t[3]<=1))return\"rgb\"}}),d.prototype.alpha=function(t,r){return void 0===r&&(r=!1),void 0!==t&&\"number\"===u(t)?r?(this._rgb[3]=t,this):new d([this._rgb[0],this._rgb[1],this._rgb[2],t],\"rgb\"):this._rgb[3]},d.prototype.darken=function(t){void 0===t&&(t=1);var r=this.lab();return r[0]-=M.Kn*t,new d(r,\"lab\").alpha(this.alpha(),!0)},d.prototype.brighten=function(t){return void 0===t&&(t=1),this.darken(-t)},d.prototype.darker=d.prototype.darken,d.prototype.brighter=d.prototype.brighten,d.prototype.get=function(t){var r=t.split(\".\"),n=r[0],e=r[1],o=this[n]();if(e){var a=n.indexOf(e)-(\"ok\"===n.substr(0,2)?2:0);if(a>-1)return o[a];throw new Error(\"unknown channel \"+e+\" in mode \"+n)}return o};var jt={};function _t(t,r,n){void 0===n&&(n=.5);for(var e=[],o=arguments.length-3;o-- >0;)e[o]=arguments[o+3];var a=e[0]||\"lrgb\";if(jt[a]||e.length||(a=Object.keys(jt)[0]),!jt[a])throw new Error(\"interpolation mode \"+a+\" is not defined\");return\"object\"!==u(t)&&(t=new d(t)),\"object\"!==u(r)&&(r=new d(r)),jt[a](t,r,n).alpha(t.alpha()+n*(r.alpha()-t.alpha()))}d.prototype.mix=d.prototype.interpolate=function(t,r){void 0===r&&(r=.5);for(var n=[],e=arguments.length-2;e-- >0;)n[e]=arguments[e+2];return _t.apply(void 0,[this,t,r].concat(n))},d.prototype.set=function(t,r,n){void 0===n&&(n=!1);var e=t.split(\".\"),o=e[0],a=e[1],i=this[o]();if(a){var l=o.indexOf(a)-(\"ok\"===o.substr(0,2)?2:0);if(l>-1){if(\"string\"==u(r))switch(r.charAt(0)){case\"+\":case\"-\":i[l]+=+r;break;case\"*\":i[l]*=+r.substr(1);break;case\"/\":i[l]/=+r.substr(1);break;default:i[l]=+r}else{if(\"number\"!==u(r))throw new Error(\"unsupported value for Color.set\");i[l]=r}var c=new d(i,o);return n?(this._rgb=c._rgb,this):c}throw new Error(\"unknown channel \"+a+\" in mode \"+o)}return i},d.prototype.tint=function(t){void 0===t&&(t=.5);for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];return _t.apply(void 0,[this,\"white\",t].concat(r))},d.prototype.shade=function(t){void 0===t&&(t=.5);for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];return _t.apply(void 0,[this,\"black\",t].concat(r))};var Et=Math.sqrt,At=Math.pow;jt.lrgb=function(t,r,n){var e=t._rgb,o=e[0],a=e[1],i=e[2],u=r._rgb,l=u[0],c=u[1],h=u[2];return new d(Et(At(o,2)*(1-n)+At(l,2)*n),Et(At(a,2)*(1-n)+At(c,2)*n),Et(At(i,2)*(1-n)+At(h,2)*n),\"rgb\")};return jt.oklab=function(t,r,n){var e=t.oklab(),o=r.oklab();return new d(e[0]+n*(o[0]-e[0]),e[1]+n*(o[1]-e[1]),e[2]+n*(o[2]-e[2]),\"oklab\")},Object.assign(y,{Color:d,valid:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];try{return new(Function.prototype.bind.apply(d,[null].concat(t))),!0}catch(t){return!1}},css:pt,hex:dt,hsl:yt,lab:wt,oklab:Mt,rgb:xt,mix:_t,interpolate:_t}),y}));\n"
  },
  {
    "path": "dist/chroma.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.chroma = factory());\n})(this, (function () { 'use strict';\n\n    var min$4 = Math.min;\n    var max$4 = Math.max;\n\n    function limit (x, low, high) {\n        if ( low === void 0 ) low = 0;\n        if ( high === void 0 ) high = 1;\n\n        return min$4(max$4(low, x), high);\n    }\n\n    function clip_rgb (rgb) {\n        rgb._clipped = false;\n        rgb._unclipped = rgb.slice(0);\n        for (var i = 0; i <= 3; i++) {\n            if (i < 3) {\n                if (rgb[i] < 0 || rgb[i] > 255) { rgb._clipped = true; }\n                rgb[i] = limit(rgb[i], 0, 255);\n            } else if (i === 3) {\n                rgb[i] = limit(rgb[i], 0, 1);\n            }\n        }\n        return rgb;\n    }\n\n    // ported from jQuery's $.type\n    var classToType = {};\n    for (var i = 0, list = [\n        'Boolean',\n        'Number',\n        'String',\n        'Function',\n        'Array',\n        'Date',\n        'RegExp',\n        'Undefined',\n        'Null'\n    ]; i < list.length; i += 1) {\n        var name = list[i];\n\n        classToType[(\"[object \" + name + \"]\")] = name.toLowerCase();\n    }\n    function type (obj) {\n        return classToType[Object.prototype.toString.call(obj)] || 'object';\n    }\n\n    function unpack (args, keyOrder) {\n        if ( keyOrder === void 0 ) keyOrder = null;\n\n        // if called with more than 3 arguments, we return the arguments\n        if (args.length >= 3) { return Array.prototype.slice.call(args); }\n        // with less than 3 args we check if first arg is object\n        // and use the keyOrder string to extract and sort properties\n        if (type(args[0]) == 'object' && keyOrder) {\n            return keyOrder\n                .split('')\n                .filter(function (k) { return args[0][k] !== undefined; })\n                .map(function (k) { return args[0][k]; });\n        }\n        // otherwise we just return the first argument\n        // (which we suppose is an array of args)\n        return args[0].slice(0);\n    }\n\n    function last (args) {\n        if (args.length < 2) { return null; }\n        var l = args.length - 1;\n        if (type(args[l]) == 'string') { return args[l].toLowerCase(); }\n        return null;\n    }\n\n    var PI$2 = Math.PI;\n    var min$3 = Math.min;\n    var max$3 = Math.max;\n\n    var rnd2 = function (a) { return Math.round(a * 100) / 100; };\n    var rnd3 = function (a) { return Math.round(a * 100) / 100; };\n\n    var TWOPI = PI$2 * 2;\n    var PITHIRD = PI$2 / 3;\n    var DEG2RAD = PI$2 / 180;\n    var RAD2DEG = 180 / PI$2;\n\n    /**\n     * Reverse the first three elements of an array\n     *\n     * @param {any[]} arr\n     * @returns {any[]}\n     */\n    function reverse3(arr) {\n        return arr.slice(0, 3).reverse().concat( arr.slice(3));\n    }\n\n    var input = {\n        format: {},\n        autodetect: []\n    };\n\n    var Color = function Color() {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var me = this;\n        if (\n            type(args[0]) === 'object' &&\n            args[0].constructor &&\n            args[0].constructor === this.constructor\n        ) {\n            // the argument is already a Color instance\n            return args[0];\n        }\n        // last argument could be the mode\n        var mode = last(args);\n        var autodetect = false;\n        if (!mode) {\n            autodetect = true;\n\n            if (!input.sorted) {\n                input.autodetect = input.autodetect.sort(function (a, b) { return b.p - a.p; });\n                input.sorted = true;\n            }\n\n            // auto-detect format\n            for (var i = 0, list = input.autodetect; i < list.length; i += 1) {\n                var chk = list[i];\n\n                mode = chk.test.apply(chk, args);\n                if (mode) { break; }\n            }\n        }\n        if (input.format[mode]) {\n            var rgb = input.format[mode].apply(\n                null,\n                autodetect ? args : args.slice(0, -1)\n            );\n            me._rgb = clip_rgb(rgb);\n        } else {\n            throw new Error('unknown format: ' + args);\n        }\n        // add alpha channel\n        if (me._rgb.length === 3) { me._rgb.push(1); }\n    };\n    Color.prototype.toString = function toString () {\n        if (type(this.hex) == 'function') { return this.hex(); }\n        return (\"[\" + (this._rgb.join(',')) + \"]\");\n    };\n\n    // this gets updated automatically\n    var version = '3.2.0';\n\n    var chroma = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n    };\n\n    chroma.version = version;\n\n    var cmyk2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'cmyk');\n        var c = args[0];\n        var m = args[1];\n        var y = args[2];\n        var k = args[3];\n        var alpha = args.length > 4 ? args[4] : 1;\n        if (k === 1) { return [0, 0, 0, alpha]; }\n        return [\n            c >= 1 ? 0 : 255 * (1 - c) * (1 - k), // r\n            m >= 1 ? 0 : 255 * (1 - m) * (1 - k), // g\n            y >= 1 ? 0 : 255 * (1 - y) * (1 - k), // b\n            alpha\n        ];\n    };\n\n    var max$2 = Math.max;\n\n    var rgb2cmyk = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        r = r / 255;\n        g = g / 255;\n        b = b / 255;\n        var k = 1 - max$2(r, max$2(g, b));\n        var f = k < 1 ? 1 / (1 - k) : 0;\n        var c = (1 - r - k) * f;\n        var m = (1 - g - k) * f;\n        var y = (1 - b - k) * f;\n        return [c, m, y, k];\n    };\n\n    Color.prototype.cmyk = function () {\n        return rgb2cmyk(this._rgb);\n    };\n\n    var cmyk = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['cmyk']) ));\n    };\n    Object.assign(chroma, { cmyk: cmyk });\n\n    input.format.cmyk = cmyk2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'cmyk');\n            if (type(args) === 'array' && args.length === 4) {\n                return 'cmyk';\n            }\n        }\n    });\n\n    /*\n     * supported arguments:\n     * - hsl2css(h,s,l)\n     * - hsl2css(h,s,l,a)\n     * - hsl2css([h,s,l], mode)\n     * - hsl2css([h,s,l,a], mode)\n     * - hsl2css({h,s,l,a}, mode)\n     */\n    var hsl2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var hsla = unpack(args, 'hsla');\n        var mode = last(args) || 'lsa';\n        hsla[0] = rnd2(hsla[0] || 0) + 'deg';\n        hsla[1] = rnd2(hsla[1] * 100) + '%';\n        hsla[2] = rnd2(hsla[2] * 100) + '%';\n        if (mode === 'hsla' || (hsla.length > 3 && hsla[3] < 1)) {\n            hsla[3] = '/ ' + (hsla.length > 3 ? hsla[3] : 1);\n            mode = 'hsla';\n        } else {\n            hsla.length = 3;\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (hsla.join(' ')) + \")\");\n    };\n\n    /*\n     * supported arguments:\n     * - rgb2hsl(r,g,b)\n     * - rgb2hsl(r,g,b,a)\n     * - rgb2hsl([r,g,b])\n     * - rgb2hsl([r,g,b,a])\n     * - rgb2hsl({r,g,b,a})\n     */\n    var rgb2hsl$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'rgba');\n        var r = args[0];\n        var g = args[1];\n        var b = args[2];\n\n        r /= 255;\n        g /= 255;\n        b /= 255;\n\n        var minRgb = min$3(r, g, b);\n        var maxRgb = max$3(r, g, b);\n\n        var l = (maxRgb + minRgb) / 2;\n        var s, h;\n\n        if (maxRgb === minRgb) {\n            s = 0;\n            h = Number.NaN;\n        } else {\n            s =\n                l < 0.5\n                    ? (maxRgb - minRgb) / (maxRgb + minRgb)\n                    : (maxRgb - minRgb) / (2 - maxRgb - minRgb);\n        }\n\n        if (r == maxRgb) { h = (g - b) / (maxRgb - minRgb); }\n        else if (g == maxRgb) { h = 2 + (b - r) / (maxRgb - minRgb); }\n        else if (b == maxRgb) { h = 4 + (r - g) / (maxRgb - minRgb); }\n\n        h *= 60;\n        if (h < 0) { h += 360; }\n        if (args.length > 3 && args[3] !== undefined) { return [h, s, l, args[3]]; }\n        return [h, s, l];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        var mode = last(args) || 'lab';\n        laba[0] = rnd2(laba[0]) + '%';\n        laba[1] = rnd2(laba[1]);\n        laba[2] = rnd2(laba[2]);\n        if (mode === 'laba' || (laba.length > 3 && laba[3] < 1)) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"lab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var labConstants = {\n        // Corresponds roughly to RGB brighter/darker\n        Kn: 18,\n\n        // D65 standard referent\n        labWhitePoint: 'd65',\n        Xn: 0.95047,\n        Yn: 1,\n        Zn: 1.08883,\n\n        kE: 216.0 / 24389.0,\n        kKE: 8.0,\n        kK: 24389.0 / 27.0,\n\n        RefWhiteRGB: {\n            // sRGB\n            X: 0.95047,\n            Y: 1,\n            Z: 1.08883\n        },\n\n        MtxRGB2XYZ: {\n            m00: 0.4124564390896922,\n            m01: 0.21267285140562253,\n            m02: 0.0193338955823293,\n            m10: 0.357576077643909,\n            m11: 0.715152155287818,\n            m12: 0.11919202588130297,\n            m20: 0.18043748326639894,\n            m21: 0.07217499330655958,\n            m22: 0.9503040785363679\n        },\n\n        MtxXYZ2RGB: {\n            m00: 3.2404541621141045,\n            m01: -0.9692660305051868,\n            m02: 0.055643430959114726,\n            m10: -1.5371385127977166,\n            m11: 1.8760108454466942,\n            m12: -0.2040259135167538,\n            m20: -0.498531409556016,\n            m21: 0.041556017530349834,\n            m22: 1.0572251882231791\n        },\n\n        // used in rgb2xyz\n        As: 0.9414285350000001,\n        Bs: 1.040417467,\n        Cs: 1.089532651,\n\n        MtxAdaptMa: {\n            m00: 0.8951,\n            m01: -0.7502,\n            m02: 0.0389,\n            m10: 0.2664,\n            m11: 1.7135,\n            m12: -0.0685,\n            m20: -0.1614,\n            m21: 0.0367,\n            m22: 1.0296\n        },\n\n        MtxAdaptMaI: {\n            m00: 0.9869929054667123,\n            m01: 0.43230526972339456,\n            m02: -0.008528664575177328,\n            m10: -0.14705425642099013,\n            m11: 0.5183602715367776,\n            m12: 0.04004282165408487,\n            m20: 0.15996265166373125,\n            m21: 0.0492912282128556,\n            m22: 0.9684866957875502\n        }\n    };\n\n    // taken from https://de.mathworks.com/help/images/ref/whitepoint.html\n    var ILLUMINANTS = new Map([\n        // ASTM E308-01\n        ['a', [1.0985, 0.35585]],\n        // Wyszecki & Stiles, p. 769\n        ['b', [1.0985, 0.35585]],\n        // C ASTM E308-01\n        ['c', [0.98074, 1.18232]],\n        // D50 (ASTM E308-01)\n        ['d50', [0.96422, 0.82521]],\n        // D55 (ASTM E308-01)\n        ['d55', [0.95682, 0.92149]],\n        // D65 (ASTM E308-01)\n        ['d65', [0.95047, 1.08883]],\n        // E (ASTM E308-01)\n        ['e', [1, 1, 1]],\n        // F2 (ASTM E308-01)\n        ['f2', [0.99186, 0.67393]],\n        // F7 (ASTM E308-01)\n        ['f7', [0.95041, 1.08747]],\n        // F11 (ASTM E308-01)\n        ['f11', [1.00962, 0.6435]],\n        ['icc', [0.96422, 0.82521]]\n    ]);\n\n    function setLabWhitePoint(name) {\n        var ill = ILLUMINANTS.get(String(name).toLowerCase());\n        if (!ill) {\n            throw new Error('unknown Lab illuminant ' + name);\n        }\n        labConstants.labWhitePoint = name;\n        labConstants.Xn = ill[0];\n        labConstants.Zn = ill[1];\n    }\n\n    function getLabWhitePoint() {\n        return labConstants.labWhitePoint;\n    }\n\n    var rgb2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2xyz(r, g, b);\n        var x = ref$1[0];\n        var y = ref$1[1];\n        var z = ref$1[2];\n        var ref$2 = xyz2lab(x, y, z);\n        var L = ref$2[0];\n        var a = ref$2[1];\n        var b_ = ref$2[2];\n        return [L, a, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    function xyz2lab(x, y, z) {\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var xr = x / Xn;\n        var yr = y / Yn;\n        var zr = z / Zn;\n\n        var fx = xr > kE ? Math.pow(xr, 1.0 / 3.0) : (kK * xr + 16.0) / 116.0;\n        var fy = yr > kE ? Math.pow(yr, 1.0 / 3.0) : (kK * yr + 16.0) / 116.0;\n        var fz = zr > kE ? Math.pow(zr, 1.0 / 3.0) : (kK * zr + 16.0) / 116.0;\n\n        return [116.0 * fy - 16.0, 500.0 * (fx - fy), 200.0 * (fy - fz)];\n    }\n\n    function gammaAdjustSRGB(companded) {\n        var sign = Math.sign(companded);\n        companded = Math.abs(companded);\n        var linear =\n            companded <= 0.04045\n                ? companded / 12.92\n                : Math.pow((companded + 0.055) / 1.055, 2.4);\n        return linear * sign;\n    }\n\n    var rgb2xyz = function (r, g, b) {\n        // normalize and gamma adjust\n        r = gammaAdjustSRGB(r / 255);\n        g = gammaAdjustSRGB(g / 255);\n        b = gammaAdjustSRGB(b / 255);\n\n        var MtxRGB2XYZ = labConstants.MtxRGB2XYZ;\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var As = labConstants.As;\n        var Bs = labConstants.Bs;\n        var Cs = labConstants.Cs;\n\n        var x = r * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;\n        var y = r * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;\n        var z = r * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;\n\n        var Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20;\n        var Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21;\n        var Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22;\n\n        X *= Ad / As;\n        Y *= Bd / Bs;\n        Z *= Cd / Cs;\n\n        x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;\n        y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;\n        z = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;\n\n        return [x, y, z];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        var mode = last(args) || 'lab';\n        lcha[0] = rnd2(lcha[0]) + '%';\n        lcha[1] = rnd2(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (mode === 'lcha' || (lcha.length > 3 && lcha[3] < 1)) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"lch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var sqrt$4 = Math.sqrt;\n    var atan2$2 = Math.atan2;\n    var round$5 = Math.round;\n\n    var lab2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'lab');\n        var l = ref[0];\n        var a = ref[1];\n        var b = ref[2];\n        var c = sqrt$4(a * a + b * b);\n        var h = (atan2$2(b, a) * RAD2DEG + 360) % 360;\n        if (round$5(c * 10000) === 0) { h = Number.NaN; }\n        return [l, c, h];\n    };\n\n    var rgb2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2lab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/multiply-matrices.js\n    function multiplyMatrices(A, B) {\n        var m = A.length;\n\n        if (!Array.isArray(A[0])) {\n            // A is vector, convert to [[a, b, c, ...]]\n            A = [A];\n        }\n\n        if (!Array.isArray(B[0])) {\n            // B is vector, convert to [[a], [b], [c], ...]]\n            B = B.map(function (x) { return [x]; });\n        }\n\n        var p = B[0].length;\n        var B_cols = B[0].map(function (_, i) { return B.map(function (x) { return x[i]; }); }); // transpose B\n        var product = A.map(function (row) { return B_cols.map(function (col) {\n                if (!Array.isArray(row)) {\n                    return col.reduce(function (a, c) { return a + c * row; }, 0);\n                }\n\n                return row.reduce(function (a, c, i) { return a + c * (col[i] || 0); }, 0);\n            }); }\n        );\n\n        if (m === 1) {\n            product = product[0]; // Avoid [[a, b, c, ...]]\n        }\n\n        if (p === 1) {\n            return product.map(function (x) { return x[0]; }); // Avoid [[a], [b], [c], ...]]\n        }\n\n        return product;\n    }\n\n    var rgb2oklab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var xyz = rgb2xyz(r, g, b);\n        var oklab = XYZ_to_OKLab(xyz);\n        return oklab.concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function XYZ_to_OKLab(XYZ) {\n        // Given XYZ relative to D65, convert to OKLab\n        var XYZtoLMS = [\n            [0.819022437996703, 0.3619062600528904, -0.1288737815209879],\n            [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],\n            [0.0481771893596242, 0.2642395317527308, 0.6335478284694309]\n        ];\n        var LMStoOKLab = [\n            [0.210454268309314, 0.7936177747023054, -0.0040720430116193],\n            [1.9779985324311684, -2.42859224204858, 0.450593709617411],\n            [0.0259040424655478, 0.7827717124575296, -0.8086757549230774]\n        ];\n\n        var LMS = multiplyMatrices(XYZtoLMS, XYZ);\n        // JavaScript Math.cbrt returns a sign-matched cube root\n        // beware if porting to other languages\n        // especially if tempted to use a general power function\n        return multiplyMatrices(\n            LMStoOKLab,\n            LMS.map(function (c) { return Math.cbrt(c); })\n        );\n        // L in range [0,1]. For use in CSS, multiply by 100 and add a percent\n    }\n\n    var oklab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        laba[0] = rnd2(laba[0] * 100) + '%';\n        laba[1] = rnd3(laba[1]);\n        laba[2] = rnd3(laba[2]);\n        if (laba.length > 3 && laba[3] < 1) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"oklab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var rgb2oklch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2oklab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var oklch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        lcha[0] = rnd2(lcha[0] * 100) + '%';\n        lcha[1] = rnd3(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (lcha.length > 3 && lcha[3] < 1) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"oklch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var round$4 = Math.round;\n\n    /*\n     * supported arguments:\n     * - rgb2css(r,g,b)\n     * - rgb2css(r,g,b,a)\n     * - rgb2css([r,g,b], mode)\n     * - rgb2css([r,g,b,a], mode)\n     * - rgb2css({r,g,b,a}, mode)\n     */\n    var rgb2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        var mode = last(args) || 'rgb';\n        if (mode.substr(0, 3) === 'hsl') {\n            return hsl2css(rgb2hsl$1(rgba), mode);\n        }\n        if (mode.substr(0, 3) === 'lab') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor = lab2css(rgb2lab(rgba), mode);\n            setLabWhitePoint(prevWhitePoint);\n            return cssColor;\n        }\n        if (mode.substr(0, 3) === 'lch') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor$1 = lch2css(rgb2lch(rgba), mode);\n            setLabWhitePoint(prevWhitePoint$1);\n            return cssColor$1;\n        }\n        if (mode.substr(0, 5) === 'oklab') {\n            return oklab2css(rgb2oklab(rgba));\n        }\n        if (mode.substr(0, 5) === 'oklch') {\n            return oklch2css(rgb2oklch(rgba));\n        }\n        rgba[0] = round$4(rgba[0]);\n        rgba[1] = round$4(rgba[1]);\n        rgba[2] = round$4(rgba[2]);\n        if (mode === 'rgba' || (rgba.length > 3 && rgba[3] < 1)) {\n            rgba[3] = '/ ' + (rgba.length > 3 ? rgba[3] : 1);\n            mode = 'rgba';\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (rgba.slice(0, mode === 'rgb' ? 3 : 4).join(' ')) + \")\");\n    };\n\n    var hsl2rgb = function () {\n        var assign;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hsl');\n        var h = args[0];\n        var s = args[1];\n        var l = args[2];\n        var r, g, b;\n        if (s === 0) {\n            r = g = b = l * 255;\n        } else {\n            var t3 = [0, 0, 0];\n            var c = [0, 0, 0];\n            var t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;\n            var t1 = 2 * l - t2;\n            var h_ = h / 360;\n            t3[0] = h_ + 1 / 3;\n            t3[1] = h_;\n            t3[2] = h_ - 1 / 3;\n            for (var i = 0; i < 3; i++) {\n                if (t3[i] < 0) { t3[i] += 1; }\n                if (t3[i] > 1) { t3[i] -= 1; }\n                if (6 * t3[i] < 1) { c[i] = t1 + (t2 - t1) * 6 * t3[i]; }\n                else if (2 * t3[i] < 1) { c[i] = t2; }\n                else if (3 * t3[i] < 2) { c[i] = t1 + (t2 - t1) * (2 / 3 - t3[i]) * 6; }\n                else { c[i] = t1; }\n            }\n            (assign = [c[0] * 255, c[1] * 255, c[2] * 255], r = assign[0], g = assign[1], b = assign[2]);\n        }\n        if (args.length > 3) {\n            // keep alpha channel\n            return [r, g, b, args[3]];\n        }\n        return [r, g, b, 1];\n    };\n\n    /*\n     * L* [0..100]\n     * a [-100..100]\n     * b [-100..100]\n     */\n    var lab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var ref = lab2xyz(L, a, b);\n        var x = ref[0];\n        var y = ref[1];\n        var z = ref[2];\n        var ref$1 = xyz2rgb(x, y, z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_, args.length > 3 ? args[3] : 1];\n    };\n\n    var lab2xyz = function (L, a, b) {\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var kKE = labConstants.kKE;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var fy = (L + 16.0) / 116.0;\n        var fx = 0.002 * a + fy;\n        var fz = fy - 0.005 * b;\n\n        var fx3 = fx * fx * fx;\n        var fz3 = fz * fz * fz;\n\n        var xr = fx3 > kE ? fx3 : (116.0 * fx - 16.0) / kK;\n        var yr = L > kKE ? Math.pow((L + 16.0) / 116.0, 3.0) : L / kK;\n        var zr = fz3 > kE ? fz3 : (116.0 * fz - 16.0) / kK;\n\n        var x = xr * Xn;\n        var y = yr * Yn;\n        var z = zr * Zn;\n\n        return [x, y, z];\n    };\n\n    var compand = function (linear) {\n        /* sRGB */\n        var sign = Math.sign(linear);\n        linear = Math.abs(linear);\n        return (\n            (linear <= 0.0031308\n                ? linear * 12.92\n                : 1.055 * Math.pow(linear, 1.0 / 2.4) - 0.055) * sign\n        );\n    };\n\n    var xyz2rgb = function (x, y, z) {\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var MtxXYZ2RGB = labConstants.MtxXYZ2RGB;\n        var RefWhiteRGB = labConstants.RefWhiteRGB;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cs = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var Ad =\n            RefWhiteRGB.X * MtxAdaptMa.m00 +\n            RefWhiteRGB.Y * MtxAdaptMa.m10 +\n            RefWhiteRGB.Z * MtxAdaptMa.m20;\n        var Bd =\n            RefWhiteRGB.X * MtxAdaptMa.m01 +\n            RefWhiteRGB.Y * MtxAdaptMa.m11 +\n            RefWhiteRGB.Z * MtxAdaptMa.m21;\n        var Cd =\n            RefWhiteRGB.X * MtxAdaptMa.m02 +\n            RefWhiteRGB.Y * MtxAdaptMa.m12 +\n            RefWhiteRGB.Z * MtxAdaptMa.m22;\n\n        var X1 =\n            (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20) *\n            (Ad / As);\n        var Y1 =\n            (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21) *\n            (Bd / Bs);\n        var Z1 =\n            (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22) *\n            (Cd / Cs);\n\n        var X2 =\n            X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;\n        var Y2 =\n            X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;\n        var Z2 =\n            X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;\n\n        var r = compand(\n            X2 * MtxXYZ2RGB.m00 + Y2 * MtxXYZ2RGB.m10 + Z2 * MtxXYZ2RGB.m20\n        );\n        var g = compand(\n            X2 * MtxXYZ2RGB.m01 + Y2 * MtxXYZ2RGB.m11 + Z2 * MtxXYZ2RGB.m21\n        );\n        var b = compand(\n            X2 * MtxXYZ2RGB.m02 + Y2 * MtxXYZ2RGB.m12 + Z2 * MtxXYZ2RGB.m22\n        );\n\n        return [r * 255, g * 255, b * 255];\n    };\n\n    var sin$3 = Math.sin;\n    var cos$4 = Math.cos;\n\n    var lch2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n        These formulas were invented by David Dalrymple to obtain maximum contrast without going\n        out of gamut if the parameters are in the range 0-1.\n\n        A saturation multiplier was added by Gregor Aisch\n        */\n        var ref = unpack(args, 'lch');\n        var l = ref[0];\n        var c = ref[1];\n        var h = ref[2];\n        if (isNaN(h)) { h = 0; }\n        h = h * DEG2RAD;\n        return [l, cos$4(h) * c, sin$3(h) * c];\n    };\n\n    var lch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = lab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var oklab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var rest = args.slice(3);\n        var ref = OKLab_to_XYZ([L, a, b]);\n        var X = ref[0];\n        var Y = ref[1];\n        var Z = ref[2];\n        var ref$1 = xyz2rgb(X, Y, Z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function OKLab_to_XYZ(OKLab) {\n        // Given OKLab, convert to XYZ relative to D65\n        var LMStoXYZ = [\n            [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],\n            [-0.0405757452148008, 1.112286803280317, -0.0717110580655164],\n            [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]\n        ];\n        var OKLabtoLMS = [\n            [1.0, 0.3963377773761749, 0.2158037573099136],\n            [1.0, -0.1055613458156586, -0.0638541728258133],\n            [1.0, -0.0894841775298119, -1.2914855480194092]\n        ];\n\n        var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab);\n        return multiplyMatrices(\n            LMStoXYZ,\n            LMSnl.map(function (c) { return Math.pow( c, 3 ); })\n        );\n    }\n\n    var oklch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var rest = args.slice(3);\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = oklab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var INT_OR_PCT = /((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source;\n    var FLOAT_OR_PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source;\n    var PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source;\n    var RE_S = /\\s*/.source;\n    var SEP = /\\s+/.source;\n    var COMMA = /\\s*,\\s*/.source;\n    var ANLGE = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source;\n    var ALPHA = /\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source;\n\n    // e.g. rgb(250 20 0), rgb(100% 50% 20%), rgb(100% 50% 20% / 0.5)\n    var RE_RGB = new RegExp(\n        '^rgba?\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_RGB_LEGACY = new RegExp(\n        '^rgb\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n    var RE_RGBA_LEGACY = new RegExp(\n        '^rgba\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT, FLOAT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n\n    var RE_HSL = new RegExp(\n        '^hsla?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(SEP) + ALPHA + '\\\\)$'\n    );\n    var RE_HSL_LEGACY = new RegExp(\n        '^hsl?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(COMMA) + RE_S + '\\\\)$'\n    );\n    var RE_HSLA_LEGACY =\n        /^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/;\n\n    var RE_LAB = new RegExp(\n        '^lab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_LCH = new RegExp(\n        '^lch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLAB = new RegExp(\n        '^oklab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLCH = new RegExp(\n        '^oklch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n\n    var round$3 = Math.round;\n\n    var roundRGB = function (rgb) {\n        return rgb.map(function (v, i) { return (i <= 2 ? limit(round$3(v), 0, 255) : v); });\n    };\n\n    var percentToAbsolute = function (pct, min, max, signed) {\n        if ( min === void 0 ) min = 0;\n        if ( max === void 0 ) max = 100;\n        if ( signed === void 0 ) signed = false;\n\n        if (typeof pct === 'string' && pct.endsWith('%')) {\n            pct = parseFloat(pct.substring(0, pct.length - 1)) / 100;\n            if (signed) {\n                // signed percentages are in the range -100% to 100%\n                pct = min + (pct + 1) * 0.5 * (max - min);\n            } else {\n                pct = min + pct * (max - min);\n            }\n        }\n        return +pct;\n    };\n\n    var noneToValue = function (v, noneValue) {\n        return v === 'none' ? noneValue : v;\n    };\n\n    var css2rgb = function (css) {\n        css = css.toLowerCase().trim();\n\n        if (css === 'transparent') {\n            return [0, 0, 0, 0];\n        }\n\n        var m;\n\n        if (input.format.named) {\n            try {\n                return input.format.named(css);\n                // eslint-disable-next-line\n            } catch (e) {}\n        }\n\n        // rgb(250 20 0) or rgb(250,20,0)\n        if ((m = css.match(RE_RGB)) || (m = css.match(RE_RGB_LEGACY))) {\n            var rgb = m.slice(1, 4);\n            for (var i = 0; i < 3; i++) {\n                rgb[i] = +percentToAbsolute(noneToValue(rgb[i], 0), 0, 255);\n            }\n            rgb = roundRGB(rgb);\n            var alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb[3] = alpha; // default alpha\n            return rgb;\n        }\n\n        // rgba(250,20,0,0.4)\n        if ((m = css.match(RE_RGBA_LEGACY))) {\n            var rgb$1 = m.slice(1, 5);\n            for (var i$1 = 0; i$1 < 4; i$1++) {\n                rgb$1[i$1] = +percentToAbsolute(rgb$1[i$1], 0, 255);\n            }\n            return rgb$1;\n        }\n\n        // hsl(0,100%,50%)\n        if ((m = css.match(RE_HSL)) || (m = css.match(RE_HSL_LEGACY))) {\n            var hsl = m.slice(1, 4);\n            hsl[0] = +noneToValue(hsl[0].replace('deg', ''), 0);\n            hsl[1] = +percentToAbsolute(noneToValue(hsl[1], 0), 0, 100) * 0.01;\n            hsl[2] = +percentToAbsolute(noneToValue(hsl[2], 0), 0, 100) * 0.01;\n            var rgb$2 = roundRGB(hsl2rgb(hsl));\n            var alpha$1 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$2[3] = alpha$1;\n            return rgb$2;\n        }\n\n        // hsla(0,100%,50%,0.5)\n        if ((m = css.match(RE_HSLA_LEGACY))) {\n            var hsl$1 = m.slice(1, 4);\n            hsl$1[1] *= 0.01;\n            hsl$1[2] *= 0.01;\n            var rgb$3 = hsl2rgb(hsl$1);\n            for (var i$2 = 0; i$2 < 3; i$2++) {\n                rgb$3[i$2] = round$3(rgb$3[i$2]);\n            }\n            rgb$3[3] = +m[4]; // default alpha = 1\n            return rgb$3;\n        }\n\n        if ((m = css.match(RE_LAB))) {\n            var lab = m.slice(1, 4);\n            lab[0] = percentToAbsolute(noneToValue(lab[0], 0), 0, 100);\n            lab[1] = percentToAbsolute(noneToValue(lab[1], 0), -125, 125, true);\n            lab[2] = percentToAbsolute(noneToValue(lab[2], 0), -125, 125, true);\n            // convert to D50 Lab whitepoint\n            var wp = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$4 = roundRGB(lab2rgb(lab));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp);\n            var alpha$2 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$4[3] = alpha$2;\n            return rgb$4;\n        }\n\n        if ((m = css.match(RE_LCH))) {\n            var lch = m.slice(1, 4);\n            lch[0] = percentToAbsolute(lch[0], 0, 100);\n            lch[1] = percentToAbsolute(noneToValue(lch[1], 0), 0, 150, false);\n            lch[2] = +noneToValue(lch[2].replace('deg', ''), 0);\n            // convert to D50 Lab whitepoint\n            var wp$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$5 = roundRGB(lch2rgb(lch));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp$1);\n            var alpha$3 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$5[3] = alpha$3;\n            return rgb$5;\n        }\n\n        if ((m = css.match(RE_OKLAB))) {\n            var oklab = m.slice(1, 4);\n            oklab[0] = percentToAbsolute(noneToValue(oklab[0], 0), 0, 1);\n            oklab[1] = percentToAbsolute(noneToValue(oklab[1], 0), -0.4, 0.4, true);\n            oklab[2] = percentToAbsolute(noneToValue(oklab[2], 0), -0.4, 0.4, true);\n            var rgb$6 = roundRGB(oklab2rgb(oklab));\n            var alpha$4 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$6[3] = alpha$4;\n            return rgb$6;\n        }\n\n        if ((m = css.match(RE_OKLCH))) {\n            var oklch = m.slice(1, 4);\n            oklch[0] = percentToAbsolute(noneToValue(oklch[0], 0), 0, 1);\n            oklch[1] = percentToAbsolute(noneToValue(oklch[1], 0), 0, 0.4, false);\n            oklch[2] = +noneToValue(oklch[2].replace('deg', ''), 0);\n            var rgb$7 = roundRGB(oklch2rgb(oklch));\n            var alpha$5 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$7[3] = alpha$5;\n            return rgb$7;\n        }\n    };\n\n    css2rgb.test = function (s) {\n        return (\n            // modern\n            RE_RGB.test(s) ||\n            RE_HSL.test(s) ||\n            RE_LAB.test(s) ||\n            RE_LCH.test(s) ||\n            RE_OKLAB.test(s) ||\n            RE_OKLCH.test(s) ||\n            // legacy\n            RE_RGB_LEGACY.test(s) ||\n            RE_RGBA_LEGACY.test(s) ||\n            RE_HSL_LEGACY.test(s) ||\n            RE_HSLA_LEGACY.test(s) ||\n            s === 'transparent'\n        );\n    };\n\n    Color.prototype.css = function (mode) {\n        return rgb2css(this._rgb, mode);\n    };\n\n    var css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['css']) ));\n    };\n    chroma.css = css;\n\n    input.format.css = css2rgb;\n\n    input.autodetect.push({\n        p: 5,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (!rest.length && type(h) === 'string' && css2rgb.test(h)) {\n                return 'css';\n            }\n        }\n    });\n\n    input.format.gl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgb = unpack(args, 'rgba');\n        rgb[0] *= 255;\n        rgb[1] *= 255;\n        rgb[2] *= 255;\n        return rgb;\n    };\n\n    var gl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['gl']) ));\n    };\n    chroma.gl = gl;\n\n    Color.prototype.gl = function () {\n        var rgb = this._rgb;\n        return [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, rgb[3]];\n    };\n\n    var floor$3 = Math.floor;\n\n    /*\n     * this is basically just HSV with some minor tweaks\n     *\n     * hue.. [0..360]\n     * chroma .. [0..1]\n     * grayness .. [0..1]\n     */\n\n    var hcg2rgb = function () {\n        var assign, assign$1, assign$2, assign$3, assign$4, assign$5;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hcg');\n        var h = args[0];\n        var c = args[1];\n        var _g = args[2];\n        var r, g, b;\n        _g = _g * 255;\n        var _c = c * 255;\n        if (c === 0) {\n            r = g = b = _g;\n        } else {\n            if (h === 360) { h = 0; }\n            if (h > 360) { h -= 360; }\n            if (h < 0) { h += 360; }\n            h /= 60;\n            var i = floor$3(h);\n            var f = h - i;\n            var p = _g * (1 - c);\n            var q = p + _c * (1 - f);\n            var t = p + _c * f;\n            var v = p + _c;\n            switch (i) {\n                case 0:\n                    (assign = [v, t, p], r = assign[0], g = assign[1], b = assign[2]);\n                    break;\n                case 1:\n                    (assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]);\n                    break;\n                case 2:\n                    (assign$2 = [p, v, t], r = assign$2[0], g = assign$2[1], b = assign$2[2]);\n                    break;\n                case 3:\n                    (assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]);\n                    break;\n                case 4:\n                    (assign$4 = [t, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]);\n                    break;\n                case 5:\n                    (assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]);\n                    break;\n            }\n        }\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var rgb2hcg = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var minRgb = min$3(r, g, b);\n        var maxRgb = max$3(r, g, b);\n        var delta = maxRgb - minRgb;\n        var c = (delta * 100) / 255;\n        var _g = (minRgb / (255 - delta)) * 100;\n        var h;\n        if (delta === 0) {\n            h = Number.NaN;\n        } else {\n            if (r === maxRgb) { h = (g - b) / delta; }\n            if (g === maxRgb) { h = 2 + (b - r) / delta; }\n            if (b === maxRgb) { h = 4 + (r - g) / delta; }\n            h *= 60;\n            if (h < 0) { h += 360; }\n        }\n        return [h, c, _g];\n    };\n\n    Color.prototype.hcg = function () {\n        return rgb2hcg(this._rgb);\n    };\n\n    var hcg$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hcg']) ));\n    };\n    chroma.hcg = hcg$1;\n\n    input.format.hcg = hcg2rgb;\n\n    input.autodetect.push({\n        p: 1,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hcg');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hcg';\n            }\n        }\n    });\n\n    var RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\n    var RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/;\n\n    var hex2rgb = function (hex) {\n        if (hex.match(RE_HEX)) {\n            // remove optional leading #\n            if (hex.length === 4 || hex.length === 7) {\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full six-digit\n            if (hex.length === 3) {\n                hex = hex.split('');\n                hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n            }\n            var u = parseInt(hex, 16);\n            var r = u >> 16;\n            var g = (u >> 8) & 0xff;\n            var b = u & 0xff;\n            return [r, g, b, 1];\n        }\n\n        // match rgba hex format, eg #FF000077\n        if (hex.match(RE_HEXA)) {\n            if (hex.length === 5 || hex.length === 9) {\n                // remove optional leading #\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full eight-digit\n            if (hex.length === 4) {\n                hex = hex.split('');\n                hex =\n                    hex[0] +\n                    hex[0] +\n                    hex[1] +\n                    hex[1] +\n                    hex[2] +\n                    hex[2] +\n                    hex[3] +\n                    hex[3];\n            }\n            var u$1 = parseInt(hex, 16);\n            var r$1 = (u$1 >> 24) & 0xff;\n            var g$1 = (u$1 >> 16) & 0xff;\n            var b$1 = (u$1 >> 8) & 0xff;\n            var a = Math.round(((u$1 & 0xff) / 0xff) * 100) / 100;\n            return [r$1, g$1, b$1, a];\n        }\n\n        // we used to check for css colors here\n        // if _input.css? and rgb = _input.css hex\n        //     return rgb\n\n        throw new Error((\"unknown hex color: \" + hex));\n    };\n\n    var round$2 = Math.round;\n\n    var rgb2hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgba');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var a = ref[3];\n        var mode = last(args) || 'auto';\n        if (a === undefined) { a = 1; }\n        if (mode === 'auto') {\n            mode = a < 1 ? 'rgba' : 'rgb';\n        }\n        r = round$2(r);\n        g = round$2(g);\n        b = round$2(b);\n        var u = (r << 16) | (g << 8) | b;\n        var str = '000000' + u.toString(16); //#.toUpperCase();\n        str = str.substr(str.length - 6);\n        var hxa = '0' + round$2(a * 255).toString(16);\n        hxa = hxa.substr(hxa.length - 2);\n        switch (mode.toLowerCase()) {\n            case 'rgba':\n                return (\"#\" + str + hxa);\n            case 'argb':\n                return (\"#\" + hxa + str);\n            default:\n                return (\"#\" + str);\n        }\n    };\n\n    Color.prototype.hex = function (mode) {\n        return rgb2hex(this._rgb, mode);\n    };\n\n    var hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hex']) ));\n    };\n    chroma.hex = hex;\n\n    input.format.hex = hex2rgb;\n    input.autodetect.push({\n        p: 4,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (\n                !rest.length &&\n                type(h) === 'string' &&\n                [3, 4, 5, 6, 7, 8, 9].indexOf(h.length) >= 0\n            ) {\n                return 'hex';\n            }\n        }\n    });\n\n    var cos$3 = Math.cos;\n\n    /*\n     * hue [0..360]\n     * saturation [0..1]\n     * intensity [0..1]\n     */\n    var hsi2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        borrowed from here:\n        http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/hsi2rgb.cpp\n        */\n        args = unpack(args, 'hsi');\n        var h = args[0];\n        var s = args[1];\n        var i = args[2];\n        var r, g, b;\n\n        if (isNaN(h)) { h = 0; }\n        if (isNaN(s)) { s = 0; }\n        // normalize hue\n        if (h > 360) { h -= 360; }\n        if (h < 0) { h += 360; }\n        h /= 360;\n        if (h < 1 / 3) {\n            b = (1 - s) / 3;\n            r = (1 + (s * cos$3(TWOPI * h)) / cos$3(PITHIRD - TWOPI * h)) / 3;\n            g = 1 - (b + r);\n        } else if (h < 2 / 3) {\n            h -= 1 / 3;\n            r = (1 - s) / 3;\n            g = (1 + (s * cos$3(TWOPI * h)) / cos$3(PITHIRD - TWOPI * h)) / 3;\n            b = 1 - (r + g);\n        } else {\n            h -= 2 / 3;\n            g = (1 - s) / 3;\n            b = (1 + (s * cos$3(TWOPI * h)) / cos$3(PITHIRD - TWOPI * h)) / 3;\n            r = 1 - (g + b);\n        }\n        r = limit(i * r * 3);\n        g = limit(i * g * 3);\n        b = limit(i * b * 3);\n        return [r * 255, g * 255, b * 255, args.length > 3 ? args[3] : 1];\n    };\n\n    var min$2 = Math.min;\n    var sqrt$3 = Math.sqrt;\n    var acos = Math.acos;\n\n    var rgb2hsi = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        borrowed from here:\n        http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/rgb2hsi.cpp\n        */\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        r /= 255;\n        g /= 255;\n        b /= 255;\n        var h;\n        var min_ = min$2(r, g, b);\n        var i = (r + g + b) / 3;\n        var s = i > 0 ? 1 - min_ / i : 0;\n        if (s === 0) {\n            h = NaN;\n        } else {\n            h = (r - g + (r - b)) / 2;\n            h /= sqrt$3((r - g) * (r - g) + (r - b) * (g - b));\n            h = acos(h);\n            if (b > g) {\n                h = TWOPI - h;\n            }\n            h /= TWOPI;\n        }\n        return [h * 360, s, i];\n    };\n\n    Color.prototype.hsi = function () {\n        return rgb2hsi(this._rgb);\n    };\n\n    var hsi$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsi']) ));\n    };\n    chroma.hsi = hsi$1;\n\n    input.format.hsi = hsi2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsi');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsi';\n            }\n        }\n    });\n\n    Color.prototype.hsl = function () {\n        return rgb2hsl$1(this._rgb);\n    };\n\n    var hsl$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsl']) ));\n    };\n    chroma.hsl = hsl$1;\n\n    input.format.hsl = hsl2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsl');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsl';\n            }\n        }\n    });\n\n    var floor$2 = Math.floor;\n\n    var hsv2rgb = function () {\n        var assign, assign$1, assign$2, assign$3, assign$4, assign$5;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hsv');\n        var h = args[0];\n        var s = args[1];\n        var v = args[2];\n        var r, g, b;\n        v *= 255;\n        if (s === 0) {\n            r = g = b = v;\n        } else {\n            if (h === 360) { h = 0; }\n            if (h > 360) { h -= 360; }\n            if (h < 0) { h += 360; }\n            h /= 60;\n\n            var i = floor$2(h);\n            var f = h - i;\n            var p = v * (1 - s);\n            var q = v * (1 - s * f);\n            var t = v * (1 - s * (1 - f));\n\n            switch (i) {\n                case 0:\n                    (assign = [v, t, p], r = assign[0], g = assign[1], b = assign[2]);\n                    break;\n                case 1:\n                    (assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]);\n                    break;\n                case 2:\n                    (assign$2 = [p, v, t], r = assign$2[0], g = assign$2[1], b = assign$2[2]);\n                    break;\n                case 3:\n                    (assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]);\n                    break;\n                case 4:\n                    (assign$4 = [t, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]);\n                    break;\n                case 5:\n                    (assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]);\n                    break;\n            }\n        }\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var min$1 = Math.min;\n    var max$1 = Math.max;\n\n    /*\n     * supported arguments:\n     * - rgb2hsv(r,g,b)\n     * - rgb2hsv([r,g,b])\n     * - rgb2hsv({r,g,b})\n     */\n    var rgb2hsl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'rgb');\n        var r = args[0];\n        var g = args[1];\n        var b = args[2];\n        var min_ = min$1(r, g, b);\n        var max_ = max$1(r, g, b);\n        var delta = max_ - min_;\n        var h, s, v;\n        v = max_ / 255.0;\n        if (max_ === 0) {\n            h = Number.NaN;\n            s = 0;\n        } else {\n            s = delta / max_;\n            if (r === max_) { h = (g - b) / delta; }\n            if (g === max_) { h = 2 + (b - r) / delta; }\n            if (b === max_) { h = 4 + (r - g) / delta; }\n            h *= 60;\n            if (h < 0) { h += 360; }\n        }\n        return [h, s, v];\n    };\n\n    Color.prototype.hsv = function () {\n        return rgb2hsl(this._rgb);\n    };\n\n    var hsv$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsv']) ));\n    };\n    chroma.hsv = hsv$1;\n\n    input.format.hsv = hsv2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsv');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsv';\n            }\n        }\n    });\n\n    Color.prototype.lab = function () {\n        return rgb2lab(this._rgb);\n    };\n\n    var lab$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['lab']) ));\n    };\n    Object.assign(chroma, { lab: lab$1, getLabWhitePoint: getLabWhitePoint, setLabWhitePoint: setLabWhitePoint });\n\n    input.format.lab = lab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'lab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'lab';\n            }\n        }\n    });\n\n    var hcl2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var hcl = reverse3(unpack(args, 'hcl'));\n        return lch2rgb.apply(void 0, hcl);\n    };\n\n    Color.prototype.lch = function () {\n        return rgb2lch(this._rgb);\n    };\n    Color.prototype.hcl = function () {\n        return reverse3(rgb2lch(this._rgb));\n    };\n\n    var lch$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['lch']) ));\n    };\n    var hcl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hcl']) ));\n    };\n\n    Object.assign(chroma, { lch: lch$1, hcl: hcl });\n\n    input.format.lch = lch2rgb;\n    input.format.hcl = hcl2rgb;\n    ['lch', 'hcl'].forEach(function (m) { return input.autodetect.push({\n            p: 2,\n            test: function () {\n                var args = [], len = arguments.length;\n                while ( len-- ) args[ len ] = arguments[ len ];\n\n                args = unpack(args, m);\n                if (type(args) === 'array' && args.length === 3) {\n                    return m;\n                }\n            }\n        }); }\n    );\n\n    var num2rgb = function (num) {\n        if (type(num) == 'number' && num >= 0 && num <= 0xffffff) {\n            var r = num >> 16;\n            var g = (num >> 8) & 0xff;\n            var b = num & 0xff;\n            return [r, g, b, 1];\n        }\n        throw new Error('unknown num color: ' + num);\n    };\n\n    var rgb2num = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        return (r << 16) + (g << 8) + b;\n    };\n\n    Color.prototype.num = function () {\n        return rgb2num(this._rgb);\n    };\n\n    var num$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['num']) ));\n    };\n\n    Object.assign(chroma, { num: num$1 });\n\n    input.format.num = num2rgb;\n\n    input.autodetect.push({\n        p: 5,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            if (\n                args.length === 1 &&\n                type(args[0]) === 'number' &&\n                args[0] >= 0 &&\n                args[0] <= 0xffffff\n            ) {\n                return 'num';\n            }\n        }\n    });\n\n    var round$1 = Math.round;\n\n    Color.prototype.rgb = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        if (rnd === false) { return this._rgb.slice(0, 3); }\n        return this._rgb.slice(0, 3).map(round$1);\n    };\n\n    Color.prototype.rgba = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        return this._rgb.slice(0, 4).map(function (v, i) {\n            return i < 3 ? (rnd === false ? v : round$1(v)) : v;\n        });\n    };\n\n    var rgb$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['rgb']) ));\n    };\n    Object.assign(chroma, { rgb: rgb$1 });\n\n    input.format.rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        if (rgba[3] === undefined) { rgba[3] = 1; }\n        return rgba;\n    };\n\n    input.autodetect.push({\n        p: 3,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'rgba');\n            if (\n                type(args) === 'array' &&\n                (args.length === 3 ||\n                    (args.length === 4 &&\n                        type(args[3]) == 'number' &&\n                        args[3] >= 0 &&\n                        args[3] <= 1))\n            ) {\n                return 'rgb';\n            }\n        }\n    });\n\n    /*\n     * Based on implementation by Neil Bartlett\n     * https://github.com/neilbartlett/color-temperature\n     */\n\n    var log$1 = Math.log;\n\n    var temperature2rgb = function (kelvin) {\n        var temp = kelvin / 100;\n        var r, g, b;\n        if (temp < 66) {\n            r = 255;\n            g =\n                temp < 6\n                    ? 0\n                    : -155.25485562709179 -\n                      0.44596950469579133 * (g = temp - 2) +\n                      104.49216199393888 * log$1(g);\n            b =\n                temp < 20\n                    ? 0\n                    : -254.76935184120902 +\n                      0.8274096064007395 * (b = temp - 10) +\n                      115.67994401066147 * log$1(b);\n        } else {\n            r =\n                351.97690566805693 +\n                0.114206453784165 * (r = temp - 55) -\n                40.25366309332127 * log$1(r);\n            g =\n                325.4494125711974 +\n                0.07943456536662342 * (g = temp - 50) -\n                28.0852963507957 * log$1(g);\n            b = 255;\n        }\n        return [r, g, b, 1];\n    };\n\n    /*\n     * Based on implementation by Neil Bartlett\n     * https://github.com/neilbartlett/color-temperature\n     **/\n\n    var round = Math.round;\n\n    var rgb2temperature = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgb = unpack(args, 'rgb');\n        var r = rgb[0],\n            b = rgb[2];\n        var minTemp = 1000;\n        var maxTemp = 40000;\n        var eps = 0.4;\n        var temp;\n        while (maxTemp - minTemp > eps) {\n            temp = (maxTemp + minTemp) * 0.5;\n            var rgb$1 = temperature2rgb(temp);\n            if (rgb$1[2] / rgb$1[0] >= b / r) {\n                maxTemp = temp;\n            } else {\n                minTemp = temp;\n            }\n        }\n        return round(temp);\n    };\n\n    Color.prototype.temp =\n        Color.prototype.kelvin =\n        Color.prototype.temperature =\n            function () {\n                return rgb2temperature(this._rgb);\n            };\n\n    var temp = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['temp']) ));\n    };\n    Object.assign(chroma, { temp: temp, kelvin: temp, temperature: temp });\n\n    input.format.temp =\n        input.format.kelvin =\n        input.format.temperature =\n            temperature2rgb;\n\n    Color.prototype.oklab = function () {\n        return rgb2oklab(this._rgb);\n    };\n\n    var oklab$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['oklab']) ));\n    };\n    Object.assign(chroma, { oklab: oklab$1 });\n\n    input.format.oklab = oklab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'oklab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'oklab';\n            }\n        }\n    });\n\n    Color.prototype.oklch = function () {\n        return rgb2oklch(this._rgb);\n    };\n\n    var oklch$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['oklch']) ));\n    };\n    Object.assign(chroma, { oklch: oklch$1 });\n\n    input.format.oklch = oklch2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'oklch');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'oklch';\n            }\n        }\n    });\n\n    /**\n    \tX11 color names\n\n    \thttp://www.w3.org/TR/css3-color/#svg-color\n    */\n\n    var w3cx11 = {\n        aliceblue: '#f0f8ff',\n        antiquewhite: '#faebd7',\n        aqua: '#00ffff',\n        aquamarine: '#7fffd4',\n        azure: '#f0ffff',\n        beige: '#f5f5dc',\n        bisque: '#ffe4c4',\n        black: '#000000',\n        blanchedalmond: '#ffebcd',\n        blue: '#0000ff',\n        blueviolet: '#8a2be2',\n        brown: '#a52a2a',\n        burlywood: '#deb887',\n        cadetblue: '#5f9ea0',\n        chartreuse: '#7fff00',\n        chocolate: '#d2691e',\n        coral: '#ff7f50',\n        cornflowerblue: '#6495ed',\n        cornsilk: '#fff8dc',\n        crimson: '#dc143c',\n        cyan: '#00ffff',\n        darkblue: '#00008b',\n        darkcyan: '#008b8b',\n        darkgoldenrod: '#b8860b',\n        darkgray: '#a9a9a9',\n        darkgreen: '#006400',\n        darkgrey: '#a9a9a9',\n        darkkhaki: '#bdb76b',\n        darkmagenta: '#8b008b',\n        darkolivegreen: '#556b2f',\n        darkorange: '#ff8c00',\n        darkorchid: '#9932cc',\n        darkred: '#8b0000',\n        darksalmon: '#e9967a',\n        darkseagreen: '#8fbc8f',\n        darkslateblue: '#483d8b',\n        darkslategray: '#2f4f4f',\n        darkslategrey: '#2f4f4f',\n        darkturquoise: '#00ced1',\n        darkviolet: '#9400d3',\n        deeppink: '#ff1493',\n        deepskyblue: '#00bfff',\n        dimgray: '#696969',\n        dimgrey: '#696969',\n        dodgerblue: '#1e90ff',\n        firebrick: '#b22222',\n        floralwhite: '#fffaf0',\n        forestgreen: '#228b22',\n        fuchsia: '#ff00ff',\n        gainsboro: '#dcdcdc',\n        ghostwhite: '#f8f8ff',\n        gold: '#ffd700',\n        goldenrod: '#daa520',\n        gray: '#808080',\n        green: '#008000',\n        greenyellow: '#adff2f',\n        grey: '#808080',\n        honeydew: '#f0fff0',\n        hotpink: '#ff69b4',\n        indianred: '#cd5c5c',\n        indigo: '#4b0082',\n        ivory: '#fffff0',\n        khaki: '#f0e68c',\n        laserlemon: '#ffff54',\n        lavender: '#e6e6fa',\n        lavenderblush: '#fff0f5',\n        lawngreen: '#7cfc00',\n        lemonchiffon: '#fffacd',\n        lightblue: '#add8e6',\n        lightcoral: '#f08080',\n        lightcyan: '#e0ffff',\n        lightgoldenrod: '#fafad2',\n        lightgoldenrodyellow: '#fafad2',\n        lightgray: '#d3d3d3',\n        lightgreen: '#90ee90',\n        lightgrey: '#d3d3d3',\n        lightpink: '#ffb6c1',\n        lightsalmon: '#ffa07a',\n        lightseagreen: '#20b2aa',\n        lightskyblue: '#87cefa',\n        lightslategray: '#778899',\n        lightslategrey: '#778899',\n        lightsteelblue: '#b0c4de',\n        lightyellow: '#ffffe0',\n        lime: '#00ff00',\n        limegreen: '#32cd32',\n        linen: '#faf0e6',\n        magenta: '#ff00ff',\n        maroon: '#800000',\n        maroon2: '#7f0000',\n        maroon3: '#b03060',\n        mediumaquamarine: '#66cdaa',\n        mediumblue: '#0000cd',\n        mediumorchid: '#ba55d3',\n        mediumpurple: '#9370db',\n        mediumseagreen: '#3cb371',\n        mediumslateblue: '#7b68ee',\n        mediumspringgreen: '#00fa9a',\n        mediumturquoise: '#48d1cc',\n        mediumvioletred: '#c71585',\n        midnightblue: '#191970',\n        mintcream: '#f5fffa',\n        mistyrose: '#ffe4e1',\n        moccasin: '#ffe4b5',\n        navajowhite: '#ffdead',\n        navy: '#000080',\n        oldlace: '#fdf5e6',\n        olive: '#808000',\n        olivedrab: '#6b8e23',\n        orange: '#ffa500',\n        orangered: '#ff4500',\n        orchid: '#da70d6',\n        palegoldenrod: '#eee8aa',\n        palegreen: '#98fb98',\n        paleturquoise: '#afeeee',\n        palevioletred: '#db7093',\n        papayawhip: '#ffefd5',\n        peachpuff: '#ffdab9',\n        peru: '#cd853f',\n        pink: '#ffc0cb',\n        plum: '#dda0dd',\n        powderblue: '#b0e0e6',\n        purple: '#800080',\n        purple2: '#7f007f',\n        purple3: '#a020f0',\n        rebeccapurple: '#663399',\n        red: '#ff0000',\n        rosybrown: '#bc8f8f',\n        royalblue: '#4169e1',\n        saddlebrown: '#8b4513',\n        salmon: '#fa8072',\n        sandybrown: '#f4a460',\n        seagreen: '#2e8b57',\n        seashell: '#fff5ee',\n        sienna: '#a0522d',\n        silver: '#c0c0c0',\n        skyblue: '#87ceeb',\n        slateblue: '#6a5acd',\n        slategray: '#708090',\n        slategrey: '#708090',\n        snow: '#fffafa',\n        springgreen: '#00ff7f',\n        steelblue: '#4682b4',\n        tan: '#d2b48c',\n        teal: '#008080',\n        thistle: '#d8bfd8',\n        tomato: '#ff6347',\n        turquoise: '#40e0d0',\n        violet: '#ee82ee',\n        wheat: '#f5deb3',\n        white: '#ffffff',\n        whitesmoke: '#f5f5f5',\n        yellow: '#ffff00',\n        yellowgreen: '#9acd32'\n    };\n\n    Color.prototype.name = function () {\n        var hex = rgb2hex(this._rgb, 'rgb');\n        for (var i = 0, list = Object.keys(w3cx11); i < list.length; i += 1) {\n            var n = list[i];\n\n            if (w3cx11[n] === hex) { return n.toLowerCase(); }\n        }\n        return hex;\n    };\n\n    input.format.named = function (name) {\n        name = name.toLowerCase();\n        if (w3cx11[name]) { return hex2rgb(w3cx11[name]); }\n        throw new Error('unknown color name: ' + name);\n    };\n\n    input.autodetect.push({\n        p: 5,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (!rest.length && type(h) === 'string' && w3cx11[h.toLowerCase()]) {\n                return 'named';\n            }\n        }\n    });\n\n    Color.prototype.alpha = function (a, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        if (a !== undefined && type(a) === 'number') {\n            if (mutate) {\n                this._rgb[3] = a;\n                return this;\n            }\n            return new Color([this._rgb[0], this._rgb[1], this._rgb[2], a], 'rgb');\n        }\n        return this._rgb[3];\n    };\n\n    Color.prototype.clipped = function () {\n        return this._rgb._clipped || false;\n    };\n\n    Color.prototype.darken = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        var me = this;\n        var lab = me.lab();\n        lab[0] -= labConstants.Kn * amount;\n        return new Color(lab, 'lab').alpha(me.alpha(), true);\n    };\n\n    Color.prototype.brighten = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        return this.darken(-amount);\n    };\n\n    Color.prototype.darker = Color.prototype.darken;\n    Color.prototype.brighter = Color.prototype.brighten;\n\n    Color.prototype.get = function (mc) {\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) { return src[i]; }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    var pow$6 = Math.pow;\n\n    var EPS = 1e-7;\n    var MAX_ITER = 20;\n\n    Color.prototype.luminance = function (lum, mode) {\n        if ( mode === void 0 ) mode = 'rgb';\n\n        if (lum !== undefined && type(lum) === 'number') {\n            if (lum === 0) {\n                // return pure black\n                return new Color([0, 0, 0, this._rgb[3]], 'rgb');\n            }\n            if (lum === 1) {\n                // return pure white\n                return new Color([255, 255, 255, this._rgb[3]], 'rgb');\n            }\n            // compute new color using...\n            var cur_lum = this.luminance();\n            var max_iter = MAX_ITER;\n\n            var test = function (low, high) {\n                var mid = low.interpolate(high, 0.5, mode);\n                var lm = mid.luminance();\n                if (Math.abs(lum - lm) < EPS || !max_iter--) {\n                    // close enough\n                    return mid;\n                }\n                return lm > lum ? test(low, mid) : test(mid, high);\n            };\n\n            var rgb = (\n                cur_lum > lum\n                    ? test(new Color([0, 0, 0]), this)\n                    : test(this, new Color([255, 255, 255]))\n            ).rgb();\n            return new Color(rgb.concat( [this._rgb[3]]));\n        }\n        return rgb2luminance.apply(void 0, this._rgb.slice(0, 3));\n    };\n\n    var rgb2luminance = function (r, g, b) {\n        // relative luminance\n        // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\n        r = luminance_x(r);\n        g = luminance_x(g);\n        b = luminance_x(b);\n        return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n    };\n\n    var luminance_x = function (x) {\n        x /= 255;\n        return x <= 0.03928 ? x / 12.92 : pow$6((x + 0.055) / 1.055, 2.4);\n    };\n\n    var index = {};\n\n    function mix (col1, col2, f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 3;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 3 ];\n\n        var mode = rest[0] || 'lrgb';\n        if (!index[mode] && !rest.length) {\n            // fall back to the first supported mode\n            mode = Object.keys(index)[0];\n        }\n        if (!index[mode]) {\n            throw new Error((\"interpolation mode \" + mode + \" is not defined\"));\n        }\n        if (type(col1) !== 'object') { col1 = new Color(col1); }\n        if (type(col2) !== 'object') { col2 = new Color(col2); }\n        return index[mode](col1, col2, f).alpha(\n            col1.alpha() + f * (col2.alpha() - col1.alpha())\n        );\n    }\n\n    Color.prototype.mix = Color.prototype.interpolate = function (\n        col2,\n        f\n    ) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 2;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 2 ];\n\n        return mix.apply(void 0, [ this, col2, f ].concat( rest ));\n    };\n\n    Color.prototype.premultiply = function (mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        var rgb = this._rgb;\n        var a = rgb[3];\n        if (mutate) {\n            this._rgb = [rgb[0] * a, rgb[1] * a, rgb[2] * a, a];\n            return this;\n        } else {\n            return new Color([rgb[0] * a, rgb[1] * a, rgb[2] * a, a], 'rgb');\n        }\n    };\n\n    Color.prototype.saturate = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        var me = this;\n        var lch = me.lch();\n        lch[1] += labConstants.Kn * amount;\n        if (lch[1] < 0) { lch[1] = 0; }\n        return new Color(lch, 'lch').alpha(me.alpha(), true);\n    };\n\n    Color.prototype.desaturate = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        return this.saturate(-amount);\n    };\n\n    Color.prototype.set = function (mc, value, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) {\n                if (type(value) == 'string') {\n                    switch (value.charAt(0)) {\n                        case '+':\n                            src[i] += +value;\n                            break;\n                        case '-':\n                            src[i] += +value;\n                            break;\n                        case '*':\n                            src[i] *= +value.substr(1);\n                            break;\n                        case '/':\n                            src[i] /= +value.substr(1);\n                            break;\n                        default:\n                            src[i] = +value;\n                    }\n                } else if (type(value) === 'number') {\n                    src[i] = value;\n                } else {\n                    throw new Error(\"unsupported value for Color.set\");\n                }\n                var out = new Color(src, mode);\n                if (mutate) {\n                    this._rgb = out._rgb;\n                    return this;\n                }\n                return out;\n            }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    Color.prototype.tint = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'white', f ].concat( rest ));\n    };\n\n    Color.prototype.shade = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'black', f ].concat( rest ));\n    };\n\n    var rgb = function (col1, col2, f) {\n        var xyz0 = col1._rgb;\n        var xyz1 = col2._rgb;\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'rgb'\n        );\n    };\n\n    // register interpolator\n    index.rgb = rgb;\n\n    var sqrt$2 = Math.sqrt;\n    var pow$5 = Math.pow;\n\n    var lrgb = function (col1, col2, f) {\n        var ref = col1._rgb;\n        var x1 = ref[0];\n        var y1 = ref[1];\n        var z1 = ref[2];\n        var ref$1 = col2._rgb;\n        var x2 = ref$1[0];\n        var y2 = ref$1[1];\n        var z2 = ref$1[2];\n        return new Color(\n            sqrt$2(pow$5(x1, 2) * (1 - f) + pow$5(x2, 2) * f),\n            sqrt$2(pow$5(y1, 2) * (1 - f) + pow$5(y2, 2) * f),\n            sqrt$2(pow$5(z1, 2) * (1 - f) + pow$5(z2, 2) * f),\n            'rgb'\n        );\n    };\n\n    // register interpolator\n    index.lrgb = lrgb;\n\n    var lab = function (col1, col2, f) {\n        var xyz0 = col1.lab();\n        var xyz1 = col2.lab();\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'lab'\n        );\n    };\n\n    // register interpolator\n    index.lab = lab;\n\n    function interpolate_hsx (col1, col2, f, m) {\n        var assign, assign$1;\n\n        var xyz0, xyz1;\n        if (m === 'hsl') {\n            xyz0 = col1.hsl();\n            xyz1 = col2.hsl();\n        } else if (m === 'hsv') {\n            xyz0 = col1.hsv();\n            xyz1 = col2.hsv();\n        } else if (m === 'hcg') {\n            xyz0 = col1.hcg();\n            xyz1 = col2.hcg();\n        } else if (m === 'hsi') {\n            xyz0 = col1.hsi();\n            xyz1 = col2.hsi();\n        } else if (m === 'lch' || m === 'hcl') {\n            m = 'hcl';\n            xyz0 = col1.hcl();\n            xyz1 = col2.hcl();\n        } else if (m === 'oklch') {\n            xyz0 = col1.oklch().reverse();\n            xyz1 = col2.oklch().reverse();\n        }\n\n        var hue0, hue1, sat0, sat1, lbv0, lbv1;\n        if (m.substr(0, 1) === 'h' || m === 'oklch') {\n            (assign = xyz0, hue0 = assign[0], sat0 = assign[1], lbv0 = assign[2]);\n            (assign$1 = xyz1, hue1 = assign$1[0], sat1 = assign$1[1], lbv1 = assign$1[2]);\n        }\n\n        var sat, hue, lbv, dh;\n\n        if (!isNaN(hue0) && !isNaN(hue1)) {\n            // both colors have hue\n            if (hue1 > hue0 && hue1 - hue0 > 180) {\n                dh = hue1 - (hue0 + 360);\n            } else if (hue1 < hue0 && hue0 - hue1 > 180) {\n                dh = hue1 + 360 - hue0;\n            } else {\n                dh = hue1 - hue0;\n            }\n            hue = hue0 + f * dh;\n        } else if (!isNaN(hue0)) {\n            hue = hue0;\n            if ((lbv1 == 1 || lbv1 == 0) && m != 'hsv') { sat = sat0; }\n        } else if (!isNaN(hue1)) {\n            hue = hue1;\n            if ((lbv0 == 1 || lbv0 == 0) && m != 'hsv') { sat = sat1; }\n        } else {\n            hue = Number.NaN;\n        }\n\n        if (sat === undefined) { sat = sat0 + f * (sat1 - sat0); }\n        lbv = lbv0 + f * (lbv1 - lbv0);\n        return m === 'oklch'\n            ? new Color([lbv, sat, hue], m)\n            : new Color([hue, sat, lbv], m);\n    }\n\n    var lch = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'lch');\n    };\n\n    // register interpolator\n    index.lch = lch;\n    index.hcl = lch;\n\n    var num = function (col1, col2, f) {\n        var c1 = col1.num();\n        var c2 = col2.num();\n        return new Color(c1 + f * (c2 - c1), 'num');\n    };\n\n    // register interpolator\n    index.num = num;\n\n    var hcg = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hcg');\n    };\n\n    // register interpolator\n    index.hcg = hcg;\n\n    var hsi = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hsi');\n    };\n\n    // register interpolator\n    index.hsi = hsi;\n\n    var hsl = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hsl');\n    };\n\n    // register interpolator\n    index.hsl = hsl;\n\n    var hsv = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hsv');\n    };\n\n    // register interpolator\n    index.hsv = hsv;\n\n    var oklab = function (col1, col2, f) {\n        var xyz0 = col1.oklab();\n        var xyz1 = col2.oklab();\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'oklab'\n        );\n    };\n\n    // register interpolator\n    index.oklab = oklab;\n\n    var oklch = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'oklch');\n    };\n\n    // register interpolator\n    index.oklch = oklch;\n\n    var pow$4 = Math.pow;\n    var sqrt$1 = Math.sqrt;\n    var PI$1 = Math.PI;\n    var cos$2 = Math.cos;\n    var sin$2 = Math.sin;\n    var atan2$1 = Math.atan2;\n\n    function average (colors, mode, weights) {\n        if ( mode === void 0 ) mode = 'lrgb';\n        if ( weights === void 0 ) weights = null;\n\n        var l = colors.length;\n        if (!weights) { weights = Array.from(new Array(l)).map(function () { return 1; }); }\n        // normalize weights\n        var k =\n            l /\n            weights.reduce(function (a, b) {\n                return a + b;\n            });\n        weights.forEach(function (w, i) {\n            weights[i] *= k;\n        });\n        // convert colors to Color objects\n        colors = colors.map(function (c) { return new Color(c); });\n        if (mode === 'lrgb') {\n            return _average_lrgb(colors, weights);\n        }\n        var first = colors.shift();\n        var xyz = first.get(mode);\n        var cnt = [];\n        var dx = 0;\n        var dy = 0;\n        // initial color\n        for (var i = 0; i < xyz.length; i++) {\n            xyz[i] = (xyz[i] || 0) * weights[0];\n            cnt.push(isNaN(xyz[i]) ? 0 : weights[0]);\n            if (mode.charAt(i) === 'h' && !isNaN(xyz[i])) {\n                var A = (xyz[i] / 180) * PI$1;\n                dx += cos$2(A) * weights[0];\n                dy += sin$2(A) * weights[0];\n            }\n        }\n\n        var alpha = first.alpha() * weights[0];\n        colors.forEach(function (c, ci) {\n            var xyz2 = c.get(mode);\n            alpha += c.alpha() * weights[ci + 1];\n            for (var i = 0; i < xyz.length; i++) {\n                if (!isNaN(xyz2[i])) {\n                    cnt[i] += weights[ci + 1];\n                    if (mode.charAt(i) === 'h') {\n                        var A = (xyz2[i] / 180) * PI$1;\n                        dx += cos$2(A) * weights[ci + 1];\n                        dy += sin$2(A) * weights[ci + 1];\n                    } else {\n                        xyz[i] += xyz2[i] * weights[ci + 1];\n                    }\n                }\n            }\n        });\n\n        for (var i$1 = 0; i$1 < xyz.length; i$1++) {\n            if (mode.charAt(i$1) === 'h') {\n                var A$1 = (atan2$1(dy / cnt[i$1], dx / cnt[i$1]) / PI$1) * 180;\n                while (A$1 < 0) { A$1 += 360; }\n                while (A$1 >= 360) { A$1 -= 360; }\n                xyz[i$1] = A$1;\n            } else {\n                xyz[i$1] = xyz[i$1] / cnt[i$1];\n            }\n        }\n        alpha /= l;\n        return new Color(xyz, mode).alpha(alpha > 0.99999 ? 1 : alpha, true);\n    }\n    var _average_lrgb = function (colors, weights) {\n        var l = colors.length;\n        var xyz = [0, 0, 0, 0];\n        for (var i = 0; i < colors.length; i++) {\n            var col = colors[i];\n            var f = weights[i] / l;\n            var rgb = col._rgb;\n            xyz[0] += pow$4(rgb[0], 2) * f;\n            xyz[1] += pow$4(rgb[1], 2) * f;\n            xyz[2] += pow$4(rgb[2], 2) * f;\n            xyz[3] += rgb[3] * f;\n        }\n        xyz[0] = sqrt$1(xyz[0]);\n        xyz[1] = sqrt$1(xyz[1]);\n        xyz[2] = sqrt$1(xyz[2]);\n        if (xyz[3] > 0.9999999) { xyz[3] = 1; }\n        return new Color(clip_rgb(xyz));\n    };\n\n    // minimal multi-purpose interface\n\n\n    var pow$3 = Math.pow;\n\n    function scale (colors) {\n        // constructor\n        var _mode = 'rgb';\n        var _nacol = chroma('#ccc');\n        var _spread = 0;\n        // const _fixed = false;\n        var _positions = [0, 1];\n        var _domain = [0, 1];\n        var _pos = [];\n        var _padding = [0, 0];\n        var _classes = false;\n        var _colors = [];\n        var _out = false;\n        var _min = 0;\n        var _max = 1;\n        var _correctLightness = false;\n        var _colorCache = {};\n        var _useCache = true;\n        var _gamma = 1;\n\n        // private methods\n\n        var setColors = function (colors) {\n            colors = colors || ['#fff', '#000'];\n            if (\n                colors &&\n                type(colors) === 'string' &&\n                chroma.brewer &&\n                chroma.brewer[colors.toLowerCase()]\n            ) {\n                colors = chroma.brewer[colors.toLowerCase()];\n            }\n            if (type(colors) === 'array') {\n                // handle single color\n                if (colors.length === 1) {\n                    colors = [colors[0], colors[0]];\n                }\n                // make a copy of the colors\n                colors = colors.slice(0);\n                // convert to chroma classes\n                for (var c = 0; c < colors.length; c++) {\n                    colors[c] = chroma(colors[c]);\n                }\n                // auto-fill color position\n                _pos.length = 0;\n                for (var c$1 = 0; c$1 < colors.length; c$1++) {\n                    _pos.push(c$1 / (colors.length - 1));\n                }\n            }\n            resetCache();\n            return (_colors = colors);\n        };\n\n        var getClass = function (value) {\n            if (_classes != null) {\n                var n = _classes.length - 1;\n                var i = 0;\n                while (i < n && value >= _classes[i]) {\n                    i++;\n                }\n                return i - 1;\n            }\n            return 0;\n        };\n\n        var tMapLightness = function (t) { return t; };\n        var tMapDomain = function (t) { return t; };\n\n        // const classifyValue = function(value) {\n        //     let val = value;\n        //     if (_classes.length > 2) {\n        //         const n = _classes.length-1;\n        //         const i = getClass(value);\n        //         const minc = _classes[0] + ((_classes[1]-_classes[0]) * (0 + (_spread * 0.5)));  // center of 1st class\n        //         const maxc = _classes[n-1] + ((_classes[n]-_classes[n-1]) * (1 - (_spread * 0.5)));  // center of last class\n        //         val = _min + ((((_classes[i] + ((_classes[i+1] - _classes[i]) * 0.5)) - minc) / (maxc-minc)) * (_max - _min));\n        //     }\n        //     return val;\n        // };\n\n        var getColor = function (val, bypassMap) {\n            var col, t;\n            if (bypassMap == null) {\n                bypassMap = false;\n            }\n            if (isNaN(val) || val === null) {\n                return _nacol;\n            }\n            if (!bypassMap) {\n                if (_classes && _classes.length > 2) {\n                    // find the class\n                    var c = getClass(val);\n                    t = c / (_classes.length - 2);\n                } else if (_max !== _min) {\n                    // just interpolate between min/max\n                    t = (val - _min) / (_max - _min);\n                } else {\n                    t = 1;\n                }\n            } else {\n                t = val;\n            }\n\n            // domain map\n            t = tMapDomain(t);\n\n            if (!bypassMap) {\n                t = tMapLightness(t); // lightness correction\n            }\n\n            if (_gamma !== 1) {\n                t = pow$3(t, _gamma);\n            }\n\n            t = _padding[0] + t * (1 - _padding[0] - _padding[1]);\n\n            t = limit(t, 0, 1);\n\n            var k = Math.floor(t * 10000);\n\n            if (_useCache && _colorCache[k]) {\n                col = _colorCache[k];\n            } else {\n                if (type(_colors) === 'array') {\n                    //for i in [0.._pos.length-1]\n                    for (var i = 0; i < _pos.length; i++) {\n                        var p = _pos[i];\n                        if (t <= p) {\n                            col = _colors[i];\n                            break;\n                        }\n                        if (t >= p && i === _pos.length - 1) {\n                            col = _colors[i];\n                            break;\n                        }\n                        if (t > p && t < _pos[i + 1]) {\n                            t = (t - p) / (_pos[i + 1] - p);\n                            col = chroma.interpolate(\n                                _colors[i],\n                                _colors[i + 1],\n                                t,\n                                _mode\n                            );\n                            break;\n                        }\n                    }\n                } else if (type(_colors) === 'function') {\n                    col = _colors(t);\n                }\n                if (_useCache) {\n                    _colorCache[k] = col;\n                }\n            }\n            return col;\n        };\n\n        var resetCache = function () { return (_colorCache = {}); };\n\n        setColors(colors);\n\n        // public interface\n\n        var f = function (v) {\n            var c = chroma(getColor(v));\n            if (_out && c[_out]) {\n                return c[_out]();\n            } else {\n                return c;\n            }\n        };\n\n        f.classes = function (classes) {\n            if (classes != null) {\n                if (type(classes) === 'array') {\n                    _classes = classes;\n                    _positions = [classes[0], classes[classes.length - 1]];\n                } else {\n                    var d = chroma.analyze(_positions);\n                    if (classes === 0) {\n                        _classes = [d.min, d.max];\n                    } else {\n                        _classes = chroma.limits(d, 'e', classes);\n                    }\n                }\n                return f;\n            }\n            return _classes;\n        };\n\n        f.domain = function (domain) {\n            if (!arguments.length) {\n                // return original domain\n                return _domain;\n            }\n            // store original domain so we can return it later\n            _domain = domain.slice(0);\n            _min = domain[0];\n            _max = domain[domain.length - 1];\n            _pos = [];\n            var k = _colors.length;\n            if (domain.length === k && _min !== _max) {\n                // update positions\n                for (var i = 0, list = Array.from(domain); i < list.length; i += 1) {\n                    var d = list[i];\n\n                    _pos.push((d - _min) / (_max - _min));\n                }\n            } else {\n                for (var c = 0; c < k; c++) {\n                    _pos.push(c / (k - 1));\n                }\n                if (domain.length > 2) {\n                    // set domain map\n                    var tOut = domain.map(function (d, i) { return i / (domain.length - 1); });\n                    var tBreaks = domain.map(function (d) { return (d - _min) / (_max - _min); });\n                    if (!tBreaks.every(function (val, i) { return tOut[i] === val; })) {\n                        tMapDomain = function (t) {\n                            if (t <= 0 || t >= 1) { return t; }\n                            var i = 0;\n                            while (t >= tBreaks[i + 1]) { i++; }\n                            var f =\n                                (t - tBreaks[i]) / (tBreaks[i + 1] - tBreaks[i]);\n                            var out = tOut[i] + f * (tOut[i + 1] - tOut[i]);\n                            return out;\n                        };\n                    }\n                }\n            }\n            _positions = [_min, _max];\n            return f;\n        };\n\n        f.mode = function (_m) {\n            if (!arguments.length) {\n                return _mode;\n            }\n            _mode = _m;\n            resetCache();\n            return f;\n        };\n\n        f.range = function (colors, _pos) {\n            setColors(colors);\n            return f;\n        };\n\n        f.out = function (_o) {\n            _out = _o;\n            return f;\n        };\n\n        f.spread = function (val) {\n            if (!arguments.length) {\n                return _spread;\n            }\n            _spread = val;\n            return f;\n        };\n\n        f.correctLightness = function (v) {\n            if (v == null) {\n                v = true;\n            }\n            _correctLightness = v;\n            resetCache();\n            if (_correctLightness) {\n                tMapLightness = function (t) {\n                    var L0 = getColor(0, true).lab()[0];\n                    var L1 = getColor(1, true).lab()[0];\n                    var pol = L0 > L1;\n                    var L_actual = getColor(t, true).lab()[0];\n                    var L_ideal = L0 + (L1 - L0) * t;\n                    var L_diff = L_actual - L_ideal;\n                    var t0 = 0;\n                    var t1 = 1;\n                    var max_iter = 20;\n                    while (Math.abs(L_diff) > 1e-2 && max_iter-- > 0) {\n                        (function () {\n                            if (pol) {\n                                L_diff *= -1;\n                            }\n                            if (L_diff < 0) {\n                                t0 = t;\n                                t += (t1 - t) * 0.5;\n                            } else {\n                                t1 = t;\n                                t += (t0 - t) * 0.5;\n                            }\n                            L_actual = getColor(t, true).lab()[0];\n                            return (L_diff = L_actual - L_ideal);\n                        })();\n                    }\n                    return t;\n                };\n            } else {\n                tMapLightness = function (t) { return t; };\n            }\n            return f;\n        };\n\n        f.padding = function (p) {\n            if (p != null) {\n                if (type(p) === 'number') {\n                    p = [p, p];\n                }\n                _padding = p;\n                return f;\n            } else {\n                return _padding;\n            }\n        };\n\n        f.colors = function (numColors, out) {\n            // If no arguments are given, return the original colors that were provided\n            if (arguments.length < 2) {\n                out = 'hex';\n            }\n            var result = [];\n\n            if (arguments.length === 0) {\n                result = _colors.slice(0);\n            } else if (numColors === 1) {\n                result = [f(0.5)];\n            } else if (numColors > 1) {\n                var dm = _positions[0];\n                var dd = _positions[1] - dm;\n                result = __range__(0, numColors).map(function (i) { return f(dm + (i / (numColors - 1)) * dd); }\n                );\n            } else {\n                // returns all colors based on the defined classes\n                colors = [];\n                var samples = [];\n                if (_classes && _classes.length > 2) {\n                    for (\n                        var i = 1, end = _classes.length, asc = 1 <= end;\n                        asc ? i < end : i > end;\n                        asc ? i++ : i--\n                    ) {\n                        samples.push((_classes[i - 1] + _classes[i]) * 0.5);\n                    }\n                } else {\n                    samples = _positions;\n                }\n                result = samples.map(function (v) { return f(v); });\n            }\n\n            if (chroma[out]) {\n                result = result.map(function (c) { return c[out](); });\n            }\n            return result;\n        };\n\n        f.cache = function (c) {\n            if (c != null) {\n                _useCache = c;\n                return f;\n            } else {\n                return _useCache;\n            }\n        };\n\n        f.gamma = function (g) {\n            if (g != null) {\n                _gamma = g;\n                return f;\n            } else {\n                return _gamma;\n            }\n        };\n\n        f.nodata = function (d) {\n            if (d != null) {\n                _nacol = chroma(d);\n                return f;\n            } else {\n                return _nacol;\n            }\n        };\n\n        return f;\n    }\n\n    function __range__(left, right, inclusive) {\n        var range = [];\n        var ascending = left < right;\n        var end = right ;\n        for (var i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {\n            range.push(i);\n        }\n        return range;\n    }\n\n    //\n    // interpolates between a set of colors uzing a bezier spline\n    //\n\n\n    // nth row of the pascal triangle\n    var binom_row = function (n) {\n        var row = [1, 1];\n        for (var i = 1; i < n; i++) {\n            var newrow = [1];\n            for (var j = 1; j <= row.length; j++) {\n                newrow[j] = (row[j] || 0) + row[j - 1];\n            }\n            row = newrow;\n        }\n        return row;\n    };\n\n    var bezier = function (colors) {\n        var assign, assign$1, assign$2;\n\n        var I, lab0, lab1, lab2;\n        colors = colors.map(function (c) { return new Color(c); });\n        if (colors.length === 2) {\n            // linear interpolation\n            (assign = colors.map(function (c) { return c.lab(); }), lab0 = assign[0], lab1 = assign[1]);\n            I = function (t) {\n                var lab = [0, 1, 2].map(function (i) { return lab0[i] + t * (lab1[i] - lab0[i]); });\n                return new Color(lab, 'lab');\n            };\n        } else if (colors.length === 3) {\n            // quadratic bezier interpolation\n            (assign$1 = colors.map(function (c) { return c.lab(); }), lab0 = assign$1[0], lab1 = assign$1[1], lab2 = assign$1[2]);\n            I = function (t) {\n                var lab = [0, 1, 2].map(\n                    function (i) { return (1 - t) * (1 - t) * lab0[i] +\n                        2 * (1 - t) * t * lab1[i] +\n                        t * t * lab2[i]; }\n                );\n                return new Color(lab, 'lab');\n            };\n        } else if (colors.length === 4) {\n            // cubic bezier interpolation\n            var lab3;\n            (assign$2 = colors.map(function (c) { return c.lab(); }), lab0 = assign$2[0], lab1 = assign$2[1], lab2 = assign$2[2], lab3 = assign$2[3]);\n            I = function (t) {\n                var lab = [0, 1, 2].map(\n                    function (i) { return (1 - t) * (1 - t) * (1 - t) * lab0[i] +\n                        3 * (1 - t) * (1 - t) * t * lab1[i] +\n                        3 * (1 - t) * t * t * lab2[i] +\n                        t * t * t * lab3[i]; }\n                );\n                return new Color(lab, 'lab');\n            };\n        } else if (colors.length >= 5) {\n            // general case (degree n bezier)\n            var labs, row, n;\n            labs = colors.map(function (c) { return c.lab(); });\n            n = colors.length - 1;\n            row = binom_row(n);\n            I = function (t) {\n                var u = 1 - t;\n                var lab = [0, 1, 2].map(function (i) { return labs.reduce(\n                        function (sum, el, j) { return sum + row[j] * Math.pow( u, (n - j) ) * Math.pow( t, j ) * el[i]; },\n                        0\n                    ); }\n                );\n                return new Color(lab, 'lab');\n            };\n        } else {\n            throw new RangeError('No point in running bezier with only one color.');\n        }\n        return I;\n    };\n\n    function bezier$1 (colors) {\n        var f = bezier(colors);\n        f.scale = function () { return scale(f); };\n        return f;\n    }\n\n    /*\n     * interpolates between a set of colors uzing a bezier spline\n     * blend mode formulas taken from https://web.archive.org/web/20180110014946/http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/\n     */\n\n\n    var blend = function (bottom, top, mode) {\n        if (!blend[mode]) {\n            throw new Error('unknown blend mode ' + mode);\n        }\n        return blend[mode](bottom, top);\n    };\n\n    var blend_f = function (f) { return function (bottom, top) {\n        var c0 = chroma(top).rgb();\n        var c1 = chroma(bottom).rgb();\n        return chroma.rgb(f(c0, c1));\n    }; };\n\n    var each = function (f) { return function (c0, c1) {\n        var out = [];\n        out[0] = f(c0[0], c1[0]);\n        out[1] = f(c0[1], c1[1]);\n        out[2] = f(c0[2], c1[2]);\n        return out;\n    }; };\n\n    var normal = function (a) { return a; };\n    var multiply = function (a, b) { return (a * b) / 255; };\n    var darken = function (a, b) { return (a > b ? b : a); };\n    var lighten = function (a, b) { return (a > b ? a : b); };\n    var screen = function (a, b) { return 255 * (1 - (1 - a / 255) * (1 - b / 255)); };\n    var overlay = function (a, b) { return b < 128 ? (2 * a * b) / 255 : 255 * (1 - 2 * (1 - a / 255) * (1 - b / 255)); };\n    var burn = function (a, b) { return 255 * (1 - (1 - b / 255) / (a / 255)); };\n    var dodge = function (a, b) {\n        if (a === 255) { return 255; }\n        a = (255 * (b / 255)) / (1 - a / 255);\n        return a > 255 ? 255 : a;\n    };\n\n    // # add = (a,b) ->\n    // #     if (a + b > 255) then 255 else a + b\n\n    blend.normal = blend_f(each(normal));\n    blend.multiply = blend_f(each(multiply));\n    blend.screen = blend_f(each(screen));\n    blend.overlay = blend_f(each(overlay));\n    blend.darken = blend_f(each(darken));\n    blend.lighten = blend_f(each(lighten));\n    blend.dodge = blend_f(each(dodge));\n    blend.burn = blend_f(each(burn));\n\n    // cubehelix interpolation\n    // based on D.A. Green \"A colour scheme for the display of astronomical intensity images\"\n    // http://astron-soc.in/bulletin/11June/289392011.pdf\n    var pow$2 = Math.pow;\n    var sin$1 = Math.sin;\n    var cos$1 = Math.cos;\n\n    function cubehelix (\n        start,\n        rotations,\n        hue,\n        gamma,\n        lightness\n    ) {\n        if ( start === void 0 ) start = 300;\n        if ( rotations === void 0 ) rotations = -1.5;\n        if ( hue === void 0 ) hue = 1;\n        if ( gamma === void 0 ) gamma = 1;\n        if ( lightness === void 0 ) lightness = [0, 1];\n\n        var dh = 0,\n            dl;\n        if (type(lightness) === 'array') {\n            dl = lightness[1] - lightness[0];\n        } else {\n            dl = 0;\n            lightness = [lightness, lightness];\n        }\n        var f = function (fract) {\n            var a = TWOPI * ((start + 120) / 360 + rotations * fract);\n            var l = pow$2(lightness[0] + dl * fract, gamma);\n            var h = dh !== 0 ? hue[0] + fract * dh : hue;\n            var amp = (h * l * (1 - l)) / 2;\n            var cos_a = cos$1(a);\n            var sin_a = sin$1(a);\n            var r = l + amp * (-0.14861 * cos_a + 1.78277 * sin_a);\n            var g = l + amp * (-0.29227 * cos_a - 0.90649 * sin_a);\n            var b = l + amp * (1.97294 * cos_a);\n            return chroma(clip_rgb([r * 255, g * 255, b * 255, 1]));\n        };\n        f.start = function (s) {\n            if (s == null) {\n                return start;\n            }\n            start = s;\n            return f;\n        };\n        f.rotations = function (r) {\n            if (r == null) {\n                return rotations;\n            }\n            rotations = r;\n            return f;\n        };\n        f.gamma = function (g) {\n            if (g == null) {\n                return gamma;\n            }\n            gamma = g;\n            return f;\n        };\n        f.hue = function (h) {\n            if (h == null) {\n                return hue;\n            }\n            hue = h;\n            if (type(hue) === 'array') {\n                dh = hue[1] - hue[0];\n                if (dh === 0) {\n                    hue = hue[1];\n                }\n            } else {\n                dh = 0;\n            }\n            return f;\n        };\n        f.lightness = function (h) {\n            if (h == null) {\n                return lightness;\n            }\n            if (type(h) === 'array') {\n                lightness = h;\n                dl = h[1] - h[0];\n            } else {\n                lightness = [h, h];\n                dl = 0;\n            }\n            return f;\n        };\n        f.scale = function () { return chroma.scale(f); };\n        f.hue(hue);\n        return f;\n    }\n\n    var digits = '0123456789abcdef';\n\n    var floor$1 = Math.floor;\n    var random = Math.random;\n\n    /**\n     * Generates a random color.\n     * @param {() => number} rng - A random number generator function.\n     */\n    function random$1 (rng) {\n        if ( rng === void 0 ) rng = random;\n\n        var code = '#';\n        for (var i = 0; i < 6; i++) {\n            code += digits.charAt(floor$1(rng() * 16));\n        }\n        return new Color(code, 'hex');\n    }\n\n    var log = Math.log;\n    var pow$1 = Math.pow;\n    var floor = Math.floor;\n    var abs$1 = Math.abs;\n\n    function analyze(data, key) {\n        if ( key === void 0 ) key = null;\n\n        var r = {\n            min: Number.MAX_VALUE,\n            max: Number.MAX_VALUE * -1,\n            sum: 0,\n            values: [],\n            count: 0\n        };\n        if (type(data) === 'object') {\n            data = Object.values(data);\n        }\n        data.forEach(function (val) {\n            if (key && type(val) === 'object') { val = val[key]; }\n            if (val !== undefined && val !== null && !isNaN(val)) {\n                r.values.push(val);\n                r.sum += val;\n                if (val < r.min) { r.min = val; }\n                if (val > r.max) { r.max = val; }\n                r.count += 1;\n            }\n        });\n\n        r.domain = [r.min, r.max];\n\n        r.limits = function (mode, num) { return limits(r, mode, num); };\n\n        return r;\n    }\n\n    function limits(data, mode, num) {\n        if ( mode === void 0 ) mode = 'equal';\n        if ( num === void 0 ) num = 7;\n\n        if (type(data) == 'array') {\n            data = analyze(data);\n        }\n        var min = data.min;\n        var max = data.max;\n        var values = data.values.sort(function (a, b) { return a - b; });\n\n        if (num === 1) {\n            return [min, max];\n        }\n\n        var limits = [];\n\n        if (mode.substr(0, 1) === 'c') {\n            // continuous\n            limits.push(min);\n            limits.push(max);\n        }\n\n        if (mode.substr(0, 1) === 'e') {\n            // equal interval\n            limits.push(min);\n            for (var i = 1; i < num; i++) {\n                limits.push(min + (i / num) * (max - min));\n            }\n            limits.push(max);\n        } else if (mode.substr(0, 1) === 'l') {\n            // log scale\n            if (min <= 0) {\n                throw new Error(\n                    'Logarithmic scales are only possible for values > 0'\n                );\n            }\n            var min_log = Math.LOG10E * log(min);\n            var max_log = Math.LOG10E * log(max);\n            limits.push(min);\n            for (var i$1 = 1; i$1 < num; i$1++) {\n                limits.push(pow$1(10, min_log + (i$1 / num) * (max_log - min_log)));\n            }\n            limits.push(max);\n        } else if (mode.substr(0, 1) === 'q') {\n            // quantile scale\n            limits.push(min);\n            for (var i$2 = 1; i$2 < num; i$2++) {\n                var p = ((values.length - 1) * i$2) / num;\n                var pb = floor(p);\n                if (pb === p) {\n                    limits.push(values[pb]);\n                } else {\n                    // p > pb\n                    var pr = p - pb;\n                    limits.push(values[pb] * (1 - pr) + values[pb + 1] * pr);\n                }\n            }\n            limits.push(max);\n        } else if (mode.substr(0, 1) === 'k') {\n            // k-means clustering\n            /*\n            implementation based on\n            http://code.google.com/p/figue/source/browse/trunk/figue.js#336\n            simplified for 1-d input values\n            */\n            var cluster;\n            var n = values.length;\n            var assignments = new Array(n);\n            var clusterSizes = new Array(num);\n            var repeat = true;\n            var nb_iters = 0;\n            var centroids = null;\n\n            // get seed values\n            centroids = [];\n            centroids.push(min);\n            for (var i$3 = 1; i$3 < num; i$3++) {\n                centroids.push(min + (i$3 / num) * (max - min));\n            }\n            centroids.push(max);\n\n            while (repeat) {\n                // assignment step\n                for (var j = 0; j < num; j++) {\n                    clusterSizes[j] = 0;\n                }\n                for (var i$4 = 0; i$4 < n; i$4++) {\n                    var value = values[i$4];\n                    var mindist = Number.MAX_VALUE;\n                    var best = (void 0);\n                    for (var j$1 = 0; j$1 < num; j$1++) {\n                        var dist = abs$1(centroids[j$1] - value);\n                        if (dist < mindist) {\n                            mindist = dist;\n                            best = j$1;\n                        }\n                        clusterSizes[best]++;\n                        assignments[i$4] = best;\n                    }\n                }\n\n                // update centroids step\n                var newCentroids = new Array(num);\n                for (var j$2 = 0; j$2 < num; j$2++) {\n                    newCentroids[j$2] = null;\n                }\n                for (var i$5 = 0; i$5 < n; i$5++) {\n                    cluster = assignments[i$5];\n                    if (newCentroids[cluster] === null) {\n                        newCentroids[cluster] = values[i$5];\n                    } else {\n                        newCentroids[cluster] += values[i$5];\n                    }\n                }\n                for (var j$3 = 0; j$3 < num; j$3++) {\n                    newCentroids[j$3] *= 1 / clusterSizes[j$3];\n                }\n\n                // check convergence\n                repeat = false;\n                for (var j$4 = 0; j$4 < num; j$4++) {\n                    if (newCentroids[j$4] !== centroids[j$4]) {\n                        repeat = true;\n                        break;\n                    }\n                }\n\n                centroids = newCentroids;\n                nb_iters++;\n\n                if (nb_iters > 200) {\n                    repeat = false;\n                }\n            }\n\n            // finished k-means clustering\n            // the next part is borrowed from gabrielflor.it\n            var kClusters = {};\n            for (var j$5 = 0; j$5 < num; j$5++) {\n                kClusters[j$5] = [];\n            }\n            for (var i$6 = 0; i$6 < n; i$6++) {\n                cluster = assignments[i$6];\n                kClusters[cluster].push(values[i$6]);\n            }\n            var tmpKMeansBreaks = [];\n            for (var j$6 = 0; j$6 < num; j$6++) {\n                tmpKMeansBreaks.push(kClusters[j$6][0]);\n                tmpKMeansBreaks.push(kClusters[j$6][kClusters[j$6].length - 1]);\n            }\n            tmpKMeansBreaks = tmpKMeansBreaks.sort(function (a, b) { return a - b; });\n            limits.push(tmpKMeansBreaks[0]);\n            for (var i$7 = 1; i$7 < tmpKMeansBreaks.length; i$7 += 2) {\n                var v = tmpKMeansBreaks[i$7];\n                if (!isNaN(v) && limits.indexOf(v) === -1) {\n                    limits.push(v);\n                }\n            }\n        }\n        return limits;\n    }\n\n    function contrast (a, b) {\n        // WCAG contrast ratio\n        // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef\n        a = new Color(a);\n        b = new Color(b);\n        var l1 = a.luminance();\n        var l2 = b.luminance();\n        return l1 > l2 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05);\n    }\n\n    /**\n     * @license\n     *\n     * The APCA contrast prediction algorithm is based of the formulas published\n     * in the APCA-1.0.98G specification by Myndex. The specification is available at:\n     * https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAw3_0.1.17_APCA0.0.98G.svg\n     *\n     * Note that the APCA implementation is still beta, so please update to\n     * future versions of chroma.js when they become available.\n     *\n     * You can read more about the APCA Readability Criterion at\n     * https://readtech.org/ARC/\n     */\n\n    // constants\n    var W_offset = 0.027;\n    var P_in = 0.0005;\n    var P_out = 0.1;\n    var R_scale = 1.14;\n    var B_threshold = 0.022;\n    var B_exp = 1.414;\n\n    function contrastAPCA (text, bg) {\n        // parse input colors\n        text = new Color(text);\n        bg = new Color(bg);\n        // if text color has alpha, blend against background\n        if (text.alpha() < 1) {\n            text = mix(bg, text, text.alpha(), 'rgb');\n        }\n        var l_text = lum.apply(void 0, text.rgb());\n        var l_bg = lum.apply(void 0, bg.rgb());\n\n        // soft clamp black levels\n        var Y_text =\n            l_text >= B_threshold\n                ? l_text\n                : l_text + Math.pow(B_threshold - l_text, B_exp);\n        var Y_bg =\n            l_bg >= B_threshold ? l_bg : l_bg + Math.pow(B_threshold - l_bg, B_exp);\n\n        // normal polarity (dark text on light background)\n        var S_norm = Math.pow(Y_bg, 0.56) - Math.pow(Y_text, 0.57);\n        // reverse polarity (light text on dark background)\n        var S_rev = Math.pow(Y_bg, 0.65) - Math.pow(Y_text, 0.62);\n        // clamp noise then scale\n        var C =\n            Math.abs(Y_bg - Y_text) < P_in\n                ? 0\n                : Y_text < Y_bg\n                  ? S_norm * R_scale\n                  : S_rev * R_scale;\n        // clamp minimum contrast then offset\n        var S_apc = Math.abs(C) < P_out ? 0 : C > 0 ? C - W_offset : C + W_offset;\n        // scale to 100\n        return S_apc * 100;\n    }\n    function lum(r, g, b) {\n        return (\n            0.2126729 * Math.pow(r / 255, 2.4) +\n            0.7151522 * Math.pow(g / 255, 2.4) +\n            0.072175 * Math.pow(b / 255, 2.4)\n        );\n    }\n\n    var sqrt = Math.sqrt;\n    var pow = Math.pow;\n    var min = Math.min;\n    var max = Math.max;\n    var atan2 = Math.atan2;\n    var abs = Math.abs;\n    var cos = Math.cos;\n    var sin = Math.sin;\n    var exp = Math.exp;\n    var PI = Math.PI;\n\n    function deltaE (a, b, Kl, Kc, Kh) {\n        if ( Kl === void 0 ) Kl = 1;\n        if ( Kc === void 0 ) Kc = 1;\n        if ( Kh === void 0 ) Kh = 1;\n\n        // Delta E (CIE 2000)\n        // see http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html\n        var rad2deg = function (rad) {\n            return (360 * rad) / (2 * PI);\n        };\n        var deg2rad = function (deg) {\n            return (2 * PI * deg) / 360;\n        };\n        a = new Color(a);\n        b = new Color(b);\n        var ref = Array.from(a.lab());\n        var L1 = ref[0];\n        var a1 = ref[1];\n        var b1 = ref[2];\n        var ref$1 = Array.from(b.lab());\n        var L2 = ref$1[0];\n        var a2 = ref$1[1];\n        var b2 = ref$1[2];\n        var avgL = (L1 + L2) / 2;\n        var C1 = sqrt(pow(a1, 2) + pow(b1, 2));\n        var C2 = sqrt(pow(a2, 2) + pow(b2, 2));\n        var avgC = (C1 + C2) / 2;\n        var G = 0.5 * (1 - sqrt(pow(avgC, 7) / (pow(avgC, 7) + pow(25, 7))));\n        var a1p = a1 * (1 + G);\n        var a2p = a2 * (1 + G);\n        var C1p = sqrt(pow(a1p, 2) + pow(b1, 2));\n        var C2p = sqrt(pow(a2p, 2) + pow(b2, 2));\n        var avgCp = (C1p + C2p) / 2;\n        var arctan1 = rad2deg(atan2(b1, a1p));\n        var arctan2 = rad2deg(atan2(b2, a2p));\n        var h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360;\n        var h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360;\n        var avgHp =\n            abs(h1p - h2p) > 180 ? (h1p + h2p + 360) / 2 : (h1p + h2p) / 2;\n        var T =\n            1 -\n            0.17 * cos(deg2rad(avgHp - 30)) +\n            0.24 * cos(deg2rad(2 * avgHp)) +\n            0.32 * cos(deg2rad(3 * avgHp + 6)) -\n            0.2 * cos(deg2rad(4 * avgHp - 63));\n        var deltaHp = h2p - h1p;\n        deltaHp =\n            abs(deltaHp) <= 180\n                ? deltaHp\n                : h2p <= h1p\n                  ? deltaHp + 360\n                  : deltaHp - 360;\n        deltaHp = 2 * sqrt(C1p * C2p) * sin(deg2rad(deltaHp) / 2);\n        var deltaL = L2 - L1;\n        var deltaCp = C2p - C1p;\n        var sl = 1 + (0.015 * pow(avgL - 50, 2)) / sqrt(20 + pow(avgL - 50, 2));\n        var sc = 1 + 0.045 * avgCp;\n        var sh = 1 + 0.015 * avgCp * T;\n        var deltaTheta = 30 * exp(-pow((avgHp - 275) / 25, 2));\n        var Rc = 2 * sqrt(pow(avgCp, 7) / (pow(avgCp, 7) + pow(25, 7)));\n        var Rt = -Rc * sin(2 * deg2rad(deltaTheta));\n        var result = sqrt(\n            pow(deltaL / (Kl * sl), 2) +\n                pow(deltaCp / (Kc * sc), 2) +\n                pow(deltaHp / (Kh * sh), 2) +\n                Rt * (deltaCp / (Kc * sc)) * (deltaHp / (Kh * sh))\n        );\n        return max(0, min(100, result));\n    }\n\n    // simple Euclidean distance\n    function distance (a, b, mode) {\n        if ( mode === void 0 ) mode = 'lab';\n\n        // Delta E (CIE 1976)\n        // see http://www.brucelindbloom.com/index.html?Equations.html\n        a = new Color(a);\n        b = new Color(b);\n        var l1 = a.get(mode);\n        var l2 = b.get(mode);\n        var sum_sq = 0;\n        for (var i in l1) {\n            var d = (l1[i] || 0) - (l2[i] || 0);\n            sum_sq += d * d;\n        }\n        return Math.sqrt(sum_sq);\n    }\n\n    function valid () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        try {\n            new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n            return true;\n            // eslint-disable-next-line\n        } catch (e) {\n            return false;\n        }\n    }\n\n    // some pre-defined color scales:\n\n    var scales = {\n        cool: function cool() {\n            return scale([chroma.hsl(180, 1, 0.9), chroma.hsl(250, 0.7, 0.4)]);\n        },\n        hot: function hot() {\n            return scale(['#000', '#f00', '#ff0', '#fff']).mode(\n                'rgb'\n            );\n        }\n    };\n\n    /**\n        ColorBrewer colors for chroma.js\n\n        Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The\n        Pennsylvania State University.\n\n        Licensed under the Apache License, Version 2.0 (the \"License\");\n        you may not use this file except in compliance with the License.\n        You may obtain a copy of the License at\n        http://www.apache.org/licenses/LICENSE-2.0\n\n        Unless required by applicable law or agreed to in writing, software distributed\n        under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n        CONDITIONS OF ANY KIND, either express or implied. See the License for the\n        specific language governing permissions and limitations under the License.\n    */\n\n    var colorbrewer = {\n        // sequential\n        OrRd: ['#fff7ec', '#fee8c8', '#fdd49e', '#fdbb84', '#fc8d59', '#ef6548', '#d7301f', '#b30000', '#7f0000'],\n        PuBu: ['#fff7fb', '#ece7f2', '#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0', '#0570b0', '#045a8d', '#023858'],\n        BuPu: ['#f7fcfd', '#e0ecf4', '#bfd3e6', '#9ebcda', '#8c96c6', '#8c6bb1', '#88419d', '#810f7c', '#4d004b'],\n        Oranges: ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704'],\n        BuGn: ['#f7fcfd', '#e5f5f9', '#ccece6', '#99d8c9', '#66c2a4', '#41ae76', '#238b45', '#006d2c', '#00441b'],\n        YlOrBr: ['#ffffe5', '#fff7bc', '#fee391', '#fec44f', '#fe9929', '#ec7014', '#cc4c02', '#993404', '#662506'],\n        YlGn: ['#ffffe5', '#f7fcb9', '#d9f0a3', '#addd8e', '#78c679', '#41ab5d', '#238443', '#006837', '#004529'],\n        Reds: ['#fff5f0', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d'],\n        RdPu: ['#fff7f3', '#fde0dd', '#fcc5c0', '#fa9fb5', '#f768a1', '#dd3497', '#ae017e', '#7a0177', '#49006a'],\n        Greens: ['#f7fcf5', '#e5f5e0', '#c7e9c0', '#a1d99b', '#74c476', '#41ab5d', '#238b45', '#006d2c', '#00441b'],\n        YlGnBu: ['#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58'],\n        Purples: ['#fcfbfd', '#efedf5', '#dadaeb', '#bcbddc', '#9e9ac8', '#807dba', '#6a51a3', '#54278f', '#3f007d'],\n        GnBu: ['#f7fcf0', '#e0f3db', '#ccebc5', '#a8ddb5', '#7bccc4', '#4eb3d3', '#2b8cbe', '#0868ac', '#084081'],\n        Greys: ['#ffffff', '#f0f0f0', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525', '#000000'],\n        YlOrRd: ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'],\n        PuRd: ['#f7f4f9', '#e7e1ef', '#d4b9da', '#c994c7', '#df65b0', '#e7298a', '#ce1256', '#980043', '#67001f'],\n        Blues: ['#f7fbff', '#deebf7', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5', '#08519c', '#08306b'],\n        PuBuGn: ['#fff7fb', '#ece2f0', '#d0d1e6', '#a6bddb', '#67a9cf', '#3690c0', '#02818a', '#016c59', '#014636'],\n        Viridis: ['#440154', '#482777', '#3f4a8a', '#31678e', '#26838f', '#1f9d8a', '#6cce5a', '#b6de2b', '#fee825'],\n\n        // diverging\n        Spectral: ['#9e0142', '#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#e6f598', '#abdda4', '#66c2a5', '#3288bd', '#5e4fa2'],\n        RdYlGn: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850', '#006837'],\n        RdBu: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#f7f7f7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],\n        PiYG: ['#8e0152', '#c51b7d', '#de77ae', '#f1b6da', '#fde0ef', '#f7f7f7', '#e6f5d0', '#b8e186', '#7fbc41', '#4d9221', '#276419'],\n        PRGn: ['#40004b', '#762a83', '#9970ab', '#c2a5cf', '#e7d4e8', '#f7f7f7', '#d9f0d3', '#a6dba0', '#5aae61', '#1b7837', '#00441b'],\n        RdYlBu: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'],\n        BrBG: ['#543005', '#8c510a', '#bf812d', '#dfc27d', '#f6e8c3', '#f5f5f5', '#c7eae5', '#80cdc1', '#35978f', '#01665e', '#003c30'],\n        RdGy: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#ffffff', '#e0e0e0', '#bababa', '#878787', '#4d4d4d', '#1a1a1a'],\n        PuOr: ['#7f3b08', '#b35806', '#e08214', '#fdb863', '#fee0b6', '#f7f7f7', '#d8daeb', '#b2abd2', '#8073ac', '#542788', '#2d004b'],\n\n        // qualitative\n        Set2: ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3'],\n        Accent: ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666'],\n        Set1: ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999'],\n        Set3: ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', '#ccebc5', '#ffed6f'],\n        Dark2: ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d', '#666666'],\n        Paired: ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a', '#ffff99', '#b15928'],\n        Pastel2: ['#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc'],\n        Pastel1: ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2']\n    };\n\n    var colorbrewerTypes = Object.keys(colorbrewer);\n    var typeMap = new Map(colorbrewerTypes.map(function (key) { return [key.toLowerCase(), key]; }));\n\n    // use Proxy to allow case-insensitive access to palettes\n    var colorbrewerProxy =\n        typeof Proxy === 'function'\n            ? new Proxy(colorbrewer, {\n                  get: function get(target, prop) {\n                      var lower = prop.toLowerCase();\n                      if (typeMap.has(lower)) {\n                          return target[typeMap.get(lower)];\n                      }\n                  },\n                  getOwnPropertyNames: function getOwnPropertyNames() {\n                      return Object.getOwnPropertyNames(colorbrewerTypes);\n                  }\n              })\n            : colorbrewer;\n\n    // feel free to comment out anything to rollup\n    // a smaller chroma.js bundle\n\n    Object.assign(chroma, {\n        analyze: analyze,\n        average: average,\n        bezier: bezier$1,\n        blend: blend,\n        brewer: colorbrewerProxy,\n        Color: Color,\n        colors: w3cx11,\n        contrast: contrast,\n        contrastAPCA: contrastAPCA,\n        cubehelix: cubehelix,\n        deltaE: deltaE,\n        distance: distance,\n        input: input,\n        interpolate: mix,\n        limits: limits,\n        mix: mix,\n        random: random$1,\n        scale: scale,\n        scales: scales,\n        valid: valid,\n        cmyk: cmyk,\n        css: css,\n        gl: gl,\n        hcg: hcg$1,\n        hex: hex,\n        hsi: hsi$1,\n        hsl: hsl$1,\n        hsv: hsv$1,\n        lab: lab$1,\n        lch: lch$1,\n        hcl: hcl,\n        num: num$1,\n        rgb: rgb$1,\n        temp: temp,\n        kelvin: temp,\n        temperature: temp,\n        oklab: oklab$1,\n        oklch: oklch$1,\n        getLabWhitePoint: getLabWhitePoint,\n        setLabWhitePoint: setLabWhitePoint\n    });\n\n    return chroma;\n\n}));\n"
  },
  {
    "path": "dist/chroma.min.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n!function(r,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n():\"function\"==typeof define&&define.amd?define(n):(r=\"undefined\"!=typeof globalThis?globalThis:r||self).chroma=n()}(this,(function(){\"use strict\";var r=Math.min,n=Math.max;function e(e,t,a){return void 0===t&&(t=0),void 0===a&&(a=1),r(n(t,e),a)}function t(r){r._clipped=!1,r._unclipped=r.slice(0);for(var n=0;n<=3;n++)n<3?((r[n]<0||r[n]>255)&&(r._clipped=!0),r[n]=e(r[n],0,255)):3===n&&(r[n]=e(r[n],0,1));return r}for(var a={},f=0,o=[\"Boolean\",\"Number\",\"String\",\"Function\",\"Array\",\"Date\",\"RegExp\",\"Undefined\",\"Null\"];f<o.length;f+=1){var u=o[f];a[\"[object \"+u+\"]\"]=u.toLowerCase()}function c(r){return a[Object.prototype.toString.call(r)]||\"object\"}function i(r,n){return void 0===n&&(n=null),r.length>=3?Array.prototype.slice.call(r):\"object\"==c(r[0])&&n?n.split(\"\").filter((function(n){return void 0!==r[0][n]})).map((function(n){return r[0][n]})):r[0].slice(0)}function l(r){if(r.length<2)return null;var n=r.length-1;return\"string\"==c(r[n])?r[n].toLowerCase():null}var h=Math.PI,s=Math.min,d=Math.max,b=function(r){return Math.round(100*r)/100},g=function(r){return Math.round(100*r)/100},v=2*h,p=h/3,m=h/180,y=180/h;function w(r){return r.slice(0,3).reverse().concat(r.slice(3))}var k={format:{},autodetect:[]},M=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=this;if(\"object\"===c(r[0])&&r[0].constructor&&r[0].constructor===this.constructor)return r[0];var a=l(r),f=!1;if(!a){f=!0,k.sorted||(k.autodetect=k.autodetect.sort((function(r,n){return n.p-r.p})),k.sorted=!0);for(var o=0,u=k.autodetect;o<u.length;o+=1){var i=u[o];if(a=i.test.apply(i,r))break}}if(!k.format[a])throw new Error(\"unknown format: \"+r);var h=k.format[a].apply(null,f?r:r.slice(0,-1));e._rgb=t(h),3===e._rgb.length&&e._rgb.push(1)};M.prototype.toString=function(){return\"function\"==c(this.hex)?this.hex():\"[\"+this._rgb.join(\",\")+\"]\"};var N=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r)))};N.version=\"3.2.0\";var x=Math.max;M.prototype.cmyk=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=1-x(t/=255,x(a/=255,f/=255)),u=o<1?1/(1-o):0;return[(1-t-o)*u,(1-a-o)*u,(1-f-o)*u,o]}(this._rgb)};var _=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"cmyk\"])))};Object.assign(N,{cmyk:_}),k.format.cmyk=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"cmyk\"))[0],t=r[1],a=r[2],f=r[3],o=r.length>4?r[4]:1;return 1===f?[0,0,0,o]:[e>=1?0:255*(1-e)*(1-f),t>=1?0:255*(1-t)*(1-f),a>=1?0:255*(1-a)*(1-f),o]},k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"cmyk\"))&&4===r.length)return\"cmyk\"}});var A=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a=(r=i(r,\"rgba\"))[0],f=r[1],o=r[2],u=s(a/=255,f/=255,o/=255),c=d(a,f,o),l=(c+u)/2;return c===u?(e=0,t=Number.NaN):e=l<.5?(c-u)/(c+u):(c-u)/(2-c-u),a==c?t=(f-o)/(c-u):f==c?t=2+(o-a)/(c-u):o==c&&(t=4+(a-f)/(c-u)),(t*=60)<0&&(t+=360),r.length>3&&void 0!==r[3]?[t,e,l,r[3]]:[t,e,l]},j={Kn:18,labWhitePoint:\"d65\",Xn:.95047,Yn:1,Zn:1.08883,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},E=new Map([[\"a\",[1.0985,.35585]],[\"b\",[1.0985,.35585]],[\"c\",[.98074,1.18232]],[\"d50\",[.96422,.82521]],[\"d55\",[.95682,.92149]],[\"d65\",[.95047,1.08883]],[\"e\",[1,1,1]],[\"f2\",[.99186,.67393]],[\"f7\",[.95041,1.08747]],[\"f11\",[1.00962,.6435]],[\"icc\",[.96422,.82521]]]);function R(r){var n=E.get(String(r).toLowerCase());if(!n)throw new Error(\"unknown Lab illuminant \"+r);j.labWhitePoint=r,j.Xn=n[0],j.Zn=n[1]}function O(){return j.labWhitePoint}var P=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=e.slice(3),u=L(t,a,f),c=function(r,n,e){var t=j.Xn,a=j.Yn,f=j.Zn,o=j.kE,u=j.kK,c=r/t,i=n/a,l=e/f,h=c>o?Math.pow(c,1/3):(u*c+16)/116,s=i>o?Math.pow(i,1/3):(u*i+16)/116,d=l>o?Math.pow(l,1/3):(u*l+16)/116;return[116*s-16,500*(h-s),200*(s-d)]}(u[0],u[1],u[2]);return[c[0],c[1],c[2]].concat(o.length>0&&o[0]<1?[o[0]]:[])};function F(r){var n=Math.sign(r);return((r=Math.abs(r))<=.04045?r/12.92:Math.pow((r+.055)/1.055,2.4))*n}var L=function(r,n,e){r=F(r/255),n=F(n/255),e=F(e/255);var t=j.MtxRGB2XYZ,a=j.MtxAdaptMa,f=j.MtxAdaptMaI,o=j.Xn,u=j.Yn,c=j.Zn,i=j.As,l=j.Bs,h=j.Cs,s=r*t.m00+n*t.m10+e*t.m20,d=r*t.m01+n*t.m11+e*t.m21,b=r*t.m02+n*t.m12+e*t.m22,g=o*a.m00+u*a.m10+c*a.m20,v=o*a.m01+u*a.m11+c*a.m21,p=o*a.m02+u*a.m12+c*a.m22,m=s*a.m00+d*a.m10+b*a.m20,y=s*a.m01+d*a.m11+b*a.m21,w=s*a.m02+d*a.m12+b*a.m22;return y*=v/l,w*=p/h,[s=(m*=g/i)*f.m00+y*f.m10+w*f.m20,d=m*f.m01+y*f.m11+w*f.m21,b=m*f.m02+y*f.m12+w*f.m22]},B=Math.sqrt,G=Math.atan2,Y=Math.round,q=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lab\"),t=e[0],a=e[1],f=e[2],o=B(a*a+f*f),u=(G(f,a)*y+360)%360;return 0===Y(1e4*o)&&(u=Number.NaN),[t,o,u]},C=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=e.slice(3),u=P(t,a,f),c=u[0],l=u[1],h=u[2],s=q(c,l,h);return[s[0],s[1],s[2]].concat(o.length>0&&o[0]<1?[o[0]]:[])};function X(r,n){var e=r.length;Array.isArray(r[0])||(r=[r]),Array.isArray(n[0])||(n=n.map((function(r){return[r]})));var t=n[0].length,a=n[0].map((function(r,e){return n.map((function(r){return r[e]}))})),f=r.map((function(r){return a.map((function(n){return Array.isArray(r)?r.reduce((function(r,e,t){return r+e*(n[t]||0)}),0):n.reduce((function(n,e){return n+e*r}),0)}))}));return 1===e&&(f=f[0]),1===t?f.map((function(r){return r[0]})):f}var Z=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a=i(r,\"rgb\"),f=a[0],o=a[1],u=a[2],c=a.slice(3),l=L(f,o,u);return(e=[[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],t=X([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],l),X(e,t.map((function(r){return Math.cbrt(r)})))).concat(c.length>0&&c[0]<1?[c[0]]:[])};var $=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=e.slice(3),u=Z(t,a,f),c=u[0],l=u[1],h=u[2],s=q(c,l,h);return[s[0],s[1],s[2]].concat(o.length>0&&o[0]<1?[o[0]]:[])},S=Math.round,W=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\"),t=l(r)||\"rgb\";if(\"hsl\"===t.substr(0,3))return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"hsla\"),t=l(r)||\"lsa\";return e[0]=b(e[0]||0)+\"deg\",e[1]=b(100*e[1])+\"%\",e[2]=b(100*e[2])+\"%\",\"hsla\"===t||e.length>3&&e[3]<1?(e[3]=\"/ \"+(e.length>3?e[3]:1),t=\"hsla\"):e.length=3,t.substr(0,3)+\"(\"+e.join(\" \")+\")\"}(A(e),t);if(\"lab\"===t.substr(0,3)){var a=O();R(\"d50\");var f=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lab\"),t=l(r)||\"lab\";return e[0]=b(e[0])+\"%\",e[1]=b(e[1]),e[2]=b(e[2]),\"laba\"===t||e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"lab(\"+e.join(\" \")+\")\"}(P(e),t);return R(a),f}if(\"lch\"===t.substr(0,3)){var o=O();R(\"d50\");var u=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lch\"),t=l(r)||\"lab\";return e[0]=b(e[0])+\"%\",e[1]=b(e[1]),e[2]=isNaN(e[2])?\"none\":b(e[2])+\"deg\",\"lcha\"===t||e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"lch(\"+e.join(\" \")+\")\"}(C(e),t);return R(o),u}return\"oklab\"===t.substr(0,5)?function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lab\");return e[0]=b(100*e[0])+\"%\",e[1]=g(e[1]),e[2]=g(e[2]),e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"oklab(\"+e.join(\" \")+\")\"}(Z(e)):\"oklch\"===t.substr(0,5)?function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lch\");return e[0]=b(100*e[0])+\"%\",e[1]=g(e[1]),e[2]=isNaN(e[2])?\"none\":b(e[2])+\"deg\",e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"oklch(\"+e.join(\" \")+\")\"}($(e)):(e[0]=S(e[0]),e[1]=S(e[1]),e[2]=S(e[2]),(\"rgba\"===t||e.length>3&&e[3]<1)&&(e[3]=\"/ \"+(e.length>3?e[3]:1),t=\"rgba\"),t.substr(0,3)+\"(\"+e.slice(0,\"rgb\"===t?3:4).join(\" \")+\")\")},I=function(){for(var r,n=[],e=arguments.length;e--;)n[e]=arguments[e];var t,a,f,o=(n=i(n,\"hsl\"))[0],u=n[1],c=n[2];if(0===u)t=a=f=255*c;else{var l=[0,0,0],h=[0,0,0],s=c<.5?c*(1+u):c+u-c*u,d=2*c-s,b=o/360;l[0]=b+1/3,l[1]=b,l[2]=b-1/3;for(var g=0;g<3;g++)l[g]<0&&(l[g]+=1),l[g]>1&&(l[g]-=1),6*l[g]<1?h[g]=d+6*(s-d)*l[g]:2*l[g]<1?h[g]=s:3*l[g]<2?h[g]=d+(s-d)*(2/3-l[g])*6:h[g]=d;t=(r=[255*h[0],255*h[1],255*h[2]])[0],a=r[1],f=r[2]}return n.length>3?[t,a,f,n[3]]:[t,a,f,1]},K=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"lab\"))[0],t=r[1],a=r[2],f=z(e,t,a),o=f[0],u=f[1],c=f[2],l=V(o,u,c);return[l[0],l[1],l[2],r.length>3?r[3]:1]},z=function(r,n,e){var t=j.kE,a=j.kK,f=j.kKE,o=j.Xn,u=j.Yn,c=j.Zn,i=(r+16)/116,l=.002*n+i,h=i-.005*e,s=l*l*l,d=h*h*h;return[(s>t?s:(116*l-16)/a)*o,(r>f?Math.pow((r+16)/116,3):r/a)*u,(d>t?d:(116*h-16)/a)*c]},U=function(r){var n=Math.sign(r);return((r=Math.abs(r))<=.0031308?12.92*r:1.055*Math.pow(r,1/2.4)-.055)*n},V=function(r,n,e){var t=j.MtxAdaptMa,a=j.MtxAdaptMaI,f=j.MtxXYZ2RGB,o=j.RefWhiteRGB,u=j.Xn,c=j.Yn,i=j.Zn,l=u*t.m00+c*t.m10+i*t.m20,h=u*t.m01+c*t.m11+i*t.m21,s=u*t.m02+c*t.m12+i*t.m22,d=o.X*t.m00+o.Y*t.m10+o.Z*t.m20,b=o.X*t.m01+o.Y*t.m11+o.Z*t.m21,g=o.X*t.m02+o.Y*t.m12+o.Z*t.m22,v=(r*t.m00+n*t.m10+e*t.m20)*(d/l),p=(r*t.m01+n*t.m11+e*t.m21)*(b/h),m=(r*t.m02+n*t.m12+e*t.m22)*(g/s),y=v*a.m00+p*a.m10+m*a.m20,w=v*a.m01+p*a.m11+m*a.m21,k=v*a.m02+p*a.m12+m*a.m22;return[255*U(y*f.m00+w*f.m10+k*f.m20),255*U(y*f.m01+w*f.m11+k*f.m21),255*U(y*f.m02+w*f.m12+k*f.m22)]},D=Math.sin,T=Math.cos,H=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lch\"),t=e[0],a=e[1],f=e[2];return isNaN(f)&&(f=0),[t,T(f*=m)*a,D(f)*a]},J=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"lch\"))[0],t=r[1],a=r[2],f=H(e,t,a),o=f[0],u=f[1],c=f[2],l=K(o,u,c);return[l[0],l[1],l[2],r.length>3?r[3]:1]},Q=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a=(r=i(r,\"lab\"))[0],f=r[1],o=r[2],u=r.slice(3),c=(e=[[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],t=X([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],[a,f,o]),X(e,t.map((function(r){return Math.pow(r,3)})))),l=c[0],h=c[1],s=c[2],d=V(l,h,s);return[d[0],d[1],d[2]].concat(u.length>0&&u[0]<1?[u[0]]:[])};var rr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"lch\"))[0],t=r[1],a=r[2],f=r.slice(3),o=H(e,t,a),u=o[0],c=o[1],l=o[2],h=Q(u,c,l);return[h[0],h[1],h[2]].concat(f.length>0&&f[0]<1?[f[0]]:[])},nr=/((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source,er=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source,tr=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source,ar=/\\s*/.source,fr=/\\s+/.source,or=/\\s*,\\s*/.source,ur=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source,cr=/\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source,ir=new RegExp(\"^rgba?\\\\(\"+ar+[nr,nr,nr].join(fr)+cr+\"\\\\)$\"),lr=new RegExp(\"^rgb\\\\(\"+ar+[nr,nr,nr].join(or)+ar+\"\\\\)$\"),hr=new RegExp(\"^rgba\\\\(\"+ar+[nr,nr,nr,er].join(or)+ar+\"\\\\)$\"),sr=new RegExp(\"^hsla?\\\\(\"+ar+[ur,tr,tr].join(fr)+cr+\"\\\\)$\"),dr=new RegExp(\"^hsl?\\\\(\"+ar+[ur,tr,tr].join(or)+ar+\"\\\\)$\"),br=/^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/,gr=new RegExp(\"^lab\\\\(\"+ar+[er,er,er].join(fr)+cr+\"\\\\)$\"),vr=new RegExp(\"^lch\\\\(\"+ar+[er,er,ur].join(fr)+cr+\"\\\\)$\"),pr=new RegExp(\"^oklab\\\\(\"+ar+[er,er,er].join(fr)+cr+\"\\\\)$\"),mr=new RegExp(\"^oklch\\\\(\"+ar+[er,er,ur].join(fr)+cr+\"\\\\)$\"),yr=Math.round,wr=function(r){return r.map((function(r,n){return n<=2?e(yr(r),0,255):r}))},kr=function(r,n,e,t){return void 0===n&&(n=0),void 0===e&&(e=100),void 0===t&&(t=!1),\"string\"==typeof r&&r.endsWith(\"%\")&&(r=parseFloat(r.substring(0,r.length-1))/100,r=t?n+.5*(r+1)*(e-n):n+r*(e-n)),+r},Mr=function(r,n){return\"none\"===r?n:r},Nr=function(r){if(\"transparent\"===(r=r.toLowerCase().trim()))return[0,0,0,0];var n;if(k.format.named)try{return k.format.named(r)}catch(r){}if((n=r.match(ir))||(n=r.match(lr))){for(var e=n.slice(1,4),t=0;t<3;t++)e[t]=+kr(Mr(e[t],0),0,255);e=wr(e);var a=void 0!==n[4]?+kr(n[4],0,1):1;return e[3]=a,e}if(n=r.match(hr)){for(var f=n.slice(1,5),o=0;o<4;o++)f[o]=+kr(f[o],0,255);return f}if((n=r.match(sr))||(n=r.match(dr))){var u=n.slice(1,4);u[0]=+Mr(u[0].replace(\"deg\",\"\"),0),u[1]=.01*+kr(Mr(u[1],0),0,100),u[2]=.01*+kr(Mr(u[2],0),0,100);var c=wr(I(u)),i=void 0!==n[4]?+kr(n[4],0,1):1;return c[3]=i,c}if(n=r.match(br)){var l=n.slice(1,4);l[1]*=.01,l[2]*=.01;for(var h=I(l),s=0;s<3;s++)h[s]=yr(h[s]);return h[3]=+n[4],h}if(n=r.match(gr)){var d=n.slice(1,4);d[0]=kr(Mr(d[0],0),0,100),d[1]=kr(Mr(d[1],0),-125,125,!0),d[2]=kr(Mr(d[2],0),-125,125,!0);var b=O();R(\"d50\");var g=wr(K(d));R(b);var v=void 0!==n[4]?+kr(n[4],0,1):1;return g[3]=v,g}if(n=r.match(vr)){var p=n.slice(1,4);p[0]=kr(p[0],0,100),p[1]=kr(Mr(p[1],0),0,150,!1),p[2]=+Mr(p[2].replace(\"deg\",\"\"),0);var m=O();R(\"d50\");var y=wr(J(p));R(m);var w=void 0!==n[4]?+kr(n[4],0,1):1;return y[3]=w,y}if(n=r.match(pr)){var M=n.slice(1,4);M[0]=kr(Mr(M[0],0),0,1),M[1]=kr(Mr(M[1],0),-.4,.4,!0),M[2]=kr(Mr(M[2],0),-.4,.4,!0);var N=wr(Q(M)),x=void 0!==n[4]?+kr(n[4],0,1):1;return N[3]=x,N}if(n=r.match(mr)){var _=n.slice(1,4);_[0]=kr(Mr(_[0],0),0,1),_[1]=kr(Mr(_[1],0),0,.4,!1),_[2]=+Mr(_[2].replace(\"deg\",\"\"),0);var A=wr(rr(_)),j=void 0!==n[4]?+kr(n[4],0,1):1;return A[3]=j,A}};Nr.test=function(r){return ir.test(r)||sr.test(r)||gr.test(r)||vr.test(r)||pr.test(r)||mr.test(r)||lr.test(r)||hr.test(r)||dr.test(r)||br.test(r)||\"transparent\"===r},M.prototype.css=function(r){return W(this._rgb,r)};var xr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"css\"])))};N.css=xr,k.format.css=Nr,k.autodetect.push({p:5,test:function(r){for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];if(!n.length&&\"string\"===c(r)&&Nr.test(r))return\"css\"}}),k.format.gl=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\");return e[0]*=255,e[1]*=255,e[2]*=255,e};var _r=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"gl\"])))};N.gl=_r,M.prototype.gl=function(){var r=this._rgb;return[r[0]/255,r[1]/255,r[2]/255,r[3]]};var Ar=Math.floor;M.prototype.hcg=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t=i(r,\"rgb\"),a=t[0],f=t[1],o=t[2],u=s(a,f,o),c=d(a,f,o),l=c-u,h=100*l/255,b=u/(255-l)*100;return 0===l?e=Number.NaN:(a===c&&(e=(f-o)/l),f===c&&(e=2+(o-a)/l),o===c&&(e=4+(a-f)/l),(e*=60)<0&&(e+=360)),[e,h,b]}(this._rgb)};var jr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hcg\"])))};N.hcg=jr,k.format.hcg=function(){for(var r,n,e,t,a,f,o=[],u=arguments.length;u--;)o[u]=arguments[u];var c,l,h,s=(o=i(o,\"hcg\"))[0],d=o[1],b=o[2];b*=255;var g=255*d;if(0===d)c=l=h=b;else{360===s&&(s=0),s>360&&(s-=360),s<0&&(s+=360);var v=Ar(s/=60),p=s-v,m=b*(1-d),y=m+g*(1-p),w=m+g*p,k=m+g;switch(v){case 0:c=(r=[k,w,m])[0],l=r[1],h=r[2];break;case 1:c=(n=[y,k,m])[0],l=n[1],h=n[2];break;case 2:c=(e=[m,k,w])[0],l=e[1],h=e[2];break;case 3:c=(t=[m,y,k])[0],l=t[1],h=t[2];break;case 4:c=(a=[w,m,k])[0],l=a[1],h=a[2];break;case 5:c=(f=[k,m,y])[0],l=f[1],h=f[2]}}return[c,l,h,o.length>3?o[3]:1]},k.autodetect.push({p:1,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hcg\"))&&3===r.length)return\"hcg\"}});var Er=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,Rr=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,Or=function(r){if(r.match(Er)){4!==r.length&&7!==r.length||(r=r.substr(1)),3===r.length&&(r=(r=r.split(\"\"))[0]+r[0]+r[1]+r[1]+r[2]+r[2]);var n=parseInt(r,16);return[n>>16,n>>8&255,255&n,1]}if(r.match(Rr)){5!==r.length&&9!==r.length||(r=r.substr(1)),4===r.length&&(r=(r=r.split(\"\"))[0]+r[0]+r[1]+r[1]+r[2]+r[2]+r[3]+r[3]);var e=parseInt(r,16);return[e>>24&255,e>>16&255,e>>8&255,Math.round((255&e)/255*100)/100]}throw new Error(\"unknown hex color: \"+r)},Pr=Math.round,Fr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\"),t=e[0],a=e[1],f=e[2],o=e[3],u=l(r)||\"auto\";void 0===o&&(o=1),\"auto\"===u&&(u=o<1?\"rgba\":\"rgb\");var c=\"000000\"+((t=Pr(t))<<16|(a=Pr(a))<<8|(f=Pr(f))).toString(16);c=c.substr(c.length-6);var h=\"0\"+Pr(255*o).toString(16);switch(h=h.substr(h.length-2),u.toLowerCase()){case\"rgba\":return\"#\"+c+h;case\"argb\":return\"#\"+h+c;default:return\"#\"+c}};M.prototype.hex=function(r){return Fr(this._rgb,r)};var Lr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hex\"])))};N.hex=Lr,k.format.hex=Or,k.autodetect.push({p:4,test:function(r){for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];if(!n.length&&\"string\"===c(r)&&[3,4,5,6,7,8,9].indexOf(r.length)>=0)return\"hex\"}});var Br=Math.cos,Gr=Math.min,Yr=Math.sqrt,qr=Math.acos;M.prototype.hsi=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t=i(r,\"rgb\"),a=t[0],f=t[1],o=t[2],u=Gr(a/=255,f/=255,o/=255),c=(a+f+o)/3,l=c>0?1-u/c:0;return 0===l?e=NaN:(e=(a-f+(a-o))/2,e/=Yr((a-f)*(a-f)+(a-o)*(f-o)),e=qr(e),o>f&&(e=v-e),e/=v),[360*e,l,c]}(this._rgb)};var Cr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hsi\"])))};N.hsi=Cr,k.format.hsi=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var t,a,f,o=(r=i(r,\"hsi\"))[0],u=r[1],c=r[2];return isNaN(o)&&(o=0),isNaN(u)&&(u=0),o>360&&(o-=360),o<0&&(o+=360),(o/=360)<1/3?a=1-((f=(1-u)/3)+(t=(1+u*Br(v*o)/Br(p-v*o))/3)):o<2/3?f=1-((t=(1-u)/3)+(a=(1+u*Br(v*(o-=1/3))/Br(p-v*o))/3)):t=1-((a=(1-u)/3)+(f=(1+u*Br(v*(o-=2/3))/Br(p-v*o))/3)),[255*(t=e(c*t*3)),255*(a=e(c*a*3)),255*(f=e(c*f*3)),r.length>3?r[3]:1]},k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hsi\"))&&3===r.length)return\"hsi\"}}),M.prototype.hsl=function(){return A(this._rgb)};var Xr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hsl\"])))};N.hsl=Xr,k.format.hsl=I,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hsl\"))&&3===r.length)return\"hsl\"}});var Zr=Math.floor,$r=Math.min,Sr=Math.max;M.prototype.hsv=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a,f=(r=i(r,\"rgb\"))[0],o=r[1],u=r[2],c=$r(f,o,u),l=Sr(f,o,u),h=l-c;return a=l/255,0===l?(e=Number.NaN,t=0):(t=h/l,f===l&&(e=(o-u)/h),o===l&&(e=2+(u-f)/h),u===l&&(e=4+(f-o)/h),(e*=60)<0&&(e+=360)),[e,t,a]}(this._rgb)};var Wr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hsv\"])))};N.hsv=Wr,k.format.hsv=function(){for(var r,n,e,t,a,f,o=[],u=arguments.length;u--;)o[u]=arguments[u];var c,l,h,s=(o=i(o,\"hsv\"))[0],d=o[1],b=o[2];if(b*=255,0===d)c=l=h=b;else{360===s&&(s=0),s>360&&(s-=360),s<0&&(s+=360);var g=Zr(s/=60),v=s-g,p=b*(1-d),m=b*(1-d*v),y=b*(1-d*(1-v));switch(g){case 0:c=(r=[b,y,p])[0],l=r[1],h=r[2];break;case 1:c=(n=[m,b,p])[0],l=n[1],h=n[2];break;case 2:c=(e=[p,b,y])[0],l=e[1],h=e[2];break;case 3:c=(t=[p,m,b])[0],l=t[1],h=t[2];break;case 4:c=(a=[y,p,b])[0],l=a[1],h=a[2];break;case 5:c=(f=[b,p,m])[0],l=f[1],h=f[2]}}return[c,l,h,o.length>3?o[3]:1]},k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hsv\"))&&3===r.length)return\"hsv\"}}),M.prototype.lab=function(){return P(this._rgb)};var Ir=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"lab\"])))};Object.assign(N,{lab:Ir,getLabWhitePoint:O,setLabWhitePoint:R}),k.format.lab=K,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"lab\"))&&3===r.length)return\"lab\"}});M.prototype.lch=function(){return C(this._rgb)},M.prototype.hcl=function(){return w(C(this._rgb))};var Kr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"lch\"])))},zr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hcl\"])))};Object.assign(N,{lch:Kr,hcl:zr}),k.format.lch=J,k.format.hcl=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=w(i(r,\"hcl\"));return J.apply(void 0,e)},[\"lch\",\"hcl\"].forEach((function(r){return k.autodetect.push({p:2,test:function(){for(var n=[],e=arguments.length;e--;)n[e]=arguments[e];if(\"array\"===c(n=i(n,r))&&3===n.length)return r}})}));M.prototype.num=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\");return(e[0]<<16)+(e[1]<<8)+e[2]}(this._rgb)};var Ur=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"num\"])))};Object.assign(N,{num:Ur}),k.format.num=function(r){if(\"number\"==c(r)&&r>=0&&r<=16777215)return[r>>16,r>>8&255,255&r,1];throw new Error(\"unknown num color: \"+r)},k.autodetect.push({p:5,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(1===r.length&&\"number\"===c(r[0])&&r[0]>=0&&r[0]<=16777215)return\"num\"}});var Vr=Math.round;M.prototype.rgb=function(r){return void 0===r&&(r=!0),!1===r?this._rgb.slice(0,3):this._rgb.slice(0,3).map(Vr)},M.prototype.rgba=function(r){return void 0===r&&(r=!0),this._rgb.slice(0,4).map((function(n,e){return e<3?!1===r?n:Vr(n):n}))};var Dr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"rgb\"])))};Object.assign(N,{rgb:Dr}),k.format.rgb=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\");return void 0===e[3]&&(e[3]=1),e},k.autodetect.push({p:3,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"rgba\"))&&(3===r.length||4===r.length&&\"number\"==c(r[3])&&r[3]>=0&&r[3]<=1))return\"rgb\"}});var Tr=Math.log,Hr=function(r){var n,e,t,a=r/100;return a<66?(n=255,e=a<6?0:-155.25485562709179-.44596950469579133*(e=a-2)+104.49216199393888*Tr(e),t=a<20?0:.8274096064007395*(t=a-10)-254.76935184120902+115.67994401066147*Tr(t)):(n=351.97690566805693+.114206453784165*(n=a-55)-40.25366309332127*Tr(n),e=325.4494125711974+.07943456536662342*(e=a-50)-28.0852963507957*Tr(e),t=255),[n,e,t,1]},Jr=Math.round;M.prototype.temp=M.prototype.kelvin=M.prototype.temperature=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];for(var e,t=i(r,\"rgb\"),a=t[0],f=t[2],o=1e3,u=4e4;u-o>.4;){var c=Hr(e=.5*(u+o));c[2]/c[0]>=f/a?u=e:o=e}return Jr(e)}(this._rgb)};var Qr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"temp\"])))};Object.assign(N,{temp:Qr,kelvin:Qr,temperature:Qr}),k.format.temp=k.format.kelvin=k.format.temperature=Hr,M.prototype.oklab=function(){return Z(this._rgb)};var rn=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"oklab\"])))};Object.assign(N,{oklab:rn}),k.format.oklab=Q,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"oklab\"))&&3===r.length)return\"oklab\"}}),M.prototype.oklch=function(){return $(this._rgb)};var nn=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"oklch\"])))};Object.assign(N,{oklch:nn}),k.format.oklch=rr,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"oklch\"))&&3===r.length)return\"oklch\"}});var en={aliceblue:\"#f0f8ff\",antiquewhite:\"#faebd7\",aqua:\"#00ffff\",aquamarine:\"#7fffd4\",azure:\"#f0ffff\",beige:\"#f5f5dc\",bisque:\"#ffe4c4\",black:\"#000000\",blanchedalmond:\"#ffebcd\",blue:\"#0000ff\",blueviolet:\"#8a2be2\",brown:\"#a52a2a\",burlywood:\"#deb887\",cadetblue:\"#5f9ea0\",chartreuse:\"#7fff00\",chocolate:\"#d2691e\",coral:\"#ff7f50\",cornflowerblue:\"#6495ed\",cornsilk:\"#fff8dc\",crimson:\"#dc143c\",cyan:\"#00ffff\",darkblue:\"#00008b\",darkcyan:\"#008b8b\",darkgoldenrod:\"#b8860b\",darkgray:\"#a9a9a9\",darkgreen:\"#006400\",darkgrey:\"#a9a9a9\",darkkhaki:\"#bdb76b\",darkmagenta:\"#8b008b\",darkolivegreen:\"#556b2f\",darkorange:\"#ff8c00\",darkorchid:\"#9932cc\",darkred:\"#8b0000\",darksalmon:\"#e9967a\",darkseagreen:\"#8fbc8f\",darkslateblue:\"#483d8b\",darkslategray:\"#2f4f4f\",darkslategrey:\"#2f4f4f\",darkturquoise:\"#00ced1\",darkviolet:\"#9400d3\",deeppink:\"#ff1493\",deepskyblue:\"#00bfff\",dimgray:\"#696969\",dimgrey:\"#696969\",dodgerblue:\"#1e90ff\",firebrick:\"#b22222\",floralwhite:\"#fffaf0\",forestgreen:\"#228b22\",fuchsia:\"#ff00ff\",gainsboro:\"#dcdcdc\",ghostwhite:\"#f8f8ff\",gold:\"#ffd700\",goldenrod:\"#daa520\",gray:\"#808080\",green:\"#008000\",greenyellow:\"#adff2f\",grey:\"#808080\",honeydew:\"#f0fff0\",hotpink:\"#ff69b4\",indianred:\"#cd5c5c\",indigo:\"#4b0082\",ivory:\"#fffff0\",khaki:\"#f0e68c\",laserlemon:\"#ffff54\",lavender:\"#e6e6fa\",lavenderblush:\"#fff0f5\",lawngreen:\"#7cfc00\",lemonchiffon:\"#fffacd\",lightblue:\"#add8e6\",lightcoral:\"#f08080\",lightcyan:\"#e0ffff\",lightgoldenrod:\"#fafad2\",lightgoldenrodyellow:\"#fafad2\",lightgray:\"#d3d3d3\",lightgreen:\"#90ee90\",lightgrey:\"#d3d3d3\",lightpink:\"#ffb6c1\",lightsalmon:\"#ffa07a\",lightseagreen:\"#20b2aa\",lightskyblue:\"#87cefa\",lightslategray:\"#778899\",lightslategrey:\"#778899\",lightsteelblue:\"#b0c4de\",lightyellow:\"#ffffe0\",lime:\"#00ff00\",limegreen:\"#32cd32\",linen:\"#faf0e6\",magenta:\"#ff00ff\",maroon:\"#800000\",maroon2:\"#7f0000\",maroon3:\"#b03060\",mediumaquamarine:\"#66cdaa\",mediumblue:\"#0000cd\",mediumorchid:\"#ba55d3\",mediumpurple:\"#9370db\",mediumseagreen:\"#3cb371\",mediumslateblue:\"#7b68ee\",mediumspringgreen:\"#00fa9a\",mediumturquoise:\"#48d1cc\",mediumvioletred:\"#c71585\",midnightblue:\"#191970\",mintcream:\"#f5fffa\",mistyrose:\"#ffe4e1\",moccasin:\"#ffe4b5\",navajowhite:\"#ffdead\",navy:\"#000080\",oldlace:\"#fdf5e6\",olive:\"#808000\",olivedrab:\"#6b8e23\",orange:\"#ffa500\",orangered:\"#ff4500\",orchid:\"#da70d6\",palegoldenrod:\"#eee8aa\",palegreen:\"#98fb98\",paleturquoise:\"#afeeee\",palevioletred:\"#db7093\",papayawhip:\"#ffefd5\",peachpuff:\"#ffdab9\",peru:\"#cd853f\",pink:\"#ffc0cb\",plum:\"#dda0dd\",powderblue:\"#b0e0e6\",purple:\"#800080\",purple2:\"#7f007f\",purple3:\"#a020f0\",rebeccapurple:\"#663399\",red:\"#ff0000\",rosybrown:\"#bc8f8f\",royalblue:\"#4169e1\",saddlebrown:\"#8b4513\",salmon:\"#fa8072\",sandybrown:\"#f4a460\",seagreen:\"#2e8b57\",seashell:\"#fff5ee\",sienna:\"#a0522d\",silver:\"#c0c0c0\",skyblue:\"#87ceeb\",slateblue:\"#6a5acd\",slategray:\"#708090\",slategrey:\"#708090\",snow:\"#fffafa\",springgreen:\"#00ff7f\",steelblue:\"#4682b4\",tan:\"#d2b48c\",teal:\"#008080\",thistle:\"#d8bfd8\",tomato:\"#ff6347\",turquoise:\"#40e0d0\",violet:\"#ee82ee\",wheat:\"#f5deb3\",white:\"#ffffff\",whitesmoke:\"#f5f5f5\",yellow:\"#ffff00\",yellowgreen:\"#9acd32\"};M.prototype.name=function(){for(var r=Fr(this._rgb,\"rgb\"),n=0,e=Object.keys(en);n<e.length;n+=1){var t=e[n];if(en[t]===r)return t.toLowerCase()}return r},k.format.named=function(r){if(r=r.toLowerCase(),en[r])return Or(en[r]);throw new Error(\"unknown color name: \"+r)},k.autodetect.push({p:5,test:function(r){for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];if(!n.length&&\"string\"===c(r)&&en[r.toLowerCase()])return\"named\"}}),M.prototype.alpha=function(r,n){return void 0===n&&(n=!1),void 0!==r&&\"number\"===c(r)?n?(this._rgb[3]=r,this):new M([this._rgb[0],this._rgb[1],this._rgb[2],r],\"rgb\"):this._rgb[3]},M.prototype.clipped=function(){return this._rgb._clipped||!1},M.prototype.darken=function(r){void 0===r&&(r=1);var n=this.lab();return n[0]-=j.Kn*r,new M(n,\"lab\").alpha(this.alpha(),!0)},M.prototype.brighten=function(r){return void 0===r&&(r=1),this.darken(-r)},M.prototype.darker=M.prototype.darken,M.prototype.brighter=M.prototype.brighten,M.prototype.get=function(r){var n=r.split(\".\"),e=n[0],t=n[1],a=this[e]();if(t){var f=e.indexOf(t)-(\"ok\"===e.substr(0,2)?2:0);if(f>-1)return a[f];throw new Error(\"unknown channel \"+t+\" in mode \"+e)}return a};var tn=Math.pow;M.prototype.luminance=function(r,n){if(void 0===n&&(n=\"rgb\"),void 0!==r&&\"number\"===c(r)){if(0===r)return new M([0,0,0,this._rgb[3]],\"rgb\");if(1===r)return new M([255,255,255,this._rgb[3]],\"rgb\");var e=this.luminance(),t=20,a=function(e,f){var o=e.interpolate(f,.5,n),u=o.luminance();return Math.abs(r-u)<1e-7||!t--?o:u>r?a(e,o):a(o,f)},f=(e>r?a(new M([0,0,0]),this):a(this,new M([255,255,255]))).rgb();return new M(f.concat([this._rgb[3]]))}return an.apply(void 0,this._rgb.slice(0,3))};var an=function(r,n,e){return.2126*(r=fn(r))+.7152*(n=fn(n))+.0722*(e=fn(e))},fn=function(r){return(r/=255)<=.03928?r/12.92:tn((r+.055)/1.055,2.4)},on={};function un(r,n,e){void 0===e&&(e=.5);for(var t=[],a=arguments.length-3;a-- >0;)t[a]=arguments[a+3];var f=t[0]||\"lrgb\";if(on[f]||t.length||(f=Object.keys(on)[0]),!on[f])throw new Error(\"interpolation mode \"+f+\" is not defined\");return\"object\"!==c(r)&&(r=new M(r)),\"object\"!==c(n)&&(n=new M(n)),on[f](r,n,e).alpha(r.alpha()+e*(n.alpha()-r.alpha()))}M.prototype.mix=M.prototype.interpolate=function(r,n){void 0===n&&(n=.5);for(var e=[],t=arguments.length-2;t-- >0;)e[t]=arguments[t+2];return un.apply(void 0,[this,r,n].concat(e))},M.prototype.premultiply=function(r){void 0===r&&(r=!1);var n=this._rgb,e=n[3];return r?(this._rgb=[n[0]*e,n[1]*e,n[2]*e,e],this):new M([n[0]*e,n[1]*e,n[2]*e,e],\"rgb\")},M.prototype.saturate=function(r){void 0===r&&(r=1);var n=this.lch();return n[1]+=j.Kn*r,n[1]<0&&(n[1]=0),new M(n,\"lch\").alpha(this.alpha(),!0)},M.prototype.desaturate=function(r){return void 0===r&&(r=1),this.saturate(-r)},M.prototype.set=function(r,n,e){void 0===e&&(e=!1);var t=r.split(\".\"),a=t[0],f=t[1],o=this[a]();if(f){var u=a.indexOf(f)-(\"ok\"===a.substr(0,2)?2:0);if(u>-1){if(\"string\"==c(n))switch(n.charAt(0)){case\"+\":case\"-\":o[u]+=+n;break;case\"*\":o[u]*=+n.substr(1);break;case\"/\":o[u]/=+n.substr(1);break;default:o[u]=+n}else{if(\"number\"!==c(n))throw new Error(\"unsupported value for Color.set\");o[u]=n}var i=new M(o,a);return e?(this._rgb=i._rgb,this):i}throw new Error(\"unknown channel \"+f+\" in mode \"+a)}return o},M.prototype.tint=function(r){void 0===r&&(r=.5);for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];return un.apply(void 0,[this,\"white\",r].concat(n))},M.prototype.shade=function(r){void 0===r&&(r=.5);for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];return un.apply(void 0,[this,\"black\",r].concat(n))};on.rgb=function(r,n,e){var t=r._rgb,a=n._rgb;return new M(t[0]+e*(a[0]-t[0]),t[1]+e*(a[1]-t[1]),t[2]+e*(a[2]-t[2]),\"rgb\")};var cn=Math.sqrt,ln=Math.pow;on.lrgb=function(r,n,e){var t=r._rgb,a=t[0],f=t[1],o=t[2],u=n._rgb,c=u[0],i=u[1],l=u[2];return new M(cn(ln(a,2)*(1-e)+ln(c,2)*e),cn(ln(f,2)*(1-e)+ln(i,2)*e),cn(ln(o,2)*(1-e)+ln(l,2)*e),\"rgb\")};function hn(r,n,e,t){var a,f,o,u,c,i,l,h,s,d,b,g,v;return\"hsl\"===t?(o=r.hsl(),u=n.hsl()):\"hsv\"===t?(o=r.hsv(),u=n.hsv()):\"hcg\"===t?(o=r.hcg(),u=n.hcg()):\"hsi\"===t?(o=r.hsi(),u=n.hsi()):\"lch\"===t||\"hcl\"===t?(t=\"hcl\",o=r.hcl(),u=n.hcl()):\"oklch\"===t&&(o=r.oklch().reverse(),u=n.oklch().reverse()),\"h\"!==t.substr(0,1)&&\"oklch\"!==t||(c=(a=o)[0],l=a[1],s=a[2],i=(f=u)[0],h=f[1],d=f[2]),isNaN(c)||isNaN(i)?isNaN(c)?isNaN(i)?g=Number.NaN:(g=i,1!=s&&0!=s||\"hsv\"==t||(b=h)):(g=c,1!=d&&0!=d||\"hsv\"==t||(b=l)):g=c+e*(i>c&&i-c>180?i-(c+360):i<c&&c-i>180?i+360-c:i-c),void 0===b&&(b=l+e*(h-l)),v=s+e*(d-s),new M(\"oklch\"===t?[v,b,g]:[g,b,v],t)}on.lab=function(r,n,e){var t=r.lab(),a=n.lab();return new M(t[0]+e*(a[0]-t[0]),t[1]+e*(a[1]-t[1]),t[2]+e*(a[2]-t[2]),\"lab\")};var sn=function(r,n,e){return hn(r,n,e,\"lch\")};on.lch=sn,on.hcl=sn;on.num=function(r,n,e){var t=r.num(),a=n.num();return new M(t+e*(a-t),\"num\")};on.hcg=function(r,n,e){return hn(r,n,e,\"hcg\")};on.hsi=function(r,n,e){return hn(r,n,e,\"hsi\")};on.hsl=function(r,n,e){return hn(r,n,e,\"hsl\")};on.hsv=function(r,n,e){return hn(r,n,e,\"hsv\")};on.oklab=function(r,n,e){var t=r.oklab(),a=n.oklab();return new M(t[0]+e*(a[0]-t[0]),t[1]+e*(a[1]-t[1]),t[2]+e*(a[2]-t[2]),\"oklab\")};on.oklch=function(r,n,e){return hn(r,n,e,\"oklch\")};var dn=Math.pow,bn=Math.sqrt,gn=Math.PI,vn=Math.cos,pn=Math.sin,mn=Math.atan2;var yn=function(r,n){for(var e=r.length,a=[0,0,0,0],f=0;f<r.length;f++){var o=r[f],u=n[f]/e,c=o._rgb;a[0]+=dn(c[0],2)*u,a[1]+=dn(c[1],2)*u,a[2]+=dn(c[2],2)*u,a[3]+=c[3]*u}return a[0]=bn(a[0]),a[1]=bn(a[1]),a[2]=bn(a[2]),a[3]>.9999999&&(a[3]=1),new M(t(a))},wn=Math.pow;function kn(r){var n=\"rgb\",t=N(\"#ccc\"),a=0,f=[0,1],o=[0,1],u=[],i=[0,0],l=!1,h=[],s=!1,d=0,b=1,g=!1,v={},p=!0,m=1,y=function(r){if((r=r||[\"#fff\",\"#000\"])&&\"string\"===c(r)&&N.brewer&&N.brewer[r.toLowerCase()]&&(r=N.brewer[r.toLowerCase()]),\"array\"===c(r)){1===r.length&&(r=[r[0],r[0]]),r=r.slice(0);for(var n=0;n<r.length;n++)r[n]=N(r[n]);u.length=0;for(var e=0;e<r.length;e++)u.push(e/(r.length-1))}return x(),h=r},w=function(r){return r},k=function(r){return r},M=function(r,a){var f,o;if(null==a&&(a=!1),isNaN(r)||null===r)return t;if(a)o=r;else if(l&&l.length>2){var s=function(r){if(null!=l){for(var n=l.length-1,e=0;e<n&&r>=l[e];)e++;return e-1}return 0}(r);o=s/(l.length-2)}else o=b!==d?(r-d)/(b-d):1;o=k(o),a||(o=w(o)),1!==m&&(o=wn(o,m)),o=e(o=i[0]+o*(1-i[0]-i[1]),0,1);var g=Math.floor(1e4*o);if(p&&v[g])f=v[g];else{if(\"array\"===c(h))for(var y=0;y<u.length;y++){var M=u[y];if(o<=M){f=h[y];break}if(o>=M&&y===u.length-1){f=h[y];break}if(o>M&&o<u[y+1]){o=(o-M)/(u[y+1]-M),f=N.interpolate(h[y],h[y+1],o,n);break}}else\"function\"===c(h)&&(f=h(o));p&&(v[g]=f)}return f},x=function(){return v={}};y(r);var _=function(r){var n=N(M(r));return s&&n[s]?n[s]():n};return _.classes=function(r){if(null!=r){if(\"array\"===c(r))l=r,f=[r[0],r[r.length-1]];else{var n=N.analyze(f);l=0===r?[n.min,n.max]:N.limits(n,\"e\",r)}return _}return l},_.domain=function(r){if(!arguments.length)return o;o=r.slice(0),d=r[0],b=r[r.length-1],u=[];var n=h.length;if(r.length===n&&d!==b)for(var e=0,t=Array.from(r);e<t.length;e+=1){var a=t[e];u.push((a-d)/(b-d))}else{for(var c=0;c<n;c++)u.push(c/(n-1));if(r.length>2){var i=r.map((function(n,e){return e/(r.length-1)})),l=r.map((function(r){return(r-d)/(b-d)}));l.every((function(r,n){return i[n]===r}))||(k=function(r){if(r<=0||r>=1)return r;for(var n=0;r>=l[n+1];)n++;var e=(r-l[n])/(l[n+1]-l[n]);return i[n]+e*(i[n+1]-i[n])})}}return f=[d,b],_},_.mode=function(r){return arguments.length?(n=r,x(),_):n},_.range=function(r,n){return y(r),_},_.out=function(r){return s=r,_},_.spread=function(r){return arguments.length?(a=r,_):a},_.correctLightness=function(r){return null==r&&(r=!0),g=r,x(),w=g?function(r){for(var n=M(0,!0).lab()[0],e=M(1,!0).lab()[0],t=n>e,a=M(r,!0).lab()[0],f=n+(e-n)*r,o=a-f,u=0,c=1,i=20;Math.abs(o)>.01&&i-- >0;)t&&(o*=-1),o<0?(u=r,r+=.5*(c-r)):(c=r,r+=.5*(u-r)),a=M(r,!0).lab()[0],o=a-f;return r}:function(r){return r},_},_.padding=function(r){return null!=r?(\"number\"===c(r)&&(r=[r,r]),i=r,_):i},_.colors=function(n,e){arguments.length<2&&(e=\"hex\");var t=[];if(0===arguments.length)t=h.slice(0);else if(1===n)t=[_(.5)];else if(n>1){var a=f[0],o=f[1]-a;t=function(r,n){for(var e=[],t=r<n,a=n,f=r;t?f<a:f>a;t?f++:f--)e.push(f);return e}(0,n).map((function(r){return _(a+r/(n-1)*o)}))}else{r=[];var u=[];if(l&&l.length>2)for(var c=1,i=l.length,s=1<=i;s?c<i:c>i;s?c++:c--)u.push(.5*(l[c-1]+l[c]));else u=f;t=u.map((function(r){return _(r)}))}return N[e]&&(t=t.map((function(r){return r[e]()}))),t},_.cache=function(r){return null!=r?(p=r,_):p},_.gamma=function(r){return null!=r?(m=r,_):m},_.nodata=function(r){return null!=r?(t=N(r),_):t},_}var Mn=function(r,n,e){if(!Mn[e])throw new Error(\"unknown blend mode \"+e);return Mn[e](r,n)},Nn=function(r){return function(n,e){var t=N(e).rgb(),a=N(n).rgb();return N.rgb(r(t,a))}},xn=function(r){return function(n,e){var t=[];return t[0]=r(n[0],e[0]),t[1]=r(n[1],e[1]),t[2]=r(n[2],e[2]),t}};Mn.normal=Nn(xn((function(r){return r}))),Mn.multiply=Nn(xn((function(r,n){return r*n/255}))),Mn.screen=Nn(xn((function(r,n){return 255*(1-(1-r/255)*(1-n/255))}))),Mn.overlay=Nn(xn((function(r,n){return n<128?2*r*n/255:255*(1-2*(1-r/255)*(1-n/255))}))),Mn.darken=Nn(xn((function(r,n){return r>n?n:r}))),Mn.lighten=Nn(xn((function(r,n){return r>n?r:n}))),Mn.dodge=Nn(xn((function(r,n){return 255===r||(r=n/255*255/(1-r/255))>255?255:r}))),Mn.burn=Nn(xn((function(r,n){return 255*(1-(1-n/255)/(r/255))})));var _n=Math.pow,An=Math.sin,jn=Math.cos;var En=Math.floor,Rn=Math.random;var On=Math.log,Pn=Math.pow,Fn=Math.floor,Ln=Math.abs;function Bn(r,n){void 0===n&&(n=null);var e={min:Number.MAX_VALUE,max:-1*Number.MAX_VALUE,sum:0,values:[],count:0};return\"object\"===c(r)&&(r=Object.values(r)),r.forEach((function(r){n&&\"object\"===c(r)&&(r=r[n]),null==r||isNaN(r)||(e.values.push(r),e.sum+=r,r<e.min&&(e.min=r),r>e.max&&(e.max=r),e.count+=1)})),e.domain=[e.min,e.max],e.limits=function(r,n){return Gn(e,r,n)},e}function Gn(r,n,e){void 0===n&&(n=\"equal\"),void 0===e&&(e=7),\"array\"==c(r)&&(r=Bn(r));var t=r.min,a=r.max,f=r.values.sort((function(r,n){return r-n}));if(1===e)return[t,a];var o=[];if(\"c\"===n.substr(0,1)&&(o.push(t),o.push(a)),\"e\"===n.substr(0,1)){o.push(t);for(var u=1;u<e;u++)o.push(t+u/e*(a-t));o.push(a)}else if(\"l\"===n.substr(0,1)){if(t<=0)throw new Error(\"Logarithmic scales are only possible for values > 0\");var i=Math.LOG10E*On(t),l=Math.LOG10E*On(a);o.push(t);for(var h=1;h<e;h++)o.push(Pn(10,i+h/e*(l-i)));o.push(a)}else if(\"q\"===n.substr(0,1)){o.push(t);for(var s=1;s<e;s++){var d=(f.length-1)*s/e,b=Fn(d);if(b===d)o.push(f[b]);else{var g=d-b;o.push(f[b]*(1-g)+f[b+1]*g)}}o.push(a)}else if(\"k\"===n.substr(0,1)){var v,p=f.length,m=new Array(p),y=new Array(e),w=!0,k=0,M=null;(M=[]).push(t);for(var N=1;N<e;N++)M.push(t+N/e*(a-t));for(M.push(a);w;){for(var x=0;x<e;x++)y[x]=0;for(var _=0;_<p;_++)for(var A=f[_],j=Number.MAX_VALUE,E=void 0,R=0;R<e;R++){var O=Ln(M[R]-A);O<j&&(j=O,E=R),y[E]++,m[_]=E}for(var P=new Array(e),F=0;F<e;F++)P[F]=null;for(var L=0;L<p;L++)null===P[v=m[L]]?P[v]=f[L]:P[v]+=f[L];for(var B=0;B<e;B++)P[B]*=1/y[B];w=!1;for(var G=0;G<e;G++)if(P[G]!==M[G]){w=!0;break}M=P,++k>200&&(w=!1)}for(var Y={},q=0;q<e;q++)Y[q]=[];for(var C=0;C<p;C++)Y[v=m[C]].push(f[C]);for(var X=[],Z=0;Z<e;Z++)X.push(Y[Z][0]),X.push(Y[Z][Y[Z].length-1]);X=X.sort((function(r,n){return r-n})),o.push(X[0]);for(var $=1;$<X.length;$+=2){var S=X[$];isNaN(S)||-1!==o.indexOf(S)||o.push(S)}}return o}\n/**\n     * @license\n     *\n     * The APCA contrast prediction algorithm is based of the formulas published\n     * in the APCA-1.0.98G specification by Myndex. The specification is available at:\n     * https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAw3_0.1.17_APCA0.0.98G.svg\n     *\n     * Note that the APCA implementation is still beta, so please update to\n     * future versions of chroma.js when they become available.\n     *\n     * You can read more about the APCA Readability Criterion at\n     * https://readtech.org/ARC/\n     */\nvar Yn=.022;function qn(r,n,e){return.2126729*Math.pow(r/255,2.4)+.7151522*Math.pow(n/255,2.4)+.072175*Math.pow(e/255,2.4)}var Cn=Math.sqrt,Xn=Math.pow,Zn=Math.min,$n=Math.max,Sn=Math.atan2,Wn=Math.abs,In=Math.cos,Kn=Math.sin,zn=Math.exp,Un=Math.PI;var Vn={cool:function(){return kn([N.hsl(180,1,.9),N.hsl(250,.7,.4)])},hot:function(){return kn([\"#000\",\"#f00\",\"#ff0\",\"#fff\"]).mode(\"rgb\")}},Dn={OrRd:[\"#fff7ec\",\"#fee8c8\",\"#fdd49e\",\"#fdbb84\",\"#fc8d59\",\"#ef6548\",\"#d7301f\",\"#b30000\",\"#7f0000\"],PuBu:[\"#fff7fb\",\"#ece7f2\",\"#d0d1e6\",\"#a6bddb\",\"#74a9cf\",\"#3690c0\",\"#0570b0\",\"#045a8d\",\"#023858\"],BuPu:[\"#f7fcfd\",\"#e0ecf4\",\"#bfd3e6\",\"#9ebcda\",\"#8c96c6\",\"#8c6bb1\",\"#88419d\",\"#810f7c\",\"#4d004b\"],Oranges:[\"#fff5eb\",\"#fee6ce\",\"#fdd0a2\",\"#fdae6b\",\"#fd8d3c\",\"#f16913\",\"#d94801\",\"#a63603\",\"#7f2704\"],BuGn:[\"#f7fcfd\",\"#e5f5f9\",\"#ccece6\",\"#99d8c9\",\"#66c2a4\",\"#41ae76\",\"#238b45\",\"#006d2c\",\"#00441b\"],YlOrBr:[\"#ffffe5\",\"#fff7bc\",\"#fee391\",\"#fec44f\",\"#fe9929\",\"#ec7014\",\"#cc4c02\",\"#993404\",\"#662506\"],YlGn:[\"#ffffe5\",\"#f7fcb9\",\"#d9f0a3\",\"#addd8e\",\"#78c679\",\"#41ab5d\",\"#238443\",\"#006837\",\"#004529\"],Reds:[\"#fff5f0\",\"#fee0d2\",\"#fcbba1\",\"#fc9272\",\"#fb6a4a\",\"#ef3b2c\",\"#cb181d\",\"#a50f15\",\"#67000d\"],RdPu:[\"#fff7f3\",\"#fde0dd\",\"#fcc5c0\",\"#fa9fb5\",\"#f768a1\",\"#dd3497\",\"#ae017e\",\"#7a0177\",\"#49006a\"],Greens:[\"#f7fcf5\",\"#e5f5e0\",\"#c7e9c0\",\"#a1d99b\",\"#74c476\",\"#41ab5d\",\"#238b45\",\"#006d2c\",\"#00441b\"],YlGnBu:[\"#ffffd9\",\"#edf8b1\",\"#c7e9b4\",\"#7fcdbb\",\"#41b6c4\",\"#1d91c0\",\"#225ea8\",\"#253494\",\"#081d58\"],Purples:[\"#fcfbfd\",\"#efedf5\",\"#dadaeb\",\"#bcbddc\",\"#9e9ac8\",\"#807dba\",\"#6a51a3\",\"#54278f\",\"#3f007d\"],GnBu:[\"#f7fcf0\",\"#e0f3db\",\"#ccebc5\",\"#a8ddb5\",\"#7bccc4\",\"#4eb3d3\",\"#2b8cbe\",\"#0868ac\",\"#084081\"],Greys:[\"#ffffff\",\"#f0f0f0\",\"#d9d9d9\",\"#bdbdbd\",\"#969696\",\"#737373\",\"#525252\",\"#252525\",\"#000000\"],YlOrRd:[\"#ffffcc\",\"#ffeda0\",\"#fed976\",\"#feb24c\",\"#fd8d3c\",\"#fc4e2a\",\"#e31a1c\",\"#bd0026\",\"#800026\"],PuRd:[\"#f7f4f9\",\"#e7e1ef\",\"#d4b9da\",\"#c994c7\",\"#df65b0\",\"#e7298a\",\"#ce1256\",\"#980043\",\"#67001f\"],Blues:[\"#f7fbff\",\"#deebf7\",\"#c6dbef\",\"#9ecae1\",\"#6baed6\",\"#4292c6\",\"#2171b5\",\"#08519c\",\"#08306b\"],PuBuGn:[\"#fff7fb\",\"#ece2f0\",\"#d0d1e6\",\"#a6bddb\",\"#67a9cf\",\"#3690c0\",\"#02818a\",\"#016c59\",\"#014636\"],Viridis:[\"#440154\",\"#482777\",\"#3f4a8a\",\"#31678e\",\"#26838f\",\"#1f9d8a\",\"#6cce5a\",\"#b6de2b\",\"#fee825\"],Spectral:[\"#9e0142\",\"#d53e4f\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#e6f598\",\"#abdda4\",\"#66c2a5\",\"#3288bd\",\"#5e4fa2\"],RdYlGn:[\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#d9ef8b\",\"#a6d96a\",\"#66bd63\",\"#1a9850\",\"#006837\"],RdBu:[\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#f7f7f7\",\"#d1e5f0\",\"#92c5de\",\"#4393c3\",\"#2166ac\",\"#053061\"],PiYG:[\"#8e0152\",\"#c51b7d\",\"#de77ae\",\"#f1b6da\",\"#fde0ef\",\"#f7f7f7\",\"#e6f5d0\",\"#b8e186\",\"#7fbc41\",\"#4d9221\",\"#276419\"],PRGn:[\"#40004b\",\"#762a83\",\"#9970ab\",\"#c2a5cf\",\"#e7d4e8\",\"#f7f7f7\",\"#d9f0d3\",\"#a6dba0\",\"#5aae61\",\"#1b7837\",\"#00441b\"],RdYlBu:[\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee090\",\"#ffffbf\",\"#e0f3f8\",\"#abd9e9\",\"#74add1\",\"#4575b4\",\"#313695\"],BrBG:[\"#543005\",\"#8c510a\",\"#bf812d\",\"#dfc27d\",\"#f6e8c3\",\"#f5f5f5\",\"#c7eae5\",\"#80cdc1\",\"#35978f\",\"#01665e\",\"#003c30\"],RdGy:[\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#ffffff\",\"#e0e0e0\",\"#bababa\",\"#878787\",\"#4d4d4d\",\"#1a1a1a\"],PuOr:[\"#7f3b08\",\"#b35806\",\"#e08214\",\"#fdb863\",\"#fee0b6\",\"#f7f7f7\",\"#d8daeb\",\"#b2abd2\",\"#8073ac\",\"#542788\",\"#2d004b\"],Set2:[\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\",\"#a6d854\",\"#ffd92f\",\"#e5c494\",\"#b3b3b3\"],Accent:[\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\",\"#386cb0\",\"#f0027f\",\"#bf5b17\",\"#666666\"],Set1:[\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\",\"#ffff33\",\"#a65628\",\"#f781bf\",\"#999999\"],Set3:[\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\",\"#d9d9d9\",\"#bc80bd\",\"#ccebc5\",\"#ffed6f\"],Dark2:[\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\",\"#66a61e\",\"#e6ab02\",\"#a6761d\",\"#666666\"],Paired:[\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\",\"#cab2d6\",\"#6a3d9a\",\"#ffff99\",\"#b15928\"],Pastel2:[\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\",\"#e6f5c9\",\"#fff2ae\",\"#f1e2cc\",\"#cccccc\"],Pastel1:[\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\",\"#ffffcc\",\"#e5d8bd\",\"#fddaec\",\"#f2f2f2\"]},Tn=Object.keys(Dn),Hn=new Map(Tn.map((function(r){return[r.toLowerCase(),r]}))),Jn=\"function\"==typeof Proxy?new Proxy(Dn,{get:function(r,n){var e=n.toLowerCase();if(Hn.has(e))return r[Hn.get(e)]},getOwnPropertyNames:function(){return Object.getOwnPropertyNames(Tn)}}):Dn;return Object.assign(N,{analyze:Bn,average:function(r,n,e){void 0===n&&(n=\"lrgb\"),void 0===e&&(e=null);var t=r.length;e||(e=Array.from(new Array(t)).map((function(){return 1})));var a=t/e.reduce((function(r,n){return r+n}));if(e.forEach((function(r,n){e[n]*=a})),r=r.map((function(r){return new M(r)})),\"lrgb\"===n)return yn(r,e);for(var f=r.shift(),o=f.get(n),u=[],c=0,i=0,l=0;l<o.length;l++)if(o[l]=(o[l]||0)*e[0],u.push(isNaN(o[l])?0:e[0]),\"h\"===n.charAt(l)&&!isNaN(o[l])){var h=o[l]/180*gn;c+=vn(h)*e[0],i+=pn(h)*e[0]}var s=f.alpha()*e[0];r.forEach((function(r,t){var a=r.get(n);s+=r.alpha()*e[t+1];for(var f=0;f<o.length;f++)if(!isNaN(a[f]))if(u[f]+=e[t+1],\"h\"===n.charAt(f)){var l=a[f]/180*gn;c+=vn(l)*e[t+1],i+=pn(l)*e[t+1]}else o[f]+=a[f]*e[t+1]}));for(var d=0;d<o.length;d++)if(\"h\"===n.charAt(d)){for(var b=mn(i/u[d],c/u[d])/gn*180;b<0;)b+=360;for(;b>=360;)b-=360;o[d]=b}else o[d]=o[d]/u[d];return s/=t,new M(o,n).alpha(s>.99999?1:s,!0)},bezier:function(r){var n=function(r){var n,e,t,a,f,o,u;if(2===(r=r.map((function(r){return new M(r)}))).length)n=r.map((function(r){return r.lab()})),f=n[0],o=n[1],a=function(r){var n=[0,1,2].map((function(n){return f[n]+r*(o[n]-f[n])}));return new M(n,\"lab\")};else if(3===r.length)e=r.map((function(r){return r.lab()})),f=e[0],o=e[1],u=e[2],a=function(r){var n=[0,1,2].map((function(n){return(1-r)*(1-r)*f[n]+2*(1-r)*r*o[n]+r*r*u[n]}));return new M(n,\"lab\")};else if(4===r.length){var c;t=r.map((function(r){return r.lab()})),f=t[0],o=t[1],u=t[2],c=t[3],a=function(r){var n=[0,1,2].map((function(n){return(1-r)*(1-r)*(1-r)*f[n]+3*(1-r)*(1-r)*r*o[n]+3*(1-r)*r*r*u[n]+r*r*r*c[n]}));return new M(n,\"lab\")}}else{if(!(r.length>=5))throw new RangeError(\"No point in running bezier with only one color.\");var i,l,h;i=r.map((function(r){return r.lab()})),h=r.length-1,l=function(r){for(var n=[1,1],e=1;e<r;e++){for(var t=[1],a=1;a<=n.length;a++)t[a]=(n[a]||0)+n[a-1];n=t}return n}(h),a=function(r){var n=1-r,e=[0,1,2].map((function(e){return i.reduce((function(t,a,f){return t+l[f]*Math.pow(n,h-f)*Math.pow(r,f)*a[e]}),0)}));return new M(e,\"lab\")}}return a}(r);return n.scale=function(){return kn(n)},n},blend:Mn,brewer:Jn,Color:M,colors:en,contrast:function(r,n){r=new M(r),n=new M(n);var e=r.luminance(),t=n.luminance();return e>t?(e+.05)/(t+.05):(t+.05)/(e+.05)},contrastAPCA:function(r,n){r=new M(r),n=new M(n),r.alpha()<1&&(r=un(n,r,r.alpha(),\"rgb\"));var e=qn.apply(void 0,r.rgb()),t=qn.apply(void 0,n.rgb()),a=e>=Yn?e:e+Math.pow(Yn-e,1.414),f=t>=Yn?t:t+Math.pow(Yn-t,1.414),o=Math.pow(f,.56)-Math.pow(a,.57),u=Math.pow(f,.65)-Math.pow(a,.62),c=Math.abs(f-a)<5e-4?0:a<f?1.14*o:1.14*u;return 100*(Math.abs(c)<.1?0:c>0?c-.027:c+.027)},cubehelix:function(r,n,e,a,f){void 0===r&&(r=300),void 0===n&&(n=-1.5),void 0===e&&(e=1),void 0===a&&(a=1),void 0===f&&(f=[0,1]);var o,u=0;\"array\"===c(f)?o=f[1]-f[0]:(o=0,f=[f,f]);var i=function(c){var i=v*((r+120)/360+n*c),l=_n(f[0]+o*c,a),h=(0!==u?e[0]+c*u:e)*l*(1-l)/2,s=jn(i),d=An(i);return N(t([255*(l+h*(-.14861*s+1.78277*d)),255*(l+h*(-.29227*s-.90649*d)),255*(l+h*(1.97294*s)),1]))};return i.start=function(n){return null==n?r:(r=n,i)},i.rotations=function(r){return null==r?n:(n=r,i)},i.gamma=function(r){return null==r?a:(a=r,i)},i.hue=function(r){return null==r?e:(\"array\"===c(e=r)?0===(u=e[1]-e[0])&&(e=e[1]):u=0,i)},i.lightness=function(r){return null==r?f:(\"array\"===c(r)?(f=r,o=r[1]-r[0]):(f=[r,r],o=0),i)},i.scale=function(){return N.scale(i)},i.hue(e),i},deltaE:function(r,n,e,t,a){void 0===e&&(e=1),void 0===t&&(t=1),void 0===a&&(a=1);var f=function(r){return 360*r/(2*Un)},o=function(r){return 2*Un*r/360};r=new M(r),n=new M(n);var u=Array.from(r.lab()),c=u[0],i=u[1],l=u[2],h=Array.from(n.lab()),s=h[0],d=h[1],b=h[2],g=(c+s)/2,v=(Cn(Xn(i,2)+Xn(l,2))+Cn(Xn(d,2)+Xn(b,2)))/2,p=.5*(1-Cn(Xn(v,7)/(Xn(v,7)+Xn(25,7)))),m=i*(1+p),y=d*(1+p),w=Cn(Xn(m,2)+Xn(l,2)),k=Cn(Xn(y,2)+Xn(b,2)),N=(w+k)/2,x=f(Sn(l,m)),_=f(Sn(b,y)),A=x>=0?x:x+360,j=_>=0?_:_+360,E=Wn(A-j)>180?(A+j+360)/2:(A+j)/2,R=1-.17*In(o(E-30))+.24*In(o(2*E))+.32*In(o(3*E+6))-.2*In(o(4*E-63)),O=j-A;O=Wn(O)<=180?O:j<=A?O+360:O-360,O=2*Cn(w*k)*Kn(o(O)/2);var P=s-c,F=k-w,L=1+.015*Xn(g-50,2)/Cn(20+Xn(g-50,2)),B=1+.045*N,G=1+.015*N*R,Y=30*zn(-Xn((E-275)/25,2)),q=-(2*Cn(Xn(N,7)/(Xn(N,7)+Xn(25,7))))*Kn(2*o(Y)),C=Cn(Xn(P/(e*L),2)+Xn(F/(t*B),2)+Xn(O/(a*G),2)+q*(F/(t*B))*(O/(a*G)));return $n(0,Zn(100,C))},distance:function(r,n,e){void 0===e&&(e=\"lab\"),r=new M(r),n=new M(n);var t=r.get(e),a=n.get(e),f=0;for(var o in t){var u=(t[o]||0)-(a[o]||0);f+=u*u}return Math.sqrt(f)},input:k,interpolate:un,limits:Gn,mix:un,random:function(r){void 0===r&&(r=Rn);for(var n=\"#\",e=0;e<6;e++)n+=\"0123456789abcdef\".charAt(En(16*r()));return new M(n,\"hex\")},scale:kn,scales:Vn,valid:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];try{return new(Function.prototype.bind.apply(M,[null].concat(r))),!0}catch(r){return!1}},cmyk:_,css:xr,gl:_r,hcg:jr,hex:Lr,hsi:Cr,hsl:Xr,hsv:Wr,lab:Ir,lch:Kr,hcl:zr,num:Ur,rgb:Dr,temp:Qr,kelvin:Qr,temperature:Qr,oklab:rn,oklch:nn,getLabWhitePoint:O,setLabWhitePoint:R}),N}));\n"
  },
  {
    "path": "docs/Makefile",
    "content": "all:\n\tcat src/index.md ../CHANGELOG.md > tmp.md\n\t../node_modules/.bin/markdown -f gfm -s src/index.css tmp.md --title \"chroma.js api docs!\" > index.html\n\tbin/post-process\n\trm tmp.md\n\tcp ../dist/chroma*.cjs libs/\n\npreview:\n\t../node_modules/.bin/http-server\n\t# python -m SimpleHTTPServer\n"
  },
  {
    "path": "docs/bin/post-process",
    "content": "#!/usr/bin/env node\nimport fs from 'fs';\n\nconst index = fs.readFileSync('index.html', 'utf-8');\nconst footer = fs.readFileSync('src/footer.inc.html', 'utf-8');\n\nlet modifiedIndex = index.replace('</body>', '\\n'+footer+'\\n</body>');\nmodifiedIndex = modifiedIndex.replace('</head>', '  <link rel=\"me\" href=\"https://vis.social/@gka\">\\n</head>');\nmodifiedIndex = modifiedIndex.replace('</head>', '  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> \\n</head>');\nmodifiedIndex = modifiedIndex.replace('<body>', '<body><div class=\"wrap\"><button class=\"menu-button\" onclick=\"toggleMenu()\"><span></span><span></span><span></span></button>');\nmodifiedIndex = modifiedIndex.replace('</body>', '</div></body>');\n\nfs.writeFileSync('index.html', modifiedIndex);\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>chroma.js api docs!</title>\n  <link rel=\"stylesheet\" href=\"src/index.css\">\n  <link rel=\"me\" href=\"https://vis.social/@gka\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> \n</head>\n<body><div class=\"wrap\"><button class=\"menu-button\" onclick=\"toggleMenu()\"><span></span><span></span><span></span></button>\n<h1 id=\"chroma-js\">chroma.js</h1>\n<p><strong>chroma.js</strong> is a <a href=\"https://bundlephobia.com/result?p=chroma-js\">small-ish</a> zero-dependency JavaScript library (<a href=\"https://bundlephobia.com/result?p=chroma-js\">13.5kB</a>) for all kinds of color conversions and color scales.</p>\n<p><a href=\"https://travis-ci.com/gka/chroma.js\"><img src=\"https://api.travis-ci.com/gka/chroma.js.svg?branch=master\" alt=\"Build Status\"></a></p>\n<h2 id=\"quick-start\">Quick-start</h2>\n<p>Here are a couple of things chroma.js can do for you:</p>\n<ul>\n<li>read colors from a wide range of formats</li>\n<li>analyze and manipulate colors</li>\n<li>convert colors into wide range of formats</li>\n<li>linear and bezier interpolation in different color spaces</li>\n</ul>\n<p>Here&#39;s an example for a simple read / manipulate / output chain:</p>\n<pre><code class=\"lang-js\">chroma(&#39;pink&#39;).darken().saturate(2).hex()\n</code></pre>\n<p>Aside from that, chroma.js can also help you <strong>generate nice colors</strong> using various methods, for instance to be <a href=\"https://www.vis4.net/blog/posts/avoid-equidistant-hsv-colors/\">used</a> in color palette for maps or data visualization.</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;#fafa6e&#39;, &#39;#2A4858&#39;])\n    .mode(&#39;lch&#39;).colors(6)\n</code></pre>\n<p>chroma.js has a lot more to offer, but that&#39;s the gist of it.</p>\n<h2 id=\"installation\">Installation</h2>\n<p>For Node.js: Install the <code>chroma-js</code> npm module using your favorite package manager:</p>\n<pre><code class=\"lang-shell\">npm install chroma-js\n# pnpm add chroma-js\n# yarn add chroma-js\n</code></pre>\n<p>Then import the module into your JavaScript:</p>\n<pre><code class=\"lang-js\">import chroma from &#39;chroma-js&#39;;\n</code></pre>\n<p>If you just want to use parts of chroma.js and not bundle the entire package, you can import directly from <code>chroma-js/src/*</code> to benefit from treeshaking. For instance, the following import would only result in a <a href=\"https://bundlejs.com/?q=chroma-js%2Fsrc%2Futils%2Fdelta-e.js&amp;treeshake=%5B*+as+default%5D&amp;config=%7B%22analysis%22%3A%22treemap%22%7D\">1.24kB bundle increase</a>:</p>\n<pre><code class=\"lang-js\">import deltaE from &#39;chroma-js/src/utils/deltaE.js\n</code></pre>\n<p>And for browsers, download <a href=\"https://unpkg.com/chroma-js/dist/chroma.min.cjs\"><code>chroma.min.js</code></a> or use the <a href=\"https://unpkg.com/chroma-js/dist/chroma.min.cjs\">hosted version on unpkg.com</a>.</p>\n<p>You can also just import chroma.js as ES module, as demonstrated in this <a href=\"https://stackblitz.com/edit/stackblitz-starters-axiqsz?description=HTML/CSS/JS%20Starter&amp;file=script.js,styles.css&amp;terminalHeight=10&amp;title=Static%20Starter\">StackBlitz</a>. </p>\n<p>To use chroma.js in <a href=\"https://observablehq.com/\">Observable notebooks</a>, you can import it like this:</p>\n<pre><code class=\"lang-js\">import { chroma } from &quot;@gka/chroma-js&quot;\n</code></pre>\n<p>The <a href=\"http://gka.github.io/chroma.js/\">interactive documentation</a> continues below (and there&#39;s a <a href=\"https://github.com/gka/chroma.js/blob/master/docs/src/index.md\">static version</a>, too) for usage examples. Or use it from SASS using <a href=\"https://github.com/bugsnag/chromatic-sass\">chromatic-sass</a>!</p>\n<h2 id=\"api\">API</h2>\n<h3 id=\"chroma\">chroma</h3>\n<h4 id=\"-color-\">(<em>color</em>)</h4>\n<p>The first step is to get your color into chroma.js. That&#39;s what the generic constructor <code>chroma()</code> does. This function attempts to guess the format of the input color for you. For instance, it will recognize any named color from the W3CX11 specification:</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;)\n</code></pre>\n<p>If there&#39;s no matching named color, chroma.js checks for a <strong>hexadecimal string</strong>. It ignores case, the <code>#</code> sign is optional, and it can  recognize the shorter three letter format as well. So, any of these are valid hexadecimal representations: <code>#ff3399</code>, <code>FF3399</code>, <code>#f39</code>, etc.</p>\n<pre><code class=\"lang-js\">chroma(&#39;#ff3399&#39;);\nchroma(&#39;F39&#39;);\n</code></pre>\n<p>In addition to hex strings, <strong>hexadecimal numbers</strong> (in fact, just any number between <code>0</code> and <code>16777215</code>) will be recognized, too.</p>\n<pre><code class=\"lang-js\">chroma(0xff3399)\n</code></pre>\n<p>You also can pass RGB values individually. Each parameter must be within <code>0..255</code>. You can pass the numbers as individual arguments or as an array.</p>\n<pre><code class=\"lang-js\">chroma(0xff, 0x33, 0x99);\nchroma(255, 51, 153);\nchroma([255, 51, 153]);\n</code></pre>\n<p>You can construct colors from different color spaces by passing the name of color space as the last argument. Here we define the same color in HSL by passing the h<em>ue angle (0-360) and percentages for </em>s<em>aturation and </em>l*ightness:</p>\n<pre><code class=\"lang-js\">chroma(330, 1, 0.6, &#39;hsl&#39;)\n</code></pre>\n<p><strong>New (since 2.0):</strong> you can also construct colors by passing an plain JS object with attributes corresponding to a color space supported by chroma.js:</p>\n<pre><code class=\"lang-js\">chroma({ h:120, s:1, l:0.75});\nchroma({ l:80, c:25, h:200 });\nchroma({ c:1, m:0.5, y:0, k:0.2});\n</code></pre>\n<h3 id=\"chroma-valid\">chroma.valid</h3>\n<p>Also new: you can use <code>chroma.valid</code> to try if a color argument can be correctly parsed as color by chroma.js:</p>\n<pre><code class=\"lang-js\">chroma.valid(&#39;red&#39;);\nchroma.valid(&#39;bread&#39;);\nchroma.valid(&#39;#F0000D&#39;);\nchroma.valid(&#39;#FOOOOD&#39;);\n</code></pre>\n<h3 id=\"chroma-hsl\">chroma.hsl</h3>\n<h4 id=\"-hue-saturation-lightness-\">(hue, saturation, lightness)</h4>\n<p>Alternatively, every color space has its own constructor function under the <code>chroma</code> namespace. For a list of all supported color spaces, check the <a href=\"#supported-color-spaces-and-output-formats\">appendix</a>.</p>\n<pre><code class=\"lang-js\">chroma.hsl(330, 1, 0.6)\n</code></pre>\n<h3 id=\"chroma-hsv\">chroma.hsv</h3>\n<h4 id=\"-hue-saturation-value-\">(hue, saturation, value)</h4>\n<h3 id=\"chroma-lab\">chroma.lab</h3>\n<h4 id=\"-lightness-a-b-\">(Lightness, a, b)</h4>\n<p>CIE Lab color space. To calculate the lightness value of a color, the CIE Lab color space uses a reference white point. This reference white point defines what is considered to be &quot;white&quot; in the color space. By default chroma.js is using the D65 reference point.</p>\n<pre><code class=\"lang-js\">chroma.lab(40, -20, 50);\nchroma.lab(50, -20, 50);\nchroma.lab(80, -20, 50);\n</code></pre>\n<h3 id=\"chroma-setlabwhitepoint\">chroma.setLabWhitePoint</h3>\n<h4 id=\"-whitepoint-\">(whitePoint)</h4>\n<p>Sets the current CIE Lab white reference point. </p>\n<p>Possible values:</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>D50</code></td>\n<td>Represents the color temperature of daylight at 5000K.</td>\n</tr>\n<tr>\n<td><code>D55</code></td>\n<td>Represents mid-morning or mid-afternoon daylight at 5500K.</td>\n</tr>\n<tr>\n<td><code>D65</code></td>\n<td>Represents average daylight at 6500K.</td>\n</tr>\n<tr>\n<td><code>A</code></td>\n<td>Represents the color temperature of a typical incandescent light bulb at approximately 2856K.</td>\n</tr>\n<tr>\n<td><code>B</code></td>\n<td>Represents noon daylight with a color temperature of approximately 4874K.</td>\n</tr>\n<tr>\n<td><code>C</code></td>\n<td>Represents average or north sky daylight; it&#39;s a theoretical construct, not often used in practical applications.</td>\n</tr>\n<tr>\n<td><code>F2</code></td>\n<td>Represents cool white fluorescent light.</td>\n</tr>\n<tr>\n<td><code>F7</code></td>\n<td>This is a broad-band fluorescent light source with a color temperature of approximately 6500K.</td>\n</tr>\n<tr>\n<td><code>F11</code></td>\n<td>This is a narrow tri-band fluorescent light source with a color temperature of approximately 4000K.</td>\n</tr>\n<tr>\n<td><code>E</code></td>\n<td>Represents an equal energy white point, where all wavelengths in the visible spectrum are equally represented.</td>\n</tr>\n</tbody>\n</table>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).lab();\nchroma.setLabWhitePoint(&#39;F2&#39;);\nchroma(&#39;hotpink&#39;).lab();\n</code></pre>\n<h3 id=\"chroma-getlabwhitepoint\">chroma.getLabWhitePoint</h3>\n<p>Returns the name of the currently set CIE Lab white reference point. </p>\n<pre><code class=\"lang-js\">chroma.getLabWhitePoint();\n</code></pre>\n<h3 id=\"chroma-lch\">chroma.lch</h3>\n<h4 id=\"-lightness-chroma-hue-\">(Lightness, chroma, hue)</h4>\n<p>The range for <code>lightness</code> and <code>chroma</code> depend on the hue, but go roughly from 0..100-150. The range for <code>hue</code> is 0..360.</p>\n<pre><code class=\"lang-js\">chroma.lch(80, 40, 130);\nchroma(80, 40, 130, &#39;lch&#39;);\n</code></pre>\n<h3 id=\"chroma-hcl\">chroma.hcl</h3>\n<h4 id=\"-hue-chroma-lightness-\">(hue, chroma, lightness)</h4>\n<p>You can use <strong>hcl</strong> instead of Lch. Lightness and hue channels are switched to be more consistent with HSL.</p>\n<pre><code class=\"lang-js\">chroma.hcl(130, 40, 80);\nchroma(130, 40, 80, &#39;hcl&#39;);\n</code></pre>\n<h3 id=\"chroma-oklab\">chroma.oklab</h3>\n<h4 id=\"-lightness-a-b-\">(Lightness, a, b)</h4>\n<p><a href=\"https://bottosson.github.io/posts/oklab/\">Oklab color space</a></p>\n<pre><code class=\"lang-js\">chroma.oklab(0.4,-0.2,0.5);\nchroma.oklab(0.5,-0.2,0.5);\nchroma.oklab(0.8,-0.2,0.5);\n</code></pre>\n<h3 id=\"chroma-oklch\">chroma.oklch</h3>\n<h4 id=\"-lightness-chromacity-hue-\">(Lightness, chromacity, hue)</h4>\n<pre><code class=\"lang-js\">chroma.oklch(0.5, 0.2, 240);\nchroma(0.8, 0.12, 60, &#39;oklch&#39;);\n</code></pre>\n<h3 id=\"chroma-cmyk\">chroma.cmyk</h3>\n<h4 id=\"-cyan-magenta-yellow-black-\">(cyan, magenta, yellow, black)</h4>\n<p>Each between 0 and 1.</p>\n<pre><code class=\"lang-js\">chroma.cmyk(0.2, 0.8, 0, 0);\nchroma(0.2, 0.8, 0, 0, &#39;cmyk&#39;);\n</code></pre>\n<h3 id=\"chroma-gl\">chroma.gl</h3>\n<h4 id=\"-red-green-blue-alpha-\">(red, green, blue, [alpha])</h4>\n<p><strong>GL</strong> is a variant of RGB(A), with the only difference that the components are normalized to the range of <code>0..1</code>.</p>\n<pre><code class=\"lang-js\">chroma.gl(0.6, 0, 0.8);\nchroma.gl(0.6, 0, 0.8, 0.5);\nchroma(0.6, 0, 0.8, &#39;gl&#39;);\n</code></pre>\n<h3 id=\"chroma-temperature\">chroma.temperature</h3>\n<h4 id=\"-k-\">(K)</h4>\n<p>Returns a color from the <a href=\"http://www.zombieprototypes.com/?p=210\">color temperature</a> scale. Based on <a href=\"https://github.com/neilbartlett/color-temperature\">Neil Bartlett&#39;s implementation</a>.</p>\n<pre><code class=\"lang-js\">chroma.temperature(2000); // candle light\nchroma.temperature(3500); // sunset\nchroma.temperature(6500); // daylight\n</code></pre>\n<p>The effective temperature range goes from <code>0</code> to about <code>30000</code> Kelvin,</p>\n<pre><code class=\"lang-js\">f = function(i) {\n    return chroma.temperature(i * 30000)\n}\n</code></pre>\n<h3 id=\"chroma-mix\">chroma.mix</h3>\n<h4 id=\"-color1-color2-ratio-0-5-mode-lrgb-\">(color1, color2, ratio=0.5, mode=&#39;lrgb&#39;)</h4>\n<p>Mixes two colors. The mix <em>ratio</em> is a value between 0 and 1.</p>\n<pre><code class=\"lang-js\">chroma.mix(&#39;red&#39;, &#39;blue&#39;);\nchroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.25);\nchroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.75);\n</code></pre>\n<p>The color mixing produces different results based the color space used for interpolation.</p>\n<pre><code class=\"lang-js\">chroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.5, &#39;rgb&#39;);\nchroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.5, &#39;hsl&#39;);\nchroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.5, &#39;lab&#39;);\nchroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.5, &#39;lch&#39;);\nchroma.mix(&#39;red&#39;, &#39;blue&#39;, 0.5, &#39;lrgb&#39;);\n</code></pre>\n<h3 id=\"chroma-average\">chroma.average</h3>\n<h4 id=\"-colors-mode-lrgb-weights-\">(colors, mode=&#39;lrgb&#39;, weights=[])</h4>\n<p>Similar to <code>chroma.mix</code>, but accepts more than two colors. Simple averaging of R,G,B components and the alpha channel.</p>\n<pre><code class=\"lang-js\">colors = [&#39;#ddd&#39;, &#39;yellow&#39;, &#39;red&#39;, &#39;teal&#39;];\nchroma.average(colors); // lrgb\nchroma.average(colors, &#39;rgb&#39;);\nchroma.average(colors, &#39;lab&#39;);\nchroma.average(colors, &#39;lch&#39;);\n</code></pre>\n<p>Also works with alpha channels.</p>\n<pre><code class=\"lang-js\">chroma.average([&#39;red&#39;, &#39;rgba(0,0,0,0.5)&#39;]).css();\n</code></pre>\n<p>As of version 2.1 you can also provide an array of <code>weights</code> to<br>compute a <strong>weighted average</strong> of colors.</p>\n<pre><code class=\"lang-js\">colors = [&#39;#ddd&#39;, &#39;yellow&#39;, &#39;red&#39;, &#39;teal&#39;];\nchroma.average(colors, &#39;lch&#39;); // unweighted\nchroma.average(colors, &#39;lch&#39;, [1,1,2,1]);\nchroma.average(colors, &#39;lch&#39;, [1.5,0.5,1,2.3]);\n</code></pre>\n<h3 id=\"chroma-blend\">chroma.blend</h3>\n<h4 id=\"-color1-color2-mode-\">(color1, color2, mode)</h4>\n<p>Blends two colors using RGB channel-wise blend functions. Valid blend modes are <code>multiply</code>, <code>darken</code>, <code>lighten</code>, <code>screen</code>, <code>overlay</code>, <code>burn</code>, and <code>dodge</code>.</p>\n<pre><code class=\"lang-js\">chroma.blend(&#39;4CBBFC&#39;, &#39;EEEE22&#39;, &#39;multiply&#39;);\nchroma.blend(&#39;4CBBFC&#39;, &#39;EEEE22&#39;, &#39;darken&#39;);\nchroma.blend(&#39;4CBBFC&#39;, &#39;EEEE22&#39;, &#39;lighten&#39;);\n</code></pre>\n<h3 id=\"chroma-random\">chroma.random</h3>\n<h4 id=\"-rng-math-random-\">(rng = Math.random)</h4>\n<p>Creates a random color by generating a <a href=\"https://github.com/gka/chroma.js/blob/main/src/generator/random.js#L7-L11\">random hexadecimal string</a>. You can also pass a custom random number generator function as first argument.</p>\n<pre><code class=\"lang-js\">chroma.random();\nchroma.random();\nchroma.random();\n</code></pre>\n<h3 id=\"chroma-contrast\">chroma.contrast</h3>\n<h4 id=\"-color1-color2-\">(color1, color2)</h4>\n<p>Computes the WCAG contrast ratio between two colors. A minimum contrast of 4.5:1 <a href=\"http://www.w3.org/TR/WCAG20-TECHS/G18.html\">is recommended</a> to ensure that text is still readable against a background color.</p>\n<pre><code class=\"lang-js\">// contrast smaller than 4.5 = too low\nchroma.contrast(&#39;pink&#39;, &#39;hotpink&#39;);\n// contrast greater than 4.5 = high enough\nchroma.contrast(&#39;pink&#39;, &#39;purple&#39;);\n</code></pre>\n<h3 id=\"chroma-contrastapca\">chroma.contrastAPCA</h3>\n<h4 id=\"-text-background-\">(text, background)</h4>\n<p><strong>New (3.1):</strong> Computes the <a href=\"https://www.myndex.com/APCA/\">APCA contrast</a> ratio of a text color against its background color. The basic idea is that you check the contrast between the text and background color and then use <a href=\"https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAlookupByContrast.jpeg\">this lookup table</a> to find the minimum font size you&#39;re allowed to use (given the font weight and purpose of the text). </p>\n<pre><code class=\"lang-js\">chroma.contrastAPCA(&#39;hotpink&#39;, &#39;pink&#39;);\nchroma.contrastAPCA(&#39;purple&#39;, &#39;pink&#39;);\n</code></pre>\n<p>Read more about how to interpret and use this metric at <a href=\"https://readtech.org/ARC\">APCA Readability Criterion</a>. Please note that the APCA algorithm is still in beta and may change be subject to changes in the future.</p>\n<h3 id=\"chroma-distance\">chroma.distance</h3>\n<h4 id=\"-color1-color2-mode-lab-\">(color1, color2, mode=&#39;lab&#39;)</h4>\n<p>Computes the <a href=\"https://en.wikipedia.org/wiki/Euclidean_distance#Three_dimensions\">Euclidean distance</a> between two colors in a given color space (default is <code>Lab</code>).</p>\n<pre><code class=\"lang-js\">chroma.distance(&#39;#fff&#39;, &#39;#ff0&#39;, &#39;rgb&#39;);\nchroma.distance(&#39;#fff&#39;, &#39;#f0f&#39;, &#39;rgb&#39;);\nchroma.distance(&#39;#fff&#39;, &#39;#ff0&#39;);\nchroma.distance(&#39;#fff&#39;, &#39;#f0f&#39;);\n</code></pre>\n<h3 id=\"chroma-deltae\">chroma.deltaE</h3>\n<h4 id=\"-color1-color2-kl-1-kc-1-kh-1-\">(color1, color2, Kl=1, Kc=1, Kh=1)</h4>\n<p>Computes <a href=\"https://en.wikipedia.org/wiki/Color_difference#CIEDE2000\">color difference</a> as developed by the International Commission on Illumination (CIE) in 2000. The implementation is based on the formula from <a href=\"http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html\">Bruce Lindbloom</a>. Resulting values range from 0 (no difference) to 100 (maximum difference), and are a metric for how the human eye percieves color difference. The optional parameters Kl, Kc, and Kh may be used to adjust weightings of lightness, chroma, and hue.</p>\n<pre><code class=\"lang-js\">chroma.deltaE(&#39;#ededee&#39;, &#39;#ededee&#39;);\nchroma.deltaE(&#39;#ededee&#39;, &#39;#edeeed&#39;);\nchroma.deltaE(&#39;#ececee&#39;, &#39;#eceeec&#39;);\nchroma.deltaE(&#39;#e9e9ee&#39;, &#39;#e9eee9&#39;);\nchroma.deltaE(&#39;#e4e4ee&#39;, &#39;#e4eee4&#39;);\nchroma.deltaE(&#39;#e0e0ee&#39;, &#39;#e0eee0&#39;);\nchroma.deltaE(&#39;#000000&#39;, &#39;#ffffff&#39;);\n\n</code></pre>\n<h3 id=\"chroma-brewer\">chroma.brewer</h3>\n<p>chroma.brewer is an map of <a href=\"http://colorbrewer2.org/\">ColorBrewer palettes</a> that are included in chroma.js for convenience. chroma.scale uses the colors to construct. </p>\n<pre><code class=\"lang-js\">chroma.brewer.OrRd\n</code></pre>\n<p>Note that chroma.js only includes the 9-step versions of the palettes (11 steps for the diverging palettes). So, for instance, if you use chroma.js to construct a 5-color palette, they will be different from the &quot;official&quot; 5-color palettes in ColorBrewer (which have lower contrast).</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;RdBu&#39;).colors(5);\n// offical 5-color RdBu:\n[&#39;#ca0020&#39;, &#39;#f4a582&#39;, &#39;#f7f7f7&#39;, &#39;#92c5de&#39;, &#39;#0571b0&#39;]\n</code></pre>\n<p>One way to compensate for this would be to &quot;slice off&quot; the extreme colors:</p>\n<pre><code class=\"lang-js\">chroma\n    .scale(chroma.brewer.RdBu.slice(1,-1))\n    .colors(5);\n</code></pre>\n<p>Of course you can also just construct the scale from the official 5-step colors that you can copy and paste from <a href=\"https://colorbrewer2.org/#type=diverging&amp;scheme=RdBu&amp;n=5\">colorbrewer2.org</a>:</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;#ca0020&#39;, &#39;#f4a582&#39;, &#39;#f7f7f7&#39;, &#39;#92c5de&#39;, &#39;#0571b0&#39;])\n</code></pre>\n<p>You can access a list of all available palettes via <code>Object.keys(chroma.brewer)</code>:</p>\n<pre><code class=\"lang-js\">Object.keys(chroma.brewer)\n// [&#39;OrRd&#39;, &#39;PuBu&#39;, &#39;BuPu&#39;, &#39;Oranges&#39;, &#39;BuGn&#39;, &#39;YlOrBr&#39;, &#39;YlGn&#39;, &#39;Reds&#39;, &#39;RdPu&#39;, &#39;Greens&#39;, &#39;YlGnBu&#39;, &#39;Purples&#39;, &#39;GnBu&#39;, &#39;Greys&#39;, &#39;YlOrRd&#39;, &#39;PuRd&#39;, &#39;Blues&#39;, &#39;PuBuGn&#39;, &#39;Viridis&#39;, &#39;Spectral&#39;, &#39;RdYlGn&#39;, &#39;RdBu&#39;, &#39;PiYG&#39;, &#39;PRGn&#39;, &#39;RdYlBu&#39;, &#39;BrBG&#39;, &#39;RdGy&#39;, &#39;PuOr&#39;, &#39;Set2&#39;, &#39;Accent&#39;, &#39;Set1&#39;, &#39;Set3&#39;, &#39;Dark2&#39;, &#39;Paired&#39;, &#39;Pastel2&#39;, &#39;Pastel1&#39;]\n</code></pre>\n<h3 id=\"chroma-limits\">chroma.limits</h3>\n<h4 id=\"-data-mode-n-\">(data, mode, n)</h4>\n<p>A helper function that computes class breaks for you, based on data. It supports the modes <em>equidistant</em> (e), <em>quantile</em> (q), <em>logarithmic</em> (l), and <em>k-means</em> (k). Let&#39;s take a few numbers as sample data.</p>\n<pre><code class=\"lang-js\">var data = [2.0,3.5,3.6,3.8,3.8,4.1,4.3,4.4,\n            4.6,4.9,5.2,5.3,5.4,5.7,5.8,5.9,\n            6.2,6.5,6.8,7.2,8];\n</code></pre>\n<p><strong>equidistant</strong> breaks are computed by dividing the total range of the data into _n_ groups of equal size.</p>\n<pre><code class=\"lang-js\">chroma.limits(data, &#39;e&#39;, 4);\n</code></pre>\n<p>In the <strong>quantile</strong> mode, the input domain is divided by quantile ranges.</p>\n<pre><code class=\"lang-js\">chroma.limits(data, &#39;q&#39;, 4);\n</code></pre>\n<p><strong>logarithmic</strong> breaks are equidistant breaks but on a logarithmic scale.</p>\n<pre><code class=\"lang-js\">chroma.limits(data, &#39;l&#39;, 4);\n</code></pre>\n<p><strong>k-means</strong> break is using the 1-dimensional <a href=\"https://en.wikipedia.org/wiki/K-means_clustering\">k-means clustering</a> algorithm to find (roughly) _n_ groups of &quot;similar&quot; values. Note that this k-means implementation does not guarantee to find exactly _n_ groups.</p>\n<pre><code class=\"lang-js\">chroma.limits(data, &#39;k&#39;, 4);\n</code></pre>\n<h2 id=\"color\">color</h2>\n<h3 id=\"color-alpha\">color.alpha</h3>\n<h4 id=\"-a-\">(a)</h4>\n<p>Get and set the color opacity using <code>color.alpha</code>.</p>\n<pre><code class=\"lang-js\">chroma(&#39;red&#39;).alpha(0.5);\nchroma(&#39;rgba(255,0,0,0.35)&#39;).alpha();\n</code></pre>\n<h3 id=\"color-darken\">color.darken</h3>\n<h4 id=\"-value-1-\">(value=1)</h4>\n<p>Once loaded, chroma.js can change colors. One way we already saw above, you can change the lightness.</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).darken();\nchroma(&#39;hotpink&#39;).darken(2);\nchroma(&#39;hotpink&#39;).darken(2.6);\n</code></pre>\n<h3 id=\"color-brighten\">color.brighten</h3>\n<h4 id=\"-value-1-\">(value=1)</h4>\n<p>Similar to <code>darken</code>, but the opposite direction</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).brighten();\nchroma(&#39;hotpink&#39;).brighten(2);\nchroma(&#39;hotpink&#39;).brighten(3);\n</code></pre>\n<h3 id=\"color-saturate\">color.saturate</h3>\n<h4 id=\"-value-1-\">(value=1)</h4>\n<p>Changes the saturation of a color by manipulating the Lch chromaticity.</p>\n<pre><code class=\"lang-js\">chroma(&#39;slategray&#39;).saturate();\nchroma(&#39;slategray&#39;).saturate(2);\nchroma(&#39;slategray&#39;).saturate(3);\n</code></pre>\n<h3 id=\"color-desaturate\">color.desaturate</h3>\n<h4 id=\"-value-1-\">(value=1)</h4>\n<p>Similar to <code>saturate</code>, but the opposite direction.</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).desaturate();\nchroma(&#39;hotpink&#39;).desaturate(2);\nchroma(&#39;hotpink&#39;).desaturate(3);\n</code></pre>\n<h3 id=\"color-mix\">color.mix</h3>\n<h4 id=\"-targetcolor-ratio-0-5-mode-lrgb-\">(targetcolor, ratio=0.5, mode=&#39;lrgb&#39;)</h4>\n<p>Mix this color with a target color. The mix <em>ratio</em> is a value between 0 and 1. This is the same as <code>chroma.mix</code> but with the first parameter already set. As such, the color space used can be adjusted.</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).mix(&#39;blue&#39;);\nchroma(&#39;hotpink&#39;).mix(&#39;blue&#39;, 0.25);\nchroma(&#39;hotpink&#39;).mix(&#39;blue&#39;, 0.75, &#39;lab&#39;);\n</code></pre>\n<h3 id=\"color-shade\">color.shade</h3>\n<h4 id=\"-ratio-0-5-mode-lrgb-\">(ratio=0.5, mode=&#39;lrgb&#39;)</h4>\n<p>Produce a shade of the color. This is syntactic sugar for <code>color.mix</code> with a target color of black.</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).shade(0.25);\nchroma(&#39;hotpink&#39;).shade(0.5);\nchroma(&#39;hotpink&#39;).shade(0.75);\n</code></pre>\n<h3 id=\"color-tint\">color.tint</h3>\n<h4 id=\"-ratio-0-5-mode-lrgb-\">(ratio=0.5, mode=&#39;lrgb&#39;)</h4>\n<p>Produce a tint of the color. This is syntactic sugar for <code>color.mix</code> with a target color of white.</p>\n<pre><code class=\"lang-js\">chroma(&#39;hotpink&#39;).tint(0.25);\nchroma(&#39;hotpink&#39;).tint(0.5);\nchroma(&#39;hotpink&#39;).tint(0.75);\n</code></pre>\n<h3 id=\"color-set\">color.set</h3>\n<h4 id=\"-channel-value-\">(channel, value)</h4>\n<p>Changes a single channel and returns the result a new <code>chroma</code> object.</p>\n<pre><code class=\"lang-js\">// change hue to 0 deg (=red)\nchroma(&#39;skyblue&#39;).set(&#39;hsl.h&#39;, 0);\n// set chromaticity to 30\nchroma(&#39;hotpink&#39;).set(&#39;lch.c&#39;, 30);\n</code></pre>\n<p>Relative changes work, too:</p>\n<pre><code class=\"lang-js\">// half Lab lightness\nchroma(&#39;orangered&#39;).set(&#39;lab.l&#39;, &#39;*0.5&#39;);\n// double Lch saturation\nchroma(&#39;darkseagreen&#39;).set(&#39;lch.c&#39;, &#39;*2&#39;);\n</code></pre>\n<h3 id=\"color-get\">color.get</h3>\n<h4 id=\"-channel-\">(channel)</h4>\n<p>Returns a single channel value.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orangered&#39;).get(&#39;lab.l&#39;);\nchroma(&#39;orangered&#39;).get(&#39;hsl.l&#39;);\nchroma(&#39;orangered&#39;).get(&#39;rgb.g&#39;);\n</code></pre>\n<h3 id=\"color-luminance\">color.luminance</h3>\n<h4 id=\"-lum-mode-rgb-\">([lum, mode=&#39;rgb&#39;])</h4>\n<p>If called without arguments color.luminance returns the relative brightness, according to the <a href=\"http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\">WCAG definition</a>. Normalized to <code>0</code> for darkest black and <code>1</code> for lightest white.</p>\n<pre><code class=\"lang-js\">chroma(&#39;white&#39;).luminance();\nchroma(&#39;aquamarine&#39;).luminance();\nchroma(&#39;hotpink&#39;).luminance();\nchroma(&#39;darkslateblue&#39;).luminance();\nchroma(&#39;black&#39;).luminance();\n</code></pre>\n<p>chroma.js also allows you to <strong>adjust the luminance</strong> of a color. The source color will be interpolated with black or white until the correct luminance is found.</p>\n<pre><code class=\"lang-js\">// set lumincance to 50% for all colors\nchroma(&#39;white&#39;).luminance(0.5);\nchroma(&#39;aquamarine&#39;).luminance(0.5);\nchroma(&#39;hotpink&#39;).luminance(0.5);\nchroma(&#39;darkslateblue&#39;).luminance(0.5);\n</code></pre>\n<p>By default, this interpolation is done in RGB, but you can interpolate in different color spaces by passing them as second argument:</p>\n<pre><code class=\"lang-js\">chroma(&#39;aquamarine&#39;).luminance(0.5); // rgb\nchroma(&#39;aquamarine&#39;).luminance(0.5, &#39;lab&#39;);\nchroma(&#39;aquamarine&#39;).luminance(0.5, &#39;hsl&#39;);\n</code></pre>\n<h3 id=\"color-hex\">color.hex</h3>\n<h4 id=\"-mode-auto-rgb-rgba-argb-\">(mode=&#39;auto|rgb|rgba|argb&#39;)</h4>\n<p>Finally, chroma.js allows you to output colors in various color spaces and formats. Most often you will want to output the color as hexadecimal string.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).hex()\n</code></pre>\n<p><strong>Note</strong> that as of version 1.4.0 the default mode is &quot;auto&quot; which means that the hex string will include the alpha channel if it&#39;s less than 1. If you don&#39;t want the alpha channel to be included you must explicitly set the mode to &quot;rgb&quot; now:</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).hex();\nchroma(&#39;orange&#39;).alpha(0.5).hex();\nchroma(&#39;orange&#39;).alpha(0.5).hex(&#39;rgb&#39;);\n</code></pre>\n<p>You can use <code>.hex(&#39;argb&#39;)</code> in <a href=\"https://developer.android.com/reference/android/graphics/Color\">case</a> you need to encode the color with the alpha channel as first byte rather than the last:</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).hex(&#39;argb&#39;);; // &#39;#ffffa500&#39;\n</code></pre>\n<h3 id=\"color-name\">color.name</h3>\n<p>Returns the named color. Falls back to hexadecimal RGB string, if the color isn&#39;t present.</p>\n<pre><code class=\"lang-js\">chroma(&#39;#ffa500&#39;).name();\nchroma(&#39;#ffa505&#39;).name();\n</code></pre>\n<h3 id=\"color-css\">color.css</h3>\n<p>Returns a CSS string representation that can be used as CSS-color definition.</p>\n<pre><code class=\"lang-js\">chroma(&#39;teal&#39;).css();\nchroma(&#39;teal&#39;).alpha(0.5).css();\n</code></pre>\n<p>By default chroma is using the rgb() color space, but you can pass a color space name as first argument. Accepted color spaces are <code>rgb</code>, <code>hsl</code>, <code>lab</code>, <code>lch</code>, <code>oklab</code>, and <code>oklch</code>.</p>\n<pre><code class=\"lang-js\">chroma(&#39;teal&#39;).css(&#39;hsl&#39;);\nchroma(&#39;teal&#39;).css(&#39;lab&#39;);\nchroma(&#39;teal&#39;).css(&#39;oklch&#39;);\n</code></pre>\n<h3 id=\"color-rgb\">color.rgb</h3>\n<h4 id=\"-round-true-\">(round=true)</h4>\n<p>Returns an array with the <code>red</code>, <code>green</code>, and <code>blue</code> component, each as number within the range <code>0..255</code>. Chroma internally stores RGB channels as floats but rounds the numbers before returning them. You can pass <code>false</code> to prevent the rounding.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).rgb();\nchroma(&#39;orange&#39;).darken().rgb();\nchroma(&#39;orange&#39;).darken().rgb(false);\n</code></pre>\n<h3 id=\"color-rgba\">color.rgba</h3>\n<h4 id=\"-round-true-\">(round=true)</h4>\n<p>Just like <code>color.rgb</code> but adds the alpha channel to the returned array.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).rgba();\nchroma(&#39;hsla(20, 100%, 40%, 0.5)&#39;).rgba();\n</code></pre>\n<h3 id=\"color-hsl\">color.hsl</h3>\n<p>Returns an array with the <code>hue</code>, <code>saturation</code>, and <code>lightness</code> component. Hue is the color angle in degree (<code>0..360</code>), saturation and lightness are within <code>0..1</code>. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).hsl();\nchroma(&#39;white&#39;).hsl();\n</code></pre>\n<h3 id=\"color-hsv\">color.hsv</h3>\n<p>Returns an array with the <code>hue</code>, <code>saturation</code>, and <code>value</code> components. Hue is the color angle in degree (<code>0..360</code>), saturation and value are within <code>0..1</code>. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).hsv();\nchroma(&#39;white&#39;).hsv();\n</code></pre>\n<h3 id=\"color-hsi\">color.hsi</h3>\n<p>Returns an array with the <code>hue</code>, <code>saturation</code>, and <code>intensity</code> components, each as number between 0 and 255. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).hsi();\nchroma(&#39;white&#39;).hsi();\n</code></pre>\n<h3 id=\"color-lab\">color.lab</h3>\n<p>Returns an array with the <strong>L</strong>, <strong>a</strong>, and <strong>b</strong> components.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).lab()\n</code></pre>\n<h3 id=\"color-lch\">color.lch</h3>\n<p>Returns an array with the <strong>Lightness</strong>, <strong>chroma</strong>, and <strong>hue</strong> components.</p>\n<pre><code class=\"lang-js\">chroma(&#39;skyblue&#39;).lch()\n</code></pre>\n<h3 id=\"color-hcl\">color.hcl</h3>\n<p>Alias of <a href=\"#color-lch\">lch</a>, but with the components in reverse order.</p>\n<pre><code class=\"lang-js\">chroma(&#39;skyblue&#39;).hcl()\n</code></pre>\n<h3 id=\"color-oklab\">color.oklab</h3>\n<p>Returns an array with the <strong>L</strong>, <strong>a</strong>, and <strong>b</strong> components in the <a href=\"https://bottosson.github.io/posts/oklab/\">OKLab</a> color space.</p>\n<pre><code class=\"lang-js\">chroma(&#39;orange&#39;).oklab()\n</code></pre>\n<h3 id=\"color-oklch\">color.oklch</h3>\n<p>Returns an array with the <strong>Lightness</strong>, <strong>chroma</strong>, and <strong>hue</strong> components in the <a href=\"https://bottosson.github.io/posts/oklab/\">OKLch</a> color space.</p>\n<pre><code class=\"lang-js\">chroma(&#39;skyblue&#39;).oklch()\n</code></pre>\n<h3 id=\"color-num\">color.num</h3>\n<p>Returns the numeric representation of the hexadecimal RGB color.</p>\n<pre><code class=\"lang-js\">chroma(&#39;#000000&#39;).num();\nchroma(&#39;#0000ff&#39;).num();\nchroma(&#39;#00ff00&#39;).num();\nchroma(&#39;#ff0000&#39;).num();\n</code></pre>\n<h3 id=\"color-temperature\">color.temperature</h3>\n<p>Estimate the temperature in Kelvin of any given color, though this makes the only sense for colors from the <a href=\"#chroma-temperature\">temperature gradient</a> above.</p>\n<pre><code class=\"lang-js\">chroma(&#39;#ff3300&#39;).temperature();\nchroma(&#39;#ff8a13&#39;).temperature();\nchroma(&#39;#ffe3cd&#39;).temperature();\nchroma(&#39;#cbdbff&#39;).temperature();\nchroma(&#39;#b3ccff&#39;).temperature();\n</code></pre>\n<h3 id=\"color-gl\">color.gl</h3>\n<p>Like RGB, but in the channel range of <code>[0..1]</code> instead of <code>[0..255]</code></p>\n<pre><code class=\"lang-js\">chroma(&#39;33cc00&#39;).gl();\n</code></pre>\n<h3 id=\"color-clipped\">color.clipped</h3>\n<p>When converting colors from CIELab color spaces to RGB the color channels get clipped to the range of <code>[0..255]</code>. Colors outside that range may exist in nature but are not displayable on RGB monitors (such as ultraviolet). you can use color.clipped to test if a color has been clipped or not.</p>\n<pre><code class=\"lang-js\">[c = chroma.hcl(50, 40, 20), c.clipped()];\n[c = chroma.hcl(50, 40, 40), c.clipped()];\n[c = chroma.hcl(50, 40, 60), c.clipped()];\n[c = chroma.hcl(50, 40, 80), c.clipped()];\n[c = chroma.hcl(50, 40, 100), c.clipped()];\n</code></pre>\n<p>As a bonus feature you can access the unclipped RGB components using <code>color._rgb._unclipped</code>.</p>\n<pre><code class=\"lang-js\">chroma.hcl(50, 40, 100).rgb();\nchroma.hcl(50, 40, 100)._rgb._unclipped;\n</code></pre>\n<h2 id=\"color-scales\">color scales</h2>\n<h3 id=\"chroma-scale\">chroma.scale</h3>\n<h4 id=\"-colors-white-black-\">(colors=[&#39;white&#39;, &#39;black&#39;])</h4>\n<p>A color scale, created with <code>chroma.scale</code>, is a function that maps numeric values to a color palette. The default scale has the domain <code>0..1</code> and goes from white to black.</p>\n<pre><code class=\"lang-js\">f = chroma.scale();\nf(0.25);\nf(0.5);\nf(0.75);\n</code></pre>\n<p>You can pass an array of colors to <code>chroma.scale</code>. Any color that can be read by <code>chroma()</code> will work here, too. If you pass more than two colors, they will be evenly distributed along the gradient.</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;yellow&#39;, &#39;008ae5&#39;]);\nchroma.scale([&#39;yellow&#39;, &#39;red&#39;, &#39;black&#39;]);\n</code></pre>\n<h3 id=\"scale-domain\">scale.domain</h3>\n<h4 id=\"-domain-\">(domain)</h4>\n<p>You can change the input domain to match your specific use case. If called with no arguments, <code>scale.domain</code> returns the original array of positions along the scale where the color ramp was sampled.</p>\n<pre><code class=\"lang-js\">// default domain is [0,1]\nchroma.scale([&#39;yellow&#39;, &#39;008ae5&#39;]);\n// set domain to [0,100]\nchroma.scale([&#39;yellow&#39;, &#39;008ae5&#39;]).domain([0,100]);\n</code></pre>\n<p>You can use the domain to set the exact positions of each color.</p>\n<pre><code class=\"lang-js\">// default domain is [0,1]\nchroma.scale([&#39;yellow&#39;, &#39;lightgreen&#39;, &#39;008ae5&#39;])\n    .domain([0,0.25,1]);\n</code></pre>\n<h3 id=\"scale-mode\">scale.mode</h3>\n<h4 id=\"-mode-\">(mode)</h4>\n<p>As with <code>chroma.mix</code>, the result of the color interpolation will depend on the color mode in which the channels are interpolated. The default mode is <code>RGB</code>:</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;yellow&#39;, &#39;008ae5&#39;]);\n</code></pre>\n<p>This is often fine, but sometimes, two-color <code>RGB</code> gradients goes through kind of grayish colors, and <code>Lab</code> interpolation produces better results:</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;yellow&#39;, &#39;navy&#39;]);\nchroma.scale([&#39;yellow&#39;, &#39;navy&#39;]).mode(&#39;lab&#39;);\n</code></pre>\n<p>Also note how the RGB interpolation can get very dark around the center. You can achieve better results using <a href=\"https://www.youtube.com/watch?v=LKnqECcg6Gw\">linear RGB interpolation</a>:</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;#f00&#39;, &#39;#0f0&#39;]);\nchroma.scale([&#39;#f00&#39;, &#39;#0f0&#39;]).mode(&#39;lrgb&#39;);\n</code></pre>\n<p>Other useful interpolation modes could be <code>HSL</code> or <code>Lch</code>, though both tend to produce too saturated / glowing gradients.</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;yellow&#39;, &#39;navy&#39;]).mode(&#39;lab&#39;);\nchroma.scale([&#39;yellow&#39;, &#39;navy&#39;]).mode(&#39;hsl&#39;);\nchroma.scale([&#39;yellow&#39;, &#39;navy&#39;]).mode(&#39;lch&#39;);\n</code></pre>\n<h3 id=\"scale-gamma\">scale.gamma</h3>\n<p>Gamma-correction can be used to &quot;shift&quot; a scale&#39;s center more the the beginning (gamma &lt; 1) or end (gamma &gt; 1), typically used to &quot;even&quot; the lightness gradient. Default is 1.</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;YlGn&#39;).gamma(0.5);\nchroma.scale(&#39;YlGn&#39;).gamma(1);\nchroma.scale(&#39;YlGn&#39;).gamma(2);\n</code></pre>\n<h3 id=\"scale-correctlightness\">scale.correctLightness</h3>\n<p>This makes sure the lightness range is spread evenly across a color scale. Especially useful when working with <a href=\"https://www.vis4.net/blog/2013/09/mastering-multi-hued-color-scales/\">multi-hue color scales</a>, where simple gamma correction can&#39;t help you very much.</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;black&#39;, &#39;red&#39;, &#39;yellow&#39;, &#39;white&#39;]);\n\nchroma.scale([&#39;black&#39;, &#39;red&#39;, &#39;yellow&#39;, &#39;white&#39;])\n    .correctLightness();\n</code></pre>\n<h3 id=\"scale-cache\">scale.cache</h3>\n<h4 id=\"-true-false-\">(true|false)</h4>\n<p>By default <code>chroma.scale</code> instances will cache each computed value =&gt; color pair. You can turn off the cache by setting</p>\n<pre><code class=\"lang-js\">chroma.scale([&#39;yellow&#39;, &#39;008ae5&#39;]).cache(false);\n</code></pre>\n<h3 id=\"scale-padding\">scale.padding</h3>\n<h4 id=\"-pad-\">(pad)</h4>\n<p>Reduces the color range by cutting of a fraction of the gradient on both sides. If you pass a single number, the same padding will be applied to both ends.</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;RdYlBu&#39;);\nchroma.scale(&#39;RdYlBu&#39;).padding(0.15);\nchroma.scale(&#39;RdYlBu&#39;).padding(0.3);\nchroma.scale(&#39;RdYlBu&#39;).padding(-0.15);\n</code></pre>\n<p>Alternatively you can specify the padding for each sides individually by passing an array of two numbers.</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;OrRd&#39;);\nchroma.scale(&#39;OrRd&#39;).padding([0.2, 0]);\n</code></pre>\n<h3 id=\"scale-colors\">scale.colors</h3>\n<h4 id=\"-num-format-hex-\">(num, format=&#39;hex&#39;)</h4>\n<p>You can call <code>scale.colors(n)</code> to quickly grab <code>n</code> equi-distant colors from a color scale. If called with no arguments, <code>scale.colors</code> returns the original array of colors used to create the scale.</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;OrRd&#39;).colors(5);\nchroma.scale([&#39;white&#39;, &#39;black&#39;]).colors(12);\n</code></pre>\n<p>If you want to return <code>chroma</code> instances just pass <em>null</em> as <code>format</code>.</p>\n<h3 id=\"scale-classes\">scale.classes</h3>\n<h4 id=\"-numorarray-\">(numOrArray)</h4>\n<p>If you want the scale function to return a distinct set of colors instead of a continuous gradient, you can use <code>scale.classes</code>. If you pass a number the scale will broken into equi-distant classes:</p>\n<pre><code class=\"lang-js\">// continuous\nchroma.scale(&#39;OrRd&#39;);\n// class breaks\nchroma.scale(&#39;OrRd&#39;).classes(5);\nchroma.scale(&#39;OrRd&#39;).classes(8);\n</code></pre>\n<p>You can also define custom class breaks by passing them as array:</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;OrRd&#39;).classes([0,0.3,0.55,0.85,1]);\n</code></pre>\n<h3 id=\"scale-nodata\">scale.nodata</h3>\n<h4 id=\"-color-\">(color)</h4>\n<p>When you pass a non-numeric value like <code>null</code> or <code>undefined</code> to a chroma.scale, &quot;#cccccc&quot; is returned as fallback or &quot;no data&quot; color. You can change the no-data color:</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;OrRd&#39;)(null);\nchroma.scale(&#39;OrRd&#39;)(undefined);\nchroma.scale(&#39;OrRd&#39;).nodata(&#39;#eee&#39;)(null);\n</code></pre>\n<h3 id=\"chroma-brewer\">chroma.brewer</h3>\n<p>chroma.js includes the definitions from <a href=\"http://colorbrewer2.org/\">ColorBrewer2.org</a>. Read more about these colors <a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.361.6082&amp;rep=rep1&amp;type=pdf\">in the corresponding paper</a> by Mark Harrower and Cynthia A. Brewer.</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;YlGnBu&#39;);\nchroma.scale(&#39;Spectral&#39;);\n</code></pre>\n<p>To reverse the colors you could simply reverse the domain:</p>\n<pre><code class=\"lang-js\">chroma.scale(&#39;Spectral&#39;).domain([1,0]);\n</code></pre>\n<p>You can access the colors directly using <code>chroma.brewer</code>.</p>\n<pre><code class=\"lang-js\">chroma.brewer.OrRd\n</code></pre>\n<h3 id=\"chroma-bezier\">chroma.bezier</h3>\n<h4 id=\"-colors-\">(colors)</h4>\n<p><code>chroma.bezier</code> returns a function that <a href=\"https://www.vis4.net/blog/mastering-multi-hued-color-scales/\">bezier-interpolates between colors</a> in <code>Lab</code> space. The input range of the function is <code>[0..1]</code>.</p>\n<pre><code class=\"lang-js\">// linear interpolation\nchroma.scale([&#39;yellow&#39;, &#39;red&#39;, &#39;black&#39;]);\n// bezier interpolation\nchroma.bezier([&#39;yellow&#39;, &#39;red&#39;, &#39;black&#39;]);\n</code></pre>\n<p>You can convert an bezier interpolator into a chroma.scale instance</p>\n<pre><code class=\"lang-js\">chroma.bezier([&#39;yellow&#39;, &#39;red&#39;, &#39;black&#39;])\n    .scale()\n    .colors(5);\n</code></pre>\n<h2 id=\"cubehelix\">cubehelix</h2>\n<h3 id=\"chroma-cubehelix\">chroma.cubehelix</h3>\n<h4 id=\"-start-300-rotations-1-5-hue-1-gamma-1-lightness-0-1-\">(start=300, rotations=-1.5, hue=1, gamma=1, lightness=[0,1])</h4>\n<p>Dave Green&#39;s <a href=\"http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/\">cubehelix color scheme</a>!!</p>\n<pre><code class=\"lang-js\">// use the default helix...\nchroma.cubehelix();\n// or customize it\nchroma.cubehelix()\n    .start(200)\n    .rotations(-0.5)\n    .gamma(0.8)\n    .lightness([0.3, 0.8]);\n</code></pre>\n<h3 id=\"cubehelix-start\">cubehelix.start</h3>\n<h4 id=\"-hue-\">(hue)</h4>\n<p><strong>start</strong> color for <a href=\"http://en.wikipedia.org/wiki/Hue#/media/File:HueScale.svg\">hue rotation</a>, default=<code>300</code></p>\n<pre><code class=\"lang-js\">chroma.cubehelix().start(300);\nchroma.cubehelix().start(200);\n</code></pre>\n<h3 id=\"cubehelix-rotations\">cubehelix.rotations</h3>\n<h4 id=\"-num-\">(num)</h4>\n<p>number (and direction) of hue rotations (e.g. 1=<code>360°</code>, 1.5=<code>540°`</code>), default=-1.5</p>\n<pre><code class=\"lang-js\">chroma.cubehelix().rotations(-1.5);\nchroma.cubehelix().rotations(0.5);\nchroma.cubehelix().rotations(3);\n</code></pre>\n<h3 id=\"cubehelix-hue\">cubehelix.hue</h3>\n<h4 id=\"-numorrange-\">(numOrRange)</h4>\n<p>hue controls how saturated the colour of all hues are. either single value or range, default=1</p>\n<pre><code class=\"lang-js\">chroma.cubehelix();\nchroma.cubehelix().hue(0.5);\nchroma.cubehelix().hue([1,0]);\n</code></pre>\n<h3 id=\"cubehelix-gamma\">cubehelix.gamma</h3>\n<h4 id=\"-factor-\">(factor)</h4>\n<p>gamma factor can be used to emphasise low or high intensity values, default=1</p>\n<pre><code class=\"lang-js\">chroma.cubehelix().gamma(1);\nchroma.cubehelix().gamma(0.5);\n</code></pre>\n<h3 id=\"cubehelix-lightness\">cubehelix.lightness</h3>\n<h4 id=\"-range-\">(range)</h4>\n<p>lightness range: default: [0,1]  (black -&gt; white)</p>\n<pre><code class=\"lang-js\">chroma.cubehelix().lightness([0,1]);\nchroma.cubehelix().lightness([1,0]);\nchroma.cubehelix().lightness([0.3,0.7]);\n</code></pre>\n<h3 id=\"cubehelix-scale\">cubehelix.scale</h3>\n<p>You can call <code>cubehelix.scale()</code> to use the cube-helix through the <code>chroma.scale</code> interface.</p>\n<pre><code class=\"lang-js\">chroma.cubehelix()\n    .start(200)\n    .rotations(-0.35)\n    .gamma(0.7)\n    .lightness([0.3, 0.8])\n  .scale() // convert to chroma.scale\n    .correctLightness()\n    .colors(5);\n</code></pre>\n<h2 id=\"changelog\">Changelog</h2>\n<h3 id=\"3-2-0\">3.2.0</h3>\n<ul>\n<li>scale.domain now returns the original domain array when called with no arguments</li>\n</ul>\n<h3 id=\"3-1-3\">3.1.3</h3>\n<ul>\n<li>updated dependencies</li>\n</ul>\n<h3 id=\"3-1-2\">3.1.2</h3>\n<ul>\n<li>fixed a bug in Lch interpolation of hue-less colors</li>\n</ul>\n<h3 id=\"3-1-1\">3.1.1</h3>\n<ul>\n<li>fix: allow deep-imports in vite projects</li>\n</ul>\n<h3 id=\"3-1-0\">3.1.0</h3>\n<ul>\n<li>feat: parse <code>&#39;transparent&#39;</code> as black with 0% opacity - resolves <a href=\"https://github.com/gka/chroma.js/issues/280\">#280</a></li>\n<li>make it easier to access colorbrewer palette names - resolves <a href=\"https://github.com/gka/chroma.js/issues/314\">#314</a></li>\n<li>docs: explain differences to official colorbrewer scales - resolves <a href=\"https://github.com/gka/chroma.js/issues/316\">#316</a></li>\n<li>fix: correct parsing of modern css colors with percentage alpha - resolves <a href=\"https://github.com/gka/chroma.js/issues/297\">#297</a></li>\n<li>fix: css output for hue-less colors in lch() and oklch() - resolves <a href=\"https://github.com/gka/chroma.js/issues/357\">#357</a></li>\n</ul>\n<h3 id=\"3-0-0\">3.0.0</h3>\n<ul>\n<li>🎉 NEW: Add support for modern CSS color spaces. This means you can now export and parse CSS colors in <code>lab()</code>, <code>lch()</code>, <code>oklab()</code>, <code>oklch()</code> space.</li>\n<li>🎉 NEW: you can now control the standard white reference point for the CIE Lab and CIE Lch color spaces via <code>setLabWhitePoint</code>.</li>\n<li>Breaking: <code>color.css()</code> will no longer return <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb#legacy_syntax_comma-separated_values\">legacy CSS colors</a> like <code>rgb(255, 255, 0)</code> but use modern CSS colors like <code>rgb(255 255 0)</code> instead.</li>\n<li>fix: you can now use chroma.js both via the default export as well as named exports in ES6.</li>\n<li>fix: switch to W3C implementation of OKLab color space</li>\n</ul>\n<h3 id=\"2-6-0\">2.6.0</h3>\n<ul>\n<li>🎉 NEW: add <a href=\"#color-shade\"><code>color.shade()</code></a>, <a href=\"#color-shade\"><code>color.tint()</code></a>.</li>\n<li>fix: remove false w3c color cornflower</li>\n</ul>\n<h3 id=\"2-5-0\">2.5.0</h3>\n<ul>\n<li>refactored code base to ES6 modules</li>\n</ul>\n<h3 id=\"2-4-0\">2.4.0</h3>\n<ul>\n<li>add support for Oklab and Oklch color spaces</li>\n</ul>\n<h3 id=\"2-3-0\">2.3.0</h3>\n<ul>\n<li>use binom of degree n in chroma.bezier</li>\n</ul>\n<h3 id=\"2-2-0\">2.2.0</h3>\n<ul>\n<li>use Delta e2000 for chroma.deltaE #269</li>\n</ul>\n<h3 id=\"2-0-3\">2.0.3</h3>\n<ul>\n<li>hsl2rgb will, like other x2rgb conversions now set the default alpha to 1</li>\n</ul>\n<h3 id=\"2-0-2\">2.0.2</h3>\n<ul>\n<li>use a more mangle-safe check for Color class constructor to fix issues with uglifyjs and terser</li>\n</ul>\n<h3 id=\"2-0-1\">2.0.1</h3>\n<ul>\n<li>added <code>chroma.valid()</code> for checking if a color can be parsed by chroma.js</li>\n</ul>\n<h3 id=\"2-0-0\">2.0.0</h3>\n<ul>\n<li>chroma.js has been ported from CoffeeScript to ES6! This means you can now import parts of chroma in your projects!</li>\n<li>changed HCG input space from [0..360,0..100,0..100] to [0..360,0..1,0..1] (to be in line with HSL)</li>\n<li>added new object unpacking (e.g. <code>hsl2rgb({h,s,l})</code>)</li>\n<li>changed default interpolation to <code>lrgb</code> in mix/interpolate and average.</li>\n<li>if colors can&#39;t be parsed correctly, chroma will now throw Errors instead of silently failing with console.errors</li>\n</ul>\n<h3 id=\"1-4-1\">1.4.1</h3>\n<ul>\n<li>chroma.scale() now interprets <code>null</code> as NaN and returns the fallback color. Before it had interpreted <code>null</code> as <code>0</code></li>\n<li>added <code>scale.nodata()</code> to allow customizing the previously hard-coded fallback (aka &quot;no data&quot;) color #cccccc</li>\n</ul>\n<h3 id=\"1-4-0\">1.4.0</h3>\n<ul>\n<li>color.hex() now automatically sets the mode to &#39;rgba&#39; if the colors alpha channel is &lt; 1. so <code>chroma(&#39;rgba(255,0,0,.5)&#39;).hex()</code> will now return <code>&quot;#ff000080&quot;</code> instead of <code>&quot;#ff0000&quot;</code>. if this is not what you want, you must explicitly set the mode to <code>rgb</code> using <code>.hex(&quot;rgb&quot;)</code>.</li>\n<li>bugfix in chroma.average in LRGB mode (<a href=\"https://github.com/gka/chroma.js/issues/187\">#187</a>)</li>\n<li>chroma.scale now also works with just one color (<a href=\"https://github.com/gka/chroma.js/issues/180\">#180</a>)</li>\n</ul>\n<h3 id=\"1-3-5\">1.3.5</h3>\n<ul>\n<li>added LRGB interpolation</li>\n</ul>\n<h3 id=\"1-3-4\">1.3.4</h3>\n<ul>\n<li>passing <em>null</em> as mode in scale.colors will return chroma objects</li>\n</ul>\n<h3 id=\"1-3-3\">1.3.3</h3>\n<ul>\n<li>added <a href=\"https://gka.github.io/chroma.js/#color-clipped\">color.clipped</a></li>\n<li>added <a href=\"https://gka.github.io/chroma.js/#chroma-distance\">chroma.distance</a></li>\n<li>added <a href=\"https://gka.github.io/chroma.js/#chroma-deltae\">chroma.deltaE</a></li>\n<li><a href=\"https://gka.github.io/chroma.js/#color-set\">color.set</a> now returns a new chroma instance</li>\n<li>chroma.scale now allows <a href=\"https://gka.github.io/chroma.js/#scale-cache\">disabling of internal cache</a></li>\n<li><a href=\"https://gka.github.io/chroma.js/#chroma-average\">chroma.average</a> now works with any color mode</li>\n<li>added unit tests for color conversions</li>\n<li>use hex colors as default string representation</li>\n<li>RGB channels are now stored as floats internally for higher precision</li>\n<li>bugfix with cubehelix and constant lightness</li>\n<li>bugfix in chroma.limits quantiles</li>\n<li>bugfix when running scale.colors(1)</li>\n<li>bugfix in hsi2rgb color conversion</li>\n</ul>\n<h3 id=\"1-2-2\">1.2.2</h3>\n<ul>\n<li>scale.colors() now returns the original colors instead of just min/max range</li>\n</ul>\n<h3 id=\"1-2-0\">1.2.0</h3>\n<ul>\n<li>added chroma.average for averaging colors</li>\n</ul>\n<h3 id=\"1-1-0\">1.1.0</h3>\n<ul>\n<li>refactored chroma.scale</li>\n<li>changed behaviour of scale.domain</li>\n<li>added scale.classes</li>\n<li>added scale.padding</li>\n</ul>\n<h3 id=\"1-0-2\">1.0.2</h3>\n<ul>\n<li>standardized alpha channel construction</li>\n<li>chroma.bezier automatically returns chroma.scale</li>\n</ul>\n<h3 id=\"1-0-1\">1.0.1</h3>\n<ul>\n<li>added simple color output to chroma.scale().colors()</li>\n</ul>\n<h3 id=\"1-0-0\">1.0.0</h3>\n<ul>\n<li>numeric interpolation does what it should</li>\n<li>refactored and modularized code base</li>\n<li>changed argument order of Color::interpolate</li>\n</ul>\n\n﻿<link rel=\"stylesheet\" type=\"text/css\" href=\"libs/codemirror/lib/codemirror.css\" />\n<script type=\"text/javascript\" src=\"libs/jquery/jquery-1.11.1.min.js\"></script>\n<script type=\"text/javascript\" src=\"libs/chroma.min.cjs\"></script>\n<script type=\"text/javascript\" src=\"libs/codemirror/lib/codemirror.js\"></script>\n<script type=\"text/javascript\" src=\"libs/codemirror/mode/javascript/javascript.js\"></script>\n<script type=\"text/javascript\" src=\"libs/codemirror/mode/shell/shell.js\"></script>\n\n<script type=\"text/javascript\">\n    function toggleMenu() {\n        const menu = document.querySelector('.toc-container');\n        menu.classList.toggle('open');\n        menu.addEventListener('click', function (e) {\n            if (e.target.tagName === 'A') {\n                menu.classList.remove('open');\n            }\n        });\n    }\n\n    (function ($) {\n        $('code.lang-shell').each(function () {\n            var code = this;\n\n            var cm = CodeMirror(\n                function (elt) {\n                    code.parentNode.replaceChild(elt, code);\n                },\n                {\n                    value: code.innerHTML.trim(),\n                    indentUnit: 4,\n                    mode: 'shell',\n                    readOnly: true,\n                    lineWrapping: true,\n                    scrollbarStyle: 'null',\n                }\n            );\n        });\n        $('code.lang-js').each(function () {\n            var code = this;\n\n            var cm = CodeMirror(\n                function (elt) {\n                    code.parentNode.replaceChild(elt, code);\n                },\n                {\n                    value: code.innerHTML.trim(),\n                    indentUnit: 4,\n                    mode: 'javascript',\n                    lineWrapping: true,\n                    scrollbarStyle: 'null'\n                }\n            );\n\n            cm.on('update', function (_cm, change) {\n                showColors(_cm);\n            });\n\n            var resDisplay = $('<div class=\"result-display\" />').appendTo(\n                cm.display.wrapper.parentNode\n            );\n\n            if (!cm.getDoc().getValue().includes('import')) showColors(cm);\n\n            function showColors(cm) {\n                $('.cm-string', cm.display.wrapper).each(styleSpan);\n                $('.cm-number', cm.display.wrapper).each(enableSlider);\n\n                // evaluate script\n                var src = cm.getDoc().getValue();\n                //resDisplay.html('');\n                chroma.setLabWhitePoint('D65');\n                try {\n                    var s = src.split(';').filter(d => d).map(eval);\n                    resDisplay.html(\n                        '<ol><li>' +\n                            s\n                                .map(resRec)\n                                .map(d => d || '&nbsp;')\n                                // .filter(function (d) {\n                                //     return d !== undefined;\n                                // })\n                                .join('</li><li>') +\n                            '</li></ol>'\n                    );\n\n                    $('.cm-string', resDisplay).each(styleSpan);\n                } catch (e) {\n                    // console.warn(e);\n                }\n\n                function resRec(d) {\n                    if ($.isArray(d)) {\n                        return '[' + d.map(d.length > 2 ? resShort : resLong).join(',') + ']';\n                    }\n                    return resLong(d);\n\n                    function resLong(d) {\n                        if (typeof d == 'boolean') {\n                            return '<span class=\"cm-number\">' + (d ? 'true' : 'false') + '</span>';\n                        } else if (typeof d == 'string') {\n                            // string color, e.g. hex value\n                            return '<span class=\"cm-string\">\"' + d + '\"</span>';\n                        } else if (typeof d == 'object' && d._rgb) {\n                            // chroma.js object\n                            return (\n                                '<span class=\"cm-string cm-color\" data-color=\"' +\n                                d.css() +\n                                '\">' +\n                                d.hex() +\n                                '</span>'\n                            );\n                        } else if ($.isNumeric(d)) {\n                            return '<span class=\"cm-number\">' + round(d, 3) + '</span>';\n                        } else if ($.isFunction(d)) {\n                            var s = '';\n                            var dom = d.domain ? d.domain() : [0, 1],\n                                dmin = Math.min(dom[0], dom[dom.length - 1]),\n                                dmax = Math.max(dom[dom.length - 1], dom[0]);\n                            for (var i = 0; i <= 100; i++) {\n                                s +=\n                                    '<span class=\"grad-step\" style=\"background-color:' +\n                                    d(dmin + (i / 100) * (dmax - dmin)) +\n                                    '\"></span>';\n                            }\n                            s += '<span class=\"domain-min\">' + dmin + '</span>';\n                            s += '<span class=\"domain-med\">' + (dmin + dmax) * 0.5 + '</span>';\n                            s += '<span class=\"domain-max\">' + dmax + '</span>';\n                            return '<div class=\"gradient\">' + s + '</div>';\n                        }\n                    }\n\n                    function resShort(d) {\n                        if (typeof d == 'string') {\n                            // string color, e.g. hex value\n                            return (\n                                '<span class=\"cm-string cm-color cm-small\" data-color=\"' +\n                                d +\n                                '\"><span class=\"cm-hidden-text\">\\'' +\n                                chroma(d).hex() +\n                                \"'</span></span>\"\n                            );\n                        } else if (typeof d == 'object' && d._rgb) {\n                            // chroma.js object\n                            return (\n                                '<span class=\"cm-string cm-color cm-small\" data-color=\"' +\n                                d.css() +\n                                '\"><span class=\"cm-hidden-text\">\\'' +\n                                d.hex() +\n                                \"'</span></span>\"\n                            );\n                        } else if ($.isNumeric(d)) {\n                            return '<span class=\"cm-number\">' + round(d, 2) + '</span>';\n                        } else if (isNaN(d)) {\n                            return '<span class=\"cm-number cm-nan\">NaN</span>';\n                        }\n                    }\n\n                    function round(d, p) {\n                        var n = Math.pow(10, p);\n                        return Math.round(d * n) / n;\n                    }\n                }\n            }\n\n            function styleSpan() {\n                var span = $(this);\n                //setTimeout(function() {\n                val = span.data('color') || span.html().replace(/['\"]/g, '').trim();\n                if (chroma[val]) {\n                    span.attr('style', '');\n                    return;\n                }\n\n                try {\n                    if (chroma.valid(val)) {\n                        const col = chroma(val);\n                        const l = col.oklch()[0];\n                        const isCSS = /[a-z]+\\([^\\)]+\\)/.test(val)\n                        span.attr(\n                            'style',\n                            [\n                                'background-color:' + (isCSS ? val : col.hex()),\n                                'color:' + (l < 0.7 ? 'white' : 'black'),\n                                ...(isCSS ? [] : ['opacity:' + col.alpha()])\n                            ].join(';')\n                        );\n                        \n                    }\n                } catch (e) {\n                    //console.log(e);\n                    span.attr('style', '');\n                    // not a color, so ignore\n                }\n                //}, 50);\n            }\n\n            function enableSlider() {\n                return;\n                var span = $(this),\n                    slider = $('<div></div>').addClass('slider'),\n                    input = $('<input type=\"range\" />').appendTo(slider);\n\n                span.off('mouseenter').on('mouseenter', function () {\n                    var v = +span.text(),\n                        d = Math.pow(10, Math.max(1, Math.log10(v))),\n                        min = v - d,\n                        max = v + d;\n                    input.attr({ min: min, max: max }).prop('value', v);\n                    console.log('span', v);\n\n                    span.append(slider);\n                });\n                span.off('mouseleave').on('mouseleave', function () {\n                    //slider.remove();\n                });\n            }\n        });\n\n        var toc = $('<ul />')\n            .addClass('toc')\n            .appendTo($('<div>').addClass('toc-container').appendTo('.wrap'));\n\n        var hue = Math.random() * 360;\n        $('h2,h3').each(function () {\n            var h = $(this),\n                l = h.attr('id'),\n                t = h.is('h2');\n            toc.append(\n                '<li class=\"' +\n                    ('level-' + (t ? '1' : '2')) +\n                    '\"><a style=\"color:' +\n                    chroma.lch(50, 80, hue) +\n                    '\" href=\"#' +\n                    l +\n                    '\">' +\n                    h.text() +\n                    '</a></li>'\n            );\n            hue = (hue + 20) % 360;\n            var a = $('<a />')\n                .attr('href', '#' + l)\n                .html(h.html());\n            h.html('').append(a);\n        });\n\n        $('h3+h4').each(function (i, el) {\n            el.previousElementSibling.appendChild(el);\n        });\n    })(jQuery);\n</script>\n<a href=\"https://github.com/gka/chroma.js\" class=\"github-corner\"\n    ><svg\n        width=\"80\"\n        height=\"80\"\n        viewBox=\"0 0 250 250\"\n        style=\"fill: #64ceaa; color: #fff; position: absolute; top: 0; border: 0; right: 0\"\n    >\n        <path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path>\n        <path\n            d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\"\n            fill=\"currentColor\"\n            style=\"transform-origin: 130px 106px\"\n            class=\"octo-arm\"\n        ></path>\n        <path\n            d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\"\n            fill=\"currentColor\"\n            class=\"octo-body\"\n        ></path></svg\n></a>\n<style>\n    .github-corner:hover .octo-arm {\n        animation: octocat-wave 560ms ease-in-out;\n    }\n    @keyframes octocat-wave {\n        0%,\n        100% {\n            transform: rotate(0);\n        }\n        20%,\n        60% {\n            transform: rotate(-25deg);\n        }\n        40%,\n        80% {\n            transform: rotate(10deg);\n        }\n    }\n    @media (max-width: 500px) {\n        .github-corner:hover .octo-arm {\n            animation: none;\n        }\n        .github-corner .octo-arm {\n            animation: octocat-wave 560ms ease-in-out;\n        }\n    }\n</style>\n\n</div></body>\n</html>\n"
  },
  {
    "path": "docs/libs/chroma-light.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.chroma = factory());\n})(this, (function () { 'use strict';\n\n    var min$1 = Math.min;\n    var max$1 = Math.max;\n\n    function limit (x, low, high) {\n        if ( high === void 0 ) high = 1;\n\n        return min$1(max$1(low, x), high);\n    }\n\n    function clip_rgb (rgb) {\n        rgb._clipped = false;\n        rgb._unclipped = rgb.slice(0);\n        for (var i = 0; i <= 3; i++) {\n            if (i < 3) {\n                if (rgb[i] < 0 || rgb[i] > 255) { rgb._clipped = true; }\n                rgb[i] = limit(rgb[i], 0, 255);\n            } else if (i === 3) {\n                rgb[i] = limit(rgb[i], 0, 1);\n            }\n        }\n        return rgb;\n    }\n\n    // ported from jQuery's $.type\n    var classToType = {};\n    for (var i = 0, list = [\n        'Boolean',\n        'Number',\n        'String',\n        'Function',\n        'Array',\n        'Date',\n        'RegExp',\n        'Undefined',\n        'Null'\n    ]; i < list.length; i += 1) {\n        var name = list[i];\n\n        classToType[(\"[object \" + name + \"]\")] = name.toLowerCase();\n    }\n    function type (obj) {\n        return classToType[Object.prototype.toString.call(obj)] || 'object';\n    }\n\n    function unpack (args, keyOrder) {\n        if ( keyOrder === void 0 ) keyOrder = null;\n\n        // if called with more than 3 arguments, we return the arguments\n        if (args.length >= 3) { return Array.prototype.slice.call(args); }\n        // with less than 3 args we check if first arg is object\n        // and use the keyOrder string to extract and sort properties\n        if (type(args[0]) == 'object' && keyOrder) {\n            return keyOrder\n                .split('')\n                .filter(function (k) { return args[0][k] !== undefined; })\n                .map(function (k) { return args[0][k]; });\n        }\n        // otherwise we just return the first argument\n        // (which we suppose is an array of args)\n        return args[0].slice(0);\n    }\n\n    function last (args) {\n        if (args.length < 2) { return null; }\n        var l = args.length - 1;\n        if (type(args[l]) == 'string') { return args[l].toLowerCase(); }\n        return null;\n    }\n\n    var PI = Math.PI;\n    var min = Math.min;\n    var max = Math.max;\n\n    var rnd2 = function (a) { return Math.round(a * 100) / 100; };\n    var rnd3 = function (a) { return Math.round(a * 100) / 100; };\n    var DEG2RAD = PI / 180;\n    var RAD2DEG = 180 / PI;\n\n    var input = {\n        format: {},\n        autodetect: []\n    };\n\n    var Color = function Color() {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var me = this;\n        if (\n            type(args[0]) === 'object' &&\n            args[0].constructor &&\n            args[0].constructor === this.constructor\n        ) {\n            // the argument is already a Color instance\n            return args[0];\n        }\n        // last argument could be the mode\n        var mode = last(args);\n        var autodetect = false;\n        if (!mode) {\n            autodetect = true;\n\n            if (!input.sorted) {\n                input.autodetect = input.autodetect.sort(function (a, b) { return b.p - a.p; });\n                input.sorted = true;\n            }\n\n            // auto-detect format\n            for (var i = 0, list = input.autodetect; i < list.length; i += 1) {\n                var chk = list[i];\n\n                mode = chk.test.apply(chk, args);\n                if (mode) { break; }\n            }\n        }\n        if (input.format[mode]) {\n            var rgb = input.format[mode].apply(\n                null,\n                autodetect ? args : args.slice(0, -1)\n            );\n            me._rgb = clip_rgb(rgb);\n        } else {\n            throw new Error('unknown format: ' + args);\n        }\n        // add alpha channel\n        if (me._rgb.length === 3) { me._rgb.push(1); }\n    };\n    Color.prototype.toString = function toString () {\n        if (type(this.hex) == 'function') { return this.hex(); }\n        return (\"[\" + (this._rgb.join(',')) + \"]\");\n    };\n\n    // this gets updated automatically\n    var version = '3.2.0';\n\n    var chroma = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n    };\n\n    chroma.version = version;\n\n    /*\n     * supported arguments:\n     * - hsl2css(h,s,l)\n     * - hsl2css(h,s,l,a)\n     * - hsl2css([h,s,l], mode)\n     * - hsl2css([h,s,l,a], mode)\n     * - hsl2css({h,s,l,a}, mode)\n     */\n    var hsl2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var hsla = unpack(args, 'hsla');\n        var mode = last(args) || 'lsa';\n        hsla[0] = rnd2(hsla[0] || 0) + 'deg';\n        hsla[1] = rnd2(hsla[1] * 100) + '%';\n        hsla[2] = rnd2(hsla[2] * 100) + '%';\n        if (mode === 'hsla' || (hsla.length > 3 && hsla[3] < 1)) {\n            hsla[3] = '/ ' + (hsla.length > 3 ? hsla[3] : 1);\n            mode = 'hsla';\n        } else {\n            hsla.length = 3;\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (hsla.join(' ')) + \")\");\n    };\n\n    /*\n     * supported arguments:\n     * - rgb2hsl(r,g,b)\n     * - rgb2hsl(r,g,b,a)\n     * - rgb2hsl([r,g,b])\n     * - rgb2hsl([r,g,b,a])\n     * - rgb2hsl({r,g,b,a})\n     */\n    var rgb2hsl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'rgba');\n        var r = args[0];\n        var g = args[1];\n        var b = args[2];\n\n        r /= 255;\n        g /= 255;\n        b /= 255;\n\n        var minRgb = min(r, g, b);\n        var maxRgb = max(r, g, b);\n\n        var l = (maxRgb + minRgb) / 2;\n        var s, h;\n\n        if (maxRgb === minRgb) {\n            s = 0;\n            h = Number.NaN;\n        } else {\n            s =\n                l < 0.5\n                    ? (maxRgb - minRgb) / (maxRgb + minRgb)\n                    : (maxRgb - minRgb) / (2 - maxRgb - minRgb);\n        }\n\n        if (r == maxRgb) { h = (g - b) / (maxRgb - minRgb); }\n        else if (g == maxRgb) { h = 2 + (b - r) / (maxRgb - minRgb); }\n        else if (b == maxRgb) { h = 4 + (r - g) / (maxRgb - minRgb); }\n\n        h *= 60;\n        if (h < 0) { h += 360; }\n        if (args.length > 3 && args[3] !== undefined) { return [h, s, l, args[3]]; }\n        return [h, s, l];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        var mode = last(args) || 'lab';\n        laba[0] = rnd2(laba[0]) + '%';\n        laba[1] = rnd2(laba[1]);\n        laba[2] = rnd2(laba[2]);\n        if (mode === 'laba' || (laba.length > 3 && laba[3] < 1)) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"lab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var labConstants = {\n        // Corresponds roughly to RGB brighter/darker\n        Kn: 18,\n\n        // D65 standard referent\n        labWhitePoint: 'd65',\n        Xn: 0.95047,\n        Yn: 1,\n        Zn: 1.08883,\n\n        kE: 216.0 / 24389.0,\n        kKE: 8.0,\n        kK: 24389.0 / 27.0,\n\n        RefWhiteRGB: {\n            // sRGB\n            X: 0.95047,\n            Y: 1,\n            Z: 1.08883\n        },\n\n        MtxRGB2XYZ: {\n            m00: 0.4124564390896922,\n            m01: 0.21267285140562253,\n            m02: 0.0193338955823293,\n            m10: 0.357576077643909,\n            m11: 0.715152155287818,\n            m12: 0.11919202588130297,\n            m20: 0.18043748326639894,\n            m21: 0.07217499330655958,\n            m22: 0.9503040785363679\n        },\n\n        MtxXYZ2RGB: {\n            m00: 3.2404541621141045,\n            m01: -0.9692660305051868,\n            m02: 0.055643430959114726,\n            m10: -1.5371385127977166,\n            m11: 1.8760108454466942,\n            m12: -0.2040259135167538,\n            m20: -0.498531409556016,\n            m21: 0.041556017530349834,\n            m22: 1.0572251882231791\n        },\n\n        // used in rgb2xyz\n        As: 0.9414285350000001,\n        Bs: 1.040417467,\n        Cs: 1.089532651,\n\n        MtxAdaptMa: {\n            m00: 0.8951,\n            m01: -0.7502,\n            m02: 0.0389,\n            m10: 0.2664,\n            m11: 1.7135,\n            m12: -0.0685,\n            m20: -0.1614,\n            m21: 0.0367,\n            m22: 1.0296\n        },\n\n        MtxAdaptMaI: {\n            m00: 0.9869929054667123,\n            m01: 0.43230526972339456,\n            m02: -0.008528664575177328,\n            m10: -0.14705425642099013,\n            m11: 0.5183602715367776,\n            m12: 0.04004282165408487,\n            m20: 0.15996265166373125,\n            m21: 0.0492912282128556,\n            m22: 0.9684866957875502\n        }\n    };\n\n    // taken from https://de.mathworks.com/help/images/ref/whitepoint.html\n    var ILLUMINANTS = new Map([\n        // ASTM E308-01\n        ['a', [1.0985, 0.35585]],\n        // Wyszecki & Stiles, p. 769\n        ['b', [1.0985, 0.35585]],\n        // C ASTM E308-01\n        ['c', [0.98074, 1.18232]],\n        // D50 (ASTM E308-01)\n        ['d50', [0.96422, 0.82521]],\n        // D55 (ASTM E308-01)\n        ['d55', [0.95682, 0.92149]],\n        // D65 (ASTM E308-01)\n        ['d65', [0.95047, 1.08883]],\n        // E (ASTM E308-01)\n        ['e', [1, 1, 1]],\n        // F2 (ASTM E308-01)\n        ['f2', [0.99186, 0.67393]],\n        // F7 (ASTM E308-01)\n        ['f7', [0.95041, 1.08747]],\n        // F11 (ASTM E308-01)\n        ['f11', [1.00962, 0.6435]],\n        ['icc', [0.96422, 0.82521]]\n    ]);\n\n    function setLabWhitePoint(name) {\n        var ill = ILLUMINANTS.get(String(name).toLowerCase());\n        if (!ill) {\n            throw new Error('unknown Lab illuminant ' + name);\n        }\n        labConstants.labWhitePoint = name;\n        labConstants.Xn = ill[0];\n        labConstants.Zn = ill[1];\n    }\n\n    function getLabWhitePoint() {\n        return labConstants.labWhitePoint;\n    }\n\n    var rgb2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2xyz(r, g, b);\n        var x = ref$1[0];\n        var y = ref$1[1];\n        var z = ref$1[2];\n        var ref$2 = xyz2lab(x, y, z);\n        var L = ref$2[0];\n        var a = ref$2[1];\n        var b_ = ref$2[2];\n        return [L, a, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    function xyz2lab(x, y, z) {\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var xr = x / Xn;\n        var yr = y / Yn;\n        var zr = z / Zn;\n\n        var fx = xr > kE ? Math.pow(xr, 1.0 / 3.0) : (kK * xr + 16.0) / 116.0;\n        var fy = yr > kE ? Math.pow(yr, 1.0 / 3.0) : (kK * yr + 16.0) / 116.0;\n        var fz = zr > kE ? Math.pow(zr, 1.0 / 3.0) : (kK * zr + 16.0) / 116.0;\n\n        return [116.0 * fy - 16.0, 500.0 * (fx - fy), 200.0 * (fy - fz)];\n    }\n\n    function gammaAdjustSRGB(companded) {\n        var sign = Math.sign(companded);\n        companded = Math.abs(companded);\n        var linear =\n            companded <= 0.04045\n                ? companded / 12.92\n                : Math.pow((companded + 0.055) / 1.055, 2.4);\n        return linear * sign;\n    }\n\n    var rgb2xyz = function (r, g, b) {\n        // normalize and gamma adjust\n        r = gammaAdjustSRGB(r / 255);\n        g = gammaAdjustSRGB(g / 255);\n        b = gammaAdjustSRGB(b / 255);\n\n        var MtxRGB2XYZ = labConstants.MtxRGB2XYZ;\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var As = labConstants.As;\n        var Bs = labConstants.Bs;\n        var Cs = labConstants.Cs;\n\n        var x = r * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;\n        var y = r * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;\n        var z = r * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;\n\n        var Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20;\n        var Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21;\n        var Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22;\n\n        X *= Ad / As;\n        Y *= Bd / Bs;\n        Z *= Cd / Cs;\n\n        x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;\n        y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;\n        z = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;\n\n        return [x, y, z];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        var mode = last(args) || 'lab';\n        lcha[0] = rnd2(lcha[0]) + '%';\n        lcha[1] = rnd2(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (mode === 'lcha' || (lcha.length > 3 && lcha[3] < 1)) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"lch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var sqrt$1 = Math.sqrt;\n    var atan2 = Math.atan2;\n    var round$4 = Math.round;\n\n    var lab2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'lab');\n        var l = ref[0];\n        var a = ref[1];\n        var b = ref[2];\n        var c = sqrt$1(a * a + b * b);\n        var h = (atan2(b, a) * RAD2DEG + 360) % 360;\n        if (round$4(c * 10000) === 0) { h = Number.NaN; }\n        return [l, c, h];\n    };\n\n    var rgb2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2lab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/multiply-matrices.js\n    function multiplyMatrices(A, B) {\n        var m = A.length;\n\n        if (!Array.isArray(A[0])) {\n            // A is vector, convert to [[a, b, c, ...]]\n            A = [A];\n        }\n\n        if (!Array.isArray(B[0])) {\n            // B is vector, convert to [[a], [b], [c], ...]]\n            B = B.map(function (x) { return [x]; });\n        }\n\n        var p = B[0].length;\n        var B_cols = B[0].map(function (_, i) { return B.map(function (x) { return x[i]; }); }); // transpose B\n        var product = A.map(function (row) { return B_cols.map(function (col) {\n                if (!Array.isArray(row)) {\n                    return col.reduce(function (a, c) { return a + c * row; }, 0);\n                }\n\n                return row.reduce(function (a, c, i) { return a + c * (col[i] || 0); }, 0);\n            }); }\n        );\n\n        if (m === 1) {\n            product = product[0]; // Avoid [[a, b, c, ...]]\n        }\n\n        if (p === 1) {\n            return product.map(function (x) { return x[0]; }); // Avoid [[a], [b], [c], ...]]\n        }\n\n        return product;\n    }\n\n    var rgb2oklab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var xyz = rgb2xyz(r, g, b);\n        var oklab = XYZ_to_OKLab(xyz);\n        return oklab.concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function XYZ_to_OKLab(XYZ) {\n        // Given XYZ relative to D65, convert to OKLab\n        var XYZtoLMS = [\n            [0.819022437996703, 0.3619062600528904, -0.1288737815209879],\n            [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],\n            [0.0481771893596242, 0.2642395317527308, 0.6335478284694309]\n        ];\n        var LMStoOKLab = [\n            [0.210454268309314, 0.7936177747023054, -0.0040720430116193],\n            [1.9779985324311684, -2.42859224204858, 0.450593709617411],\n            [0.0259040424655478, 0.7827717124575296, -0.8086757549230774]\n        ];\n\n        var LMS = multiplyMatrices(XYZtoLMS, XYZ);\n        // JavaScript Math.cbrt returns a sign-matched cube root\n        // beware if porting to other languages\n        // especially if tempted to use a general power function\n        return multiplyMatrices(\n            LMStoOKLab,\n            LMS.map(function (c) { return Math.cbrt(c); })\n        );\n        // L in range [0,1]. For use in CSS, multiply by 100 and add a percent\n    }\n\n    var oklab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        laba[0] = rnd2(laba[0] * 100) + '%';\n        laba[1] = rnd3(laba[1]);\n        laba[2] = rnd3(laba[2]);\n        if (laba.length > 3 && laba[3] < 1) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"oklab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var rgb2oklch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2oklab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var oklch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        lcha[0] = rnd2(lcha[0] * 100) + '%';\n        lcha[1] = rnd3(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (lcha.length > 3 && lcha[3] < 1) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"oklch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var round$3 = Math.round;\n\n    /*\n     * supported arguments:\n     * - rgb2css(r,g,b)\n     * - rgb2css(r,g,b,a)\n     * - rgb2css([r,g,b], mode)\n     * - rgb2css([r,g,b,a], mode)\n     * - rgb2css({r,g,b,a}, mode)\n     */\n    var rgb2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        var mode = last(args) || 'rgb';\n        if (mode.substr(0, 3) === 'hsl') {\n            return hsl2css(rgb2hsl(rgba), mode);\n        }\n        if (mode.substr(0, 3) === 'lab') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor = lab2css(rgb2lab(rgba), mode);\n            setLabWhitePoint(prevWhitePoint);\n            return cssColor;\n        }\n        if (mode.substr(0, 3) === 'lch') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor$1 = lch2css(rgb2lch(rgba), mode);\n            setLabWhitePoint(prevWhitePoint$1);\n            return cssColor$1;\n        }\n        if (mode.substr(0, 5) === 'oklab') {\n            return oklab2css(rgb2oklab(rgba));\n        }\n        if (mode.substr(0, 5) === 'oklch') {\n            return oklch2css(rgb2oklch(rgba));\n        }\n        rgba[0] = round$3(rgba[0]);\n        rgba[1] = round$3(rgba[1]);\n        rgba[2] = round$3(rgba[2]);\n        if (mode === 'rgba' || (rgba.length > 3 && rgba[3] < 1)) {\n            rgba[3] = '/ ' + (rgba.length > 3 ? rgba[3] : 1);\n            mode = 'rgba';\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (rgba.slice(0, mode === 'rgb' ? 3 : 4).join(' ')) + \")\");\n    };\n\n    var hsl2rgb = function () {\n        var assign;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hsl');\n        var h = args[0];\n        var s = args[1];\n        var l = args[2];\n        var r, g, b;\n        if (s === 0) {\n            r = g = b = l * 255;\n        } else {\n            var t3 = [0, 0, 0];\n            var c = [0, 0, 0];\n            var t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;\n            var t1 = 2 * l - t2;\n            var h_ = h / 360;\n            t3[0] = h_ + 1 / 3;\n            t3[1] = h_;\n            t3[2] = h_ - 1 / 3;\n            for (var i = 0; i < 3; i++) {\n                if (t3[i] < 0) { t3[i] += 1; }\n                if (t3[i] > 1) { t3[i] -= 1; }\n                if (6 * t3[i] < 1) { c[i] = t1 + (t2 - t1) * 6 * t3[i]; }\n                else if (2 * t3[i] < 1) { c[i] = t2; }\n                else if (3 * t3[i] < 2) { c[i] = t1 + (t2 - t1) * (2 / 3 - t3[i]) * 6; }\n                else { c[i] = t1; }\n            }\n            (assign = [c[0] * 255, c[1] * 255, c[2] * 255], r = assign[0], g = assign[1], b = assign[2]);\n        }\n        if (args.length > 3) {\n            // keep alpha channel\n            return [r, g, b, args[3]];\n        }\n        return [r, g, b, 1];\n    };\n\n    /*\n     * L* [0..100]\n     * a [-100..100]\n     * b [-100..100]\n     */\n    var lab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var ref = lab2xyz(L, a, b);\n        var x = ref[0];\n        var y = ref[1];\n        var z = ref[2];\n        var ref$1 = xyz2rgb(x, y, z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_, args.length > 3 ? args[3] : 1];\n    };\n\n    var lab2xyz = function (L, a, b) {\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var kKE = labConstants.kKE;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var fy = (L + 16.0) / 116.0;\n        var fx = 0.002 * a + fy;\n        var fz = fy - 0.005 * b;\n\n        var fx3 = fx * fx * fx;\n        var fz3 = fz * fz * fz;\n\n        var xr = fx3 > kE ? fx3 : (116.0 * fx - 16.0) / kK;\n        var yr = L > kKE ? Math.pow((L + 16.0) / 116.0, 3.0) : L / kK;\n        var zr = fz3 > kE ? fz3 : (116.0 * fz - 16.0) / kK;\n\n        var x = xr * Xn;\n        var y = yr * Yn;\n        var z = zr * Zn;\n\n        return [x, y, z];\n    };\n\n    var compand = function (linear) {\n        /* sRGB */\n        var sign = Math.sign(linear);\n        linear = Math.abs(linear);\n        return (\n            (linear <= 0.0031308\n                ? linear * 12.92\n                : 1.055 * Math.pow(linear, 1.0 / 2.4) - 0.055) * sign\n        );\n    };\n\n    var xyz2rgb = function (x, y, z) {\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var MtxXYZ2RGB = labConstants.MtxXYZ2RGB;\n        var RefWhiteRGB = labConstants.RefWhiteRGB;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cs = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var Ad =\n            RefWhiteRGB.X * MtxAdaptMa.m00 +\n            RefWhiteRGB.Y * MtxAdaptMa.m10 +\n            RefWhiteRGB.Z * MtxAdaptMa.m20;\n        var Bd =\n            RefWhiteRGB.X * MtxAdaptMa.m01 +\n            RefWhiteRGB.Y * MtxAdaptMa.m11 +\n            RefWhiteRGB.Z * MtxAdaptMa.m21;\n        var Cd =\n            RefWhiteRGB.X * MtxAdaptMa.m02 +\n            RefWhiteRGB.Y * MtxAdaptMa.m12 +\n            RefWhiteRGB.Z * MtxAdaptMa.m22;\n\n        var X1 =\n            (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20) *\n            (Ad / As);\n        var Y1 =\n            (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21) *\n            (Bd / Bs);\n        var Z1 =\n            (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22) *\n            (Cd / Cs);\n\n        var X2 =\n            X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;\n        var Y2 =\n            X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;\n        var Z2 =\n            X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;\n\n        var r = compand(\n            X2 * MtxXYZ2RGB.m00 + Y2 * MtxXYZ2RGB.m10 + Z2 * MtxXYZ2RGB.m20\n        );\n        var g = compand(\n            X2 * MtxXYZ2RGB.m01 + Y2 * MtxXYZ2RGB.m11 + Z2 * MtxXYZ2RGB.m21\n        );\n        var b = compand(\n            X2 * MtxXYZ2RGB.m02 + Y2 * MtxXYZ2RGB.m12 + Z2 * MtxXYZ2RGB.m22\n        );\n\n        return [r * 255, g * 255, b * 255];\n    };\n\n    var sin = Math.sin;\n    var cos = Math.cos;\n\n    var lch2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n        These formulas were invented by David Dalrymple to obtain maximum contrast without going\n        out of gamut if the parameters are in the range 0-1.\n\n        A saturation multiplier was added by Gregor Aisch\n        */\n        var ref = unpack(args, 'lch');\n        var l = ref[0];\n        var c = ref[1];\n        var h = ref[2];\n        if (isNaN(h)) { h = 0; }\n        h = h * DEG2RAD;\n        return [l, cos(h) * c, sin(h) * c];\n    };\n\n    var lch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = lab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var oklab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var rest = args.slice(3);\n        var ref = OKLab_to_XYZ([L, a, b]);\n        var X = ref[0];\n        var Y = ref[1];\n        var Z = ref[2];\n        var ref$1 = xyz2rgb(X, Y, Z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function OKLab_to_XYZ(OKLab) {\n        // Given OKLab, convert to XYZ relative to D65\n        var LMStoXYZ = [\n            [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],\n            [-0.0405757452148008, 1.112286803280317, -0.0717110580655164],\n            [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]\n        ];\n        var OKLabtoLMS = [\n            [1.0, 0.3963377773761749, 0.2158037573099136],\n            [1.0, -0.1055613458156586, -0.0638541728258133],\n            [1.0, -0.0894841775298119, -1.2914855480194092]\n        ];\n\n        var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab);\n        return multiplyMatrices(\n            LMStoXYZ,\n            LMSnl.map(function (c) { return Math.pow( c, 3 ); })\n        );\n    }\n\n    var oklch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var rest = args.slice(3);\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = oklab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var INT_OR_PCT = /((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source;\n    var FLOAT_OR_PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source;\n    var PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source;\n    var RE_S = /\\s*/.source;\n    var SEP = /\\s+/.source;\n    var COMMA = /\\s*,\\s*/.source;\n    var ANLGE = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source;\n    var ALPHA = /\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source;\n\n    // e.g. rgb(250 20 0), rgb(100% 50% 20%), rgb(100% 50% 20% / 0.5)\n    var RE_RGB = new RegExp(\n        '^rgba?\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_RGB_LEGACY = new RegExp(\n        '^rgb\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n    var RE_RGBA_LEGACY = new RegExp(\n        '^rgba\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT, FLOAT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n\n    var RE_HSL = new RegExp(\n        '^hsla?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(SEP) + ALPHA + '\\\\)$'\n    );\n    var RE_HSL_LEGACY = new RegExp(\n        '^hsl?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(COMMA) + RE_S + '\\\\)$'\n    );\n    var RE_HSLA_LEGACY =\n        /^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/;\n\n    var RE_LAB = new RegExp(\n        '^lab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_LCH = new RegExp(\n        '^lch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLAB = new RegExp(\n        '^oklab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLCH = new RegExp(\n        '^oklch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n\n    var round$2 = Math.round;\n\n    var roundRGB = function (rgb) {\n        return rgb.map(function (v, i) { return (i <= 2 ? limit(round$2(v), 0, 255) : v); });\n    };\n\n    var percentToAbsolute = function (pct, min, max, signed) {\n        if ( min === void 0 ) min = 0;\n        if ( max === void 0 ) max = 100;\n        if ( signed === void 0 ) signed = false;\n\n        if (typeof pct === 'string' && pct.endsWith('%')) {\n            pct = parseFloat(pct.substring(0, pct.length - 1)) / 100;\n            if (signed) {\n                // signed percentages are in the range -100% to 100%\n                pct = min + (pct + 1) * 0.5 * (max - min);\n            } else {\n                pct = min + pct * (max - min);\n            }\n        }\n        return +pct;\n    };\n\n    var noneToValue = function (v, noneValue) {\n        return v === 'none' ? noneValue : v;\n    };\n\n    var css2rgb = function (css) {\n        css = css.toLowerCase().trim();\n\n        if (css === 'transparent') {\n            return [0, 0, 0, 0];\n        }\n\n        var m;\n\n        if (input.format.named) {\n            try {\n                return input.format.named(css);\n                // eslint-disable-next-line\n            } catch (e) {}\n        }\n\n        // rgb(250 20 0) or rgb(250,20,0)\n        if ((m = css.match(RE_RGB)) || (m = css.match(RE_RGB_LEGACY))) {\n            var rgb = m.slice(1, 4);\n            for (var i = 0; i < 3; i++) {\n                rgb[i] = +percentToAbsolute(noneToValue(rgb[i], 0), 0, 255);\n            }\n            rgb = roundRGB(rgb);\n            var alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb[3] = alpha; // default alpha\n            return rgb;\n        }\n\n        // rgba(250,20,0,0.4)\n        if ((m = css.match(RE_RGBA_LEGACY))) {\n            var rgb$1 = m.slice(1, 5);\n            for (var i$1 = 0; i$1 < 4; i$1++) {\n                rgb$1[i$1] = +percentToAbsolute(rgb$1[i$1], 0, 255);\n            }\n            return rgb$1;\n        }\n\n        // hsl(0,100%,50%)\n        if ((m = css.match(RE_HSL)) || (m = css.match(RE_HSL_LEGACY))) {\n            var hsl = m.slice(1, 4);\n            hsl[0] = +noneToValue(hsl[0].replace('deg', ''), 0);\n            hsl[1] = +percentToAbsolute(noneToValue(hsl[1], 0), 0, 100) * 0.01;\n            hsl[2] = +percentToAbsolute(noneToValue(hsl[2], 0), 0, 100) * 0.01;\n            var rgb$2 = roundRGB(hsl2rgb(hsl));\n            var alpha$1 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$2[3] = alpha$1;\n            return rgb$2;\n        }\n\n        // hsla(0,100%,50%,0.5)\n        if ((m = css.match(RE_HSLA_LEGACY))) {\n            var hsl$1 = m.slice(1, 4);\n            hsl$1[1] *= 0.01;\n            hsl$1[2] *= 0.01;\n            var rgb$3 = hsl2rgb(hsl$1);\n            for (var i$2 = 0; i$2 < 3; i$2++) {\n                rgb$3[i$2] = round$2(rgb$3[i$2]);\n            }\n            rgb$3[3] = +m[4]; // default alpha = 1\n            return rgb$3;\n        }\n\n        if ((m = css.match(RE_LAB))) {\n            var lab = m.slice(1, 4);\n            lab[0] = percentToAbsolute(noneToValue(lab[0], 0), 0, 100);\n            lab[1] = percentToAbsolute(noneToValue(lab[1], 0), -125, 125, true);\n            lab[2] = percentToAbsolute(noneToValue(lab[2], 0), -125, 125, true);\n            // convert to D50 Lab whitepoint\n            var wp = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$4 = roundRGB(lab2rgb(lab));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp);\n            var alpha$2 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$4[3] = alpha$2;\n            return rgb$4;\n        }\n\n        if ((m = css.match(RE_LCH))) {\n            var lch = m.slice(1, 4);\n            lch[0] = percentToAbsolute(lch[0], 0, 100);\n            lch[1] = percentToAbsolute(noneToValue(lch[1], 0), 0, 150, false);\n            lch[2] = +noneToValue(lch[2].replace('deg', ''), 0);\n            // convert to D50 Lab whitepoint\n            var wp$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$5 = roundRGB(lch2rgb(lch));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp$1);\n            var alpha$3 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$5[3] = alpha$3;\n            return rgb$5;\n        }\n\n        if ((m = css.match(RE_OKLAB))) {\n            var oklab = m.slice(1, 4);\n            oklab[0] = percentToAbsolute(noneToValue(oklab[0], 0), 0, 1);\n            oklab[1] = percentToAbsolute(noneToValue(oklab[1], 0), -0.4, 0.4, true);\n            oklab[2] = percentToAbsolute(noneToValue(oklab[2], 0), -0.4, 0.4, true);\n            var rgb$6 = roundRGB(oklab2rgb(oklab));\n            var alpha$4 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$6[3] = alpha$4;\n            return rgb$6;\n        }\n\n        if ((m = css.match(RE_OKLCH))) {\n            var oklch = m.slice(1, 4);\n            oklch[0] = percentToAbsolute(noneToValue(oklch[0], 0), 0, 1);\n            oklch[1] = percentToAbsolute(noneToValue(oklch[1], 0), 0, 0.4, false);\n            oklch[2] = +noneToValue(oklch[2].replace('deg', ''), 0);\n            var rgb$7 = roundRGB(oklch2rgb(oklch));\n            var alpha$5 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$7[3] = alpha$5;\n            return rgb$7;\n        }\n    };\n\n    css2rgb.test = function (s) {\n        return (\n            // modern\n            RE_RGB.test(s) ||\n            RE_HSL.test(s) ||\n            RE_LAB.test(s) ||\n            RE_LCH.test(s) ||\n            RE_OKLAB.test(s) ||\n            RE_OKLCH.test(s) ||\n            // legacy\n            RE_RGB_LEGACY.test(s) ||\n            RE_RGBA_LEGACY.test(s) ||\n            RE_HSL_LEGACY.test(s) ||\n            RE_HSLA_LEGACY.test(s) ||\n            s === 'transparent'\n        );\n    };\n\n    Color.prototype.css = function (mode) {\n        return rgb2css(this._rgb, mode);\n    };\n\n    var css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['css']) ));\n    };\n    chroma.css = css;\n\n    input.format.css = css2rgb;\n\n    input.autodetect.push({\n        p: 5,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (!rest.length && type(h) === 'string' && css2rgb.test(h)) {\n                return 'css';\n            }\n        }\n    });\n\n    var RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\n    var RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/;\n\n    var hex2rgb = function (hex) {\n        if (hex.match(RE_HEX)) {\n            // remove optional leading #\n            if (hex.length === 4 || hex.length === 7) {\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full six-digit\n            if (hex.length === 3) {\n                hex = hex.split('');\n                hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n            }\n            var u = parseInt(hex, 16);\n            var r = u >> 16;\n            var g = (u >> 8) & 0xff;\n            var b = u & 0xff;\n            return [r, g, b, 1];\n        }\n\n        // match rgba hex format, eg #FF000077\n        if (hex.match(RE_HEXA)) {\n            if (hex.length === 5 || hex.length === 9) {\n                // remove optional leading #\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full eight-digit\n            if (hex.length === 4) {\n                hex = hex.split('');\n                hex =\n                    hex[0] +\n                    hex[0] +\n                    hex[1] +\n                    hex[1] +\n                    hex[2] +\n                    hex[2] +\n                    hex[3] +\n                    hex[3];\n            }\n            var u$1 = parseInt(hex, 16);\n            var r$1 = (u$1 >> 24) & 0xff;\n            var g$1 = (u$1 >> 16) & 0xff;\n            var b$1 = (u$1 >> 8) & 0xff;\n            var a = Math.round(((u$1 & 0xff) / 0xff) * 100) / 100;\n            return [r$1, g$1, b$1, a];\n        }\n\n        // we used to check for css colors here\n        // if _input.css? and rgb = _input.css hex\n        //     return rgb\n\n        throw new Error((\"unknown hex color: \" + hex));\n    };\n\n    var round$1 = Math.round;\n\n    var rgb2hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgba');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var a = ref[3];\n        var mode = last(args) || 'auto';\n        if (a === undefined) { a = 1; }\n        if (mode === 'auto') {\n            mode = a < 1 ? 'rgba' : 'rgb';\n        }\n        r = round$1(r);\n        g = round$1(g);\n        b = round$1(b);\n        var u = (r << 16) | (g << 8) | b;\n        var str = '000000' + u.toString(16); //#.toUpperCase();\n        str = str.substr(str.length - 6);\n        var hxa = '0' + round$1(a * 255).toString(16);\n        hxa = hxa.substr(hxa.length - 2);\n        switch (mode.toLowerCase()) {\n            case 'rgba':\n                return (\"#\" + str + hxa);\n            case 'argb':\n                return (\"#\" + hxa + str);\n            default:\n                return (\"#\" + str);\n        }\n    };\n\n    Color.prototype.hex = function (mode) {\n        return rgb2hex(this._rgb, mode);\n    };\n\n    var hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hex']) ));\n    };\n    chroma.hex = hex;\n\n    input.format.hex = hex2rgb;\n    input.autodetect.push({\n        p: 4,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (\n                !rest.length &&\n                type(h) === 'string' &&\n                [3, 4, 5, 6, 7, 8, 9].indexOf(h.length) >= 0\n            ) {\n                return 'hex';\n            }\n        }\n    });\n\n    Color.prototype.hsl = function () {\n        return rgb2hsl(this._rgb);\n    };\n\n    var hsl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsl']) ));\n    };\n    chroma.hsl = hsl;\n\n    input.format.hsl = hsl2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsl');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsl';\n            }\n        }\n    });\n\n    Color.prototype.lab = function () {\n        return rgb2lab(this._rgb);\n    };\n\n    var lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['lab']) ));\n    };\n    Object.assign(chroma, { lab: lab, getLabWhitePoint: getLabWhitePoint, setLabWhitePoint: setLabWhitePoint });\n\n    input.format.lab = lab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'lab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'lab';\n            }\n        }\n    });\n\n    Color.prototype.oklab = function () {\n        return rgb2oklab(this._rgb);\n    };\n\n    var oklab$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['oklab']) ));\n    };\n    Object.assign(chroma, { oklab: oklab$1 });\n\n    input.format.oklab = oklab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'oklab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'oklab';\n            }\n        }\n    });\n\n    var round = Math.round;\n\n    Color.prototype.rgb = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        if (rnd === false) { return this._rgb.slice(0, 3); }\n        return this._rgb.slice(0, 3).map(round);\n    };\n\n    Color.prototype.rgba = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        return this._rgb.slice(0, 4).map(function (v, i) {\n            return i < 3 ? (rnd === false ? v : round(v)) : v;\n        });\n    };\n\n    var rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['rgb']) ));\n    };\n    Object.assign(chroma, { rgb: rgb });\n\n    input.format.rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        if (rgba[3] === undefined) { rgba[3] = 1; }\n        return rgba;\n    };\n\n    input.autodetect.push({\n        p: 3,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'rgba');\n            if (\n                type(args) === 'array' &&\n                (args.length === 3 ||\n                    (args.length === 4 &&\n                        type(args[3]) == 'number' &&\n                        args[3] >= 0 &&\n                        args[3] <= 1))\n            ) {\n                return 'rgb';\n            }\n        }\n    });\n\n    Color.prototype.alpha = function (a, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        if (a !== undefined && type(a) === 'number') {\n            if (mutate) {\n                this._rgb[3] = a;\n                return this;\n            }\n            return new Color([this._rgb[0], this._rgb[1], this._rgb[2], a], 'rgb');\n        }\n        return this._rgb[3];\n    };\n\n    Color.prototype.darken = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        var me = this;\n        var lab = me.lab();\n        lab[0] -= labConstants.Kn * amount;\n        return new Color(lab, 'lab').alpha(me.alpha(), true);\n    };\n\n    Color.prototype.brighten = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        return this.darken(-amount);\n    };\n\n    Color.prototype.darker = Color.prototype.darken;\n    Color.prototype.brighter = Color.prototype.brighten;\n\n    Color.prototype.get = function (mc) {\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) { return src[i]; }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    var index = {};\n\n    function mix (col1, col2, f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 3;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 3 ];\n\n        var mode = rest[0] || 'lrgb';\n        if (!index[mode] && !rest.length) {\n            // fall back to the first supported mode\n            mode = Object.keys(index)[0];\n        }\n        if (!index[mode]) {\n            throw new Error((\"interpolation mode \" + mode + \" is not defined\"));\n        }\n        if (type(col1) !== 'object') { col1 = new Color(col1); }\n        if (type(col2) !== 'object') { col2 = new Color(col2); }\n        return index[mode](col1, col2, f).alpha(\n            col1.alpha() + f * (col2.alpha() - col1.alpha())\n        );\n    }\n\n    Color.prototype.mix = Color.prototype.interpolate = function (\n        col2,\n        f\n    ) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 2;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 2 ];\n\n        return mix.apply(void 0, [ this, col2, f ].concat( rest ));\n    };\n\n    Color.prototype.set = function (mc, value, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) {\n                if (type(value) == 'string') {\n                    switch (value.charAt(0)) {\n                        case '+':\n                            src[i] += +value;\n                            break;\n                        case '-':\n                            src[i] += +value;\n                            break;\n                        case '*':\n                            src[i] *= +value.substr(1);\n                            break;\n                        case '/':\n                            src[i] /= +value.substr(1);\n                            break;\n                        default:\n                            src[i] = +value;\n                    }\n                } else if (type(value) === 'number') {\n                    src[i] = value;\n                } else {\n                    throw new Error(\"unsupported value for Color.set\");\n                }\n                var out = new Color(src, mode);\n                if (mutate) {\n                    this._rgb = out._rgb;\n                    return this;\n                }\n                return out;\n            }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    Color.prototype.tint = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'white', f ].concat( rest ));\n    };\n\n    Color.prototype.shade = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'black', f ].concat( rest ));\n    };\n\n    var sqrt = Math.sqrt;\n    var pow = Math.pow;\n\n    var lrgb = function (col1, col2, f) {\n        var ref = col1._rgb;\n        var x1 = ref[0];\n        var y1 = ref[1];\n        var z1 = ref[2];\n        var ref$1 = col2._rgb;\n        var x2 = ref$1[0];\n        var y2 = ref$1[1];\n        var z2 = ref$1[2];\n        return new Color(\n            sqrt(pow(x1, 2) * (1 - f) + pow(x2, 2) * f),\n            sqrt(pow(y1, 2) * (1 - f) + pow(y2, 2) * f),\n            sqrt(pow(z1, 2) * (1 - f) + pow(z2, 2) * f),\n            'rgb'\n        );\n    };\n\n    // register interpolator\n    index.lrgb = lrgb;\n\n    var oklab = function (col1, col2, f) {\n        var xyz0 = col1.oklab();\n        var xyz1 = col2.oklab();\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'oklab'\n        );\n    };\n\n    // register interpolator\n    index.oklab = oklab;\n\n    function valid () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        try {\n            new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n            return true;\n            // eslint-disable-next-line\n        } catch (e) {\n            return false;\n        }\n    }\n\n    Object.assign(chroma, {\n        Color: Color,\n        valid: valid,\n        css: css,\n        hex: hex,\n        hsl: hsl,\n        lab: lab,\n        oklab: oklab$1,\n        rgb: rgb,\n        mix: mix,\n        interpolate: mix\n    });\n\n    return chroma;\n\n}));\n"
  },
  {
    "path": "docs/libs/chroma-light.min.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n!function(t,r){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=r():\"function\"==typeof define&&define.amd?define(r):(t=\"undefined\"!=typeof globalThis?globalThis:t||self).chroma=r()}(this,(function(){\"use strict\";var t=Math.min,r=Math.max;function n(n,e,o){return void 0===o&&(o=1),t(r(e,n),o)}for(var e={},o=0,a=[\"Boolean\",\"Number\",\"String\",\"Function\",\"Array\",\"Date\",\"RegExp\",\"Undefined\",\"Null\"];o<a.length;o+=1){var i=a[o];e[\"[object \"+i+\"]\"]=i.toLowerCase()}function u(t){return e[Object.prototype.toString.call(t)]||\"object\"}function l(t,r){return void 0===r&&(r=null),t.length>=3?Array.prototype.slice.call(t):\"object\"==u(t[0])&&r?r.split(\"\").filter((function(r){return void 0!==t[0][r]})).map((function(r){return t[0][r]})):t[0].slice(0)}function c(t){if(t.length<2)return null;var r=t.length-1;return\"string\"==u(t[r])?t[r].toLowerCase():null}var h=Math.PI,s=Math.min,f=Math.max,g=function(t){return Math.round(100*t)/100},p=function(t){return Math.round(100*t)/100},m=h/180,v=180/h,b={format:{},autodetect:[]},d=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var e=this;if(\"object\"===u(t[0])&&t[0].constructor&&t[0].constructor===this.constructor)return t[0];var o=c(t),a=!1;if(!o){a=!0,b.sorted||(b.autodetect=b.autodetect.sort((function(t,r){return r.p-t.p})),b.sorted=!0);for(var i=0,l=b.autodetect;i<l.length;i+=1){var h=l[i];if(o=h.test.apply(h,t))break}}if(!b.format[o])throw new Error(\"unknown format: \"+t);var s=b.format[o].apply(null,a?t:t.slice(0,-1));e._rgb=function(t){t._clipped=!1,t._unclipped=t.slice(0);for(var r=0;r<=3;r++)r<3?((t[r]<0||t[r]>255)&&(t._clipped=!0),t[r]=n(t[r],0,255)):3===r&&(t[r]=n(t[r],0,1));return t}(s),3===e._rgb.length&&e._rgb.push(1)};d.prototype.toString=function(){return\"function\"==u(this.hex)?this.hex():\"[\"+this._rgb.join(\",\")+\"]\"};var y=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t)))};y.version=\"3.2.0\";var w=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n,e,o=(t=l(t,\"rgba\"))[0],a=t[1],i=t[2],u=s(o/=255,a/=255,i/=255),c=f(o,a,i),h=(c+u)/2;return c===u?(n=0,e=Number.NaN):n=h<.5?(c-u)/(c+u):(c-u)/(2-c-u),o==c?e=(a-i)/(c-u):a==c?e=2+(i-o)/(c-u):i==c&&(e=4+(o-a)/(c-u)),(e*=60)<0&&(e+=360),t.length>3&&void 0!==t[3]?[e,n,h,t[3]]:[e,n,h]},M={Kn:18,labWhitePoint:\"d65\",Xn:.95047,Yn:1,Zn:1.08883,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},k=new Map([[\"a\",[1.0985,.35585]],[\"b\",[1.0985,.35585]],[\"c\",[.98074,1.18232]],[\"d50\",[.96422,.82521]],[\"d55\",[.95682,.92149]],[\"d65\",[.95047,1.08883]],[\"e\",[1,1,1]],[\"f2\",[.99186,.67393]],[\"f7\",[.95041,1.08747]],[\"f11\",[1.00962,.6435]],[\"icc\",[.96422,.82521]]]);function x(t){var r=k.get(String(t).toLowerCase());if(!r)throw new Error(\"unknown Lab illuminant \"+t);M.labWhitePoint=t,M.Xn=r[0],M.Zn=r[1]}function j(){return M.labWhitePoint}var _=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgb\"),e=n[0],o=n[1],a=n[2],i=n.slice(3),u=A(e,o,a),c=function(t,r,n){var e=M.Xn,o=M.Yn,a=M.Zn,i=M.kE,u=M.kK,l=t/e,c=r/o,h=n/a,s=l>i?Math.pow(l,1/3):(u*l+16)/116,f=c>i?Math.pow(c,1/3):(u*c+16)/116,g=h>i?Math.pow(h,1/3):(u*h+16)/116;return[116*f-16,500*(s-f),200*(f-g)]}(u[0],u[1],u[2]);return[c[0],c[1],c[2]].concat(i.length>0&&i[0]<1?[i[0]]:[])};function E(t){var r=Math.sign(t);return((t=Math.abs(t))<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4))*r}var A=function(t,r,n){t=E(t/255),r=E(r/255),n=E(n/255);var e=M.MtxRGB2XYZ,o=M.MtxAdaptMa,a=M.MtxAdaptMaI,i=M.Xn,u=M.Yn,l=M.Zn,c=M.As,h=M.Bs,s=M.Cs,f=t*e.m00+r*e.m10+n*e.m20,g=t*e.m01+r*e.m11+n*e.m21,p=t*e.m02+r*e.m12+n*e.m22,m=i*o.m00+u*o.m10+l*o.m20,v=i*o.m01+u*o.m11+l*o.m21,b=i*o.m02+u*o.m12+l*o.m22,d=f*o.m00+g*o.m10+p*o.m20,y=f*o.m01+g*o.m11+p*o.m21,w=f*o.m02+g*o.m12+p*o.m22;return y*=v/h,w*=b/s,[f=(d*=m/c)*a.m00+y*a.m10+w*a.m20,g=d*a.m01+y*a.m11+w*a.m21,p=d*a.m02+y*a.m12+w*a.m22]},R=Math.sqrt,F=Math.atan2,N=Math.round,X=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lab\"),e=n[0],o=n[1],a=n[2],i=R(o*o+a*a),u=(F(a,o)*v+360)%360;return 0===N(1e4*i)&&(u=Number.NaN),[e,i,u]};function Z(t,r){var n=t.length;Array.isArray(t[0])||(t=[t]),Array.isArray(r[0])||(r=r.map((function(t){return[t]})));var e=r[0].length,o=r[0].map((function(t,n){return r.map((function(t){return t[n]}))})),a=t.map((function(t){return o.map((function(r){return Array.isArray(t)?t.reduce((function(t,n,e){return t+n*(r[e]||0)}),0):r.reduce((function(r,n){return r+n*t}),0)}))}));return 1===n&&(a=a[0]),1===e?a.map((function(t){return t[0]})):a}var Y=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n,e,o=l(t,\"rgb\"),a=o[0],i=o[1],u=o[2],c=o.slice(3),h=A(a,i,u);return(n=[[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],e=Z([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],h),Z(n,e.map((function(t){return Math.cbrt(t)})))).concat(c.length>0&&c[0]<1?[c[0]]:[])};var $=Math.round,B=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgba\"),e=c(t)||\"rgb\";if(\"hsl\"===e.substr(0,3))return function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"hsla\"),e=c(t)||\"lsa\";return n[0]=g(n[0]||0)+\"deg\",n[1]=g(100*n[1])+\"%\",n[2]=g(100*n[2])+\"%\",\"hsla\"===e||n.length>3&&n[3]<1?(n[3]=\"/ \"+(n.length>3?n[3]:1),e=\"hsla\"):n.length=3,e.substr(0,3)+\"(\"+n.join(\" \")+\")\"}(w(n),e);if(\"lab\"===e.substr(0,3)){var o=j();x(\"d50\");var a=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lab\"),e=c(t)||\"lab\";return n[0]=g(n[0])+\"%\",n[1]=g(n[1]),n[2]=g(n[2]),\"laba\"===e||n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"lab(\"+n.join(\" \")+\")\"}(_(n),e);return x(o),a}if(\"lch\"===e.substr(0,3)){var i=j();x(\"d50\");var u=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lch\"),e=c(t)||\"lab\";return n[0]=g(n[0])+\"%\",n[1]=g(n[1]),n[2]=isNaN(n[2])?\"none\":g(n[2])+\"deg\",\"lcha\"===e||n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"lch(\"+n.join(\" \")+\")\"}(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgb\"),e=n[0],o=n[1],a=n[2],i=n.slice(3),u=_(e,o,a),c=u[0],h=u[1],s=u[2],f=X(c,h,s);return[f[0],f[1],f[2]].concat(i.length>0&&i[0]<1?[i[0]]:[])}(n),e);return x(i),u}return\"oklab\"===e.substr(0,5)?function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lab\");return n[0]=g(100*n[0])+\"%\",n[1]=p(n[1]),n[2]=p(n[2]),n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"oklab(\"+n.join(\" \")+\")\"}(Y(n)):\"oklch\"===e.substr(0,5)?function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lch\");return n[0]=g(100*n[0])+\"%\",n[1]=p(n[1]),n[2]=isNaN(n[2])?\"none\":g(n[2])+\"deg\",n.length>3&&n[3]<1?n[3]=\"/ \"+(n.length>3?n[3]:1):n.length=3,\"oklch(\"+n.join(\" \")+\")\"}(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgb\"),e=n[0],o=n[1],a=n[2],i=n.slice(3),u=Y(e,o,a),c=u[0],h=u[1],s=u[2],f=X(c,h,s);return[f[0],f[1],f[2]].concat(i.length>0&&i[0]<1?[i[0]]:[])}(n)):(n[0]=$(n[0]),n[1]=$(n[1]),n[2]=$(n[2]),(\"rgba\"===e||n.length>3&&n[3]<1)&&(n[3]=\"/ \"+(n.length>3?n[3]:1),e=\"rgba\"),e.substr(0,3)+\"(\"+n.slice(0,\"rgb\"===e?3:4).join(\" \")+\")\")},C=function(){for(var t,r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,o,a,i=(r=l(r,\"hsl\"))[0],u=r[1],c=r[2];if(0===u)e=o=a=255*c;else{var h=[0,0,0],s=[0,0,0],f=c<.5?c*(1+u):c+u-c*u,g=2*c-f,p=i/360;h[0]=p+1/3,h[1]=p,h[2]=p-1/3;for(var m=0;m<3;m++)h[m]<0&&(h[m]+=1),h[m]>1&&(h[m]-=1),6*h[m]<1?s[m]=g+6*(f-g)*h[m]:2*h[m]<1?s[m]=f:3*h[m]<2?s[m]=g+(f-g)*(2/3-h[m])*6:s[m]=g;e=(t=[255*s[0],255*s[1],255*s[2]])[0],o=t[1],a=t[2]}return r.length>3?[e,o,a,r[3]]:[e,o,a,1]},O=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=(t=l(t,\"lab\"))[0],e=t[1],o=t[2],a=L(n,e,o),i=a[0],u=a[1],c=a[2],h=K(i,u,c);return[h[0],h[1],h[2],t.length>3?t[3]:1]},L=function(t,r,n){var e=M.kE,o=M.kK,a=M.kKE,i=M.Xn,u=M.Yn,l=M.Zn,c=(t+16)/116,h=.002*r+c,s=c-.005*n,f=h*h*h,g=s*s*s;return[(f>e?f:(116*h-16)/o)*i,(t>a?Math.pow((t+16)/116,3):t/o)*u,(g>e?g:(116*s-16)/o)*l]},W=function(t){var r=Math.sign(t);return((t=Math.abs(t))<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)*r},K=function(t,r,n){var e=M.MtxAdaptMa,o=M.MtxAdaptMaI,a=M.MtxXYZ2RGB,i=M.RefWhiteRGB,u=M.Xn,l=M.Yn,c=M.Zn,h=u*e.m00+l*e.m10+c*e.m20,s=u*e.m01+l*e.m11+c*e.m21,f=u*e.m02+l*e.m12+c*e.m22,g=i.X*e.m00+i.Y*e.m10+i.Z*e.m20,p=i.X*e.m01+i.Y*e.m11+i.Z*e.m21,m=i.X*e.m02+i.Y*e.m12+i.Z*e.m22,v=(t*e.m00+r*e.m10+n*e.m20)*(g/h),b=(t*e.m01+r*e.m11+n*e.m21)*(p/s),d=(t*e.m02+r*e.m12+n*e.m22)*(m/f),y=v*o.m00+b*o.m10+d*o.m20,w=v*o.m01+b*o.m11+d*o.m21,k=v*o.m02+b*o.m12+d*o.m22;return[255*W(y*a.m00+w*a.m10+k*a.m20),255*W(y*a.m01+w*a.m11+k*a.m21),255*W(y*a.m02+w*a.m12+k*a.m22)]},G=Math.sin,I=Math.cos,P=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"lch\"),e=n[0],o=n[1],a=n[2];return isNaN(a)&&(a=0),[e,I(a*=m)*o,G(a)*o]},S=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n,e,o=(t=l(t,\"lab\"))[0],a=t[1],i=t[2],u=t.slice(3),c=(n=[[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],e=Z([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],[o,a,i]),Z(n,e.map((function(t){return Math.pow(t,3)})))),h=c[0],s=c[1],f=c[2],g=K(h,s,f);return[g[0],g[1],g[2]].concat(u.length>0&&u[0]<1?[u[0]]:[])};var q=/((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source,T=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source,D=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source,U=/\\s*/.source,z=/\\s+/.source,H=/\\s*,\\s*/.source,J=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source,Q=/\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source,V=new RegExp(\"^rgba?\\\\(\"+U+[q,q,q].join(z)+Q+\"\\\\)$\"),tt=new RegExp(\"^rgb\\\\(\"+U+[q,q,q].join(H)+U+\"\\\\)$\"),rt=new RegExp(\"^rgba\\\\(\"+U+[q,q,q,T].join(H)+U+\"\\\\)$\"),nt=new RegExp(\"^hsla?\\\\(\"+U+[J,D,D].join(z)+Q+\"\\\\)$\"),et=new RegExp(\"^hsl?\\\\(\"+U+[J,D,D].join(H)+U+\"\\\\)$\"),ot=/^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/,at=new RegExp(\"^lab\\\\(\"+U+[T,T,T].join(z)+Q+\"\\\\)$\"),it=new RegExp(\"^lch\\\\(\"+U+[T,T,J].join(z)+Q+\"\\\\)$\"),ut=new RegExp(\"^oklab\\\\(\"+U+[T,T,T].join(z)+Q+\"\\\\)$\"),lt=new RegExp(\"^oklch\\\\(\"+U+[T,T,J].join(z)+Q+\"\\\\)$\"),ct=Math.round,ht=function(t){return t.map((function(t,r){return r<=2?n(ct(t),0,255):t}))},st=function(t,r,n,e){return void 0===r&&(r=0),void 0===n&&(n=100),void 0===e&&(e=!1),\"string\"==typeof t&&t.endsWith(\"%\")&&(t=parseFloat(t.substring(0,t.length-1))/100,t=e?r+.5*(t+1)*(n-r):r+t*(n-r)),+t},ft=function(t,r){return\"none\"===t?r:t},gt=function(t){if(\"transparent\"===(t=t.toLowerCase().trim()))return[0,0,0,0];var r;if(b.format.named)try{return b.format.named(t)}catch(t){}if((r=t.match(V))||(r=t.match(tt))){for(var n=r.slice(1,4),e=0;e<3;e++)n[e]=+st(ft(n[e],0),0,255);n=ht(n);var o=void 0!==r[4]?+st(r[4],0,1):1;return n[3]=o,n}if(r=t.match(rt)){for(var a=r.slice(1,5),i=0;i<4;i++)a[i]=+st(a[i],0,255);return a}if((r=t.match(nt))||(r=t.match(et))){var u=r.slice(1,4);u[0]=+ft(u[0].replace(\"deg\",\"\"),0),u[1]=.01*+st(ft(u[1],0),0,100),u[2]=.01*+st(ft(u[2],0),0,100);var c=ht(C(u)),h=void 0!==r[4]?+st(r[4],0,1):1;return c[3]=h,c}if(r=t.match(ot)){var s=r.slice(1,4);s[1]*=.01,s[2]*=.01;for(var f=C(s),g=0;g<3;g++)f[g]=ct(f[g]);return f[3]=+r[4],f}if(r=t.match(at)){var p=r.slice(1,4);p[0]=st(ft(p[0],0),0,100),p[1]=st(ft(p[1],0),-125,125,!0),p[2]=st(ft(p[2],0),-125,125,!0);var m=j();x(\"d50\");var v=ht(O(p));x(m);var d=void 0!==r[4]?+st(r[4],0,1):1;return v[3]=d,v}if(r=t.match(it)){var y=r.slice(1,4);y[0]=st(y[0],0,100),y[1]=st(ft(y[1],0),0,150,!1),y[2]=+ft(y[2].replace(\"deg\",\"\"),0);var w=j();x(\"d50\");var M=ht(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=(t=l(t,\"lch\"))[0],e=t[1],o=t[2],a=P(n,e,o),i=a[0],u=a[1],c=a[2],h=O(i,u,c);return[h[0],h[1],h[2],t.length>3?t[3]:1]}(y));x(w);var k=void 0!==r[4]?+st(r[4],0,1):1;return M[3]=k,M}if(r=t.match(ut)){var _=r.slice(1,4);_[0]=st(ft(_[0],0),0,1),_[1]=st(ft(_[1],0),-.4,.4,!0),_[2]=st(ft(_[2],0),-.4,.4,!0);var E=ht(S(_)),A=void 0!==r[4]?+st(r[4],0,1):1;return E[3]=A,E}if(r=t.match(lt)){var R=r.slice(1,4);R[0]=st(ft(R[0],0),0,1),R[1]=st(ft(R[1],0),0,.4,!1),R[2]=+ft(R[2].replace(\"deg\",\"\"),0);var F=ht(function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=(t=l(t,\"lch\"))[0],e=t[1],o=t[2],a=t.slice(3),i=P(n,e,o),u=i[0],c=i[1],h=i[2],s=S(u,c,h);return[s[0],s[1],s[2]].concat(a.length>0&&a[0]<1?[a[0]]:[])}(R)),N=void 0!==r[4]?+st(r[4],0,1):1;return F[3]=N,F}};gt.test=function(t){return V.test(t)||nt.test(t)||at.test(t)||it.test(t)||ut.test(t)||lt.test(t)||tt.test(t)||rt.test(t)||et.test(t)||ot.test(t)||\"transparent\"===t},d.prototype.css=function(t){return B(this._rgb,t)};var pt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"css\"])))};y.css=pt,b.format.css=gt,b.autodetect.push({p:5,test:function(t){for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];if(!r.length&&\"string\"===u(t)&&gt.test(t))return\"css\"}});var mt=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,vt=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,bt=Math.round;d.prototype.hex=function(t){return function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgba\"),e=n[0],o=n[1],a=n[2],i=n[3],u=c(t)||\"auto\";void 0===i&&(i=1),\"auto\"===u&&(u=i<1?\"rgba\":\"rgb\");var h=\"000000\"+((e=bt(e))<<16|(o=bt(o))<<8|(a=bt(a))).toString(16);h=h.substr(h.length-6);var s=\"0\"+bt(255*i).toString(16);switch(s=s.substr(s.length-2),u.toLowerCase()){case\"rgba\":return\"#\"+h+s;case\"argb\":return\"#\"+s+h;default:return\"#\"+h}}(this._rgb,t)};var dt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"hex\"])))};y.hex=dt,b.format.hex=function(t){if(t.match(mt)){4!==t.length&&7!==t.length||(t=t.substr(1)),3===t.length&&(t=(t=t.split(\"\"))[0]+t[0]+t[1]+t[1]+t[2]+t[2]);var r=parseInt(t,16);return[r>>16,r>>8&255,255&r,1]}if(t.match(vt)){5!==t.length&&9!==t.length||(t=t.substr(1)),4===t.length&&(t=(t=t.split(\"\"))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);var n=parseInt(t,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error(\"unknown hex color: \"+t)},b.autodetect.push({p:4,test:function(t){for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];if(!r.length&&\"string\"===u(t)&&[3,4,5,6,7,8,9].indexOf(t.length)>=0)return\"hex\"}}),d.prototype.hsl=function(){return w(this._rgb)};var yt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"hsl\"])))};y.hsl=yt,b.format.hsl=C,b.autodetect.push({p:2,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"hsl\"))&&3===t.length)return\"hsl\"}}),d.prototype.lab=function(){return _(this._rgb)};var wt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"lab\"])))};Object.assign(y,{lab:wt,getLabWhitePoint:j,setLabWhitePoint:x}),b.format.lab=O,b.autodetect.push({p:2,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"lab\"))&&3===t.length)return\"lab\"}}),d.prototype.oklab=function(){return Y(this._rgb)};var Mt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"oklab\"])))};Object.assign(y,{oklab:Mt}),b.format.oklab=S,b.autodetect.push({p:2,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"oklab\"))&&3===t.length)return\"oklab\"}});var kt=Math.round;d.prototype.rgb=function(t){return void 0===t&&(t=!0),!1===t?this._rgb.slice(0,3):this._rgb.slice(0,3).map(kt)},d.prototype.rgba=function(t){return void 0===t&&(t=!0),this._rgb.slice(0,4).map((function(r,n){return n<3?!1===t?r:kt(r):r}))};var xt=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];return new(Function.prototype.bind.apply(d,[null].concat(t,[\"rgb\"])))};Object.assign(y,{rgb:xt}),b.format.rgb=function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];var n=l(t,\"rgba\");return void 0===n[3]&&(n[3]=1),n},b.autodetect.push({p:3,test:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];if(\"array\"===u(t=l(t,\"rgba\"))&&(3===t.length||4===t.length&&\"number\"==u(t[3])&&t[3]>=0&&t[3]<=1))return\"rgb\"}}),d.prototype.alpha=function(t,r){return void 0===r&&(r=!1),void 0!==t&&\"number\"===u(t)?r?(this._rgb[3]=t,this):new d([this._rgb[0],this._rgb[1],this._rgb[2],t],\"rgb\"):this._rgb[3]},d.prototype.darken=function(t){void 0===t&&(t=1);var r=this.lab();return r[0]-=M.Kn*t,new d(r,\"lab\").alpha(this.alpha(),!0)},d.prototype.brighten=function(t){return void 0===t&&(t=1),this.darken(-t)},d.prototype.darker=d.prototype.darken,d.prototype.brighter=d.prototype.brighten,d.prototype.get=function(t){var r=t.split(\".\"),n=r[0],e=r[1],o=this[n]();if(e){var a=n.indexOf(e)-(\"ok\"===n.substr(0,2)?2:0);if(a>-1)return o[a];throw new Error(\"unknown channel \"+e+\" in mode \"+n)}return o};var jt={};function _t(t,r,n){void 0===n&&(n=.5);for(var e=[],o=arguments.length-3;o-- >0;)e[o]=arguments[o+3];var a=e[0]||\"lrgb\";if(jt[a]||e.length||(a=Object.keys(jt)[0]),!jt[a])throw new Error(\"interpolation mode \"+a+\" is not defined\");return\"object\"!==u(t)&&(t=new d(t)),\"object\"!==u(r)&&(r=new d(r)),jt[a](t,r,n).alpha(t.alpha()+n*(r.alpha()-t.alpha()))}d.prototype.mix=d.prototype.interpolate=function(t,r){void 0===r&&(r=.5);for(var n=[],e=arguments.length-2;e-- >0;)n[e]=arguments[e+2];return _t.apply(void 0,[this,t,r].concat(n))},d.prototype.set=function(t,r,n){void 0===n&&(n=!1);var e=t.split(\".\"),o=e[0],a=e[1],i=this[o]();if(a){var l=o.indexOf(a)-(\"ok\"===o.substr(0,2)?2:0);if(l>-1){if(\"string\"==u(r))switch(r.charAt(0)){case\"+\":case\"-\":i[l]+=+r;break;case\"*\":i[l]*=+r.substr(1);break;case\"/\":i[l]/=+r.substr(1);break;default:i[l]=+r}else{if(\"number\"!==u(r))throw new Error(\"unsupported value for Color.set\");i[l]=r}var c=new d(i,o);return n?(this._rgb=c._rgb,this):c}throw new Error(\"unknown channel \"+a+\" in mode \"+o)}return i},d.prototype.tint=function(t){void 0===t&&(t=.5);for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];return _t.apply(void 0,[this,\"white\",t].concat(r))},d.prototype.shade=function(t){void 0===t&&(t=.5);for(var r=[],n=arguments.length-1;n-- >0;)r[n]=arguments[n+1];return _t.apply(void 0,[this,\"black\",t].concat(r))};var Et=Math.sqrt,At=Math.pow;jt.lrgb=function(t,r,n){var e=t._rgb,o=e[0],a=e[1],i=e[2],u=r._rgb,l=u[0],c=u[1],h=u[2];return new d(Et(At(o,2)*(1-n)+At(l,2)*n),Et(At(a,2)*(1-n)+At(c,2)*n),Et(At(i,2)*(1-n)+At(h,2)*n),\"rgb\")};return jt.oklab=function(t,r,n){var e=t.oklab(),o=r.oklab();return new d(e[0]+n*(o[0]-e[0]),e[1]+n*(o[1]-e[1]),e[2]+n*(o[2]-e[2]),\"oklab\")},Object.assign(y,{Color:d,valid:function(){for(var t=[],r=arguments.length;r--;)t[r]=arguments[r];try{return new(Function.prototype.bind.apply(d,[null].concat(t))),!0}catch(t){return!1}},css:pt,hex:dt,hsl:yt,lab:wt,oklab:Mt,rgb:xt,mix:_t,interpolate:_t}),y}));\n"
  },
  {
    "path": "docs/libs/chroma.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.chroma = factory());\n})(this, (function () { 'use strict';\n\n    var min$4 = Math.min;\n    var max$4 = Math.max;\n\n    function limit (x, low, high) {\n        if ( low === void 0 ) low = 0;\n        if ( high === void 0 ) high = 1;\n\n        return min$4(max$4(low, x), high);\n    }\n\n    function clip_rgb (rgb) {\n        rgb._clipped = false;\n        rgb._unclipped = rgb.slice(0);\n        for (var i = 0; i <= 3; i++) {\n            if (i < 3) {\n                if (rgb[i] < 0 || rgb[i] > 255) { rgb._clipped = true; }\n                rgb[i] = limit(rgb[i], 0, 255);\n            } else if (i === 3) {\n                rgb[i] = limit(rgb[i], 0, 1);\n            }\n        }\n        return rgb;\n    }\n\n    // ported from jQuery's $.type\n    var classToType = {};\n    for (var i = 0, list = [\n        'Boolean',\n        'Number',\n        'String',\n        'Function',\n        'Array',\n        'Date',\n        'RegExp',\n        'Undefined',\n        'Null'\n    ]; i < list.length; i += 1) {\n        var name = list[i];\n\n        classToType[(\"[object \" + name + \"]\")] = name.toLowerCase();\n    }\n    function type (obj) {\n        return classToType[Object.prototype.toString.call(obj)] || 'object';\n    }\n\n    function unpack (args, keyOrder) {\n        if ( keyOrder === void 0 ) keyOrder = null;\n\n        // if called with more than 3 arguments, we return the arguments\n        if (args.length >= 3) { return Array.prototype.slice.call(args); }\n        // with less than 3 args we check if first arg is object\n        // and use the keyOrder string to extract and sort properties\n        if (type(args[0]) == 'object' && keyOrder) {\n            return keyOrder\n                .split('')\n                .filter(function (k) { return args[0][k] !== undefined; })\n                .map(function (k) { return args[0][k]; });\n        }\n        // otherwise we just return the first argument\n        // (which we suppose is an array of args)\n        return args[0].slice(0);\n    }\n\n    function last (args) {\n        if (args.length < 2) { return null; }\n        var l = args.length - 1;\n        if (type(args[l]) == 'string') { return args[l].toLowerCase(); }\n        return null;\n    }\n\n    var PI$2 = Math.PI;\n    var min$3 = Math.min;\n    var max$3 = Math.max;\n\n    var rnd2 = function (a) { return Math.round(a * 100) / 100; };\n    var rnd3 = function (a) { return Math.round(a * 100) / 100; };\n\n    var TWOPI = PI$2 * 2;\n    var PITHIRD = PI$2 / 3;\n    var DEG2RAD = PI$2 / 180;\n    var RAD2DEG = 180 / PI$2;\n\n    /**\n     * Reverse the first three elements of an array\n     *\n     * @param {any[]} arr\n     * @returns {any[]}\n     */\n    function reverse3(arr) {\n        return arr.slice(0, 3).reverse().concat( arr.slice(3));\n    }\n\n    var input = {\n        format: {},\n        autodetect: []\n    };\n\n    var Color = function Color() {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var me = this;\n        if (\n            type(args[0]) === 'object' &&\n            args[0].constructor &&\n            args[0].constructor === this.constructor\n        ) {\n            // the argument is already a Color instance\n            return args[0];\n        }\n        // last argument could be the mode\n        var mode = last(args);\n        var autodetect = false;\n        if (!mode) {\n            autodetect = true;\n\n            if (!input.sorted) {\n                input.autodetect = input.autodetect.sort(function (a, b) { return b.p - a.p; });\n                input.sorted = true;\n            }\n\n            // auto-detect format\n            for (var i = 0, list = input.autodetect; i < list.length; i += 1) {\n                var chk = list[i];\n\n                mode = chk.test.apply(chk, args);\n                if (mode) { break; }\n            }\n        }\n        if (input.format[mode]) {\n            var rgb = input.format[mode].apply(\n                null,\n                autodetect ? args : args.slice(0, -1)\n            );\n            me._rgb = clip_rgb(rgb);\n        } else {\n            throw new Error('unknown format: ' + args);\n        }\n        // add alpha channel\n        if (me._rgb.length === 3) { me._rgb.push(1); }\n    };\n    Color.prototype.toString = function toString () {\n        if (type(this.hex) == 'function') { return this.hex(); }\n        return (\"[\" + (this._rgb.join(',')) + \"]\");\n    };\n\n    // this gets updated automatically\n    var version = '3.2.0';\n\n    var chroma = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n    };\n\n    chroma.version = version;\n\n    var cmyk2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'cmyk');\n        var c = args[0];\n        var m = args[1];\n        var y = args[2];\n        var k = args[3];\n        var alpha = args.length > 4 ? args[4] : 1;\n        if (k === 1) { return [0, 0, 0, alpha]; }\n        return [\n            c >= 1 ? 0 : 255 * (1 - c) * (1 - k), // r\n            m >= 1 ? 0 : 255 * (1 - m) * (1 - k), // g\n            y >= 1 ? 0 : 255 * (1 - y) * (1 - k), // b\n            alpha\n        ];\n    };\n\n    var max$2 = Math.max;\n\n    var rgb2cmyk = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        r = r / 255;\n        g = g / 255;\n        b = b / 255;\n        var k = 1 - max$2(r, max$2(g, b));\n        var f = k < 1 ? 1 / (1 - k) : 0;\n        var c = (1 - r - k) * f;\n        var m = (1 - g - k) * f;\n        var y = (1 - b - k) * f;\n        return [c, m, y, k];\n    };\n\n    Color.prototype.cmyk = function () {\n        return rgb2cmyk(this._rgb);\n    };\n\n    var cmyk = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['cmyk']) ));\n    };\n    Object.assign(chroma, { cmyk: cmyk });\n\n    input.format.cmyk = cmyk2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'cmyk');\n            if (type(args) === 'array' && args.length === 4) {\n                return 'cmyk';\n            }\n        }\n    });\n\n    /*\n     * supported arguments:\n     * - hsl2css(h,s,l)\n     * - hsl2css(h,s,l,a)\n     * - hsl2css([h,s,l], mode)\n     * - hsl2css([h,s,l,a], mode)\n     * - hsl2css({h,s,l,a}, mode)\n     */\n    var hsl2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var hsla = unpack(args, 'hsla');\n        var mode = last(args) || 'lsa';\n        hsla[0] = rnd2(hsla[0] || 0) + 'deg';\n        hsla[1] = rnd2(hsla[1] * 100) + '%';\n        hsla[2] = rnd2(hsla[2] * 100) + '%';\n        if (mode === 'hsla' || (hsla.length > 3 && hsla[3] < 1)) {\n            hsla[3] = '/ ' + (hsla.length > 3 ? hsla[3] : 1);\n            mode = 'hsla';\n        } else {\n            hsla.length = 3;\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (hsla.join(' ')) + \")\");\n    };\n\n    /*\n     * supported arguments:\n     * - rgb2hsl(r,g,b)\n     * - rgb2hsl(r,g,b,a)\n     * - rgb2hsl([r,g,b])\n     * - rgb2hsl([r,g,b,a])\n     * - rgb2hsl({r,g,b,a})\n     */\n    var rgb2hsl$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'rgba');\n        var r = args[0];\n        var g = args[1];\n        var b = args[2];\n\n        r /= 255;\n        g /= 255;\n        b /= 255;\n\n        var minRgb = min$3(r, g, b);\n        var maxRgb = max$3(r, g, b);\n\n        var l = (maxRgb + minRgb) / 2;\n        var s, h;\n\n        if (maxRgb === minRgb) {\n            s = 0;\n            h = Number.NaN;\n        } else {\n            s =\n                l < 0.5\n                    ? (maxRgb - minRgb) / (maxRgb + minRgb)\n                    : (maxRgb - minRgb) / (2 - maxRgb - minRgb);\n        }\n\n        if (r == maxRgb) { h = (g - b) / (maxRgb - minRgb); }\n        else if (g == maxRgb) { h = 2 + (b - r) / (maxRgb - minRgb); }\n        else if (b == maxRgb) { h = 4 + (r - g) / (maxRgb - minRgb); }\n\n        h *= 60;\n        if (h < 0) { h += 360; }\n        if (args.length > 3 && args[3] !== undefined) { return [h, s, l, args[3]]; }\n        return [h, s, l];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        var mode = last(args) || 'lab';\n        laba[0] = rnd2(laba[0]) + '%';\n        laba[1] = rnd2(laba[1]);\n        laba[2] = rnd2(laba[2]);\n        if (mode === 'laba' || (laba.length > 3 && laba[3] < 1)) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"lab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var labConstants = {\n        // Corresponds roughly to RGB brighter/darker\n        Kn: 18,\n\n        // D65 standard referent\n        labWhitePoint: 'd65',\n        Xn: 0.95047,\n        Yn: 1,\n        Zn: 1.08883,\n\n        kE: 216.0 / 24389.0,\n        kKE: 8.0,\n        kK: 24389.0 / 27.0,\n\n        RefWhiteRGB: {\n            // sRGB\n            X: 0.95047,\n            Y: 1,\n            Z: 1.08883\n        },\n\n        MtxRGB2XYZ: {\n            m00: 0.4124564390896922,\n            m01: 0.21267285140562253,\n            m02: 0.0193338955823293,\n            m10: 0.357576077643909,\n            m11: 0.715152155287818,\n            m12: 0.11919202588130297,\n            m20: 0.18043748326639894,\n            m21: 0.07217499330655958,\n            m22: 0.9503040785363679\n        },\n\n        MtxXYZ2RGB: {\n            m00: 3.2404541621141045,\n            m01: -0.9692660305051868,\n            m02: 0.055643430959114726,\n            m10: -1.5371385127977166,\n            m11: 1.8760108454466942,\n            m12: -0.2040259135167538,\n            m20: -0.498531409556016,\n            m21: 0.041556017530349834,\n            m22: 1.0572251882231791\n        },\n\n        // used in rgb2xyz\n        As: 0.9414285350000001,\n        Bs: 1.040417467,\n        Cs: 1.089532651,\n\n        MtxAdaptMa: {\n            m00: 0.8951,\n            m01: -0.7502,\n            m02: 0.0389,\n            m10: 0.2664,\n            m11: 1.7135,\n            m12: -0.0685,\n            m20: -0.1614,\n            m21: 0.0367,\n            m22: 1.0296\n        },\n\n        MtxAdaptMaI: {\n            m00: 0.9869929054667123,\n            m01: 0.43230526972339456,\n            m02: -0.008528664575177328,\n            m10: -0.14705425642099013,\n            m11: 0.5183602715367776,\n            m12: 0.04004282165408487,\n            m20: 0.15996265166373125,\n            m21: 0.0492912282128556,\n            m22: 0.9684866957875502\n        }\n    };\n\n    // taken from https://de.mathworks.com/help/images/ref/whitepoint.html\n    var ILLUMINANTS = new Map([\n        // ASTM E308-01\n        ['a', [1.0985, 0.35585]],\n        // Wyszecki & Stiles, p. 769\n        ['b', [1.0985, 0.35585]],\n        // C ASTM E308-01\n        ['c', [0.98074, 1.18232]],\n        // D50 (ASTM E308-01)\n        ['d50', [0.96422, 0.82521]],\n        // D55 (ASTM E308-01)\n        ['d55', [0.95682, 0.92149]],\n        // D65 (ASTM E308-01)\n        ['d65', [0.95047, 1.08883]],\n        // E (ASTM E308-01)\n        ['e', [1, 1, 1]],\n        // F2 (ASTM E308-01)\n        ['f2', [0.99186, 0.67393]],\n        // F7 (ASTM E308-01)\n        ['f7', [0.95041, 1.08747]],\n        // F11 (ASTM E308-01)\n        ['f11', [1.00962, 0.6435]],\n        ['icc', [0.96422, 0.82521]]\n    ]);\n\n    function setLabWhitePoint(name) {\n        var ill = ILLUMINANTS.get(String(name).toLowerCase());\n        if (!ill) {\n            throw new Error('unknown Lab illuminant ' + name);\n        }\n        labConstants.labWhitePoint = name;\n        labConstants.Xn = ill[0];\n        labConstants.Zn = ill[1];\n    }\n\n    function getLabWhitePoint() {\n        return labConstants.labWhitePoint;\n    }\n\n    var rgb2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2xyz(r, g, b);\n        var x = ref$1[0];\n        var y = ref$1[1];\n        var z = ref$1[2];\n        var ref$2 = xyz2lab(x, y, z);\n        var L = ref$2[0];\n        var a = ref$2[1];\n        var b_ = ref$2[2];\n        return [L, a, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    function xyz2lab(x, y, z) {\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var xr = x / Xn;\n        var yr = y / Yn;\n        var zr = z / Zn;\n\n        var fx = xr > kE ? Math.pow(xr, 1.0 / 3.0) : (kK * xr + 16.0) / 116.0;\n        var fy = yr > kE ? Math.pow(yr, 1.0 / 3.0) : (kK * yr + 16.0) / 116.0;\n        var fz = zr > kE ? Math.pow(zr, 1.0 / 3.0) : (kK * zr + 16.0) / 116.0;\n\n        return [116.0 * fy - 16.0, 500.0 * (fx - fy), 200.0 * (fy - fz)];\n    }\n\n    function gammaAdjustSRGB(companded) {\n        var sign = Math.sign(companded);\n        companded = Math.abs(companded);\n        var linear =\n            companded <= 0.04045\n                ? companded / 12.92\n                : Math.pow((companded + 0.055) / 1.055, 2.4);\n        return linear * sign;\n    }\n\n    var rgb2xyz = function (r, g, b) {\n        // normalize and gamma adjust\n        r = gammaAdjustSRGB(r / 255);\n        g = gammaAdjustSRGB(g / 255);\n        b = gammaAdjustSRGB(b / 255);\n\n        var MtxRGB2XYZ = labConstants.MtxRGB2XYZ;\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n        var As = labConstants.As;\n        var Bs = labConstants.Bs;\n        var Cs = labConstants.Cs;\n\n        var x = r * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;\n        var y = r * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;\n        var z = r * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;\n\n        var Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20;\n        var Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21;\n        var Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22;\n\n        X *= Ad / As;\n        Y *= Bd / Bs;\n        Z *= Cd / Cs;\n\n        x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;\n        y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;\n        z = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;\n\n        return [x, y, z];\n    };\n\n    /*\n     * supported arguments:\n     * - lab2css(l,a,b)\n     * - lab2css(l,a,b,alpha)\n     * - lab2css([l,a,b], mode)\n     * - lab2css([l,a,b,alpha], mode)\n     */\n    var lch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        var mode = last(args) || 'lab';\n        lcha[0] = rnd2(lcha[0]) + '%';\n        lcha[1] = rnd2(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (mode === 'lcha' || (lcha.length > 3 && lcha[3] < 1)) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"lch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var sqrt$4 = Math.sqrt;\n    var atan2$2 = Math.atan2;\n    var round$5 = Math.round;\n\n    var lab2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'lab');\n        var l = ref[0];\n        var a = ref[1];\n        var b = ref[2];\n        var c = sqrt$4(a * a + b * b);\n        var h = (atan2$2(b, a) * RAD2DEG + 360) % 360;\n        if (round$5(c * 10000) === 0) { h = Number.NaN; }\n        return [l, c, h];\n    };\n\n    var rgb2lch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2lab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/multiply-matrices.js\n    function multiplyMatrices(A, B) {\n        var m = A.length;\n\n        if (!Array.isArray(A[0])) {\n            // A is vector, convert to [[a, b, c, ...]]\n            A = [A];\n        }\n\n        if (!Array.isArray(B[0])) {\n            // B is vector, convert to [[a], [b], [c], ...]]\n            B = B.map(function (x) { return [x]; });\n        }\n\n        var p = B[0].length;\n        var B_cols = B[0].map(function (_, i) { return B.map(function (x) { return x[i]; }); }); // transpose B\n        var product = A.map(function (row) { return B_cols.map(function (col) {\n                if (!Array.isArray(row)) {\n                    return col.reduce(function (a, c) { return a + c * row; }, 0);\n                }\n\n                return row.reduce(function (a, c, i) { return a + c * (col[i] || 0); }, 0);\n            }); }\n        );\n\n        if (m === 1) {\n            product = product[0]; // Avoid [[a, b, c, ...]]\n        }\n\n        if (p === 1) {\n            return product.map(function (x) { return x[0]; }); // Avoid [[a], [b], [c], ...]]\n        }\n\n        return product;\n    }\n\n    var rgb2oklab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var xyz = rgb2xyz(r, g, b);\n        var oklab = XYZ_to_OKLab(xyz);\n        return oklab.concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function XYZ_to_OKLab(XYZ) {\n        // Given XYZ relative to D65, convert to OKLab\n        var XYZtoLMS = [\n            [0.819022437996703, 0.3619062600528904, -0.1288737815209879],\n            [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],\n            [0.0481771893596242, 0.2642395317527308, 0.6335478284694309]\n        ];\n        var LMStoOKLab = [\n            [0.210454268309314, 0.7936177747023054, -0.0040720430116193],\n            [1.9779985324311684, -2.42859224204858, 0.450593709617411],\n            [0.0259040424655478, 0.7827717124575296, -0.8086757549230774]\n        ];\n\n        var LMS = multiplyMatrices(XYZtoLMS, XYZ);\n        // JavaScript Math.cbrt returns a sign-matched cube root\n        // beware if porting to other languages\n        // especially if tempted to use a general power function\n        return multiplyMatrices(\n            LMStoOKLab,\n            LMS.map(function (c) { return Math.cbrt(c); })\n        );\n        // L in range [0,1]. For use in CSS, multiply by 100 and add a percent\n    }\n\n    var oklab2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var laba = unpack(args, 'lab');\n        laba[0] = rnd2(laba[0] * 100) + '%';\n        laba[1] = rnd3(laba[1]);\n        laba[2] = rnd3(laba[2]);\n        if (laba.length > 3 && laba[3] < 1) {\n            laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n        } else {\n            laba.length = 3;\n        }\n        return (\"oklab(\" + (laba.join(' ')) + \")\");\n    };\n\n    var rgb2oklch = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var rest = ref.slice(3);\n        var ref$1 = rgb2oklab(r, g, b);\n        var l = ref$1[0];\n        var a = ref$1[1];\n        var b_ = ref$1[2];\n        var ref$2 = lab2lch(l, a, b_);\n        var L = ref$2[0];\n        var c = ref$2[1];\n        var h = ref$2[2];\n        return [L, c, h ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var oklch2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var lcha = unpack(args, 'lch');\n        lcha[0] = rnd2(lcha[0] * 100) + '%';\n        lcha[1] = rnd3(lcha[1]);\n        lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n        if (lcha.length > 3 && lcha[3] < 1) {\n            lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n        } else {\n            lcha.length = 3;\n        }\n        return (\"oklch(\" + (lcha.join(' ')) + \")\");\n    };\n\n    var round$4 = Math.round;\n\n    /*\n     * supported arguments:\n     * - rgb2css(r,g,b)\n     * - rgb2css(r,g,b,a)\n     * - rgb2css([r,g,b], mode)\n     * - rgb2css([r,g,b,a], mode)\n     * - rgb2css({r,g,b,a}, mode)\n     */\n    var rgb2css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        var mode = last(args) || 'rgb';\n        if (mode.substr(0, 3) === 'hsl') {\n            return hsl2css(rgb2hsl$1(rgba), mode);\n        }\n        if (mode.substr(0, 3) === 'lab') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor = lab2css(rgb2lab(rgba), mode);\n            setLabWhitePoint(prevWhitePoint);\n            return cssColor;\n        }\n        if (mode.substr(0, 3) === 'lch') {\n            // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n            var prevWhitePoint$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var cssColor$1 = lch2css(rgb2lch(rgba), mode);\n            setLabWhitePoint(prevWhitePoint$1);\n            return cssColor$1;\n        }\n        if (mode.substr(0, 5) === 'oklab') {\n            return oklab2css(rgb2oklab(rgba));\n        }\n        if (mode.substr(0, 5) === 'oklch') {\n            return oklch2css(rgb2oklch(rgba));\n        }\n        rgba[0] = round$4(rgba[0]);\n        rgba[1] = round$4(rgba[1]);\n        rgba[2] = round$4(rgba[2]);\n        if (mode === 'rgba' || (rgba.length > 3 && rgba[3] < 1)) {\n            rgba[3] = '/ ' + (rgba.length > 3 ? rgba[3] : 1);\n            mode = 'rgba';\n        }\n        return ((mode.substr(0, 3)) + \"(\" + (rgba.slice(0, mode === 'rgb' ? 3 : 4).join(' ')) + \")\");\n    };\n\n    var hsl2rgb = function () {\n        var assign;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hsl');\n        var h = args[0];\n        var s = args[1];\n        var l = args[2];\n        var r, g, b;\n        if (s === 0) {\n            r = g = b = l * 255;\n        } else {\n            var t3 = [0, 0, 0];\n            var c = [0, 0, 0];\n            var t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;\n            var t1 = 2 * l - t2;\n            var h_ = h / 360;\n            t3[0] = h_ + 1 / 3;\n            t3[1] = h_;\n            t3[2] = h_ - 1 / 3;\n            for (var i = 0; i < 3; i++) {\n                if (t3[i] < 0) { t3[i] += 1; }\n                if (t3[i] > 1) { t3[i] -= 1; }\n                if (6 * t3[i] < 1) { c[i] = t1 + (t2 - t1) * 6 * t3[i]; }\n                else if (2 * t3[i] < 1) { c[i] = t2; }\n                else if (3 * t3[i] < 2) { c[i] = t1 + (t2 - t1) * (2 / 3 - t3[i]) * 6; }\n                else { c[i] = t1; }\n            }\n            (assign = [c[0] * 255, c[1] * 255, c[2] * 255], r = assign[0], g = assign[1], b = assign[2]);\n        }\n        if (args.length > 3) {\n            // keep alpha channel\n            return [r, g, b, args[3]];\n        }\n        return [r, g, b, 1];\n    };\n\n    /*\n     * L* [0..100]\n     * a [-100..100]\n     * b [-100..100]\n     */\n    var lab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var ref = lab2xyz(L, a, b);\n        var x = ref[0];\n        var y = ref[1];\n        var z = ref[2];\n        var ref$1 = xyz2rgb(x, y, z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_, args.length > 3 ? args[3] : 1];\n    };\n\n    var lab2xyz = function (L, a, b) {\n        var kE = labConstants.kE;\n        var kK = labConstants.kK;\n        var kKE = labConstants.kKE;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var fy = (L + 16.0) / 116.0;\n        var fx = 0.002 * a + fy;\n        var fz = fy - 0.005 * b;\n\n        var fx3 = fx * fx * fx;\n        var fz3 = fz * fz * fz;\n\n        var xr = fx3 > kE ? fx3 : (116.0 * fx - 16.0) / kK;\n        var yr = L > kKE ? Math.pow((L + 16.0) / 116.0, 3.0) : L / kK;\n        var zr = fz3 > kE ? fz3 : (116.0 * fz - 16.0) / kK;\n\n        var x = xr * Xn;\n        var y = yr * Yn;\n        var z = zr * Zn;\n\n        return [x, y, z];\n    };\n\n    var compand = function (linear) {\n        /* sRGB */\n        var sign = Math.sign(linear);\n        linear = Math.abs(linear);\n        return (\n            (linear <= 0.0031308\n                ? linear * 12.92\n                : 1.055 * Math.pow(linear, 1.0 / 2.4) - 0.055) * sign\n        );\n    };\n\n    var xyz2rgb = function (x, y, z) {\n        var MtxAdaptMa = labConstants.MtxAdaptMa;\n        var MtxAdaptMaI = labConstants.MtxAdaptMaI;\n        var MtxXYZ2RGB = labConstants.MtxXYZ2RGB;\n        var RefWhiteRGB = labConstants.RefWhiteRGB;\n        var Xn = labConstants.Xn;\n        var Yn = labConstants.Yn;\n        var Zn = labConstants.Zn;\n\n        var As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n        var Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n        var Cs = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n        var Ad =\n            RefWhiteRGB.X * MtxAdaptMa.m00 +\n            RefWhiteRGB.Y * MtxAdaptMa.m10 +\n            RefWhiteRGB.Z * MtxAdaptMa.m20;\n        var Bd =\n            RefWhiteRGB.X * MtxAdaptMa.m01 +\n            RefWhiteRGB.Y * MtxAdaptMa.m11 +\n            RefWhiteRGB.Z * MtxAdaptMa.m21;\n        var Cd =\n            RefWhiteRGB.X * MtxAdaptMa.m02 +\n            RefWhiteRGB.Y * MtxAdaptMa.m12 +\n            RefWhiteRGB.Z * MtxAdaptMa.m22;\n\n        var X1 =\n            (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20) *\n            (Ad / As);\n        var Y1 =\n            (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21) *\n            (Bd / Bs);\n        var Z1 =\n            (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22) *\n            (Cd / Cs);\n\n        var X2 =\n            X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;\n        var Y2 =\n            X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;\n        var Z2 =\n            X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;\n\n        var r = compand(\n            X2 * MtxXYZ2RGB.m00 + Y2 * MtxXYZ2RGB.m10 + Z2 * MtxXYZ2RGB.m20\n        );\n        var g = compand(\n            X2 * MtxXYZ2RGB.m01 + Y2 * MtxXYZ2RGB.m11 + Z2 * MtxXYZ2RGB.m21\n        );\n        var b = compand(\n            X2 * MtxXYZ2RGB.m02 + Y2 * MtxXYZ2RGB.m12 + Z2 * MtxXYZ2RGB.m22\n        );\n\n        return [r * 255, g * 255, b * 255];\n    };\n\n    var sin$3 = Math.sin;\n    var cos$4 = Math.cos;\n\n    var lch2lab = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n        These formulas were invented by David Dalrymple to obtain maximum contrast without going\n        out of gamut if the parameters are in the range 0-1.\n\n        A saturation multiplier was added by Gregor Aisch\n        */\n        var ref = unpack(args, 'lch');\n        var l = ref[0];\n        var c = ref[1];\n        var h = ref[2];\n        if (isNaN(h)) { h = 0; }\n        h = h * DEG2RAD;\n        return [l, cos$4(h) * c, sin$3(h) * c];\n    };\n\n    var lch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = lab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var oklab2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lab');\n        var L = args[0];\n        var a = args[1];\n        var b = args[2];\n        var rest = args.slice(3);\n        var ref = OKLab_to_XYZ([L, a, b]);\n        var X = ref[0];\n        var Y = ref[1];\n        var Z = ref[2];\n        var ref$1 = xyz2rgb(X, Y, Z);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b_ = ref$1[2];\n        return [r, g, b_ ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    // from https://www.w3.org/TR/css-color-4/#color-conversion-code\n    function OKLab_to_XYZ(OKLab) {\n        // Given OKLab, convert to XYZ relative to D65\n        var LMStoXYZ = [\n            [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],\n            [-0.0405757452148008, 1.112286803280317, -0.0717110580655164],\n            [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]\n        ];\n        var OKLabtoLMS = [\n            [1.0, 0.3963377773761749, 0.2158037573099136],\n            [1.0, -0.1055613458156586, -0.0638541728258133],\n            [1.0, -0.0894841775298119, -1.2914855480194092]\n        ];\n\n        var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab);\n        return multiplyMatrices(\n            LMStoXYZ,\n            LMSnl.map(function (c) { return Math.pow( c, 3 ); })\n        );\n    }\n\n    var oklch2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'lch');\n        var l = args[0];\n        var c = args[1];\n        var h = args[2];\n        var rest = args.slice(3);\n        var ref = lch2lab(l, c, h);\n        var L = ref[0];\n        var a = ref[1];\n        var b_ = ref[2];\n        var ref$1 = oklab2rgb(L, a, b_);\n        var r = ref$1[0];\n        var g = ref$1[1];\n        var b = ref$1[2];\n        return [r, g, b ].concat( (rest.length > 0 && rest[0] < 1 ? [rest[0]] : []));\n    };\n\n    var INT_OR_PCT = /((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source;\n    var FLOAT_OR_PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source;\n    var PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source;\n    var RE_S = /\\s*/.source;\n    var SEP = /\\s+/.source;\n    var COMMA = /\\s*,\\s*/.source;\n    var ANLGE = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source;\n    var ALPHA = /\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source;\n\n    // e.g. rgb(250 20 0), rgb(100% 50% 20%), rgb(100% 50% 20% / 0.5)\n    var RE_RGB = new RegExp(\n        '^rgba?\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_RGB_LEGACY = new RegExp(\n        '^rgb\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n    var RE_RGBA_LEGACY = new RegExp(\n        '^rgba\\\\(' +\n            RE_S +\n            [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT, FLOAT_OR_PCT].join(COMMA) +\n            RE_S +\n            '\\\\)$'\n    );\n\n    var RE_HSL = new RegExp(\n        '^hsla?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(SEP) + ALPHA + '\\\\)$'\n    );\n    var RE_HSL_LEGACY = new RegExp(\n        '^hsl?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(COMMA) + RE_S + '\\\\)$'\n    );\n    var RE_HSLA_LEGACY =\n        /^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/;\n\n    var RE_LAB = new RegExp(\n        '^lab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_LCH = new RegExp(\n        '^lch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLAB = new RegExp(\n        '^oklab\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n    var RE_OKLCH = new RegExp(\n        '^oklch\\\\(' +\n            RE_S +\n            [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n            ALPHA +\n            '\\\\)$'\n    );\n\n    var round$3 = Math.round;\n\n    var roundRGB = function (rgb) {\n        return rgb.map(function (v, i) { return (i <= 2 ? limit(round$3(v), 0, 255) : v); });\n    };\n\n    var percentToAbsolute = function (pct, min, max, signed) {\n        if ( min === void 0 ) min = 0;\n        if ( max === void 0 ) max = 100;\n        if ( signed === void 0 ) signed = false;\n\n        if (typeof pct === 'string' && pct.endsWith('%')) {\n            pct = parseFloat(pct.substring(0, pct.length - 1)) / 100;\n            if (signed) {\n                // signed percentages are in the range -100% to 100%\n                pct = min + (pct + 1) * 0.5 * (max - min);\n            } else {\n                pct = min + pct * (max - min);\n            }\n        }\n        return +pct;\n    };\n\n    var noneToValue = function (v, noneValue) {\n        return v === 'none' ? noneValue : v;\n    };\n\n    var css2rgb = function (css) {\n        css = css.toLowerCase().trim();\n\n        if (css === 'transparent') {\n            return [0, 0, 0, 0];\n        }\n\n        var m;\n\n        if (input.format.named) {\n            try {\n                return input.format.named(css);\n                // eslint-disable-next-line\n            } catch (e) {}\n        }\n\n        // rgb(250 20 0) or rgb(250,20,0)\n        if ((m = css.match(RE_RGB)) || (m = css.match(RE_RGB_LEGACY))) {\n            var rgb = m.slice(1, 4);\n            for (var i = 0; i < 3; i++) {\n                rgb[i] = +percentToAbsolute(noneToValue(rgb[i], 0), 0, 255);\n            }\n            rgb = roundRGB(rgb);\n            var alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb[3] = alpha; // default alpha\n            return rgb;\n        }\n\n        // rgba(250,20,0,0.4)\n        if ((m = css.match(RE_RGBA_LEGACY))) {\n            var rgb$1 = m.slice(1, 5);\n            for (var i$1 = 0; i$1 < 4; i$1++) {\n                rgb$1[i$1] = +percentToAbsolute(rgb$1[i$1], 0, 255);\n            }\n            return rgb$1;\n        }\n\n        // hsl(0,100%,50%)\n        if ((m = css.match(RE_HSL)) || (m = css.match(RE_HSL_LEGACY))) {\n            var hsl = m.slice(1, 4);\n            hsl[0] = +noneToValue(hsl[0].replace('deg', ''), 0);\n            hsl[1] = +percentToAbsolute(noneToValue(hsl[1], 0), 0, 100) * 0.01;\n            hsl[2] = +percentToAbsolute(noneToValue(hsl[2], 0), 0, 100) * 0.01;\n            var rgb$2 = roundRGB(hsl2rgb(hsl));\n            var alpha$1 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$2[3] = alpha$1;\n            return rgb$2;\n        }\n\n        // hsla(0,100%,50%,0.5)\n        if ((m = css.match(RE_HSLA_LEGACY))) {\n            var hsl$1 = m.slice(1, 4);\n            hsl$1[1] *= 0.01;\n            hsl$1[2] *= 0.01;\n            var rgb$3 = hsl2rgb(hsl$1);\n            for (var i$2 = 0; i$2 < 3; i$2++) {\n                rgb$3[i$2] = round$3(rgb$3[i$2]);\n            }\n            rgb$3[3] = +m[4]; // default alpha = 1\n            return rgb$3;\n        }\n\n        if ((m = css.match(RE_LAB))) {\n            var lab = m.slice(1, 4);\n            lab[0] = percentToAbsolute(noneToValue(lab[0], 0), 0, 100);\n            lab[1] = percentToAbsolute(noneToValue(lab[1], 0), -125, 125, true);\n            lab[2] = percentToAbsolute(noneToValue(lab[2], 0), -125, 125, true);\n            // convert to D50 Lab whitepoint\n            var wp = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$4 = roundRGB(lab2rgb(lab));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp);\n            var alpha$2 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$4[3] = alpha$2;\n            return rgb$4;\n        }\n\n        if ((m = css.match(RE_LCH))) {\n            var lch = m.slice(1, 4);\n            lch[0] = percentToAbsolute(lch[0], 0, 100);\n            lch[1] = percentToAbsolute(noneToValue(lch[1], 0), 0, 150, false);\n            lch[2] = +noneToValue(lch[2].replace('deg', ''), 0);\n            // convert to D50 Lab whitepoint\n            var wp$1 = getLabWhitePoint();\n            setLabWhitePoint('d50');\n            var rgb$5 = roundRGB(lch2rgb(lch));\n            // convert back to original Lab whitepoint\n            setLabWhitePoint(wp$1);\n            var alpha$3 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$5[3] = alpha$3;\n            return rgb$5;\n        }\n\n        if ((m = css.match(RE_OKLAB))) {\n            var oklab = m.slice(1, 4);\n            oklab[0] = percentToAbsolute(noneToValue(oklab[0], 0), 0, 1);\n            oklab[1] = percentToAbsolute(noneToValue(oklab[1], 0), -0.4, 0.4, true);\n            oklab[2] = percentToAbsolute(noneToValue(oklab[2], 0), -0.4, 0.4, true);\n            var rgb$6 = roundRGB(oklab2rgb(oklab));\n            var alpha$4 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$6[3] = alpha$4;\n            return rgb$6;\n        }\n\n        if ((m = css.match(RE_OKLCH))) {\n            var oklch = m.slice(1, 4);\n            oklch[0] = percentToAbsolute(noneToValue(oklch[0], 0), 0, 1);\n            oklch[1] = percentToAbsolute(noneToValue(oklch[1], 0), 0, 0.4, false);\n            oklch[2] = +noneToValue(oklch[2].replace('deg', ''), 0);\n            var rgb$7 = roundRGB(oklch2rgb(oklch));\n            var alpha$5 = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n            rgb$7[3] = alpha$5;\n            return rgb$7;\n        }\n    };\n\n    css2rgb.test = function (s) {\n        return (\n            // modern\n            RE_RGB.test(s) ||\n            RE_HSL.test(s) ||\n            RE_LAB.test(s) ||\n            RE_LCH.test(s) ||\n            RE_OKLAB.test(s) ||\n            RE_OKLCH.test(s) ||\n            // legacy\n            RE_RGB_LEGACY.test(s) ||\n            RE_RGBA_LEGACY.test(s) ||\n            RE_HSL_LEGACY.test(s) ||\n            RE_HSLA_LEGACY.test(s) ||\n            s === 'transparent'\n        );\n    };\n\n    Color.prototype.css = function (mode) {\n        return rgb2css(this._rgb, mode);\n    };\n\n    var css = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['css']) ));\n    };\n    chroma.css = css;\n\n    input.format.css = css2rgb;\n\n    input.autodetect.push({\n        p: 5,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (!rest.length && type(h) === 'string' && css2rgb.test(h)) {\n                return 'css';\n            }\n        }\n    });\n\n    input.format.gl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgb = unpack(args, 'rgba');\n        rgb[0] *= 255;\n        rgb[1] *= 255;\n        rgb[2] *= 255;\n        return rgb;\n    };\n\n    var gl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['gl']) ));\n    };\n    chroma.gl = gl;\n\n    Color.prototype.gl = function () {\n        var rgb = this._rgb;\n        return [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, rgb[3]];\n    };\n\n    var floor$3 = Math.floor;\n\n    /*\n     * this is basically just HSV with some minor tweaks\n     *\n     * hue.. [0..360]\n     * chroma .. [0..1]\n     * grayness .. [0..1]\n     */\n\n    var hcg2rgb = function () {\n        var assign, assign$1, assign$2, assign$3, assign$4, assign$5;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hcg');\n        var h = args[0];\n        var c = args[1];\n        var _g = args[2];\n        var r, g, b;\n        _g = _g * 255;\n        var _c = c * 255;\n        if (c === 0) {\n            r = g = b = _g;\n        } else {\n            if (h === 360) { h = 0; }\n            if (h > 360) { h -= 360; }\n            if (h < 0) { h += 360; }\n            h /= 60;\n            var i = floor$3(h);\n            var f = h - i;\n            var p = _g * (1 - c);\n            var q = p + _c * (1 - f);\n            var t = p + _c * f;\n            var v = p + _c;\n            switch (i) {\n                case 0:\n                    (assign = [v, t, p], r = assign[0], g = assign[1], b = assign[2]);\n                    break;\n                case 1:\n                    (assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]);\n                    break;\n                case 2:\n                    (assign$2 = [p, v, t], r = assign$2[0], g = assign$2[1], b = assign$2[2]);\n                    break;\n                case 3:\n                    (assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]);\n                    break;\n                case 4:\n                    (assign$4 = [t, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]);\n                    break;\n                case 5:\n                    (assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]);\n                    break;\n            }\n        }\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var rgb2hcg = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var minRgb = min$3(r, g, b);\n        var maxRgb = max$3(r, g, b);\n        var delta = maxRgb - minRgb;\n        var c = (delta * 100) / 255;\n        var _g = (minRgb / (255 - delta)) * 100;\n        var h;\n        if (delta === 0) {\n            h = Number.NaN;\n        } else {\n            if (r === maxRgb) { h = (g - b) / delta; }\n            if (g === maxRgb) { h = 2 + (b - r) / delta; }\n            if (b === maxRgb) { h = 4 + (r - g) / delta; }\n            h *= 60;\n            if (h < 0) { h += 360; }\n        }\n        return [h, c, _g];\n    };\n\n    Color.prototype.hcg = function () {\n        return rgb2hcg(this._rgb);\n    };\n\n    var hcg$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hcg']) ));\n    };\n    chroma.hcg = hcg$1;\n\n    input.format.hcg = hcg2rgb;\n\n    input.autodetect.push({\n        p: 1,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hcg');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hcg';\n            }\n        }\n    });\n\n    var RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\n    var RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/;\n\n    var hex2rgb = function (hex) {\n        if (hex.match(RE_HEX)) {\n            // remove optional leading #\n            if (hex.length === 4 || hex.length === 7) {\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full six-digit\n            if (hex.length === 3) {\n                hex = hex.split('');\n                hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n            }\n            var u = parseInt(hex, 16);\n            var r = u >> 16;\n            var g = (u >> 8) & 0xff;\n            var b = u & 0xff;\n            return [r, g, b, 1];\n        }\n\n        // match rgba hex format, eg #FF000077\n        if (hex.match(RE_HEXA)) {\n            if (hex.length === 5 || hex.length === 9) {\n                // remove optional leading #\n                hex = hex.substr(1);\n            }\n            // expand short-notation to full eight-digit\n            if (hex.length === 4) {\n                hex = hex.split('');\n                hex =\n                    hex[0] +\n                    hex[0] +\n                    hex[1] +\n                    hex[1] +\n                    hex[2] +\n                    hex[2] +\n                    hex[3] +\n                    hex[3];\n            }\n            var u$1 = parseInt(hex, 16);\n            var r$1 = (u$1 >> 24) & 0xff;\n            var g$1 = (u$1 >> 16) & 0xff;\n            var b$1 = (u$1 >> 8) & 0xff;\n            var a = Math.round(((u$1 & 0xff) / 0xff) * 100) / 100;\n            return [r$1, g$1, b$1, a];\n        }\n\n        // we used to check for css colors here\n        // if _input.css? and rgb = _input.css hex\n        //     return rgb\n\n        throw new Error((\"unknown hex color: \" + hex));\n    };\n\n    var round$2 = Math.round;\n\n    var rgb2hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgba');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        var a = ref[3];\n        var mode = last(args) || 'auto';\n        if (a === undefined) { a = 1; }\n        if (mode === 'auto') {\n            mode = a < 1 ? 'rgba' : 'rgb';\n        }\n        r = round$2(r);\n        g = round$2(g);\n        b = round$2(b);\n        var u = (r << 16) | (g << 8) | b;\n        var str = '000000' + u.toString(16); //#.toUpperCase();\n        str = str.substr(str.length - 6);\n        var hxa = '0' + round$2(a * 255).toString(16);\n        hxa = hxa.substr(hxa.length - 2);\n        switch (mode.toLowerCase()) {\n            case 'rgba':\n                return (\"#\" + str + hxa);\n            case 'argb':\n                return (\"#\" + hxa + str);\n            default:\n                return (\"#\" + str);\n        }\n    };\n\n    Color.prototype.hex = function (mode) {\n        return rgb2hex(this._rgb, mode);\n    };\n\n    var hex = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hex']) ));\n    };\n    chroma.hex = hex;\n\n    input.format.hex = hex2rgb;\n    input.autodetect.push({\n        p: 4,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (\n                !rest.length &&\n                type(h) === 'string' &&\n                [3, 4, 5, 6, 7, 8, 9].indexOf(h.length) >= 0\n            ) {\n                return 'hex';\n            }\n        }\n    });\n\n    var cos$3 = Math.cos;\n\n    /*\n     * hue [0..360]\n     * saturation [0..1]\n     * intensity [0..1]\n     */\n    var hsi2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        borrowed from here:\n        http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/hsi2rgb.cpp\n        */\n        args = unpack(args, 'hsi');\n        var h = args[0];\n        var s = args[1];\n        var i = args[2];\n        var r, g, b;\n\n        if (isNaN(h)) { h = 0; }\n        if (isNaN(s)) { s = 0; }\n        // normalize hue\n        if (h > 360) { h -= 360; }\n        if (h < 0) { h += 360; }\n        h /= 360;\n        if (h < 1 / 3) {\n            b = (1 - s) / 3;\n            r = (1 + (s * cos$3(TWOPI * h)) / cos$3(PITHIRD - TWOPI * h)) / 3;\n            g = 1 - (b + r);\n        } else if (h < 2 / 3) {\n            h -= 1 / 3;\n            r = (1 - s) / 3;\n            g = (1 + (s * cos$3(TWOPI * h)) / cos$3(PITHIRD - TWOPI * h)) / 3;\n            b = 1 - (r + g);\n        } else {\n            h -= 2 / 3;\n            g = (1 - s) / 3;\n            b = (1 + (s * cos$3(TWOPI * h)) / cos$3(PITHIRD - TWOPI * h)) / 3;\n            r = 1 - (g + b);\n        }\n        r = limit(i * r * 3);\n        g = limit(i * g * 3);\n        b = limit(i * b * 3);\n        return [r * 255, g * 255, b * 255, args.length > 3 ? args[3] : 1];\n    };\n\n    var min$2 = Math.min;\n    var sqrt$3 = Math.sqrt;\n    var acos = Math.acos;\n\n    var rgb2hsi = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        /*\n        borrowed from here:\n        http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/rgb2hsi.cpp\n        */\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        r /= 255;\n        g /= 255;\n        b /= 255;\n        var h;\n        var min_ = min$2(r, g, b);\n        var i = (r + g + b) / 3;\n        var s = i > 0 ? 1 - min_ / i : 0;\n        if (s === 0) {\n            h = NaN;\n        } else {\n            h = (r - g + (r - b)) / 2;\n            h /= sqrt$3((r - g) * (r - g) + (r - b) * (g - b));\n            h = acos(h);\n            if (b > g) {\n                h = TWOPI - h;\n            }\n            h /= TWOPI;\n        }\n        return [h * 360, s, i];\n    };\n\n    Color.prototype.hsi = function () {\n        return rgb2hsi(this._rgb);\n    };\n\n    var hsi$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsi']) ));\n    };\n    chroma.hsi = hsi$1;\n\n    input.format.hsi = hsi2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsi');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsi';\n            }\n        }\n    });\n\n    Color.prototype.hsl = function () {\n        return rgb2hsl$1(this._rgb);\n    };\n\n    var hsl$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsl']) ));\n    };\n    chroma.hsl = hsl$1;\n\n    input.format.hsl = hsl2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsl');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsl';\n            }\n        }\n    });\n\n    var floor$2 = Math.floor;\n\n    var hsv2rgb = function () {\n        var assign, assign$1, assign$2, assign$3, assign$4, assign$5;\n\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n        args = unpack(args, 'hsv');\n        var h = args[0];\n        var s = args[1];\n        var v = args[2];\n        var r, g, b;\n        v *= 255;\n        if (s === 0) {\n            r = g = b = v;\n        } else {\n            if (h === 360) { h = 0; }\n            if (h > 360) { h -= 360; }\n            if (h < 0) { h += 360; }\n            h /= 60;\n\n            var i = floor$2(h);\n            var f = h - i;\n            var p = v * (1 - s);\n            var q = v * (1 - s * f);\n            var t = v * (1 - s * (1 - f));\n\n            switch (i) {\n                case 0:\n                    (assign = [v, t, p], r = assign[0], g = assign[1], b = assign[2]);\n                    break;\n                case 1:\n                    (assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]);\n                    break;\n                case 2:\n                    (assign$2 = [p, v, t], r = assign$2[0], g = assign$2[1], b = assign$2[2]);\n                    break;\n                case 3:\n                    (assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]);\n                    break;\n                case 4:\n                    (assign$4 = [t, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]);\n                    break;\n                case 5:\n                    (assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]);\n                    break;\n            }\n        }\n        return [r, g, b, args.length > 3 ? args[3] : 1];\n    };\n\n    var min$1 = Math.min;\n    var max$1 = Math.max;\n\n    /*\n     * supported arguments:\n     * - rgb2hsv(r,g,b)\n     * - rgb2hsv([r,g,b])\n     * - rgb2hsv({r,g,b})\n     */\n    var rgb2hsl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        args = unpack(args, 'rgb');\n        var r = args[0];\n        var g = args[1];\n        var b = args[2];\n        var min_ = min$1(r, g, b);\n        var max_ = max$1(r, g, b);\n        var delta = max_ - min_;\n        var h, s, v;\n        v = max_ / 255.0;\n        if (max_ === 0) {\n            h = Number.NaN;\n            s = 0;\n        } else {\n            s = delta / max_;\n            if (r === max_) { h = (g - b) / delta; }\n            if (g === max_) { h = 2 + (b - r) / delta; }\n            if (b === max_) { h = 4 + (r - g) / delta; }\n            h *= 60;\n            if (h < 0) { h += 360; }\n        }\n        return [h, s, v];\n    };\n\n    Color.prototype.hsv = function () {\n        return rgb2hsl(this._rgb);\n    };\n\n    var hsv$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hsv']) ));\n    };\n    chroma.hsv = hsv$1;\n\n    input.format.hsv = hsv2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'hsv');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'hsv';\n            }\n        }\n    });\n\n    Color.prototype.lab = function () {\n        return rgb2lab(this._rgb);\n    };\n\n    var lab$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['lab']) ));\n    };\n    Object.assign(chroma, { lab: lab$1, getLabWhitePoint: getLabWhitePoint, setLabWhitePoint: setLabWhitePoint });\n\n    input.format.lab = lab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'lab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'lab';\n            }\n        }\n    });\n\n    var hcl2rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var hcl = reverse3(unpack(args, 'hcl'));\n        return lch2rgb.apply(void 0, hcl);\n    };\n\n    Color.prototype.lch = function () {\n        return rgb2lch(this._rgb);\n    };\n    Color.prototype.hcl = function () {\n        return reverse3(rgb2lch(this._rgb));\n    };\n\n    var lch$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['lch']) ));\n    };\n    var hcl = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['hcl']) ));\n    };\n\n    Object.assign(chroma, { lch: lch$1, hcl: hcl });\n\n    input.format.lch = lch2rgb;\n    input.format.hcl = hcl2rgb;\n    ['lch', 'hcl'].forEach(function (m) { return input.autodetect.push({\n            p: 2,\n            test: function () {\n                var args = [], len = arguments.length;\n                while ( len-- ) args[ len ] = arguments[ len ];\n\n                args = unpack(args, m);\n                if (type(args) === 'array' && args.length === 3) {\n                    return m;\n                }\n            }\n        }); }\n    );\n\n    var num2rgb = function (num) {\n        if (type(num) == 'number' && num >= 0 && num <= 0xffffff) {\n            var r = num >> 16;\n            var g = (num >> 8) & 0xff;\n            var b = num & 0xff;\n            return [r, g, b, 1];\n        }\n        throw new Error('unknown num color: ' + num);\n    };\n\n    var rgb2num = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var ref = unpack(args, 'rgb');\n        var r = ref[0];\n        var g = ref[1];\n        var b = ref[2];\n        return (r << 16) + (g << 8) + b;\n    };\n\n    Color.prototype.num = function () {\n        return rgb2num(this._rgb);\n    };\n\n    var num$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['num']) ));\n    };\n\n    Object.assign(chroma, { num: num$1 });\n\n    input.format.num = num2rgb;\n\n    input.autodetect.push({\n        p: 5,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            if (\n                args.length === 1 &&\n                type(args[0]) === 'number' &&\n                args[0] >= 0 &&\n                args[0] <= 0xffffff\n            ) {\n                return 'num';\n            }\n        }\n    });\n\n    var round$1 = Math.round;\n\n    Color.prototype.rgb = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        if (rnd === false) { return this._rgb.slice(0, 3); }\n        return this._rgb.slice(0, 3).map(round$1);\n    };\n\n    Color.prototype.rgba = function (rnd) {\n        if ( rnd === void 0 ) rnd = true;\n\n        return this._rgb.slice(0, 4).map(function (v, i) {\n            return i < 3 ? (rnd === false ? v : round$1(v)) : v;\n        });\n    };\n\n    var rgb$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['rgb']) ));\n    };\n    Object.assign(chroma, { rgb: rgb$1 });\n\n    input.format.rgb = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgba = unpack(args, 'rgba');\n        if (rgba[3] === undefined) { rgba[3] = 1; }\n        return rgba;\n    };\n\n    input.autodetect.push({\n        p: 3,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'rgba');\n            if (\n                type(args) === 'array' &&\n                (args.length === 3 ||\n                    (args.length === 4 &&\n                        type(args[3]) == 'number' &&\n                        args[3] >= 0 &&\n                        args[3] <= 1))\n            ) {\n                return 'rgb';\n            }\n        }\n    });\n\n    /*\n     * Based on implementation by Neil Bartlett\n     * https://github.com/neilbartlett/color-temperature\n     */\n\n    var log$1 = Math.log;\n\n    var temperature2rgb = function (kelvin) {\n        var temp = kelvin / 100;\n        var r, g, b;\n        if (temp < 66) {\n            r = 255;\n            g =\n                temp < 6\n                    ? 0\n                    : -155.25485562709179 -\n                      0.44596950469579133 * (g = temp - 2) +\n                      104.49216199393888 * log$1(g);\n            b =\n                temp < 20\n                    ? 0\n                    : -254.76935184120902 +\n                      0.8274096064007395 * (b = temp - 10) +\n                      115.67994401066147 * log$1(b);\n        } else {\n            r =\n                351.97690566805693 +\n                0.114206453784165 * (r = temp - 55) -\n                40.25366309332127 * log$1(r);\n            g =\n                325.4494125711974 +\n                0.07943456536662342 * (g = temp - 50) -\n                28.0852963507957 * log$1(g);\n            b = 255;\n        }\n        return [r, g, b, 1];\n    };\n\n    /*\n     * Based on implementation by Neil Bartlett\n     * https://github.com/neilbartlett/color-temperature\n     **/\n\n    var round = Math.round;\n\n    var rgb2temperature = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        var rgb = unpack(args, 'rgb');\n        var r = rgb[0],\n            b = rgb[2];\n        var minTemp = 1000;\n        var maxTemp = 40000;\n        var eps = 0.4;\n        var temp;\n        while (maxTemp - minTemp > eps) {\n            temp = (maxTemp + minTemp) * 0.5;\n            var rgb$1 = temperature2rgb(temp);\n            if (rgb$1[2] / rgb$1[0] >= b / r) {\n                maxTemp = temp;\n            } else {\n                minTemp = temp;\n            }\n        }\n        return round(temp);\n    };\n\n    Color.prototype.temp =\n        Color.prototype.kelvin =\n        Color.prototype.temperature =\n            function () {\n                return rgb2temperature(this._rgb);\n            };\n\n    var temp = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['temp']) ));\n    };\n    Object.assign(chroma, { temp: temp, kelvin: temp, temperature: temp });\n\n    input.format.temp =\n        input.format.kelvin =\n        input.format.temperature =\n            temperature2rgb;\n\n    Color.prototype.oklab = function () {\n        return rgb2oklab(this._rgb);\n    };\n\n    var oklab$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['oklab']) ));\n    };\n    Object.assign(chroma, { oklab: oklab$1 });\n\n    input.format.oklab = oklab2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'oklab');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'oklab';\n            }\n        }\n    });\n\n    Color.prototype.oklch = function () {\n        return rgb2oklch(this._rgb);\n    };\n\n    var oklch$1 = function () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        return new (Function.prototype.bind.apply( Color, [ null ].concat( args, ['oklch']) ));\n    };\n    Object.assign(chroma, { oklch: oklch$1 });\n\n    input.format.oklch = oklch2rgb;\n\n    input.autodetect.push({\n        p: 2,\n        test: function () {\n            var args = [], len = arguments.length;\n            while ( len-- ) args[ len ] = arguments[ len ];\n\n            args = unpack(args, 'oklch');\n            if (type(args) === 'array' && args.length === 3) {\n                return 'oklch';\n            }\n        }\n    });\n\n    /**\n    \tX11 color names\n\n    \thttp://www.w3.org/TR/css3-color/#svg-color\n    */\n\n    var w3cx11 = {\n        aliceblue: '#f0f8ff',\n        antiquewhite: '#faebd7',\n        aqua: '#00ffff',\n        aquamarine: '#7fffd4',\n        azure: '#f0ffff',\n        beige: '#f5f5dc',\n        bisque: '#ffe4c4',\n        black: '#000000',\n        blanchedalmond: '#ffebcd',\n        blue: '#0000ff',\n        blueviolet: '#8a2be2',\n        brown: '#a52a2a',\n        burlywood: '#deb887',\n        cadetblue: '#5f9ea0',\n        chartreuse: '#7fff00',\n        chocolate: '#d2691e',\n        coral: '#ff7f50',\n        cornflowerblue: '#6495ed',\n        cornsilk: '#fff8dc',\n        crimson: '#dc143c',\n        cyan: '#00ffff',\n        darkblue: '#00008b',\n        darkcyan: '#008b8b',\n        darkgoldenrod: '#b8860b',\n        darkgray: '#a9a9a9',\n        darkgreen: '#006400',\n        darkgrey: '#a9a9a9',\n        darkkhaki: '#bdb76b',\n        darkmagenta: '#8b008b',\n        darkolivegreen: '#556b2f',\n        darkorange: '#ff8c00',\n        darkorchid: '#9932cc',\n        darkred: '#8b0000',\n        darksalmon: '#e9967a',\n        darkseagreen: '#8fbc8f',\n        darkslateblue: '#483d8b',\n        darkslategray: '#2f4f4f',\n        darkslategrey: '#2f4f4f',\n        darkturquoise: '#00ced1',\n        darkviolet: '#9400d3',\n        deeppink: '#ff1493',\n        deepskyblue: '#00bfff',\n        dimgray: '#696969',\n        dimgrey: '#696969',\n        dodgerblue: '#1e90ff',\n        firebrick: '#b22222',\n        floralwhite: '#fffaf0',\n        forestgreen: '#228b22',\n        fuchsia: '#ff00ff',\n        gainsboro: '#dcdcdc',\n        ghostwhite: '#f8f8ff',\n        gold: '#ffd700',\n        goldenrod: '#daa520',\n        gray: '#808080',\n        green: '#008000',\n        greenyellow: '#adff2f',\n        grey: '#808080',\n        honeydew: '#f0fff0',\n        hotpink: '#ff69b4',\n        indianred: '#cd5c5c',\n        indigo: '#4b0082',\n        ivory: '#fffff0',\n        khaki: '#f0e68c',\n        laserlemon: '#ffff54',\n        lavender: '#e6e6fa',\n        lavenderblush: '#fff0f5',\n        lawngreen: '#7cfc00',\n        lemonchiffon: '#fffacd',\n        lightblue: '#add8e6',\n        lightcoral: '#f08080',\n        lightcyan: '#e0ffff',\n        lightgoldenrod: '#fafad2',\n        lightgoldenrodyellow: '#fafad2',\n        lightgray: '#d3d3d3',\n        lightgreen: '#90ee90',\n        lightgrey: '#d3d3d3',\n        lightpink: '#ffb6c1',\n        lightsalmon: '#ffa07a',\n        lightseagreen: '#20b2aa',\n        lightskyblue: '#87cefa',\n        lightslategray: '#778899',\n        lightslategrey: '#778899',\n        lightsteelblue: '#b0c4de',\n        lightyellow: '#ffffe0',\n        lime: '#00ff00',\n        limegreen: '#32cd32',\n        linen: '#faf0e6',\n        magenta: '#ff00ff',\n        maroon: '#800000',\n        maroon2: '#7f0000',\n        maroon3: '#b03060',\n        mediumaquamarine: '#66cdaa',\n        mediumblue: '#0000cd',\n        mediumorchid: '#ba55d3',\n        mediumpurple: '#9370db',\n        mediumseagreen: '#3cb371',\n        mediumslateblue: '#7b68ee',\n        mediumspringgreen: '#00fa9a',\n        mediumturquoise: '#48d1cc',\n        mediumvioletred: '#c71585',\n        midnightblue: '#191970',\n        mintcream: '#f5fffa',\n        mistyrose: '#ffe4e1',\n        moccasin: '#ffe4b5',\n        navajowhite: '#ffdead',\n        navy: '#000080',\n        oldlace: '#fdf5e6',\n        olive: '#808000',\n        olivedrab: '#6b8e23',\n        orange: '#ffa500',\n        orangered: '#ff4500',\n        orchid: '#da70d6',\n        palegoldenrod: '#eee8aa',\n        palegreen: '#98fb98',\n        paleturquoise: '#afeeee',\n        palevioletred: '#db7093',\n        papayawhip: '#ffefd5',\n        peachpuff: '#ffdab9',\n        peru: '#cd853f',\n        pink: '#ffc0cb',\n        plum: '#dda0dd',\n        powderblue: '#b0e0e6',\n        purple: '#800080',\n        purple2: '#7f007f',\n        purple3: '#a020f0',\n        rebeccapurple: '#663399',\n        red: '#ff0000',\n        rosybrown: '#bc8f8f',\n        royalblue: '#4169e1',\n        saddlebrown: '#8b4513',\n        salmon: '#fa8072',\n        sandybrown: '#f4a460',\n        seagreen: '#2e8b57',\n        seashell: '#fff5ee',\n        sienna: '#a0522d',\n        silver: '#c0c0c0',\n        skyblue: '#87ceeb',\n        slateblue: '#6a5acd',\n        slategray: '#708090',\n        slategrey: '#708090',\n        snow: '#fffafa',\n        springgreen: '#00ff7f',\n        steelblue: '#4682b4',\n        tan: '#d2b48c',\n        teal: '#008080',\n        thistle: '#d8bfd8',\n        tomato: '#ff6347',\n        turquoise: '#40e0d0',\n        violet: '#ee82ee',\n        wheat: '#f5deb3',\n        white: '#ffffff',\n        whitesmoke: '#f5f5f5',\n        yellow: '#ffff00',\n        yellowgreen: '#9acd32'\n    };\n\n    Color.prototype.name = function () {\n        var hex = rgb2hex(this._rgb, 'rgb');\n        for (var i = 0, list = Object.keys(w3cx11); i < list.length; i += 1) {\n            var n = list[i];\n\n            if (w3cx11[n] === hex) { return n.toLowerCase(); }\n        }\n        return hex;\n    };\n\n    input.format.named = function (name) {\n        name = name.toLowerCase();\n        if (w3cx11[name]) { return hex2rgb(w3cx11[name]); }\n        throw new Error('unknown color name: ' + name);\n    };\n\n    input.autodetect.push({\n        p: 5,\n        test: function (h) {\n            var rest = [], len = arguments.length - 1;\n            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n            if (!rest.length && type(h) === 'string' && w3cx11[h.toLowerCase()]) {\n                return 'named';\n            }\n        }\n    });\n\n    Color.prototype.alpha = function (a, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        if (a !== undefined && type(a) === 'number') {\n            if (mutate) {\n                this._rgb[3] = a;\n                return this;\n            }\n            return new Color([this._rgb[0], this._rgb[1], this._rgb[2], a], 'rgb');\n        }\n        return this._rgb[3];\n    };\n\n    Color.prototype.clipped = function () {\n        return this._rgb._clipped || false;\n    };\n\n    Color.prototype.darken = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        var me = this;\n        var lab = me.lab();\n        lab[0] -= labConstants.Kn * amount;\n        return new Color(lab, 'lab').alpha(me.alpha(), true);\n    };\n\n    Color.prototype.brighten = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        return this.darken(-amount);\n    };\n\n    Color.prototype.darker = Color.prototype.darken;\n    Color.prototype.brighter = Color.prototype.brighten;\n\n    Color.prototype.get = function (mc) {\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) { return src[i]; }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    var pow$6 = Math.pow;\n\n    var EPS = 1e-7;\n    var MAX_ITER = 20;\n\n    Color.prototype.luminance = function (lum, mode) {\n        if ( mode === void 0 ) mode = 'rgb';\n\n        if (lum !== undefined && type(lum) === 'number') {\n            if (lum === 0) {\n                // return pure black\n                return new Color([0, 0, 0, this._rgb[3]], 'rgb');\n            }\n            if (lum === 1) {\n                // return pure white\n                return new Color([255, 255, 255, this._rgb[3]], 'rgb');\n            }\n            // compute new color using...\n            var cur_lum = this.luminance();\n            var max_iter = MAX_ITER;\n\n            var test = function (low, high) {\n                var mid = low.interpolate(high, 0.5, mode);\n                var lm = mid.luminance();\n                if (Math.abs(lum - lm) < EPS || !max_iter--) {\n                    // close enough\n                    return mid;\n                }\n                return lm > lum ? test(low, mid) : test(mid, high);\n            };\n\n            var rgb = (\n                cur_lum > lum\n                    ? test(new Color([0, 0, 0]), this)\n                    : test(this, new Color([255, 255, 255]))\n            ).rgb();\n            return new Color(rgb.concat( [this._rgb[3]]));\n        }\n        return rgb2luminance.apply(void 0, this._rgb.slice(0, 3));\n    };\n\n    var rgb2luminance = function (r, g, b) {\n        // relative luminance\n        // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\n        r = luminance_x(r);\n        g = luminance_x(g);\n        b = luminance_x(b);\n        return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n    };\n\n    var luminance_x = function (x) {\n        x /= 255;\n        return x <= 0.03928 ? x / 12.92 : pow$6((x + 0.055) / 1.055, 2.4);\n    };\n\n    var index = {};\n\n    function mix (col1, col2, f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 3;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 3 ];\n\n        var mode = rest[0] || 'lrgb';\n        if (!index[mode] && !rest.length) {\n            // fall back to the first supported mode\n            mode = Object.keys(index)[0];\n        }\n        if (!index[mode]) {\n            throw new Error((\"interpolation mode \" + mode + \" is not defined\"));\n        }\n        if (type(col1) !== 'object') { col1 = new Color(col1); }\n        if (type(col2) !== 'object') { col2 = new Color(col2); }\n        return index[mode](col1, col2, f).alpha(\n            col1.alpha() + f * (col2.alpha() - col1.alpha())\n        );\n    }\n\n    Color.prototype.mix = Color.prototype.interpolate = function (\n        col2,\n        f\n    ) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 2;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 2 ];\n\n        return mix.apply(void 0, [ this, col2, f ].concat( rest ));\n    };\n\n    Color.prototype.premultiply = function (mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        var rgb = this._rgb;\n        var a = rgb[3];\n        if (mutate) {\n            this._rgb = [rgb[0] * a, rgb[1] * a, rgb[2] * a, a];\n            return this;\n        } else {\n            return new Color([rgb[0] * a, rgb[1] * a, rgb[2] * a, a], 'rgb');\n        }\n    };\n\n    Color.prototype.saturate = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        var me = this;\n        var lch = me.lch();\n        lch[1] += labConstants.Kn * amount;\n        if (lch[1] < 0) { lch[1] = 0; }\n        return new Color(lch, 'lch').alpha(me.alpha(), true);\n    };\n\n    Color.prototype.desaturate = function (amount) {\n        if ( amount === void 0 ) amount = 1;\n\n        return this.saturate(-amount);\n    };\n\n    Color.prototype.set = function (mc, value, mutate) {\n        if ( mutate === void 0 ) mutate = false;\n\n        var ref = mc.split('.');\n        var mode = ref[0];\n        var channel = ref[1];\n        var src = this[mode]();\n        if (channel) {\n            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n            if (i > -1) {\n                if (type(value) == 'string') {\n                    switch (value.charAt(0)) {\n                        case '+':\n                            src[i] += +value;\n                            break;\n                        case '-':\n                            src[i] += +value;\n                            break;\n                        case '*':\n                            src[i] *= +value.substr(1);\n                            break;\n                        case '/':\n                            src[i] /= +value.substr(1);\n                            break;\n                        default:\n                            src[i] = +value;\n                    }\n                } else if (type(value) === 'number') {\n                    src[i] = value;\n                } else {\n                    throw new Error(\"unsupported value for Color.set\");\n                }\n                var out = new Color(src, mode);\n                if (mutate) {\n                    this._rgb = out._rgb;\n                    return this;\n                }\n                return out;\n            }\n            throw new Error((\"unknown channel \" + channel + \" in mode \" + mode));\n        } else {\n            return src;\n        }\n    };\n\n    Color.prototype.tint = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'white', f ].concat( rest ));\n    };\n\n    Color.prototype.shade = function (f) {\n        if ( f === void 0 ) f = 0.5;\n        var rest = [], len = arguments.length - 1;\n        while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];\n\n        return mix.apply(void 0, [ this, 'black', f ].concat( rest ));\n    };\n\n    var rgb = function (col1, col2, f) {\n        var xyz0 = col1._rgb;\n        var xyz1 = col2._rgb;\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'rgb'\n        );\n    };\n\n    // register interpolator\n    index.rgb = rgb;\n\n    var sqrt$2 = Math.sqrt;\n    var pow$5 = Math.pow;\n\n    var lrgb = function (col1, col2, f) {\n        var ref = col1._rgb;\n        var x1 = ref[0];\n        var y1 = ref[1];\n        var z1 = ref[2];\n        var ref$1 = col2._rgb;\n        var x2 = ref$1[0];\n        var y2 = ref$1[1];\n        var z2 = ref$1[2];\n        return new Color(\n            sqrt$2(pow$5(x1, 2) * (1 - f) + pow$5(x2, 2) * f),\n            sqrt$2(pow$5(y1, 2) * (1 - f) + pow$5(y2, 2) * f),\n            sqrt$2(pow$5(z1, 2) * (1 - f) + pow$5(z2, 2) * f),\n            'rgb'\n        );\n    };\n\n    // register interpolator\n    index.lrgb = lrgb;\n\n    var lab = function (col1, col2, f) {\n        var xyz0 = col1.lab();\n        var xyz1 = col2.lab();\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'lab'\n        );\n    };\n\n    // register interpolator\n    index.lab = lab;\n\n    function interpolate_hsx (col1, col2, f, m) {\n        var assign, assign$1;\n\n        var xyz0, xyz1;\n        if (m === 'hsl') {\n            xyz0 = col1.hsl();\n            xyz1 = col2.hsl();\n        } else if (m === 'hsv') {\n            xyz0 = col1.hsv();\n            xyz1 = col2.hsv();\n        } else if (m === 'hcg') {\n            xyz0 = col1.hcg();\n            xyz1 = col2.hcg();\n        } else if (m === 'hsi') {\n            xyz0 = col1.hsi();\n            xyz1 = col2.hsi();\n        } else if (m === 'lch' || m === 'hcl') {\n            m = 'hcl';\n            xyz0 = col1.hcl();\n            xyz1 = col2.hcl();\n        } else if (m === 'oklch') {\n            xyz0 = col1.oklch().reverse();\n            xyz1 = col2.oklch().reverse();\n        }\n\n        var hue0, hue1, sat0, sat1, lbv0, lbv1;\n        if (m.substr(0, 1) === 'h' || m === 'oklch') {\n            (assign = xyz0, hue0 = assign[0], sat0 = assign[1], lbv0 = assign[2]);\n            (assign$1 = xyz1, hue1 = assign$1[0], sat1 = assign$1[1], lbv1 = assign$1[2]);\n        }\n\n        var sat, hue, lbv, dh;\n\n        if (!isNaN(hue0) && !isNaN(hue1)) {\n            // both colors have hue\n            if (hue1 > hue0 && hue1 - hue0 > 180) {\n                dh = hue1 - (hue0 + 360);\n            } else if (hue1 < hue0 && hue0 - hue1 > 180) {\n                dh = hue1 + 360 - hue0;\n            } else {\n                dh = hue1 - hue0;\n            }\n            hue = hue0 + f * dh;\n        } else if (!isNaN(hue0)) {\n            hue = hue0;\n            if ((lbv1 == 1 || lbv1 == 0) && m != 'hsv') { sat = sat0; }\n        } else if (!isNaN(hue1)) {\n            hue = hue1;\n            if ((lbv0 == 1 || lbv0 == 0) && m != 'hsv') { sat = sat1; }\n        } else {\n            hue = Number.NaN;\n        }\n\n        if (sat === undefined) { sat = sat0 + f * (sat1 - sat0); }\n        lbv = lbv0 + f * (lbv1 - lbv0);\n        return m === 'oklch'\n            ? new Color([lbv, sat, hue], m)\n            : new Color([hue, sat, lbv], m);\n    }\n\n    var lch = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'lch');\n    };\n\n    // register interpolator\n    index.lch = lch;\n    index.hcl = lch;\n\n    var num = function (col1, col2, f) {\n        var c1 = col1.num();\n        var c2 = col2.num();\n        return new Color(c1 + f * (c2 - c1), 'num');\n    };\n\n    // register interpolator\n    index.num = num;\n\n    var hcg = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hcg');\n    };\n\n    // register interpolator\n    index.hcg = hcg;\n\n    var hsi = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hsi');\n    };\n\n    // register interpolator\n    index.hsi = hsi;\n\n    var hsl = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hsl');\n    };\n\n    // register interpolator\n    index.hsl = hsl;\n\n    var hsv = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'hsv');\n    };\n\n    // register interpolator\n    index.hsv = hsv;\n\n    var oklab = function (col1, col2, f) {\n        var xyz0 = col1.oklab();\n        var xyz1 = col2.oklab();\n        return new Color(\n            xyz0[0] + f * (xyz1[0] - xyz0[0]),\n            xyz0[1] + f * (xyz1[1] - xyz0[1]),\n            xyz0[2] + f * (xyz1[2] - xyz0[2]),\n            'oklab'\n        );\n    };\n\n    // register interpolator\n    index.oklab = oklab;\n\n    var oklch = function (col1, col2, f) {\n        return interpolate_hsx(col1, col2, f, 'oklch');\n    };\n\n    // register interpolator\n    index.oklch = oklch;\n\n    var pow$4 = Math.pow;\n    var sqrt$1 = Math.sqrt;\n    var PI$1 = Math.PI;\n    var cos$2 = Math.cos;\n    var sin$2 = Math.sin;\n    var atan2$1 = Math.atan2;\n\n    function average (colors, mode, weights) {\n        if ( mode === void 0 ) mode = 'lrgb';\n        if ( weights === void 0 ) weights = null;\n\n        var l = colors.length;\n        if (!weights) { weights = Array.from(new Array(l)).map(function () { return 1; }); }\n        // normalize weights\n        var k =\n            l /\n            weights.reduce(function (a, b) {\n                return a + b;\n            });\n        weights.forEach(function (w, i) {\n            weights[i] *= k;\n        });\n        // convert colors to Color objects\n        colors = colors.map(function (c) { return new Color(c); });\n        if (mode === 'lrgb') {\n            return _average_lrgb(colors, weights);\n        }\n        var first = colors.shift();\n        var xyz = first.get(mode);\n        var cnt = [];\n        var dx = 0;\n        var dy = 0;\n        // initial color\n        for (var i = 0; i < xyz.length; i++) {\n            xyz[i] = (xyz[i] || 0) * weights[0];\n            cnt.push(isNaN(xyz[i]) ? 0 : weights[0]);\n            if (mode.charAt(i) === 'h' && !isNaN(xyz[i])) {\n                var A = (xyz[i] / 180) * PI$1;\n                dx += cos$2(A) * weights[0];\n                dy += sin$2(A) * weights[0];\n            }\n        }\n\n        var alpha = first.alpha() * weights[0];\n        colors.forEach(function (c, ci) {\n            var xyz2 = c.get(mode);\n            alpha += c.alpha() * weights[ci + 1];\n            for (var i = 0; i < xyz.length; i++) {\n                if (!isNaN(xyz2[i])) {\n                    cnt[i] += weights[ci + 1];\n                    if (mode.charAt(i) === 'h') {\n                        var A = (xyz2[i] / 180) * PI$1;\n                        dx += cos$2(A) * weights[ci + 1];\n                        dy += sin$2(A) * weights[ci + 1];\n                    } else {\n                        xyz[i] += xyz2[i] * weights[ci + 1];\n                    }\n                }\n            }\n        });\n\n        for (var i$1 = 0; i$1 < xyz.length; i$1++) {\n            if (mode.charAt(i$1) === 'h') {\n                var A$1 = (atan2$1(dy / cnt[i$1], dx / cnt[i$1]) / PI$1) * 180;\n                while (A$1 < 0) { A$1 += 360; }\n                while (A$1 >= 360) { A$1 -= 360; }\n                xyz[i$1] = A$1;\n            } else {\n                xyz[i$1] = xyz[i$1] / cnt[i$1];\n            }\n        }\n        alpha /= l;\n        return new Color(xyz, mode).alpha(alpha > 0.99999 ? 1 : alpha, true);\n    }\n    var _average_lrgb = function (colors, weights) {\n        var l = colors.length;\n        var xyz = [0, 0, 0, 0];\n        for (var i = 0; i < colors.length; i++) {\n            var col = colors[i];\n            var f = weights[i] / l;\n            var rgb = col._rgb;\n            xyz[0] += pow$4(rgb[0], 2) * f;\n            xyz[1] += pow$4(rgb[1], 2) * f;\n            xyz[2] += pow$4(rgb[2], 2) * f;\n            xyz[3] += rgb[3] * f;\n        }\n        xyz[0] = sqrt$1(xyz[0]);\n        xyz[1] = sqrt$1(xyz[1]);\n        xyz[2] = sqrt$1(xyz[2]);\n        if (xyz[3] > 0.9999999) { xyz[3] = 1; }\n        return new Color(clip_rgb(xyz));\n    };\n\n    // minimal multi-purpose interface\n\n\n    var pow$3 = Math.pow;\n\n    function scale (colors) {\n        // constructor\n        var _mode = 'rgb';\n        var _nacol = chroma('#ccc');\n        var _spread = 0;\n        // const _fixed = false;\n        var _positions = [0, 1];\n        var _domain = [0, 1];\n        var _pos = [];\n        var _padding = [0, 0];\n        var _classes = false;\n        var _colors = [];\n        var _out = false;\n        var _min = 0;\n        var _max = 1;\n        var _correctLightness = false;\n        var _colorCache = {};\n        var _useCache = true;\n        var _gamma = 1;\n\n        // private methods\n\n        var setColors = function (colors) {\n            colors = colors || ['#fff', '#000'];\n            if (\n                colors &&\n                type(colors) === 'string' &&\n                chroma.brewer &&\n                chroma.brewer[colors.toLowerCase()]\n            ) {\n                colors = chroma.brewer[colors.toLowerCase()];\n            }\n            if (type(colors) === 'array') {\n                // handle single color\n                if (colors.length === 1) {\n                    colors = [colors[0], colors[0]];\n                }\n                // make a copy of the colors\n                colors = colors.slice(0);\n                // convert to chroma classes\n                for (var c = 0; c < colors.length; c++) {\n                    colors[c] = chroma(colors[c]);\n                }\n                // auto-fill color position\n                _pos.length = 0;\n                for (var c$1 = 0; c$1 < colors.length; c$1++) {\n                    _pos.push(c$1 / (colors.length - 1));\n                }\n            }\n            resetCache();\n            return (_colors = colors);\n        };\n\n        var getClass = function (value) {\n            if (_classes != null) {\n                var n = _classes.length - 1;\n                var i = 0;\n                while (i < n && value >= _classes[i]) {\n                    i++;\n                }\n                return i - 1;\n            }\n            return 0;\n        };\n\n        var tMapLightness = function (t) { return t; };\n        var tMapDomain = function (t) { return t; };\n\n        // const classifyValue = function(value) {\n        //     let val = value;\n        //     if (_classes.length > 2) {\n        //         const n = _classes.length-1;\n        //         const i = getClass(value);\n        //         const minc = _classes[0] + ((_classes[1]-_classes[0]) * (0 + (_spread * 0.5)));  // center of 1st class\n        //         const maxc = _classes[n-1] + ((_classes[n]-_classes[n-1]) * (1 - (_spread * 0.5)));  // center of last class\n        //         val = _min + ((((_classes[i] + ((_classes[i+1] - _classes[i]) * 0.5)) - minc) / (maxc-minc)) * (_max - _min));\n        //     }\n        //     return val;\n        // };\n\n        var getColor = function (val, bypassMap) {\n            var col, t;\n            if (bypassMap == null) {\n                bypassMap = false;\n            }\n            if (isNaN(val) || val === null) {\n                return _nacol;\n            }\n            if (!bypassMap) {\n                if (_classes && _classes.length > 2) {\n                    // find the class\n                    var c = getClass(val);\n                    t = c / (_classes.length - 2);\n                } else if (_max !== _min) {\n                    // just interpolate between min/max\n                    t = (val - _min) / (_max - _min);\n                } else {\n                    t = 1;\n                }\n            } else {\n                t = val;\n            }\n\n            // domain map\n            t = tMapDomain(t);\n\n            if (!bypassMap) {\n                t = tMapLightness(t); // lightness correction\n            }\n\n            if (_gamma !== 1) {\n                t = pow$3(t, _gamma);\n            }\n\n            t = _padding[0] + t * (1 - _padding[0] - _padding[1]);\n\n            t = limit(t, 0, 1);\n\n            var k = Math.floor(t * 10000);\n\n            if (_useCache && _colorCache[k]) {\n                col = _colorCache[k];\n            } else {\n                if (type(_colors) === 'array') {\n                    //for i in [0.._pos.length-1]\n                    for (var i = 0; i < _pos.length; i++) {\n                        var p = _pos[i];\n                        if (t <= p) {\n                            col = _colors[i];\n                            break;\n                        }\n                        if (t >= p && i === _pos.length - 1) {\n                            col = _colors[i];\n                            break;\n                        }\n                        if (t > p && t < _pos[i + 1]) {\n                            t = (t - p) / (_pos[i + 1] - p);\n                            col = chroma.interpolate(\n                                _colors[i],\n                                _colors[i + 1],\n                                t,\n                                _mode\n                            );\n                            break;\n                        }\n                    }\n                } else if (type(_colors) === 'function') {\n                    col = _colors(t);\n                }\n                if (_useCache) {\n                    _colorCache[k] = col;\n                }\n            }\n            return col;\n        };\n\n        var resetCache = function () { return (_colorCache = {}); };\n\n        setColors(colors);\n\n        // public interface\n\n        var f = function (v) {\n            var c = chroma(getColor(v));\n            if (_out && c[_out]) {\n                return c[_out]();\n            } else {\n                return c;\n            }\n        };\n\n        f.classes = function (classes) {\n            if (classes != null) {\n                if (type(classes) === 'array') {\n                    _classes = classes;\n                    _positions = [classes[0], classes[classes.length - 1]];\n                } else {\n                    var d = chroma.analyze(_positions);\n                    if (classes === 0) {\n                        _classes = [d.min, d.max];\n                    } else {\n                        _classes = chroma.limits(d, 'e', classes);\n                    }\n                }\n                return f;\n            }\n            return _classes;\n        };\n\n        f.domain = function (domain) {\n            if (!arguments.length) {\n                // return original domain\n                return _domain;\n            }\n            // store original domain so we can return it later\n            _domain = domain.slice(0);\n            _min = domain[0];\n            _max = domain[domain.length - 1];\n            _pos = [];\n            var k = _colors.length;\n            if (domain.length === k && _min !== _max) {\n                // update positions\n                for (var i = 0, list = Array.from(domain); i < list.length; i += 1) {\n                    var d = list[i];\n\n                    _pos.push((d - _min) / (_max - _min));\n                }\n            } else {\n                for (var c = 0; c < k; c++) {\n                    _pos.push(c / (k - 1));\n                }\n                if (domain.length > 2) {\n                    // set domain map\n                    var tOut = domain.map(function (d, i) { return i / (domain.length - 1); });\n                    var tBreaks = domain.map(function (d) { return (d - _min) / (_max - _min); });\n                    if (!tBreaks.every(function (val, i) { return tOut[i] === val; })) {\n                        tMapDomain = function (t) {\n                            if (t <= 0 || t >= 1) { return t; }\n                            var i = 0;\n                            while (t >= tBreaks[i + 1]) { i++; }\n                            var f =\n                                (t - tBreaks[i]) / (tBreaks[i + 1] - tBreaks[i]);\n                            var out = tOut[i] + f * (tOut[i + 1] - tOut[i]);\n                            return out;\n                        };\n                    }\n                }\n            }\n            _positions = [_min, _max];\n            return f;\n        };\n\n        f.mode = function (_m) {\n            if (!arguments.length) {\n                return _mode;\n            }\n            _mode = _m;\n            resetCache();\n            return f;\n        };\n\n        f.range = function (colors, _pos) {\n            setColors(colors);\n            return f;\n        };\n\n        f.out = function (_o) {\n            _out = _o;\n            return f;\n        };\n\n        f.spread = function (val) {\n            if (!arguments.length) {\n                return _spread;\n            }\n            _spread = val;\n            return f;\n        };\n\n        f.correctLightness = function (v) {\n            if (v == null) {\n                v = true;\n            }\n            _correctLightness = v;\n            resetCache();\n            if (_correctLightness) {\n                tMapLightness = function (t) {\n                    var L0 = getColor(0, true).lab()[0];\n                    var L1 = getColor(1, true).lab()[0];\n                    var pol = L0 > L1;\n                    var L_actual = getColor(t, true).lab()[0];\n                    var L_ideal = L0 + (L1 - L0) * t;\n                    var L_diff = L_actual - L_ideal;\n                    var t0 = 0;\n                    var t1 = 1;\n                    var max_iter = 20;\n                    while (Math.abs(L_diff) > 1e-2 && max_iter-- > 0) {\n                        (function () {\n                            if (pol) {\n                                L_diff *= -1;\n                            }\n                            if (L_diff < 0) {\n                                t0 = t;\n                                t += (t1 - t) * 0.5;\n                            } else {\n                                t1 = t;\n                                t += (t0 - t) * 0.5;\n                            }\n                            L_actual = getColor(t, true).lab()[0];\n                            return (L_diff = L_actual - L_ideal);\n                        })();\n                    }\n                    return t;\n                };\n            } else {\n                tMapLightness = function (t) { return t; };\n            }\n            return f;\n        };\n\n        f.padding = function (p) {\n            if (p != null) {\n                if (type(p) === 'number') {\n                    p = [p, p];\n                }\n                _padding = p;\n                return f;\n            } else {\n                return _padding;\n            }\n        };\n\n        f.colors = function (numColors, out) {\n            // If no arguments are given, return the original colors that were provided\n            if (arguments.length < 2) {\n                out = 'hex';\n            }\n            var result = [];\n\n            if (arguments.length === 0) {\n                result = _colors.slice(0);\n            } else if (numColors === 1) {\n                result = [f(0.5)];\n            } else if (numColors > 1) {\n                var dm = _positions[0];\n                var dd = _positions[1] - dm;\n                result = __range__(0, numColors).map(function (i) { return f(dm + (i / (numColors - 1)) * dd); }\n                );\n            } else {\n                // returns all colors based on the defined classes\n                colors = [];\n                var samples = [];\n                if (_classes && _classes.length > 2) {\n                    for (\n                        var i = 1, end = _classes.length, asc = 1 <= end;\n                        asc ? i < end : i > end;\n                        asc ? i++ : i--\n                    ) {\n                        samples.push((_classes[i - 1] + _classes[i]) * 0.5);\n                    }\n                } else {\n                    samples = _positions;\n                }\n                result = samples.map(function (v) { return f(v); });\n            }\n\n            if (chroma[out]) {\n                result = result.map(function (c) { return c[out](); });\n            }\n            return result;\n        };\n\n        f.cache = function (c) {\n            if (c != null) {\n                _useCache = c;\n                return f;\n            } else {\n                return _useCache;\n            }\n        };\n\n        f.gamma = function (g) {\n            if (g != null) {\n                _gamma = g;\n                return f;\n            } else {\n                return _gamma;\n            }\n        };\n\n        f.nodata = function (d) {\n            if (d != null) {\n                _nacol = chroma(d);\n                return f;\n            } else {\n                return _nacol;\n            }\n        };\n\n        return f;\n    }\n\n    function __range__(left, right, inclusive) {\n        var range = [];\n        var ascending = left < right;\n        var end = right ;\n        for (var i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {\n            range.push(i);\n        }\n        return range;\n    }\n\n    //\n    // interpolates between a set of colors uzing a bezier spline\n    //\n\n\n    // nth row of the pascal triangle\n    var binom_row = function (n) {\n        var row = [1, 1];\n        for (var i = 1; i < n; i++) {\n            var newrow = [1];\n            for (var j = 1; j <= row.length; j++) {\n                newrow[j] = (row[j] || 0) + row[j - 1];\n            }\n            row = newrow;\n        }\n        return row;\n    };\n\n    var bezier = function (colors) {\n        var assign, assign$1, assign$2;\n\n        var I, lab0, lab1, lab2;\n        colors = colors.map(function (c) { return new Color(c); });\n        if (colors.length === 2) {\n            // linear interpolation\n            (assign = colors.map(function (c) { return c.lab(); }), lab0 = assign[0], lab1 = assign[1]);\n            I = function (t) {\n                var lab = [0, 1, 2].map(function (i) { return lab0[i] + t * (lab1[i] - lab0[i]); });\n                return new Color(lab, 'lab');\n            };\n        } else if (colors.length === 3) {\n            // quadratic bezier interpolation\n            (assign$1 = colors.map(function (c) { return c.lab(); }), lab0 = assign$1[0], lab1 = assign$1[1], lab2 = assign$1[2]);\n            I = function (t) {\n                var lab = [0, 1, 2].map(\n                    function (i) { return (1 - t) * (1 - t) * lab0[i] +\n                        2 * (1 - t) * t * lab1[i] +\n                        t * t * lab2[i]; }\n                );\n                return new Color(lab, 'lab');\n            };\n        } else if (colors.length === 4) {\n            // cubic bezier interpolation\n            var lab3;\n            (assign$2 = colors.map(function (c) { return c.lab(); }), lab0 = assign$2[0], lab1 = assign$2[1], lab2 = assign$2[2], lab3 = assign$2[3]);\n            I = function (t) {\n                var lab = [0, 1, 2].map(\n                    function (i) { return (1 - t) * (1 - t) * (1 - t) * lab0[i] +\n                        3 * (1 - t) * (1 - t) * t * lab1[i] +\n                        3 * (1 - t) * t * t * lab2[i] +\n                        t * t * t * lab3[i]; }\n                );\n                return new Color(lab, 'lab');\n            };\n        } else if (colors.length >= 5) {\n            // general case (degree n bezier)\n            var labs, row, n;\n            labs = colors.map(function (c) { return c.lab(); });\n            n = colors.length - 1;\n            row = binom_row(n);\n            I = function (t) {\n                var u = 1 - t;\n                var lab = [0, 1, 2].map(function (i) { return labs.reduce(\n                        function (sum, el, j) { return sum + row[j] * Math.pow( u, (n - j) ) * Math.pow( t, j ) * el[i]; },\n                        0\n                    ); }\n                );\n                return new Color(lab, 'lab');\n            };\n        } else {\n            throw new RangeError('No point in running bezier with only one color.');\n        }\n        return I;\n    };\n\n    function bezier$1 (colors) {\n        var f = bezier(colors);\n        f.scale = function () { return scale(f); };\n        return f;\n    }\n\n    /*\n     * interpolates between a set of colors uzing a bezier spline\n     * blend mode formulas taken from https://web.archive.org/web/20180110014946/http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/\n     */\n\n\n    var blend = function (bottom, top, mode) {\n        if (!blend[mode]) {\n            throw new Error('unknown blend mode ' + mode);\n        }\n        return blend[mode](bottom, top);\n    };\n\n    var blend_f = function (f) { return function (bottom, top) {\n        var c0 = chroma(top).rgb();\n        var c1 = chroma(bottom).rgb();\n        return chroma.rgb(f(c0, c1));\n    }; };\n\n    var each = function (f) { return function (c0, c1) {\n        var out = [];\n        out[0] = f(c0[0], c1[0]);\n        out[1] = f(c0[1], c1[1]);\n        out[2] = f(c0[2], c1[2]);\n        return out;\n    }; };\n\n    var normal = function (a) { return a; };\n    var multiply = function (a, b) { return (a * b) / 255; };\n    var darken = function (a, b) { return (a > b ? b : a); };\n    var lighten = function (a, b) { return (a > b ? a : b); };\n    var screen = function (a, b) { return 255 * (1 - (1 - a / 255) * (1 - b / 255)); };\n    var overlay = function (a, b) { return b < 128 ? (2 * a * b) / 255 : 255 * (1 - 2 * (1 - a / 255) * (1 - b / 255)); };\n    var burn = function (a, b) { return 255 * (1 - (1 - b / 255) / (a / 255)); };\n    var dodge = function (a, b) {\n        if (a === 255) { return 255; }\n        a = (255 * (b / 255)) / (1 - a / 255);\n        return a > 255 ? 255 : a;\n    };\n\n    // # add = (a,b) ->\n    // #     if (a + b > 255) then 255 else a + b\n\n    blend.normal = blend_f(each(normal));\n    blend.multiply = blend_f(each(multiply));\n    blend.screen = blend_f(each(screen));\n    blend.overlay = blend_f(each(overlay));\n    blend.darken = blend_f(each(darken));\n    blend.lighten = blend_f(each(lighten));\n    blend.dodge = blend_f(each(dodge));\n    blend.burn = blend_f(each(burn));\n\n    // cubehelix interpolation\n    // based on D.A. Green \"A colour scheme for the display of astronomical intensity images\"\n    // http://astron-soc.in/bulletin/11June/289392011.pdf\n    var pow$2 = Math.pow;\n    var sin$1 = Math.sin;\n    var cos$1 = Math.cos;\n\n    function cubehelix (\n        start,\n        rotations,\n        hue,\n        gamma,\n        lightness\n    ) {\n        if ( start === void 0 ) start = 300;\n        if ( rotations === void 0 ) rotations = -1.5;\n        if ( hue === void 0 ) hue = 1;\n        if ( gamma === void 0 ) gamma = 1;\n        if ( lightness === void 0 ) lightness = [0, 1];\n\n        var dh = 0,\n            dl;\n        if (type(lightness) === 'array') {\n            dl = lightness[1] - lightness[0];\n        } else {\n            dl = 0;\n            lightness = [lightness, lightness];\n        }\n        var f = function (fract) {\n            var a = TWOPI * ((start + 120) / 360 + rotations * fract);\n            var l = pow$2(lightness[0] + dl * fract, gamma);\n            var h = dh !== 0 ? hue[0] + fract * dh : hue;\n            var amp = (h * l * (1 - l)) / 2;\n            var cos_a = cos$1(a);\n            var sin_a = sin$1(a);\n            var r = l + amp * (-0.14861 * cos_a + 1.78277 * sin_a);\n            var g = l + amp * (-0.29227 * cos_a - 0.90649 * sin_a);\n            var b = l + amp * (1.97294 * cos_a);\n            return chroma(clip_rgb([r * 255, g * 255, b * 255, 1]));\n        };\n        f.start = function (s) {\n            if (s == null) {\n                return start;\n            }\n            start = s;\n            return f;\n        };\n        f.rotations = function (r) {\n            if (r == null) {\n                return rotations;\n            }\n            rotations = r;\n            return f;\n        };\n        f.gamma = function (g) {\n            if (g == null) {\n                return gamma;\n            }\n            gamma = g;\n            return f;\n        };\n        f.hue = function (h) {\n            if (h == null) {\n                return hue;\n            }\n            hue = h;\n            if (type(hue) === 'array') {\n                dh = hue[1] - hue[0];\n                if (dh === 0) {\n                    hue = hue[1];\n                }\n            } else {\n                dh = 0;\n            }\n            return f;\n        };\n        f.lightness = function (h) {\n            if (h == null) {\n                return lightness;\n            }\n            if (type(h) === 'array') {\n                lightness = h;\n                dl = h[1] - h[0];\n            } else {\n                lightness = [h, h];\n                dl = 0;\n            }\n            return f;\n        };\n        f.scale = function () { return chroma.scale(f); };\n        f.hue(hue);\n        return f;\n    }\n\n    var digits = '0123456789abcdef';\n\n    var floor$1 = Math.floor;\n    var random = Math.random;\n\n    /**\n     * Generates a random color.\n     * @param {() => number} rng - A random number generator function.\n     */\n    function random$1 (rng) {\n        if ( rng === void 0 ) rng = random;\n\n        var code = '#';\n        for (var i = 0; i < 6; i++) {\n            code += digits.charAt(floor$1(rng() * 16));\n        }\n        return new Color(code, 'hex');\n    }\n\n    var log = Math.log;\n    var pow$1 = Math.pow;\n    var floor = Math.floor;\n    var abs$1 = Math.abs;\n\n    function analyze(data, key) {\n        if ( key === void 0 ) key = null;\n\n        var r = {\n            min: Number.MAX_VALUE,\n            max: Number.MAX_VALUE * -1,\n            sum: 0,\n            values: [],\n            count: 0\n        };\n        if (type(data) === 'object') {\n            data = Object.values(data);\n        }\n        data.forEach(function (val) {\n            if (key && type(val) === 'object') { val = val[key]; }\n            if (val !== undefined && val !== null && !isNaN(val)) {\n                r.values.push(val);\n                r.sum += val;\n                if (val < r.min) { r.min = val; }\n                if (val > r.max) { r.max = val; }\n                r.count += 1;\n            }\n        });\n\n        r.domain = [r.min, r.max];\n\n        r.limits = function (mode, num) { return limits(r, mode, num); };\n\n        return r;\n    }\n\n    function limits(data, mode, num) {\n        if ( mode === void 0 ) mode = 'equal';\n        if ( num === void 0 ) num = 7;\n\n        if (type(data) == 'array') {\n            data = analyze(data);\n        }\n        var min = data.min;\n        var max = data.max;\n        var values = data.values.sort(function (a, b) { return a - b; });\n\n        if (num === 1) {\n            return [min, max];\n        }\n\n        var limits = [];\n\n        if (mode.substr(0, 1) === 'c') {\n            // continuous\n            limits.push(min);\n            limits.push(max);\n        }\n\n        if (mode.substr(0, 1) === 'e') {\n            // equal interval\n            limits.push(min);\n            for (var i = 1; i < num; i++) {\n                limits.push(min + (i / num) * (max - min));\n            }\n            limits.push(max);\n        } else if (mode.substr(0, 1) === 'l') {\n            // log scale\n            if (min <= 0) {\n                throw new Error(\n                    'Logarithmic scales are only possible for values > 0'\n                );\n            }\n            var min_log = Math.LOG10E * log(min);\n            var max_log = Math.LOG10E * log(max);\n            limits.push(min);\n            for (var i$1 = 1; i$1 < num; i$1++) {\n                limits.push(pow$1(10, min_log + (i$1 / num) * (max_log - min_log)));\n            }\n            limits.push(max);\n        } else if (mode.substr(0, 1) === 'q') {\n            // quantile scale\n            limits.push(min);\n            for (var i$2 = 1; i$2 < num; i$2++) {\n                var p = ((values.length - 1) * i$2) / num;\n                var pb = floor(p);\n                if (pb === p) {\n                    limits.push(values[pb]);\n                } else {\n                    // p > pb\n                    var pr = p - pb;\n                    limits.push(values[pb] * (1 - pr) + values[pb + 1] * pr);\n                }\n            }\n            limits.push(max);\n        } else if (mode.substr(0, 1) === 'k') {\n            // k-means clustering\n            /*\n            implementation based on\n            http://code.google.com/p/figue/source/browse/trunk/figue.js#336\n            simplified for 1-d input values\n            */\n            var cluster;\n            var n = values.length;\n            var assignments = new Array(n);\n            var clusterSizes = new Array(num);\n            var repeat = true;\n            var nb_iters = 0;\n            var centroids = null;\n\n            // get seed values\n            centroids = [];\n            centroids.push(min);\n            for (var i$3 = 1; i$3 < num; i$3++) {\n                centroids.push(min + (i$3 / num) * (max - min));\n            }\n            centroids.push(max);\n\n            while (repeat) {\n                // assignment step\n                for (var j = 0; j < num; j++) {\n                    clusterSizes[j] = 0;\n                }\n                for (var i$4 = 0; i$4 < n; i$4++) {\n                    var value = values[i$4];\n                    var mindist = Number.MAX_VALUE;\n                    var best = (void 0);\n                    for (var j$1 = 0; j$1 < num; j$1++) {\n                        var dist = abs$1(centroids[j$1] - value);\n                        if (dist < mindist) {\n                            mindist = dist;\n                            best = j$1;\n                        }\n                        clusterSizes[best]++;\n                        assignments[i$4] = best;\n                    }\n                }\n\n                // update centroids step\n                var newCentroids = new Array(num);\n                for (var j$2 = 0; j$2 < num; j$2++) {\n                    newCentroids[j$2] = null;\n                }\n                for (var i$5 = 0; i$5 < n; i$5++) {\n                    cluster = assignments[i$5];\n                    if (newCentroids[cluster] === null) {\n                        newCentroids[cluster] = values[i$5];\n                    } else {\n                        newCentroids[cluster] += values[i$5];\n                    }\n                }\n                for (var j$3 = 0; j$3 < num; j$3++) {\n                    newCentroids[j$3] *= 1 / clusterSizes[j$3];\n                }\n\n                // check convergence\n                repeat = false;\n                for (var j$4 = 0; j$4 < num; j$4++) {\n                    if (newCentroids[j$4] !== centroids[j$4]) {\n                        repeat = true;\n                        break;\n                    }\n                }\n\n                centroids = newCentroids;\n                nb_iters++;\n\n                if (nb_iters > 200) {\n                    repeat = false;\n                }\n            }\n\n            // finished k-means clustering\n            // the next part is borrowed from gabrielflor.it\n            var kClusters = {};\n            for (var j$5 = 0; j$5 < num; j$5++) {\n                kClusters[j$5] = [];\n            }\n            for (var i$6 = 0; i$6 < n; i$6++) {\n                cluster = assignments[i$6];\n                kClusters[cluster].push(values[i$6]);\n            }\n            var tmpKMeansBreaks = [];\n            for (var j$6 = 0; j$6 < num; j$6++) {\n                tmpKMeansBreaks.push(kClusters[j$6][0]);\n                tmpKMeansBreaks.push(kClusters[j$6][kClusters[j$6].length - 1]);\n            }\n            tmpKMeansBreaks = tmpKMeansBreaks.sort(function (a, b) { return a - b; });\n            limits.push(tmpKMeansBreaks[0]);\n            for (var i$7 = 1; i$7 < tmpKMeansBreaks.length; i$7 += 2) {\n                var v = tmpKMeansBreaks[i$7];\n                if (!isNaN(v) && limits.indexOf(v) === -1) {\n                    limits.push(v);\n                }\n            }\n        }\n        return limits;\n    }\n\n    function contrast (a, b) {\n        // WCAG contrast ratio\n        // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef\n        a = new Color(a);\n        b = new Color(b);\n        var l1 = a.luminance();\n        var l2 = b.luminance();\n        return l1 > l2 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05);\n    }\n\n    /**\n     * @license\n     *\n     * The APCA contrast prediction algorithm is based of the formulas published\n     * in the APCA-1.0.98G specification by Myndex. The specification is available at:\n     * https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAw3_0.1.17_APCA0.0.98G.svg\n     *\n     * Note that the APCA implementation is still beta, so please update to\n     * future versions of chroma.js when they become available.\n     *\n     * You can read more about the APCA Readability Criterion at\n     * https://readtech.org/ARC/\n     */\n\n    // constants\n    var W_offset = 0.027;\n    var P_in = 0.0005;\n    var P_out = 0.1;\n    var R_scale = 1.14;\n    var B_threshold = 0.022;\n    var B_exp = 1.414;\n\n    function contrastAPCA (text, bg) {\n        // parse input colors\n        text = new Color(text);\n        bg = new Color(bg);\n        // if text color has alpha, blend against background\n        if (text.alpha() < 1) {\n            text = mix(bg, text, text.alpha(), 'rgb');\n        }\n        var l_text = lum.apply(void 0, text.rgb());\n        var l_bg = lum.apply(void 0, bg.rgb());\n\n        // soft clamp black levels\n        var Y_text =\n            l_text >= B_threshold\n                ? l_text\n                : l_text + Math.pow(B_threshold - l_text, B_exp);\n        var Y_bg =\n            l_bg >= B_threshold ? l_bg : l_bg + Math.pow(B_threshold - l_bg, B_exp);\n\n        // normal polarity (dark text on light background)\n        var S_norm = Math.pow(Y_bg, 0.56) - Math.pow(Y_text, 0.57);\n        // reverse polarity (light text on dark background)\n        var S_rev = Math.pow(Y_bg, 0.65) - Math.pow(Y_text, 0.62);\n        // clamp noise then scale\n        var C =\n            Math.abs(Y_bg - Y_text) < P_in\n                ? 0\n                : Y_text < Y_bg\n                  ? S_norm * R_scale\n                  : S_rev * R_scale;\n        // clamp minimum contrast then offset\n        var S_apc = Math.abs(C) < P_out ? 0 : C > 0 ? C - W_offset : C + W_offset;\n        // scale to 100\n        return S_apc * 100;\n    }\n    function lum(r, g, b) {\n        return (\n            0.2126729 * Math.pow(r / 255, 2.4) +\n            0.7151522 * Math.pow(g / 255, 2.4) +\n            0.072175 * Math.pow(b / 255, 2.4)\n        );\n    }\n\n    var sqrt = Math.sqrt;\n    var pow = Math.pow;\n    var min = Math.min;\n    var max = Math.max;\n    var atan2 = Math.atan2;\n    var abs = Math.abs;\n    var cos = Math.cos;\n    var sin = Math.sin;\n    var exp = Math.exp;\n    var PI = Math.PI;\n\n    function deltaE (a, b, Kl, Kc, Kh) {\n        if ( Kl === void 0 ) Kl = 1;\n        if ( Kc === void 0 ) Kc = 1;\n        if ( Kh === void 0 ) Kh = 1;\n\n        // Delta E (CIE 2000)\n        // see http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html\n        var rad2deg = function (rad) {\n            return (360 * rad) / (2 * PI);\n        };\n        var deg2rad = function (deg) {\n            return (2 * PI * deg) / 360;\n        };\n        a = new Color(a);\n        b = new Color(b);\n        var ref = Array.from(a.lab());\n        var L1 = ref[0];\n        var a1 = ref[1];\n        var b1 = ref[2];\n        var ref$1 = Array.from(b.lab());\n        var L2 = ref$1[0];\n        var a2 = ref$1[1];\n        var b2 = ref$1[2];\n        var avgL = (L1 + L2) / 2;\n        var C1 = sqrt(pow(a1, 2) + pow(b1, 2));\n        var C2 = sqrt(pow(a2, 2) + pow(b2, 2));\n        var avgC = (C1 + C2) / 2;\n        var G = 0.5 * (1 - sqrt(pow(avgC, 7) / (pow(avgC, 7) + pow(25, 7))));\n        var a1p = a1 * (1 + G);\n        var a2p = a2 * (1 + G);\n        var C1p = sqrt(pow(a1p, 2) + pow(b1, 2));\n        var C2p = sqrt(pow(a2p, 2) + pow(b2, 2));\n        var avgCp = (C1p + C2p) / 2;\n        var arctan1 = rad2deg(atan2(b1, a1p));\n        var arctan2 = rad2deg(atan2(b2, a2p));\n        var h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360;\n        var h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360;\n        var avgHp =\n            abs(h1p - h2p) > 180 ? (h1p + h2p + 360) / 2 : (h1p + h2p) / 2;\n        var T =\n            1 -\n            0.17 * cos(deg2rad(avgHp - 30)) +\n            0.24 * cos(deg2rad(2 * avgHp)) +\n            0.32 * cos(deg2rad(3 * avgHp + 6)) -\n            0.2 * cos(deg2rad(4 * avgHp - 63));\n        var deltaHp = h2p - h1p;\n        deltaHp =\n            abs(deltaHp) <= 180\n                ? deltaHp\n                : h2p <= h1p\n                  ? deltaHp + 360\n                  : deltaHp - 360;\n        deltaHp = 2 * sqrt(C1p * C2p) * sin(deg2rad(deltaHp) / 2);\n        var deltaL = L2 - L1;\n        var deltaCp = C2p - C1p;\n        var sl = 1 + (0.015 * pow(avgL - 50, 2)) / sqrt(20 + pow(avgL - 50, 2));\n        var sc = 1 + 0.045 * avgCp;\n        var sh = 1 + 0.015 * avgCp * T;\n        var deltaTheta = 30 * exp(-pow((avgHp - 275) / 25, 2));\n        var Rc = 2 * sqrt(pow(avgCp, 7) / (pow(avgCp, 7) + pow(25, 7)));\n        var Rt = -Rc * sin(2 * deg2rad(deltaTheta));\n        var result = sqrt(\n            pow(deltaL / (Kl * sl), 2) +\n                pow(deltaCp / (Kc * sc), 2) +\n                pow(deltaHp / (Kh * sh), 2) +\n                Rt * (deltaCp / (Kc * sc)) * (deltaHp / (Kh * sh))\n        );\n        return max(0, min(100, result));\n    }\n\n    // simple Euclidean distance\n    function distance (a, b, mode) {\n        if ( mode === void 0 ) mode = 'lab';\n\n        // Delta E (CIE 1976)\n        // see http://www.brucelindbloom.com/index.html?Equations.html\n        a = new Color(a);\n        b = new Color(b);\n        var l1 = a.get(mode);\n        var l2 = b.get(mode);\n        var sum_sq = 0;\n        for (var i in l1) {\n            var d = (l1[i] || 0) - (l2[i] || 0);\n            sum_sq += d * d;\n        }\n        return Math.sqrt(sum_sq);\n    }\n\n    function valid () {\n        var args = [], len = arguments.length;\n        while ( len-- ) args[ len ] = arguments[ len ];\n\n        try {\n            new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));\n            return true;\n            // eslint-disable-next-line\n        } catch (e) {\n            return false;\n        }\n    }\n\n    // some pre-defined color scales:\n\n    var scales = {\n        cool: function cool() {\n            return scale([chroma.hsl(180, 1, 0.9), chroma.hsl(250, 0.7, 0.4)]);\n        },\n        hot: function hot() {\n            return scale(['#000', '#f00', '#ff0', '#fff']).mode(\n                'rgb'\n            );\n        }\n    };\n\n    /**\n        ColorBrewer colors for chroma.js\n\n        Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The\n        Pennsylvania State University.\n\n        Licensed under the Apache License, Version 2.0 (the \"License\");\n        you may not use this file except in compliance with the License.\n        You may obtain a copy of the License at\n        http://www.apache.org/licenses/LICENSE-2.0\n\n        Unless required by applicable law or agreed to in writing, software distributed\n        under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n        CONDITIONS OF ANY KIND, either express or implied. See the License for the\n        specific language governing permissions and limitations under the License.\n    */\n\n    var colorbrewer = {\n        // sequential\n        OrRd: ['#fff7ec', '#fee8c8', '#fdd49e', '#fdbb84', '#fc8d59', '#ef6548', '#d7301f', '#b30000', '#7f0000'],\n        PuBu: ['#fff7fb', '#ece7f2', '#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0', '#0570b0', '#045a8d', '#023858'],\n        BuPu: ['#f7fcfd', '#e0ecf4', '#bfd3e6', '#9ebcda', '#8c96c6', '#8c6bb1', '#88419d', '#810f7c', '#4d004b'],\n        Oranges: ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704'],\n        BuGn: ['#f7fcfd', '#e5f5f9', '#ccece6', '#99d8c9', '#66c2a4', '#41ae76', '#238b45', '#006d2c', '#00441b'],\n        YlOrBr: ['#ffffe5', '#fff7bc', '#fee391', '#fec44f', '#fe9929', '#ec7014', '#cc4c02', '#993404', '#662506'],\n        YlGn: ['#ffffe5', '#f7fcb9', '#d9f0a3', '#addd8e', '#78c679', '#41ab5d', '#238443', '#006837', '#004529'],\n        Reds: ['#fff5f0', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d'],\n        RdPu: ['#fff7f3', '#fde0dd', '#fcc5c0', '#fa9fb5', '#f768a1', '#dd3497', '#ae017e', '#7a0177', '#49006a'],\n        Greens: ['#f7fcf5', '#e5f5e0', '#c7e9c0', '#a1d99b', '#74c476', '#41ab5d', '#238b45', '#006d2c', '#00441b'],\n        YlGnBu: ['#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58'],\n        Purples: ['#fcfbfd', '#efedf5', '#dadaeb', '#bcbddc', '#9e9ac8', '#807dba', '#6a51a3', '#54278f', '#3f007d'],\n        GnBu: ['#f7fcf0', '#e0f3db', '#ccebc5', '#a8ddb5', '#7bccc4', '#4eb3d3', '#2b8cbe', '#0868ac', '#084081'],\n        Greys: ['#ffffff', '#f0f0f0', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525', '#000000'],\n        YlOrRd: ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'],\n        PuRd: ['#f7f4f9', '#e7e1ef', '#d4b9da', '#c994c7', '#df65b0', '#e7298a', '#ce1256', '#980043', '#67001f'],\n        Blues: ['#f7fbff', '#deebf7', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5', '#08519c', '#08306b'],\n        PuBuGn: ['#fff7fb', '#ece2f0', '#d0d1e6', '#a6bddb', '#67a9cf', '#3690c0', '#02818a', '#016c59', '#014636'],\n        Viridis: ['#440154', '#482777', '#3f4a8a', '#31678e', '#26838f', '#1f9d8a', '#6cce5a', '#b6de2b', '#fee825'],\n\n        // diverging\n        Spectral: ['#9e0142', '#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#e6f598', '#abdda4', '#66c2a5', '#3288bd', '#5e4fa2'],\n        RdYlGn: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850', '#006837'],\n        RdBu: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#f7f7f7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],\n        PiYG: ['#8e0152', '#c51b7d', '#de77ae', '#f1b6da', '#fde0ef', '#f7f7f7', '#e6f5d0', '#b8e186', '#7fbc41', '#4d9221', '#276419'],\n        PRGn: ['#40004b', '#762a83', '#9970ab', '#c2a5cf', '#e7d4e8', '#f7f7f7', '#d9f0d3', '#a6dba0', '#5aae61', '#1b7837', '#00441b'],\n        RdYlBu: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'],\n        BrBG: ['#543005', '#8c510a', '#bf812d', '#dfc27d', '#f6e8c3', '#f5f5f5', '#c7eae5', '#80cdc1', '#35978f', '#01665e', '#003c30'],\n        RdGy: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#ffffff', '#e0e0e0', '#bababa', '#878787', '#4d4d4d', '#1a1a1a'],\n        PuOr: ['#7f3b08', '#b35806', '#e08214', '#fdb863', '#fee0b6', '#f7f7f7', '#d8daeb', '#b2abd2', '#8073ac', '#542788', '#2d004b'],\n\n        // qualitative\n        Set2: ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3'],\n        Accent: ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666'],\n        Set1: ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999'],\n        Set3: ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', '#ccebc5', '#ffed6f'],\n        Dark2: ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d', '#666666'],\n        Paired: ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a', '#ffff99', '#b15928'],\n        Pastel2: ['#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc'],\n        Pastel1: ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2']\n    };\n\n    var colorbrewerTypes = Object.keys(colorbrewer);\n    var typeMap = new Map(colorbrewerTypes.map(function (key) { return [key.toLowerCase(), key]; }));\n\n    // use Proxy to allow case-insensitive access to palettes\n    var colorbrewerProxy =\n        typeof Proxy === 'function'\n            ? new Proxy(colorbrewer, {\n                  get: function get(target, prop) {\n                      var lower = prop.toLowerCase();\n                      if (typeMap.has(lower)) {\n                          return target[typeMap.get(lower)];\n                      }\n                  },\n                  getOwnPropertyNames: function getOwnPropertyNames() {\n                      return Object.getOwnPropertyNames(colorbrewerTypes);\n                  }\n              })\n            : colorbrewer;\n\n    // feel free to comment out anything to rollup\n    // a smaller chroma.js bundle\n\n    Object.assign(chroma, {\n        analyze: analyze,\n        average: average,\n        bezier: bezier$1,\n        blend: blend,\n        brewer: colorbrewerProxy,\n        Color: Color,\n        colors: w3cx11,\n        contrast: contrast,\n        contrastAPCA: contrastAPCA,\n        cubehelix: cubehelix,\n        deltaE: deltaE,\n        distance: distance,\n        input: input,\n        interpolate: mix,\n        limits: limits,\n        mix: mix,\n        random: random$1,\n        scale: scale,\n        scales: scales,\n        valid: valid,\n        cmyk: cmyk,\n        css: css,\n        gl: gl,\n        hcg: hcg$1,\n        hex: hex,\n        hsi: hsi$1,\n        hsl: hsl$1,\n        hsv: hsv$1,\n        lab: lab$1,\n        lch: lch$1,\n        hcl: hcl,\n        num: num$1,\n        rgb: rgb$1,\n        temp: temp,\n        kelvin: temp,\n        temperature: temp,\n        oklab: oklab$1,\n        oklch: oklch$1,\n        getLabWhitePoint: getLabWhitePoint,\n        setLabWhitePoint: setLabWhitePoint\n    });\n\n    return chroma;\n\n}));\n"
  },
  {
    "path": "docs/libs/chroma.min.cjs",
    "content": "/**\n * chroma.js - JavaScript library for color conversions\n *\n * Copyright (c) 2011-2025, Gregor Aisch\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * -------------------------------------------------------\n *\n * chroma.js includes colors from colorbrewer2.org, which are released under\n * the following license:\n *\n * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,\n * and The Pennsylvania State University.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n * either express or implied. See the License for the specific\n * language governing permissions and limitations under the License.\n *\n * ------------------------------------------------------\n *\n * Named colors are taken from X11 Color Names.\n * http://www.w3.org/TR/css3-color/#svg-color\n *\n * @preserve\n */\n\n!function(r,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n():\"function\"==typeof define&&define.amd?define(n):(r=\"undefined\"!=typeof globalThis?globalThis:r||self).chroma=n()}(this,(function(){\"use strict\";var r=Math.min,n=Math.max;function e(e,t,a){return void 0===t&&(t=0),void 0===a&&(a=1),r(n(t,e),a)}function t(r){r._clipped=!1,r._unclipped=r.slice(0);for(var n=0;n<=3;n++)n<3?((r[n]<0||r[n]>255)&&(r._clipped=!0),r[n]=e(r[n],0,255)):3===n&&(r[n]=e(r[n],0,1));return r}for(var a={},f=0,o=[\"Boolean\",\"Number\",\"String\",\"Function\",\"Array\",\"Date\",\"RegExp\",\"Undefined\",\"Null\"];f<o.length;f+=1){var u=o[f];a[\"[object \"+u+\"]\"]=u.toLowerCase()}function c(r){return a[Object.prototype.toString.call(r)]||\"object\"}function i(r,n){return void 0===n&&(n=null),r.length>=3?Array.prototype.slice.call(r):\"object\"==c(r[0])&&n?n.split(\"\").filter((function(n){return void 0!==r[0][n]})).map((function(n){return r[0][n]})):r[0].slice(0)}function l(r){if(r.length<2)return null;var n=r.length-1;return\"string\"==c(r[n])?r[n].toLowerCase():null}var h=Math.PI,s=Math.min,d=Math.max,b=function(r){return Math.round(100*r)/100},g=function(r){return Math.round(100*r)/100},v=2*h,p=h/3,m=h/180,y=180/h;function w(r){return r.slice(0,3).reverse().concat(r.slice(3))}var k={format:{},autodetect:[]},M=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=this;if(\"object\"===c(r[0])&&r[0].constructor&&r[0].constructor===this.constructor)return r[0];var a=l(r),f=!1;if(!a){f=!0,k.sorted||(k.autodetect=k.autodetect.sort((function(r,n){return n.p-r.p})),k.sorted=!0);for(var o=0,u=k.autodetect;o<u.length;o+=1){var i=u[o];if(a=i.test.apply(i,r))break}}if(!k.format[a])throw new Error(\"unknown format: \"+r);var h=k.format[a].apply(null,f?r:r.slice(0,-1));e._rgb=t(h),3===e._rgb.length&&e._rgb.push(1)};M.prototype.toString=function(){return\"function\"==c(this.hex)?this.hex():\"[\"+this._rgb.join(\",\")+\"]\"};var N=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r)))};N.version=\"3.2.0\";var x=Math.max;M.prototype.cmyk=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=1-x(t/=255,x(a/=255,f/=255)),u=o<1?1/(1-o):0;return[(1-t-o)*u,(1-a-o)*u,(1-f-o)*u,o]}(this._rgb)};var _=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"cmyk\"])))};Object.assign(N,{cmyk:_}),k.format.cmyk=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"cmyk\"))[0],t=r[1],a=r[2],f=r[3],o=r.length>4?r[4]:1;return 1===f?[0,0,0,o]:[e>=1?0:255*(1-e)*(1-f),t>=1?0:255*(1-t)*(1-f),a>=1?0:255*(1-a)*(1-f),o]},k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"cmyk\"))&&4===r.length)return\"cmyk\"}});var A=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a=(r=i(r,\"rgba\"))[0],f=r[1],o=r[2],u=s(a/=255,f/=255,o/=255),c=d(a,f,o),l=(c+u)/2;return c===u?(e=0,t=Number.NaN):e=l<.5?(c-u)/(c+u):(c-u)/(2-c-u),a==c?t=(f-o)/(c-u):f==c?t=2+(o-a)/(c-u):o==c&&(t=4+(a-f)/(c-u)),(t*=60)<0&&(t+=360),r.length>3&&void 0!==r[3]?[t,e,l,r[3]]:[t,e,l]},j={Kn:18,labWhitePoint:\"d65\",Xn:.95047,Yn:1,Zn:1.08883,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},E=new Map([[\"a\",[1.0985,.35585]],[\"b\",[1.0985,.35585]],[\"c\",[.98074,1.18232]],[\"d50\",[.96422,.82521]],[\"d55\",[.95682,.92149]],[\"d65\",[.95047,1.08883]],[\"e\",[1,1,1]],[\"f2\",[.99186,.67393]],[\"f7\",[.95041,1.08747]],[\"f11\",[1.00962,.6435]],[\"icc\",[.96422,.82521]]]);function R(r){var n=E.get(String(r).toLowerCase());if(!n)throw new Error(\"unknown Lab illuminant \"+r);j.labWhitePoint=r,j.Xn=n[0],j.Zn=n[1]}function O(){return j.labWhitePoint}var P=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=e.slice(3),u=L(t,a,f),c=function(r,n,e){var t=j.Xn,a=j.Yn,f=j.Zn,o=j.kE,u=j.kK,c=r/t,i=n/a,l=e/f,h=c>o?Math.pow(c,1/3):(u*c+16)/116,s=i>o?Math.pow(i,1/3):(u*i+16)/116,d=l>o?Math.pow(l,1/3):(u*l+16)/116;return[116*s-16,500*(h-s),200*(s-d)]}(u[0],u[1],u[2]);return[c[0],c[1],c[2]].concat(o.length>0&&o[0]<1?[o[0]]:[])};function F(r){var n=Math.sign(r);return((r=Math.abs(r))<=.04045?r/12.92:Math.pow((r+.055)/1.055,2.4))*n}var L=function(r,n,e){r=F(r/255),n=F(n/255),e=F(e/255);var t=j.MtxRGB2XYZ,a=j.MtxAdaptMa,f=j.MtxAdaptMaI,o=j.Xn,u=j.Yn,c=j.Zn,i=j.As,l=j.Bs,h=j.Cs,s=r*t.m00+n*t.m10+e*t.m20,d=r*t.m01+n*t.m11+e*t.m21,b=r*t.m02+n*t.m12+e*t.m22,g=o*a.m00+u*a.m10+c*a.m20,v=o*a.m01+u*a.m11+c*a.m21,p=o*a.m02+u*a.m12+c*a.m22,m=s*a.m00+d*a.m10+b*a.m20,y=s*a.m01+d*a.m11+b*a.m21,w=s*a.m02+d*a.m12+b*a.m22;return y*=v/l,w*=p/h,[s=(m*=g/i)*f.m00+y*f.m10+w*f.m20,d=m*f.m01+y*f.m11+w*f.m21,b=m*f.m02+y*f.m12+w*f.m22]},B=Math.sqrt,G=Math.atan2,Y=Math.round,q=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lab\"),t=e[0],a=e[1],f=e[2],o=B(a*a+f*f),u=(G(f,a)*y+360)%360;return 0===Y(1e4*o)&&(u=Number.NaN),[t,o,u]},C=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=e.slice(3),u=P(t,a,f),c=u[0],l=u[1],h=u[2],s=q(c,l,h);return[s[0],s[1],s[2]].concat(o.length>0&&o[0]<1?[o[0]]:[])};function X(r,n){var e=r.length;Array.isArray(r[0])||(r=[r]),Array.isArray(n[0])||(n=n.map((function(r){return[r]})));var t=n[0].length,a=n[0].map((function(r,e){return n.map((function(r){return r[e]}))})),f=r.map((function(r){return a.map((function(n){return Array.isArray(r)?r.reduce((function(r,e,t){return r+e*(n[t]||0)}),0):n.reduce((function(n,e){return n+e*r}),0)}))}));return 1===e&&(f=f[0]),1===t?f.map((function(r){return r[0]})):f}var Z=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a=i(r,\"rgb\"),f=a[0],o=a[1],u=a[2],c=a.slice(3),l=L(f,o,u);return(e=[[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],t=X([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],l),X(e,t.map((function(r){return Math.cbrt(r)})))).concat(c.length>0&&c[0]<1?[c[0]]:[])};var $=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\"),t=e[0],a=e[1],f=e[2],o=e.slice(3),u=Z(t,a,f),c=u[0],l=u[1],h=u[2],s=q(c,l,h);return[s[0],s[1],s[2]].concat(o.length>0&&o[0]<1?[o[0]]:[])},S=Math.round,W=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\"),t=l(r)||\"rgb\";if(\"hsl\"===t.substr(0,3))return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"hsla\"),t=l(r)||\"lsa\";return e[0]=b(e[0]||0)+\"deg\",e[1]=b(100*e[1])+\"%\",e[2]=b(100*e[2])+\"%\",\"hsla\"===t||e.length>3&&e[3]<1?(e[3]=\"/ \"+(e.length>3?e[3]:1),t=\"hsla\"):e.length=3,t.substr(0,3)+\"(\"+e.join(\" \")+\")\"}(A(e),t);if(\"lab\"===t.substr(0,3)){var a=O();R(\"d50\");var f=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lab\"),t=l(r)||\"lab\";return e[0]=b(e[0])+\"%\",e[1]=b(e[1]),e[2]=b(e[2]),\"laba\"===t||e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"lab(\"+e.join(\" \")+\")\"}(P(e),t);return R(a),f}if(\"lch\"===t.substr(0,3)){var o=O();R(\"d50\");var u=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lch\"),t=l(r)||\"lab\";return e[0]=b(e[0])+\"%\",e[1]=b(e[1]),e[2]=isNaN(e[2])?\"none\":b(e[2])+\"deg\",\"lcha\"===t||e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"lch(\"+e.join(\" \")+\")\"}(C(e),t);return R(o),u}return\"oklab\"===t.substr(0,5)?function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lab\");return e[0]=b(100*e[0])+\"%\",e[1]=g(e[1]),e[2]=g(e[2]),e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"oklab(\"+e.join(\" \")+\")\"}(Z(e)):\"oklch\"===t.substr(0,5)?function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lch\");return e[0]=b(100*e[0])+\"%\",e[1]=g(e[1]),e[2]=isNaN(e[2])?\"none\":b(e[2])+\"deg\",e.length>3&&e[3]<1?e[3]=\"/ \"+(e.length>3?e[3]:1):e.length=3,\"oklch(\"+e.join(\" \")+\")\"}($(e)):(e[0]=S(e[0]),e[1]=S(e[1]),e[2]=S(e[2]),(\"rgba\"===t||e.length>3&&e[3]<1)&&(e[3]=\"/ \"+(e.length>3?e[3]:1),t=\"rgba\"),t.substr(0,3)+\"(\"+e.slice(0,\"rgb\"===t?3:4).join(\" \")+\")\")},I=function(){for(var r,n=[],e=arguments.length;e--;)n[e]=arguments[e];var t,a,f,o=(n=i(n,\"hsl\"))[0],u=n[1],c=n[2];if(0===u)t=a=f=255*c;else{var l=[0,0,0],h=[0,0,0],s=c<.5?c*(1+u):c+u-c*u,d=2*c-s,b=o/360;l[0]=b+1/3,l[1]=b,l[2]=b-1/3;for(var g=0;g<3;g++)l[g]<0&&(l[g]+=1),l[g]>1&&(l[g]-=1),6*l[g]<1?h[g]=d+6*(s-d)*l[g]:2*l[g]<1?h[g]=s:3*l[g]<2?h[g]=d+(s-d)*(2/3-l[g])*6:h[g]=d;t=(r=[255*h[0],255*h[1],255*h[2]])[0],a=r[1],f=r[2]}return n.length>3?[t,a,f,n[3]]:[t,a,f,1]},K=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"lab\"))[0],t=r[1],a=r[2],f=z(e,t,a),o=f[0],u=f[1],c=f[2],l=V(o,u,c);return[l[0],l[1],l[2],r.length>3?r[3]:1]},z=function(r,n,e){var t=j.kE,a=j.kK,f=j.kKE,o=j.Xn,u=j.Yn,c=j.Zn,i=(r+16)/116,l=.002*n+i,h=i-.005*e,s=l*l*l,d=h*h*h;return[(s>t?s:(116*l-16)/a)*o,(r>f?Math.pow((r+16)/116,3):r/a)*u,(d>t?d:(116*h-16)/a)*c]},U=function(r){var n=Math.sign(r);return((r=Math.abs(r))<=.0031308?12.92*r:1.055*Math.pow(r,1/2.4)-.055)*n},V=function(r,n,e){var t=j.MtxAdaptMa,a=j.MtxAdaptMaI,f=j.MtxXYZ2RGB,o=j.RefWhiteRGB,u=j.Xn,c=j.Yn,i=j.Zn,l=u*t.m00+c*t.m10+i*t.m20,h=u*t.m01+c*t.m11+i*t.m21,s=u*t.m02+c*t.m12+i*t.m22,d=o.X*t.m00+o.Y*t.m10+o.Z*t.m20,b=o.X*t.m01+o.Y*t.m11+o.Z*t.m21,g=o.X*t.m02+o.Y*t.m12+o.Z*t.m22,v=(r*t.m00+n*t.m10+e*t.m20)*(d/l),p=(r*t.m01+n*t.m11+e*t.m21)*(b/h),m=(r*t.m02+n*t.m12+e*t.m22)*(g/s),y=v*a.m00+p*a.m10+m*a.m20,w=v*a.m01+p*a.m11+m*a.m21,k=v*a.m02+p*a.m12+m*a.m22;return[255*U(y*f.m00+w*f.m10+k*f.m20),255*U(y*f.m01+w*f.m11+k*f.m21),255*U(y*f.m02+w*f.m12+k*f.m22)]},D=Math.sin,T=Math.cos,H=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"lch\"),t=e[0],a=e[1],f=e[2];return isNaN(f)&&(f=0),[t,T(f*=m)*a,D(f)*a]},J=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"lch\"))[0],t=r[1],a=r[2],f=H(e,t,a),o=f[0],u=f[1],c=f[2],l=K(o,u,c);return[l[0],l[1],l[2],r.length>3?r[3]:1]},Q=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a=(r=i(r,\"lab\"))[0],f=r[1],o=r[2],u=r.slice(3),c=(e=[[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],t=X([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],[a,f,o]),X(e,t.map((function(r){return Math.pow(r,3)})))),l=c[0],h=c[1],s=c[2],d=V(l,h,s);return[d[0],d[1],d[2]].concat(u.length>0&&u[0]<1?[u[0]]:[])};var rr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=(r=i(r,\"lch\"))[0],t=r[1],a=r[2],f=r.slice(3),o=H(e,t,a),u=o[0],c=o[1],l=o[2],h=Q(u,c,l);return[h[0],h[1],h[2]].concat(f.length>0&&f[0]<1?[f[0]]:[])},nr=/((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source,er=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source,tr=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source,ar=/\\s*/.source,fr=/\\s+/.source,or=/\\s*,\\s*/.source,ur=/((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source,cr=/\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source,ir=new RegExp(\"^rgba?\\\\(\"+ar+[nr,nr,nr].join(fr)+cr+\"\\\\)$\"),lr=new RegExp(\"^rgb\\\\(\"+ar+[nr,nr,nr].join(or)+ar+\"\\\\)$\"),hr=new RegExp(\"^rgba\\\\(\"+ar+[nr,nr,nr,er].join(or)+ar+\"\\\\)$\"),sr=new RegExp(\"^hsla?\\\\(\"+ar+[ur,tr,tr].join(fr)+cr+\"\\\\)$\"),dr=new RegExp(\"^hsl?\\\\(\"+ar+[ur,tr,tr].join(or)+ar+\"\\\\)$\"),br=/^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/,gr=new RegExp(\"^lab\\\\(\"+ar+[er,er,er].join(fr)+cr+\"\\\\)$\"),vr=new RegExp(\"^lch\\\\(\"+ar+[er,er,ur].join(fr)+cr+\"\\\\)$\"),pr=new RegExp(\"^oklab\\\\(\"+ar+[er,er,er].join(fr)+cr+\"\\\\)$\"),mr=new RegExp(\"^oklch\\\\(\"+ar+[er,er,ur].join(fr)+cr+\"\\\\)$\"),yr=Math.round,wr=function(r){return r.map((function(r,n){return n<=2?e(yr(r),0,255):r}))},kr=function(r,n,e,t){return void 0===n&&(n=0),void 0===e&&(e=100),void 0===t&&(t=!1),\"string\"==typeof r&&r.endsWith(\"%\")&&(r=parseFloat(r.substring(0,r.length-1))/100,r=t?n+.5*(r+1)*(e-n):n+r*(e-n)),+r},Mr=function(r,n){return\"none\"===r?n:r},Nr=function(r){if(\"transparent\"===(r=r.toLowerCase().trim()))return[0,0,0,0];var n;if(k.format.named)try{return k.format.named(r)}catch(r){}if((n=r.match(ir))||(n=r.match(lr))){for(var e=n.slice(1,4),t=0;t<3;t++)e[t]=+kr(Mr(e[t],0),0,255);e=wr(e);var a=void 0!==n[4]?+kr(n[4],0,1):1;return e[3]=a,e}if(n=r.match(hr)){for(var f=n.slice(1,5),o=0;o<4;o++)f[o]=+kr(f[o],0,255);return f}if((n=r.match(sr))||(n=r.match(dr))){var u=n.slice(1,4);u[0]=+Mr(u[0].replace(\"deg\",\"\"),0),u[1]=.01*+kr(Mr(u[1],0),0,100),u[2]=.01*+kr(Mr(u[2],0),0,100);var c=wr(I(u)),i=void 0!==n[4]?+kr(n[4],0,1):1;return c[3]=i,c}if(n=r.match(br)){var l=n.slice(1,4);l[1]*=.01,l[2]*=.01;for(var h=I(l),s=0;s<3;s++)h[s]=yr(h[s]);return h[3]=+n[4],h}if(n=r.match(gr)){var d=n.slice(1,4);d[0]=kr(Mr(d[0],0),0,100),d[1]=kr(Mr(d[1],0),-125,125,!0),d[2]=kr(Mr(d[2],0),-125,125,!0);var b=O();R(\"d50\");var g=wr(K(d));R(b);var v=void 0!==n[4]?+kr(n[4],0,1):1;return g[3]=v,g}if(n=r.match(vr)){var p=n.slice(1,4);p[0]=kr(p[0],0,100),p[1]=kr(Mr(p[1],0),0,150,!1),p[2]=+Mr(p[2].replace(\"deg\",\"\"),0);var m=O();R(\"d50\");var y=wr(J(p));R(m);var w=void 0!==n[4]?+kr(n[4],0,1):1;return y[3]=w,y}if(n=r.match(pr)){var M=n.slice(1,4);M[0]=kr(Mr(M[0],0),0,1),M[1]=kr(Mr(M[1],0),-.4,.4,!0),M[2]=kr(Mr(M[2],0),-.4,.4,!0);var N=wr(Q(M)),x=void 0!==n[4]?+kr(n[4],0,1):1;return N[3]=x,N}if(n=r.match(mr)){var _=n.slice(1,4);_[0]=kr(Mr(_[0],0),0,1),_[1]=kr(Mr(_[1],0),0,.4,!1),_[2]=+Mr(_[2].replace(\"deg\",\"\"),0);var A=wr(rr(_)),j=void 0!==n[4]?+kr(n[4],0,1):1;return A[3]=j,A}};Nr.test=function(r){return ir.test(r)||sr.test(r)||gr.test(r)||vr.test(r)||pr.test(r)||mr.test(r)||lr.test(r)||hr.test(r)||dr.test(r)||br.test(r)||\"transparent\"===r},M.prototype.css=function(r){return W(this._rgb,r)};var xr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"css\"])))};N.css=xr,k.format.css=Nr,k.autodetect.push({p:5,test:function(r){for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];if(!n.length&&\"string\"===c(r)&&Nr.test(r))return\"css\"}}),k.format.gl=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\");return e[0]*=255,e[1]*=255,e[2]*=255,e};var _r=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"gl\"])))};N.gl=_r,M.prototype.gl=function(){var r=this._rgb;return[r[0]/255,r[1]/255,r[2]/255,r[3]]};var Ar=Math.floor;M.prototype.hcg=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t=i(r,\"rgb\"),a=t[0],f=t[1],o=t[2],u=s(a,f,o),c=d(a,f,o),l=c-u,h=100*l/255,b=u/(255-l)*100;return 0===l?e=Number.NaN:(a===c&&(e=(f-o)/l),f===c&&(e=2+(o-a)/l),o===c&&(e=4+(a-f)/l),(e*=60)<0&&(e+=360)),[e,h,b]}(this._rgb)};var jr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hcg\"])))};N.hcg=jr,k.format.hcg=function(){for(var r,n,e,t,a,f,o=[],u=arguments.length;u--;)o[u]=arguments[u];var c,l,h,s=(o=i(o,\"hcg\"))[0],d=o[1],b=o[2];b*=255;var g=255*d;if(0===d)c=l=h=b;else{360===s&&(s=0),s>360&&(s-=360),s<0&&(s+=360);var v=Ar(s/=60),p=s-v,m=b*(1-d),y=m+g*(1-p),w=m+g*p,k=m+g;switch(v){case 0:c=(r=[k,w,m])[0],l=r[1],h=r[2];break;case 1:c=(n=[y,k,m])[0],l=n[1],h=n[2];break;case 2:c=(e=[m,k,w])[0],l=e[1],h=e[2];break;case 3:c=(t=[m,y,k])[0],l=t[1],h=t[2];break;case 4:c=(a=[w,m,k])[0],l=a[1],h=a[2];break;case 5:c=(f=[k,m,y])[0],l=f[1],h=f[2]}}return[c,l,h,o.length>3?o[3]:1]},k.autodetect.push({p:1,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hcg\"))&&3===r.length)return\"hcg\"}});var Er=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,Rr=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,Or=function(r){if(r.match(Er)){4!==r.length&&7!==r.length||(r=r.substr(1)),3===r.length&&(r=(r=r.split(\"\"))[0]+r[0]+r[1]+r[1]+r[2]+r[2]);var n=parseInt(r,16);return[n>>16,n>>8&255,255&n,1]}if(r.match(Rr)){5!==r.length&&9!==r.length||(r=r.substr(1)),4===r.length&&(r=(r=r.split(\"\"))[0]+r[0]+r[1]+r[1]+r[2]+r[2]+r[3]+r[3]);var e=parseInt(r,16);return[e>>24&255,e>>16&255,e>>8&255,Math.round((255&e)/255*100)/100]}throw new Error(\"unknown hex color: \"+r)},Pr=Math.round,Fr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\"),t=e[0],a=e[1],f=e[2],o=e[3],u=l(r)||\"auto\";void 0===o&&(o=1),\"auto\"===u&&(u=o<1?\"rgba\":\"rgb\");var c=\"000000\"+((t=Pr(t))<<16|(a=Pr(a))<<8|(f=Pr(f))).toString(16);c=c.substr(c.length-6);var h=\"0\"+Pr(255*o).toString(16);switch(h=h.substr(h.length-2),u.toLowerCase()){case\"rgba\":return\"#\"+c+h;case\"argb\":return\"#\"+h+c;default:return\"#\"+c}};M.prototype.hex=function(r){return Fr(this._rgb,r)};var Lr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hex\"])))};N.hex=Lr,k.format.hex=Or,k.autodetect.push({p:4,test:function(r){for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];if(!n.length&&\"string\"===c(r)&&[3,4,5,6,7,8,9].indexOf(r.length)>=0)return\"hex\"}});var Br=Math.cos,Gr=Math.min,Yr=Math.sqrt,qr=Math.acos;M.prototype.hsi=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t=i(r,\"rgb\"),a=t[0],f=t[1],o=t[2],u=Gr(a/=255,f/=255,o/=255),c=(a+f+o)/3,l=c>0?1-u/c:0;return 0===l?e=NaN:(e=(a-f+(a-o))/2,e/=Yr((a-f)*(a-f)+(a-o)*(f-o)),e=qr(e),o>f&&(e=v-e),e/=v),[360*e,l,c]}(this._rgb)};var Cr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hsi\"])))};N.hsi=Cr,k.format.hsi=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var t,a,f,o=(r=i(r,\"hsi\"))[0],u=r[1],c=r[2];return isNaN(o)&&(o=0),isNaN(u)&&(u=0),o>360&&(o-=360),o<0&&(o+=360),(o/=360)<1/3?a=1-((f=(1-u)/3)+(t=(1+u*Br(v*o)/Br(p-v*o))/3)):o<2/3?f=1-((t=(1-u)/3)+(a=(1+u*Br(v*(o-=1/3))/Br(p-v*o))/3)):t=1-((a=(1-u)/3)+(f=(1+u*Br(v*(o-=2/3))/Br(p-v*o))/3)),[255*(t=e(c*t*3)),255*(a=e(c*a*3)),255*(f=e(c*f*3)),r.length>3?r[3]:1]},k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hsi\"))&&3===r.length)return\"hsi\"}}),M.prototype.hsl=function(){return A(this._rgb)};var Xr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hsl\"])))};N.hsl=Xr,k.format.hsl=I,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hsl\"))&&3===r.length)return\"hsl\"}});var Zr=Math.floor,$r=Math.min,Sr=Math.max;M.prototype.hsv=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e,t,a,f=(r=i(r,\"rgb\"))[0],o=r[1],u=r[2],c=$r(f,o,u),l=Sr(f,o,u),h=l-c;return a=l/255,0===l?(e=Number.NaN,t=0):(t=h/l,f===l&&(e=(o-u)/h),o===l&&(e=2+(u-f)/h),u===l&&(e=4+(f-o)/h),(e*=60)<0&&(e+=360)),[e,t,a]}(this._rgb)};var Wr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hsv\"])))};N.hsv=Wr,k.format.hsv=function(){for(var r,n,e,t,a,f,o=[],u=arguments.length;u--;)o[u]=arguments[u];var c,l,h,s=(o=i(o,\"hsv\"))[0],d=o[1],b=o[2];if(b*=255,0===d)c=l=h=b;else{360===s&&(s=0),s>360&&(s-=360),s<0&&(s+=360);var g=Zr(s/=60),v=s-g,p=b*(1-d),m=b*(1-d*v),y=b*(1-d*(1-v));switch(g){case 0:c=(r=[b,y,p])[0],l=r[1],h=r[2];break;case 1:c=(n=[m,b,p])[0],l=n[1],h=n[2];break;case 2:c=(e=[p,b,y])[0],l=e[1],h=e[2];break;case 3:c=(t=[p,m,b])[0],l=t[1],h=t[2];break;case 4:c=(a=[y,p,b])[0],l=a[1],h=a[2];break;case 5:c=(f=[b,p,m])[0],l=f[1],h=f[2]}}return[c,l,h,o.length>3?o[3]:1]},k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"hsv\"))&&3===r.length)return\"hsv\"}}),M.prototype.lab=function(){return P(this._rgb)};var Ir=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"lab\"])))};Object.assign(N,{lab:Ir,getLabWhitePoint:O,setLabWhitePoint:R}),k.format.lab=K,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"lab\"))&&3===r.length)return\"lab\"}});M.prototype.lch=function(){return C(this._rgb)},M.prototype.hcl=function(){return w(C(this._rgb))};var Kr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"lch\"])))},zr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"hcl\"])))};Object.assign(N,{lch:Kr,hcl:zr}),k.format.lch=J,k.format.hcl=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=w(i(r,\"hcl\"));return J.apply(void 0,e)},[\"lch\",\"hcl\"].forEach((function(r){return k.autodetect.push({p:2,test:function(){for(var n=[],e=arguments.length;e--;)n[e]=arguments[e];if(\"array\"===c(n=i(n,r))&&3===n.length)return r}})}));M.prototype.num=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgb\");return(e[0]<<16)+(e[1]<<8)+e[2]}(this._rgb)};var Ur=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"num\"])))};Object.assign(N,{num:Ur}),k.format.num=function(r){if(\"number\"==c(r)&&r>=0&&r<=16777215)return[r>>16,r>>8&255,255&r,1];throw new Error(\"unknown num color: \"+r)},k.autodetect.push({p:5,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(1===r.length&&\"number\"===c(r[0])&&r[0]>=0&&r[0]<=16777215)return\"num\"}});var Vr=Math.round;M.prototype.rgb=function(r){return void 0===r&&(r=!0),!1===r?this._rgb.slice(0,3):this._rgb.slice(0,3).map(Vr)},M.prototype.rgba=function(r){return void 0===r&&(r=!0),this._rgb.slice(0,4).map((function(n,e){return e<3?!1===r?n:Vr(n):n}))};var Dr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"rgb\"])))};Object.assign(N,{rgb:Dr}),k.format.rgb=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];var e=i(r,\"rgba\");return void 0===e[3]&&(e[3]=1),e},k.autodetect.push({p:3,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"rgba\"))&&(3===r.length||4===r.length&&\"number\"==c(r[3])&&r[3]>=0&&r[3]<=1))return\"rgb\"}});var Tr=Math.log,Hr=function(r){var n,e,t,a=r/100;return a<66?(n=255,e=a<6?0:-155.25485562709179-.44596950469579133*(e=a-2)+104.49216199393888*Tr(e),t=a<20?0:.8274096064007395*(t=a-10)-254.76935184120902+115.67994401066147*Tr(t)):(n=351.97690566805693+.114206453784165*(n=a-55)-40.25366309332127*Tr(n),e=325.4494125711974+.07943456536662342*(e=a-50)-28.0852963507957*Tr(e),t=255),[n,e,t,1]},Jr=Math.round;M.prototype.temp=M.prototype.kelvin=M.prototype.temperature=function(){return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];for(var e,t=i(r,\"rgb\"),a=t[0],f=t[2],o=1e3,u=4e4;u-o>.4;){var c=Hr(e=.5*(u+o));c[2]/c[0]>=f/a?u=e:o=e}return Jr(e)}(this._rgb)};var Qr=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"temp\"])))};Object.assign(N,{temp:Qr,kelvin:Qr,temperature:Qr}),k.format.temp=k.format.kelvin=k.format.temperature=Hr,M.prototype.oklab=function(){return Z(this._rgb)};var rn=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"oklab\"])))};Object.assign(N,{oklab:rn}),k.format.oklab=Q,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"oklab\"))&&3===r.length)return\"oklab\"}}),M.prototype.oklch=function(){return $(this._rgb)};var nn=function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];return new(Function.prototype.bind.apply(M,[null].concat(r,[\"oklch\"])))};Object.assign(N,{oklch:nn}),k.format.oklch=rr,k.autodetect.push({p:2,test:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(\"array\"===c(r=i(r,\"oklch\"))&&3===r.length)return\"oklch\"}});var en={aliceblue:\"#f0f8ff\",antiquewhite:\"#faebd7\",aqua:\"#00ffff\",aquamarine:\"#7fffd4\",azure:\"#f0ffff\",beige:\"#f5f5dc\",bisque:\"#ffe4c4\",black:\"#000000\",blanchedalmond:\"#ffebcd\",blue:\"#0000ff\",blueviolet:\"#8a2be2\",brown:\"#a52a2a\",burlywood:\"#deb887\",cadetblue:\"#5f9ea0\",chartreuse:\"#7fff00\",chocolate:\"#d2691e\",coral:\"#ff7f50\",cornflowerblue:\"#6495ed\",cornsilk:\"#fff8dc\",crimson:\"#dc143c\",cyan:\"#00ffff\",darkblue:\"#00008b\",darkcyan:\"#008b8b\",darkgoldenrod:\"#b8860b\",darkgray:\"#a9a9a9\",darkgreen:\"#006400\",darkgrey:\"#a9a9a9\",darkkhaki:\"#bdb76b\",darkmagenta:\"#8b008b\",darkolivegreen:\"#556b2f\",darkorange:\"#ff8c00\",darkorchid:\"#9932cc\",darkred:\"#8b0000\",darksalmon:\"#e9967a\",darkseagreen:\"#8fbc8f\",darkslateblue:\"#483d8b\",darkslategray:\"#2f4f4f\",darkslategrey:\"#2f4f4f\",darkturquoise:\"#00ced1\",darkviolet:\"#9400d3\",deeppink:\"#ff1493\",deepskyblue:\"#00bfff\",dimgray:\"#696969\",dimgrey:\"#696969\",dodgerblue:\"#1e90ff\",firebrick:\"#b22222\",floralwhite:\"#fffaf0\",forestgreen:\"#228b22\",fuchsia:\"#ff00ff\",gainsboro:\"#dcdcdc\",ghostwhite:\"#f8f8ff\",gold:\"#ffd700\",goldenrod:\"#daa520\",gray:\"#808080\",green:\"#008000\",greenyellow:\"#adff2f\",grey:\"#808080\",honeydew:\"#f0fff0\",hotpink:\"#ff69b4\",indianred:\"#cd5c5c\",indigo:\"#4b0082\",ivory:\"#fffff0\",khaki:\"#f0e68c\",laserlemon:\"#ffff54\",lavender:\"#e6e6fa\",lavenderblush:\"#fff0f5\",lawngreen:\"#7cfc00\",lemonchiffon:\"#fffacd\",lightblue:\"#add8e6\",lightcoral:\"#f08080\",lightcyan:\"#e0ffff\",lightgoldenrod:\"#fafad2\",lightgoldenrodyellow:\"#fafad2\",lightgray:\"#d3d3d3\",lightgreen:\"#90ee90\",lightgrey:\"#d3d3d3\",lightpink:\"#ffb6c1\",lightsalmon:\"#ffa07a\",lightseagreen:\"#20b2aa\",lightskyblue:\"#87cefa\",lightslategray:\"#778899\",lightslategrey:\"#778899\",lightsteelblue:\"#b0c4de\",lightyellow:\"#ffffe0\",lime:\"#00ff00\",limegreen:\"#32cd32\",linen:\"#faf0e6\",magenta:\"#ff00ff\",maroon:\"#800000\",maroon2:\"#7f0000\",maroon3:\"#b03060\",mediumaquamarine:\"#66cdaa\",mediumblue:\"#0000cd\",mediumorchid:\"#ba55d3\",mediumpurple:\"#9370db\",mediumseagreen:\"#3cb371\",mediumslateblue:\"#7b68ee\",mediumspringgreen:\"#00fa9a\",mediumturquoise:\"#48d1cc\",mediumvioletred:\"#c71585\",midnightblue:\"#191970\",mintcream:\"#f5fffa\",mistyrose:\"#ffe4e1\",moccasin:\"#ffe4b5\",navajowhite:\"#ffdead\",navy:\"#000080\",oldlace:\"#fdf5e6\",olive:\"#808000\",olivedrab:\"#6b8e23\",orange:\"#ffa500\",orangered:\"#ff4500\",orchid:\"#da70d6\",palegoldenrod:\"#eee8aa\",palegreen:\"#98fb98\",paleturquoise:\"#afeeee\",palevioletred:\"#db7093\",papayawhip:\"#ffefd5\",peachpuff:\"#ffdab9\",peru:\"#cd853f\",pink:\"#ffc0cb\",plum:\"#dda0dd\",powderblue:\"#b0e0e6\",purple:\"#800080\",purple2:\"#7f007f\",purple3:\"#a020f0\",rebeccapurple:\"#663399\",red:\"#ff0000\",rosybrown:\"#bc8f8f\",royalblue:\"#4169e1\",saddlebrown:\"#8b4513\",salmon:\"#fa8072\",sandybrown:\"#f4a460\",seagreen:\"#2e8b57\",seashell:\"#fff5ee\",sienna:\"#a0522d\",silver:\"#c0c0c0\",skyblue:\"#87ceeb\",slateblue:\"#6a5acd\",slategray:\"#708090\",slategrey:\"#708090\",snow:\"#fffafa\",springgreen:\"#00ff7f\",steelblue:\"#4682b4\",tan:\"#d2b48c\",teal:\"#008080\",thistle:\"#d8bfd8\",tomato:\"#ff6347\",turquoise:\"#40e0d0\",violet:\"#ee82ee\",wheat:\"#f5deb3\",white:\"#ffffff\",whitesmoke:\"#f5f5f5\",yellow:\"#ffff00\",yellowgreen:\"#9acd32\"};M.prototype.name=function(){for(var r=Fr(this._rgb,\"rgb\"),n=0,e=Object.keys(en);n<e.length;n+=1){var t=e[n];if(en[t]===r)return t.toLowerCase()}return r},k.format.named=function(r){if(r=r.toLowerCase(),en[r])return Or(en[r]);throw new Error(\"unknown color name: \"+r)},k.autodetect.push({p:5,test:function(r){for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];if(!n.length&&\"string\"===c(r)&&en[r.toLowerCase()])return\"named\"}}),M.prototype.alpha=function(r,n){return void 0===n&&(n=!1),void 0!==r&&\"number\"===c(r)?n?(this._rgb[3]=r,this):new M([this._rgb[0],this._rgb[1],this._rgb[2],r],\"rgb\"):this._rgb[3]},M.prototype.clipped=function(){return this._rgb._clipped||!1},M.prototype.darken=function(r){void 0===r&&(r=1);var n=this.lab();return n[0]-=j.Kn*r,new M(n,\"lab\").alpha(this.alpha(),!0)},M.prototype.brighten=function(r){return void 0===r&&(r=1),this.darken(-r)},M.prototype.darker=M.prototype.darken,M.prototype.brighter=M.prototype.brighten,M.prototype.get=function(r){var n=r.split(\".\"),e=n[0],t=n[1],a=this[e]();if(t){var f=e.indexOf(t)-(\"ok\"===e.substr(0,2)?2:0);if(f>-1)return a[f];throw new Error(\"unknown channel \"+t+\" in mode \"+e)}return a};var tn=Math.pow;M.prototype.luminance=function(r,n){if(void 0===n&&(n=\"rgb\"),void 0!==r&&\"number\"===c(r)){if(0===r)return new M([0,0,0,this._rgb[3]],\"rgb\");if(1===r)return new M([255,255,255,this._rgb[3]],\"rgb\");var e=this.luminance(),t=20,a=function(e,f){var o=e.interpolate(f,.5,n),u=o.luminance();return Math.abs(r-u)<1e-7||!t--?o:u>r?a(e,o):a(o,f)},f=(e>r?a(new M([0,0,0]),this):a(this,new M([255,255,255]))).rgb();return new M(f.concat([this._rgb[3]]))}return an.apply(void 0,this._rgb.slice(0,3))};var an=function(r,n,e){return.2126*(r=fn(r))+.7152*(n=fn(n))+.0722*(e=fn(e))},fn=function(r){return(r/=255)<=.03928?r/12.92:tn((r+.055)/1.055,2.4)},on={};function un(r,n,e){void 0===e&&(e=.5);for(var t=[],a=arguments.length-3;a-- >0;)t[a]=arguments[a+3];var f=t[0]||\"lrgb\";if(on[f]||t.length||(f=Object.keys(on)[0]),!on[f])throw new Error(\"interpolation mode \"+f+\" is not defined\");return\"object\"!==c(r)&&(r=new M(r)),\"object\"!==c(n)&&(n=new M(n)),on[f](r,n,e).alpha(r.alpha()+e*(n.alpha()-r.alpha()))}M.prototype.mix=M.prototype.interpolate=function(r,n){void 0===n&&(n=.5);for(var e=[],t=arguments.length-2;t-- >0;)e[t]=arguments[t+2];return un.apply(void 0,[this,r,n].concat(e))},M.prototype.premultiply=function(r){void 0===r&&(r=!1);var n=this._rgb,e=n[3];return r?(this._rgb=[n[0]*e,n[1]*e,n[2]*e,e],this):new M([n[0]*e,n[1]*e,n[2]*e,e],\"rgb\")},M.prototype.saturate=function(r){void 0===r&&(r=1);var n=this.lch();return n[1]+=j.Kn*r,n[1]<0&&(n[1]=0),new M(n,\"lch\").alpha(this.alpha(),!0)},M.prototype.desaturate=function(r){return void 0===r&&(r=1),this.saturate(-r)},M.prototype.set=function(r,n,e){void 0===e&&(e=!1);var t=r.split(\".\"),a=t[0],f=t[1],o=this[a]();if(f){var u=a.indexOf(f)-(\"ok\"===a.substr(0,2)?2:0);if(u>-1){if(\"string\"==c(n))switch(n.charAt(0)){case\"+\":case\"-\":o[u]+=+n;break;case\"*\":o[u]*=+n.substr(1);break;case\"/\":o[u]/=+n.substr(1);break;default:o[u]=+n}else{if(\"number\"!==c(n))throw new Error(\"unsupported value for Color.set\");o[u]=n}var i=new M(o,a);return e?(this._rgb=i._rgb,this):i}throw new Error(\"unknown channel \"+f+\" in mode \"+a)}return o},M.prototype.tint=function(r){void 0===r&&(r=.5);for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];return un.apply(void 0,[this,\"white\",r].concat(n))},M.prototype.shade=function(r){void 0===r&&(r=.5);for(var n=[],e=arguments.length-1;e-- >0;)n[e]=arguments[e+1];return un.apply(void 0,[this,\"black\",r].concat(n))};on.rgb=function(r,n,e){var t=r._rgb,a=n._rgb;return new M(t[0]+e*(a[0]-t[0]),t[1]+e*(a[1]-t[1]),t[2]+e*(a[2]-t[2]),\"rgb\")};var cn=Math.sqrt,ln=Math.pow;on.lrgb=function(r,n,e){var t=r._rgb,a=t[0],f=t[1],o=t[2],u=n._rgb,c=u[0],i=u[1],l=u[2];return new M(cn(ln(a,2)*(1-e)+ln(c,2)*e),cn(ln(f,2)*(1-e)+ln(i,2)*e),cn(ln(o,2)*(1-e)+ln(l,2)*e),\"rgb\")};function hn(r,n,e,t){var a,f,o,u,c,i,l,h,s,d,b,g,v;return\"hsl\"===t?(o=r.hsl(),u=n.hsl()):\"hsv\"===t?(o=r.hsv(),u=n.hsv()):\"hcg\"===t?(o=r.hcg(),u=n.hcg()):\"hsi\"===t?(o=r.hsi(),u=n.hsi()):\"lch\"===t||\"hcl\"===t?(t=\"hcl\",o=r.hcl(),u=n.hcl()):\"oklch\"===t&&(o=r.oklch().reverse(),u=n.oklch().reverse()),\"h\"!==t.substr(0,1)&&\"oklch\"!==t||(c=(a=o)[0],l=a[1],s=a[2],i=(f=u)[0],h=f[1],d=f[2]),isNaN(c)||isNaN(i)?isNaN(c)?isNaN(i)?g=Number.NaN:(g=i,1!=s&&0!=s||\"hsv\"==t||(b=h)):(g=c,1!=d&&0!=d||\"hsv\"==t||(b=l)):g=c+e*(i>c&&i-c>180?i-(c+360):i<c&&c-i>180?i+360-c:i-c),void 0===b&&(b=l+e*(h-l)),v=s+e*(d-s),new M(\"oklch\"===t?[v,b,g]:[g,b,v],t)}on.lab=function(r,n,e){var t=r.lab(),a=n.lab();return new M(t[0]+e*(a[0]-t[0]),t[1]+e*(a[1]-t[1]),t[2]+e*(a[2]-t[2]),\"lab\")};var sn=function(r,n,e){return hn(r,n,e,\"lch\")};on.lch=sn,on.hcl=sn;on.num=function(r,n,e){var t=r.num(),a=n.num();return new M(t+e*(a-t),\"num\")};on.hcg=function(r,n,e){return hn(r,n,e,\"hcg\")};on.hsi=function(r,n,e){return hn(r,n,e,\"hsi\")};on.hsl=function(r,n,e){return hn(r,n,e,\"hsl\")};on.hsv=function(r,n,e){return hn(r,n,e,\"hsv\")};on.oklab=function(r,n,e){var t=r.oklab(),a=n.oklab();return new M(t[0]+e*(a[0]-t[0]),t[1]+e*(a[1]-t[1]),t[2]+e*(a[2]-t[2]),\"oklab\")};on.oklch=function(r,n,e){return hn(r,n,e,\"oklch\")};var dn=Math.pow,bn=Math.sqrt,gn=Math.PI,vn=Math.cos,pn=Math.sin,mn=Math.atan2;var yn=function(r,n){for(var e=r.length,a=[0,0,0,0],f=0;f<r.length;f++){var o=r[f],u=n[f]/e,c=o._rgb;a[0]+=dn(c[0],2)*u,a[1]+=dn(c[1],2)*u,a[2]+=dn(c[2],2)*u,a[3]+=c[3]*u}return a[0]=bn(a[0]),a[1]=bn(a[1]),a[2]=bn(a[2]),a[3]>.9999999&&(a[3]=1),new M(t(a))},wn=Math.pow;function kn(r){var n=\"rgb\",t=N(\"#ccc\"),a=0,f=[0,1],o=[0,1],u=[],i=[0,0],l=!1,h=[],s=!1,d=0,b=1,g=!1,v={},p=!0,m=1,y=function(r){if((r=r||[\"#fff\",\"#000\"])&&\"string\"===c(r)&&N.brewer&&N.brewer[r.toLowerCase()]&&(r=N.brewer[r.toLowerCase()]),\"array\"===c(r)){1===r.length&&(r=[r[0],r[0]]),r=r.slice(0);for(var n=0;n<r.length;n++)r[n]=N(r[n]);u.length=0;for(var e=0;e<r.length;e++)u.push(e/(r.length-1))}return x(),h=r},w=function(r){return r},k=function(r){return r},M=function(r,a){var f,o;if(null==a&&(a=!1),isNaN(r)||null===r)return t;if(a)o=r;else if(l&&l.length>2){var s=function(r){if(null!=l){for(var n=l.length-1,e=0;e<n&&r>=l[e];)e++;return e-1}return 0}(r);o=s/(l.length-2)}else o=b!==d?(r-d)/(b-d):1;o=k(o),a||(o=w(o)),1!==m&&(o=wn(o,m)),o=e(o=i[0]+o*(1-i[0]-i[1]),0,1);var g=Math.floor(1e4*o);if(p&&v[g])f=v[g];else{if(\"array\"===c(h))for(var y=0;y<u.length;y++){var M=u[y];if(o<=M){f=h[y];break}if(o>=M&&y===u.length-1){f=h[y];break}if(o>M&&o<u[y+1]){o=(o-M)/(u[y+1]-M),f=N.interpolate(h[y],h[y+1],o,n);break}}else\"function\"===c(h)&&(f=h(o));p&&(v[g]=f)}return f},x=function(){return v={}};y(r);var _=function(r){var n=N(M(r));return s&&n[s]?n[s]():n};return _.classes=function(r){if(null!=r){if(\"array\"===c(r))l=r,f=[r[0],r[r.length-1]];else{var n=N.analyze(f);l=0===r?[n.min,n.max]:N.limits(n,\"e\",r)}return _}return l},_.domain=function(r){if(!arguments.length)return o;o=r.slice(0),d=r[0],b=r[r.length-1],u=[];var n=h.length;if(r.length===n&&d!==b)for(var e=0,t=Array.from(r);e<t.length;e+=1){var a=t[e];u.push((a-d)/(b-d))}else{for(var c=0;c<n;c++)u.push(c/(n-1));if(r.length>2){var i=r.map((function(n,e){return e/(r.length-1)})),l=r.map((function(r){return(r-d)/(b-d)}));l.every((function(r,n){return i[n]===r}))||(k=function(r){if(r<=0||r>=1)return r;for(var n=0;r>=l[n+1];)n++;var e=(r-l[n])/(l[n+1]-l[n]);return i[n]+e*(i[n+1]-i[n])})}}return f=[d,b],_},_.mode=function(r){return arguments.length?(n=r,x(),_):n},_.range=function(r,n){return y(r),_},_.out=function(r){return s=r,_},_.spread=function(r){return arguments.length?(a=r,_):a},_.correctLightness=function(r){return null==r&&(r=!0),g=r,x(),w=g?function(r){for(var n=M(0,!0).lab()[0],e=M(1,!0).lab()[0],t=n>e,a=M(r,!0).lab()[0],f=n+(e-n)*r,o=a-f,u=0,c=1,i=20;Math.abs(o)>.01&&i-- >0;)t&&(o*=-1),o<0?(u=r,r+=.5*(c-r)):(c=r,r+=.5*(u-r)),a=M(r,!0).lab()[0],o=a-f;return r}:function(r){return r},_},_.padding=function(r){return null!=r?(\"number\"===c(r)&&(r=[r,r]),i=r,_):i},_.colors=function(n,e){arguments.length<2&&(e=\"hex\");var t=[];if(0===arguments.length)t=h.slice(0);else if(1===n)t=[_(.5)];else if(n>1){var a=f[0],o=f[1]-a;t=function(r,n){for(var e=[],t=r<n,a=n,f=r;t?f<a:f>a;t?f++:f--)e.push(f);return e}(0,n).map((function(r){return _(a+r/(n-1)*o)}))}else{r=[];var u=[];if(l&&l.length>2)for(var c=1,i=l.length,s=1<=i;s?c<i:c>i;s?c++:c--)u.push(.5*(l[c-1]+l[c]));else u=f;t=u.map((function(r){return _(r)}))}return N[e]&&(t=t.map((function(r){return r[e]()}))),t},_.cache=function(r){return null!=r?(p=r,_):p},_.gamma=function(r){return null!=r?(m=r,_):m},_.nodata=function(r){return null!=r?(t=N(r),_):t},_}var Mn=function(r,n,e){if(!Mn[e])throw new Error(\"unknown blend mode \"+e);return Mn[e](r,n)},Nn=function(r){return function(n,e){var t=N(e).rgb(),a=N(n).rgb();return N.rgb(r(t,a))}},xn=function(r){return function(n,e){var t=[];return t[0]=r(n[0],e[0]),t[1]=r(n[1],e[1]),t[2]=r(n[2],e[2]),t}};Mn.normal=Nn(xn((function(r){return r}))),Mn.multiply=Nn(xn((function(r,n){return r*n/255}))),Mn.screen=Nn(xn((function(r,n){return 255*(1-(1-r/255)*(1-n/255))}))),Mn.overlay=Nn(xn((function(r,n){return n<128?2*r*n/255:255*(1-2*(1-r/255)*(1-n/255))}))),Mn.darken=Nn(xn((function(r,n){return r>n?n:r}))),Mn.lighten=Nn(xn((function(r,n){return r>n?r:n}))),Mn.dodge=Nn(xn((function(r,n){return 255===r||(r=n/255*255/(1-r/255))>255?255:r}))),Mn.burn=Nn(xn((function(r,n){return 255*(1-(1-n/255)/(r/255))})));var _n=Math.pow,An=Math.sin,jn=Math.cos;var En=Math.floor,Rn=Math.random;var On=Math.log,Pn=Math.pow,Fn=Math.floor,Ln=Math.abs;function Bn(r,n){void 0===n&&(n=null);var e={min:Number.MAX_VALUE,max:-1*Number.MAX_VALUE,sum:0,values:[],count:0};return\"object\"===c(r)&&(r=Object.values(r)),r.forEach((function(r){n&&\"object\"===c(r)&&(r=r[n]),null==r||isNaN(r)||(e.values.push(r),e.sum+=r,r<e.min&&(e.min=r),r>e.max&&(e.max=r),e.count+=1)})),e.domain=[e.min,e.max],e.limits=function(r,n){return Gn(e,r,n)},e}function Gn(r,n,e){void 0===n&&(n=\"equal\"),void 0===e&&(e=7),\"array\"==c(r)&&(r=Bn(r));var t=r.min,a=r.max,f=r.values.sort((function(r,n){return r-n}));if(1===e)return[t,a];var o=[];if(\"c\"===n.substr(0,1)&&(o.push(t),o.push(a)),\"e\"===n.substr(0,1)){o.push(t);for(var u=1;u<e;u++)o.push(t+u/e*(a-t));o.push(a)}else if(\"l\"===n.substr(0,1)){if(t<=0)throw new Error(\"Logarithmic scales are only possible for values > 0\");var i=Math.LOG10E*On(t),l=Math.LOG10E*On(a);o.push(t);for(var h=1;h<e;h++)o.push(Pn(10,i+h/e*(l-i)));o.push(a)}else if(\"q\"===n.substr(0,1)){o.push(t);for(var s=1;s<e;s++){var d=(f.length-1)*s/e,b=Fn(d);if(b===d)o.push(f[b]);else{var g=d-b;o.push(f[b]*(1-g)+f[b+1]*g)}}o.push(a)}else if(\"k\"===n.substr(0,1)){var v,p=f.length,m=new Array(p),y=new Array(e),w=!0,k=0,M=null;(M=[]).push(t);for(var N=1;N<e;N++)M.push(t+N/e*(a-t));for(M.push(a);w;){for(var x=0;x<e;x++)y[x]=0;for(var _=0;_<p;_++)for(var A=f[_],j=Number.MAX_VALUE,E=void 0,R=0;R<e;R++){var O=Ln(M[R]-A);O<j&&(j=O,E=R),y[E]++,m[_]=E}for(var P=new Array(e),F=0;F<e;F++)P[F]=null;for(var L=0;L<p;L++)null===P[v=m[L]]?P[v]=f[L]:P[v]+=f[L];for(var B=0;B<e;B++)P[B]*=1/y[B];w=!1;for(var G=0;G<e;G++)if(P[G]!==M[G]){w=!0;break}M=P,++k>200&&(w=!1)}for(var Y={},q=0;q<e;q++)Y[q]=[];for(var C=0;C<p;C++)Y[v=m[C]].push(f[C]);for(var X=[],Z=0;Z<e;Z++)X.push(Y[Z][0]),X.push(Y[Z][Y[Z].length-1]);X=X.sort((function(r,n){return r-n})),o.push(X[0]);for(var $=1;$<X.length;$+=2){var S=X[$];isNaN(S)||-1!==o.indexOf(S)||o.push(S)}}return o}\n/**\n     * @license\n     *\n     * The APCA contrast prediction algorithm is based of the formulas published\n     * in the APCA-1.0.98G specification by Myndex. The specification is available at:\n     * https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAw3_0.1.17_APCA0.0.98G.svg\n     *\n     * Note that the APCA implementation is still beta, so please update to\n     * future versions of chroma.js when they become available.\n     *\n     * You can read more about the APCA Readability Criterion at\n     * https://readtech.org/ARC/\n     */\nvar Yn=.022;function qn(r,n,e){return.2126729*Math.pow(r/255,2.4)+.7151522*Math.pow(n/255,2.4)+.072175*Math.pow(e/255,2.4)}var Cn=Math.sqrt,Xn=Math.pow,Zn=Math.min,$n=Math.max,Sn=Math.atan2,Wn=Math.abs,In=Math.cos,Kn=Math.sin,zn=Math.exp,Un=Math.PI;var Vn={cool:function(){return kn([N.hsl(180,1,.9),N.hsl(250,.7,.4)])},hot:function(){return kn([\"#000\",\"#f00\",\"#ff0\",\"#fff\"]).mode(\"rgb\")}},Dn={OrRd:[\"#fff7ec\",\"#fee8c8\",\"#fdd49e\",\"#fdbb84\",\"#fc8d59\",\"#ef6548\",\"#d7301f\",\"#b30000\",\"#7f0000\"],PuBu:[\"#fff7fb\",\"#ece7f2\",\"#d0d1e6\",\"#a6bddb\",\"#74a9cf\",\"#3690c0\",\"#0570b0\",\"#045a8d\",\"#023858\"],BuPu:[\"#f7fcfd\",\"#e0ecf4\",\"#bfd3e6\",\"#9ebcda\",\"#8c96c6\",\"#8c6bb1\",\"#88419d\",\"#810f7c\",\"#4d004b\"],Oranges:[\"#fff5eb\",\"#fee6ce\",\"#fdd0a2\",\"#fdae6b\",\"#fd8d3c\",\"#f16913\",\"#d94801\",\"#a63603\",\"#7f2704\"],BuGn:[\"#f7fcfd\",\"#e5f5f9\",\"#ccece6\",\"#99d8c9\",\"#66c2a4\",\"#41ae76\",\"#238b45\",\"#006d2c\",\"#00441b\"],YlOrBr:[\"#ffffe5\",\"#fff7bc\",\"#fee391\",\"#fec44f\",\"#fe9929\",\"#ec7014\",\"#cc4c02\",\"#993404\",\"#662506\"],YlGn:[\"#ffffe5\",\"#f7fcb9\",\"#d9f0a3\",\"#addd8e\",\"#78c679\",\"#41ab5d\",\"#238443\",\"#006837\",\"#004529\"],Reds:[\"#fff5f0\",\"#fee0d2\",\"#fcbba1\",\"#fc9272\",\"#fb6a4a\",\"#ef3b2c\",\"#cb181d\",\"#a50f15\",\"#67000d\"],RdPu:[\"#fff7f3\",\"#fde0dd\",\"#fcc5c0\",\"#fa9fb5\",\"#f768a1\",\"#dd3497\",\"#ae017e\",\"#7a0177\",\"#49006a\"],Greens:[\"#f7fcf5\",\"#e5f5e0\",\"#c7e9c0\",\"#a1d99b\",\"#74c476\",\"#41ab5d\",\"#238b45\",\"#006d2c\",\"#00441b\"],YlGnBu:[\"#ffffd9\",\"#edf8b1\",\"#c7e9b4\",\"#7fcdbb\",\"#41b6c4\",\"#1d91c0\",\"#225ea8\",\"#253494\",\"#081d58\"],Purples:[\"#fcfbfd\",\"#efedf5\",\"#dadaeb\",\"#bcbddc\",\"#9e9ac8\",\"#807dba\",\"#6a51a3\",\"#54278f\",\"#3f007d\"],GnBu:[\"#f7fcf0\",\"#e0f3db\",\"#ccebc5\",\"#a8ddb5\",\"#7bccc4\",\"#4eb3d3\",\"#2b8cbe\",\"#0868ac\",\"#084081\"],Greys:[\"#ffffff\",\"#f0f0f0\",\"#d9d9d9\",\"#bdbdbd\",\"#969696\",\"#737373\",\"#525252\",\"#252525\",\"#000000\"],YlOrRd:[\"#ffffcc\",\"#ffeda0\",\"#fed976\",\"#feb24c\",\"#fd8d3c\",\"#fc4e2a\",\"#e31a1c\",\"#bd0026\",\"#800026\"],PuRd:[\"#f7f4f9\",\"#e7e1ef\",\"#d4b9da\",\"#c994c7\",\"#df65b0\",\"#e7298a\",\"#ce1256\",\"#980043\",\"#67001f\"],Blues:[\"#f7fbff\",\"#deebf7\",\"#c6dbef\",\"#9ecae1\",\"#6baed6\",\"#4292c6\",\"#2171b5\",\"#08519c\",\"#08306b\"],PuBuGn:[\"#fff7fb\",\"#ece2f0\",\"#d0d1e6\",\"#a6bddb\",\"#67a9cf\",\"#3690c0\",\"#02818a\",\"#016c59\",\"#014636\"],Viridis:[\"#440154\",\"#482777\",\"#3f4a8a\",\"#31678e\",\"#26838f\",\"#1f9d8a\",\"#6cce5a\",\"#b6de2b\",\"#fee825\"],Spectral:[\"#9e0142\",\"#d53e4f\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#e6f598\",\"#abdda4\",\"#66c2a5\",\"#3288bd\",\"#5e4fa2\"],RdYlGn:[\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee08b\",\"#ffffbf\",\"#d9ef8b\",\"#a6d96a\",\"#66bd63\",\"#1a9850\",\"#006837\"],RdBu:[\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#f7f7f7\",\"#d1e5f0\",\"#92c5de\",\"#4393c3\",\"#2166ac\",\"#053061\"],PiYG:[\"#8e0152\",\"#c51b7d\",\"#de77ae\",\"#f1b6da\",\"#fde0ef\",\"#f7f7f7\",\"#e6f5d0\",\"#b8e186\",\"#7fbc41\",\"#4d9221\",\"#276419\"],PRGn:[\"#40004b\",\"#762a83\",\"#9970ab\",\"#c2a5cf\",\"#e7d4e8\",\"#f7f7f7\",\"#d9f0d3\",\"#a6dba0\",\"#5aae61\",\"#1b7837\",\"#00441b\"],RdYlBu:[\"#a50026\",\"#d73027\",\"#f46d43\",\"#fdae61\",\"#fee090\",\"#ffffbf\",\"#e0f3f8\",\"#abd9e9\",\"#74add1\",\"#4575b4\",\"#313695\"],BrBG:[\"#543005\",\"#8c510a\",\"#bf812d\",\"#dfc27d\",\"#f6e8c3\",\"#f5f5f5\",\"#c7eae5\",\"#80cdc1\",\"#35978f\",\"#01665e\",\"#003c30\"],RdGy:[\"#67001f\",\"#b2182b\",\"#d6604d\",\"#f4a582\",\"#fddbc7\",\"#ffffff\",\"#e0e0e0\",\"#bababa\",\"#878787\",\"#4d4d4d\",\"#1a1a1a\"],PuOr:[\"#7f3b08\",\"#b35806\",\"#e08214\",\"#fdb863\",\"#fee0b6\",\"#f7f7f7\",\"#d8daeb\",\"#b2abd2\",\"#8073ac\",\"#542788\",\"#2d004b\"],Set2:[\"#66c2a5\",\"#fc8d62\",\"#8da0cb\",\"#e78ac3\",\"#a6d854\",\"#ffd92f\",\"#e5c494\",\"#b3b3b3\"],Accent:[\"#7fc97f\",\"#beaed4\",\"#fdc086\",\"#ffff99\",\"#386cb0\",\"#f0027f\",\"#bf5b17\",\"#666666\"],Set1:[\"#e41a1c\",\"#377eb8\",\"#4daf4a\",\"#984ea3\",\"#ff7f00\",\"#ffff33\",\"#a65628\",\"#f781bf\",\"#999999\"],Set3:[\"#8dd3c7\",\"#ffffb3\",\"#bebada\",\"#fb8072\",\"#80b1d3\",\"#fdb462\",\"#b3de69\",\"#fccde5\",\"#d9d9d9\",\"#bc80bd\",\"#ccebc5\",\"#ffed6f\"],Dark2:[\"#1b9e77\",\"#d95f02\",\"#7570b3\",\"#e7298a\",\"#66a61e\",\"#e6ab02\",\"#a6761d\",\"#666666\"],Paired:[\"#a6cee3\",\"#1f78b4\",\"#b2df8a\",\"#33a02c\",\"#fb9a99\",\"#e31a1c\",\"#fdbf6f\",\"#ff7f00\",\"#cab2d6\",\"#6a3d9a\",\"#ffff99\",\"#b15928\"],Pastel2:[\"#b3e2cd\",\"#fdcdac\",\"#cbd5e8\",\"#f4cae4\",\"#e6f5c9\",\"#fff2ae\",\"#f1e2cc\",\"#cccccc\"],Pastel1:[\"#fbb4ae\",\"#b3cde3\",\"#ccebc5\",\"#decbe4\",\"#fed9a6\",\"#ffffcc\",\"#e5d8bd\",\"#fddaec\",\"#f2f2f2\"]},Tn=Object.keys(Dn),Hn=new Map(Tn.map((function(r){return[r.toLowerCase(),r]}))),Jn=\"function\"==typeof Proxy?new Proxy(Dn,{get:function(r,n){var e=n.toLowerCase();if(Hn.has(e))return r[Hn.get(e)]},getOwnPropertyNames:function(){return Object.getOwnPropertyNames(Tn)}}):Dn;return Object.assign(N,{analyze:Bn,average:function(r,n,e){void 0===n&&(n=\"lrgb\"),void 0===e&&(e=null);var t=r.length;e||(e=Array.from(new Array(t)).map((function(){return 1})));var a=t/e.reduce((function(r,n){return r+n}));if(e.forEach((function(r,n){e[n]*=a})),r=r.map((function(r){return new M(r)})),\"lrgb\"===n)return yn(r,e);for(var f=r.shift(),o=f.get(n),u=[],c=0,i=0,l=0;l<o.length;l++)if(o[l]=(o[l]||0)*e[0],u.push(isNaN(o[l])?0:e[0]),\"h\"===n.charAt(l)&&!isNaN(o[l])){var h=o[l]/180*gn;c+=vn(h)*e[0],i+=pn(h)*e[0]}var s=f.alpha()*e[0];r.forEach((function(r,t){var a=r.get(n);s+=r.alpha()*e[t+1];for(var f=0;f<o.length;f++)if(!isNaN(a[f]))if(u[f]+=e[t+1],\"h\"===n.charAt(f)){var l=a[f]/180*gn;c+=vn(l)*e[t+1],i+=pn(l)*e[t+1]}else o[f]+=a[f]*e[t+1]}));for(var d=0;d<o.length;d++)if(\"h\"===n.charAt(d)){for(var b=mn(i/u[d],c/u[d])/gn*180;b<0;)b+=360;for(;b>=360;)b-=360;o[d]=b}else o[d]=o[d]/u[d];return s/=t,new M(o,n).alpha(s>.99999?1:s,!0)},bezier:function(r){var n=function(r){var n,e,t,a,f,o,u;if(2===(r=r.map((function(r){return new M(r)}))).length)n=r.map((function(r){return r.lab()})),f=n[0],o=n[1],a=function(r){var n=[0,1,2].map((function(n){return f[n]+r*(o[n]-f[n])}));return new M(n,\"lab\")};else if(3===r.length)e=r.map((function(r){return r.lab()})),f=e[0],o=e[1],u=e[2],a=function(r){var n=[0,1,2].map((function(n){return(1-r)*(1-r)*f[n]+2*(1-r)*r*o[n]+r*r*u[n]}));return new M(n,\"lab\")};else if(4===r.length){var c;t=r.map((function(r){return r.lab()})),f=t[0],o=t[1],u=t[2],c=t[3],a=function(r){var n=[0,1,2].map((function(n){return(1-r)*(1-r)*(1-r)*f[n]+3*(1-r)*(1-r)*r*o[n]+3*(1-r)*r*r*u[n]+r*r*r*c[n]}));return new M(n,\"lab\")}}else{if(!(r.length>=5))throw new RangeError(\"No point in running bezier with only one color.\");var i,l,h;i=r.map((function(r){return r.lab()})),h=r.length-1,l=function(r){for(var n=[1,1],e=1;e<r;e++){for(var t=[1],a=1;a<=n.length;a++)t[a]=(n[a]||0)+n[a-1];n=t}return n}(h),a=function(r){var n=1-r,e=[0,1,2].map((function(e){return i.reduce((function(t,a,f){return t+l[f]*Math.pow(n,h-f)*Math.pow(r,f)*a[e]}),0)}));return new M(e,\"lab\")}}return a}(r);return n.scale=function(){return kn(n)},n},blend:Mn,brewer:Jn,Color:M,colors:en,contrast:function(r,n){r=new M(r),n=new M(n);var e=r.luminance(),t=n.luminance();return e>t?(e+.05)/(t+.05):(t+.05)/(e+.05)},contrastAPCA:function(r,n){r=new M(r),n=new M(n),r.alpha()<1&&(r=un(n,r,r.alpha(),\"rgb\"));var e=qn.apply(void 0,r.rgb()),t=qn.apply(void 0,n.rgb()),a=e>=Yn?e:e+Math.pow(Yn-e,1.414),f=t>=Yn?t:t+Math.pow(Yn-t,1.414),o=Math.pow(f,.56)-Math.pow(a,.57),u=Math.pow(f,.65)-Math.pow(a,.62),c=Math.abs(f-a)<5e-4?0:a<f?1.14*o:1.14*u;return 100*(Math.abs(c)<.1?0:c>0?c-.027:c+.027)},cubehelix:function(r,n,e,a,f){void 0===r&&(r=300),void 0===n&&(n=-1.5),void 0===e&&(e=1),void 0===a&&(a=1),void 0===f&&(f=[0,1]);var o,u=0;\"array\"===c(f)?o=f[1]-f[0]:(o=0,f=[f,f]);var i=function(c){var i=v*((r+120)/360+n*c),l=_n(f[0]+o*c,a),h=(0!==u?e[0]+c*u:e)*l*(1-l)/2,s=jn(i),d=An(i);return N(t([255*(l+h*(-.14861*s+1.78277*d)),255*(l+h*(-.29227*s-.90649*d)),255*(l+h*(1.97294*s)),1]))};return i.start=function(n){return null==n?r:(r=n,i)},i.rotations=function(r){return null==r?n:(n=r,i)},i.gamma=function(r){return null==r?a:(a=r,i)},i.hue=function(r){return null==r?e:(\"array\"===c(e=r)?0===(u=e[1]-e[0])&&(e=e[1]):u=0,i)},i.lightness=function(r){return null==r?f:(\"array\"===c(r)?(f=r,o=r[1]-r[0]):(f=[r,r],o=0),i)},i.scale=function(){return N.scale(i)},i.hue(e),i},deltaE:function(r,n,e,t,a){void 0===e&&(e=1),void 0===t&&(t=1),void 0===a&&(a=1);var f=function(r){return 360*r/(2*Un)},o=function(r){return 2*Un*r/360};r=new M(r),n=new M(n);var u=Array.from(r.lab()),c=u[0],i=u[1],l=u[2],h=Array.from(n.lab()),s=h[0],d=h[1],b=h[2],g=(c+s)/2,v=(Cn(Xn(i,2)+Xn(l,2))+Cn(Xn(d,2)+Xn(b,2)))/2,p=.5*(1-Cn(Xn(v,7)/(Xn(v,7)+Xn(25,7)))),m=i*(1+p),y=d*(1+p),w=Cn(Xn(m,2)+Xn(l,2)),k=Cn(Xn(y,2)+Xn(b,2)),N=(w+k)/2,x=f(Sn(l,m)),_=f(Sn(b,y)),A=x>=0?x:x+360,j=_>=0?_:_+360,E=Wn(A-j)>180?(A+j+360)/2:(A+j)/2,R=1-.17*In(o(E-30))+.24*In(o(2*E))+.32*In(o(3*E+6))-.2*In(o(4*E-63)),O=j-A;O=Wn(O)<=180?O:j<=A?O+360:O-360,O=2*Cn(w*k)*Kn(o(O)/2);var P=s-c,F=k-w,L=1+.015*Xn(g-50,2)/Cn(20+Xn(g-50,2)),B=1+.045*N,G=1+.015*N*R,Y=30*zn(-Xn((E-275)/25,2)),q=-(2*Cn(Xn(N,7)/(Xn(N,7)+Xn(25,7))))*Kn(2*o(Y)),C=Cn(Xn(P/(e*L),2)+Xn(F/(t*B),2)+Xn(O/(a*G),2)+q*(F/(t*B))*(O/(a*G)));return $n(0,Zn(100,C))},distance:function(r,n,e){void 0===e&&(e=\"lab\"),r=new M(r),n=new M(n);var t=r.get(e),a=n.get(e),f=0;for(var o in t){var u=(t[o]||0)-(a[o]||0);f+=u*u}return Math.sqrt(f)},input:k,interpolate:un,limits:Gn,mix:un,random:function(r){void 0===r&&(r=Rn);for(var n=\"#\",e=0;e<6;e++)n+=\"0123456789abcdef\".charAt(En(16*r()));return new M(n,\"hex\")},scale:kn,scales:Vn,valid:function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];try{return new(Function.prototype.bind.apply(M,[null].concat(r))),!0}catch(r){return!1}},cmyk:_,css:xr,gl:_r,hcg:jr,hex:Lr,hsi:Cr,hsl:Xr,hsv:Wr,lab:Ir,lch:Kr,hcl:zr,num:Ur,rgb:Dr,temp:Qr,kelvin:Qr,temperature:Qr,oklab:rn,oklch:nn,getLabWhitePoint:O,setLabWhitePoint:R}),N}));\n"
  },
  {
    "path": "docs/libs/codemirror/lib/codemirror.css",
    "content": "/* BASICS */\n\n.CodeMirror {\n  /* Set height, width, borders, and global font properties here */\n  font-family: monospace;\n  height: 300px;\n  color: black;\n}\n\n/* PADDING */\n\n.CodeMirror-lines {\n  padding: 4px 0; /* Vertical padding around content */\n}\n.CodeMirror pre {\n  padding: 0 4px; /* Horizontal padding of content */\n}\n\n.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  background-color: white; /* The little square between H and V scrollbars */\n}\n\n/* GUTTER */\n\n.CodeMirror-gutters {\n  border-right: 1px solid #ddd;\n  background-color: #f7f7f7;\n  white-space: nowrap;\n}\n.CodeMirror-linenumbers {}\n.CodeMirror-linenumber {\n  padding: 0 3px 0 5px;\n  min-width: 20px;\n  text-align: right;\n  color: #999;\n  white-space: nowrap;\n}\n\n.CodeMirror-guttermarker { color: black; }\n.CodeMirror-guttermarker-subtle { color: #999; }\n\n/* CURSOR */\n\n.CodeMirror div.CodeMirror-cursor {\n  border-left: 1px solid black;\n}\n/* Shown when moving in bi-directional text */\n.CodeMirror div.CodeMirror-secondarycursor {\n  border-left: 1px solid silver;\n}\n.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {\n  width: auto;\n  border: 0;\n  background: #7e7;\n}\n.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {\n  z-index: 1;\n}\n\n.cm-animate-fat-cursor {\n  width: auto;\n  border: 0;\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n}\n@-moz-keyframes blink {\n  0% { background: #7e7; }\n  50% { background: none; }\n  100% { background: #7e7; }\n}\n@-webkit-keyframes blink {\n  0% { background: #7e7; }\n  50% { background: none; }\n  100% { background: #7e7; }\n}\n@keyframes blink {\n  0% { background: #7e7; }\n  50% { background: none; }\n  100% { background: #7e7; }\n}\n\n/* Can style cursor different in overwrite (non-insert) mode */\ndiv.CodeMirror-overwrite div.CodeMirror-cursor {}\n\n.cm-tab { display: inline-block; text-decoration: inherit; }\n\n.CodeMirror-ruler {\n  border-left: 1px solid #ccc;\n  position: absolute;\n}\n\n/* DEFAULT THEME */\n\n.cm-s-default .cm-header {color: blue;}\n.cm-s-default .cm-quote {color: #090;}\n.cm-negative {color: #d44;}\n.cm-positive {color: #292;}\n.cm-header, .cm-strong {font-weight: bold;}\n.cm-em {font-style: italic;}\n.cm-link {text-decoration: underline;}\n.cm-strikethrough {text-decoration: line-through;}\n\n.cm-s-default .cm-keyword {color: #708;}\n.cm-s-default .cm-atom {color: #219;}\n.cm-s-default .cm-number {color: #164;}\n.cm-s-default .cm-def {color: #00f;}\n.cm-s-default .cm-variable,\n.cm-s-default .cm-punctuation,\n.cm-s-default .cm-property,\n.cm-s-default .cm-operator {}\n.cm-s-default .cm-variable-2 {color: #05a;}\n.cm-s-default .cm-variable-3 {color: #085;}\n.cm-s-default .cm-comment {color: #a50;}\n.cm-s-default .cm-string {color: #a11;}\n.cm-s-default .cm-string-2 {color: #f50;}\n.cm-s-default .cm-meta {color: #555;}\n.cm-s-default .cm-qualifier {color: #555;}\n.cm-s-default .cm-builtin {color: #30a;}\n.cm-s-default .cm-bracket {color: #997;}\n.cm-s-default .cm-tag {color: #170;}\n.cm-s-default .cm-attribute {color: #00c;}\n.cm-s-default .cm-hr {color: #999;}\n.cm-s-default .cm-link {color: #00c;}\n\n.cm-s-default .cm-error {color: #f00;}\n.cm-invalidchar {color: #f00;}\n\n.CodeMirror-composing { border-bottom: 2px solid; }\n\n/* Default styles for common addons */\n\ndiv.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}\ndiv.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}\n.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }\n.CodeMirror-activeline-background {background: #e8f2ff;}\n\n/* STOP */\n\n/* The rest of this file contains styles related to the mechanics of\n   the editor. You probably shouldn't touch them. */\n\n.CodeMirror {\n  position: relative;\n  overflow: hidden;\n  background: white;\n}\n\n.CodeMirror-scroll {\n  overflow: scroll !important; /* Things will break if this is overridden */\n  /* 30px is the magic margin used to hide the element's real scrollbars */\n  /* See overflow: hidden in .CodeMirror */\n  margin-bottom: -30px; margin-right: -30px;\n  padding-bottom: 30px;\n  height: 100%;\n  outline: none; /* Prevent dragging from highlighting the element */\n  position: relative;\n}\n.CodeMirror-sizer {\n  position: relative;\n  border-right: 30px solid transparent;\n}\n\n/* The fake, visible scrollbars. Used to force redraw during scrolling\n   before actuall scrolling happens, thus preventing shaking and\n   flickering artifacts. */\n.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  position: absolute;\n  z-index: 6;\n  display: none;\n}\n.CodeMirror-vscrollbar {\n  right: 0; top: 0;\n  overflow-x: hidden;\n  overflow-y: scroll;\n}\n.CodeMirror-hscrollbar {\n  bottom: 0; left: 0;\n  overflow-y: hidden;\n  overflow-x: scroll;\n}\n.CodeMirror-scrollbar-filler {\n  right: 0; bottom: 0;\n}\n.CodeMirror-gutter-filler {\n  left: 0; bottom: 0;\n}\n\n.CodeMirror-gutters {\n  position: absolute; left: 0; top: 0;\n  z-index: 3;\n}\n.CodeMirror-gutter {\n  white-space: normal;\n  height: 100%;\n  display: inline-block;\n  margin-bottom: -30px;\n  /* Hack to make IE7 behave */\n  *zoom:1;\n  *display:inline;\n}\n.CodeMirror-gutter-wrapper {\n  position: absolute;\n  z-index: 4;\n  height: 100%;\n}\n.CodeMirror-gutter-elt {\n  position: absolute;\n  cursor: default;\n  z-index: 4;\n}\n.CodeMirror-gutter-wrapper {\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  user-select: none;\n}\n\n.CodeMirror-lines {\n  cursor: text;\n  min-height: 1px; /* prevents collapsing before first draw */\n}\n.CodeMirror pre {\n  /* Reset some styles that the rest of the page might have set */\n  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;\n  border-width: 0;\n  background: transparent;\n  font-family: inherit;\n  font-size: inherit;\n  margin: 0;\n  white-space: pre;\n  word-wrap: normal;\n  line-height: inherit;\n  color: inherit;\n  z-index: 2;\n  position: relative;\n  overflow: visible;\n  -webkit-tap-highlight-color: transparent;\n}\n.CodeMirror-wrap pre {\n  word-wrap: break-word;\n  white-space: pre-wrap;\n  word-break: normal;\n}\n\n.CodeMirror-linebackground {\n  position: absolute;\n  left: 0; right: 0; top: 0; bottom: 0;\n  z-index: 0;\n}\n\n.CodeMirror-linewidget {\n  position: relative;\n  z-index: 2;\n  overflow: auto;\n}\n\n.CodeMirror-widget {}\n\n.CodeMirror-code {\n  outline: none;\n}\n\n/* Force content-box sizing for the elements where we expect it */\n.CodeMirror-scroll,\n.CodeMirror-sizer,\n.CodeMirror-gutter,\n.CodeMirror-gutters,\n.CodeMirror-linenumber {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n}\n\n.CodeMirror-measure {\n  position: absolute;\n  width: 100%;\n  height: 0;\n  overflow: hidden;\n  visibility: hidden;\n}\n.CodeMirror-measure pre { position: static; }\n\n.CodeMirror div.CodeMirror-cursor {\n  position: absolute;\n  border-right: none;\n  width: 0;\n}\n\ndiv.CodeMirror-cursors {\n  visibility: hidden;\n  position: relative;\n  z-index: 3;\n}\n.CodeMirror-focused div.CodeMirror-cursors {\n  visibility: visible;\n}\n\n.CodeMirror-selected { background: #d9d9d9; }\n.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }\n.CodeMirror-crosshair { cursor: crosshair; }\n.CodeMirror ::selection { background: #d7d4f0; }\n.CodeMirror ::-moz-selection { background: #d7d4f0; }\n\n.cm-searching {\n  background: #ffa;\n  background: rgba(255, 255, 0, .4);\n}\n\n/* IE7 hack to prevent it from returning funny offsetTops on the spans */\n.CodeMirror span { *vertical-align: text-bottom; }\n\n/* Used to force a border model for a node */\n.cm-force-border { padding-right: .1px; }\n\n@media print {\n  /* Hide the cursor when printing */\n  .CodeMirror div.CodeMirror-cursors {\n    visibility: hidden;\n  }\n}\n\n/* See issue #2901 */\n.cm-tab-wrap-hack:after { content: ''; }\n\n/* Help users use markselection to safely style text background */\nspan.CodeMirror-selectedtext { background: none; }\n"
  },
  {
    "path": "docs/libs/codemirror/lib/codemirror.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/LICENSE\n\n// This is CodeMirror (http://codemirror.net), a code editor\n// implemented in JavaScript on top of the browser's DOM.\n//\n// You can find some technical background for some of the code below\n// at http://marijnhaverbeke.nl/blog/#cm-internals .\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    module.exports = mod();\n  else if (typeof define == \"function\" && define.amd) // AMD\n    return define([], mod);\n  else // Plain browser env\n    this.CodeMirror = mod();\n})(function() {\n  \"use strict\";\n\n  // BROWSER SNIFFING\n\n  // Kludges for bugs and behavior differences that can't be feature\n  // detected are enabled based on userAgent etc sniffing.\n\n  var gecko = /gecko\\/\\d/i.test(navigator.userAgent);\n  var ie_upto10 = /MSIE \\d/.test(navigator.userAgent);\n  var ie_11up = /Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(navigator.userAgent);\n  var ie = ie_upto10 || ie_11up;\n  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);\n  var webkit = /WebKit\\//.test(navigator.userAgent);\n  var qtwebkit = webkit && /Qt\\/\\d+\\.\\d+/.test(navigator.userAgent);\n  var chrome = /Chrome\\//.test(navigator.userAgent);\n  var presto = /Opera\\//.test(navigator.userAgent);\n  var safari = /Apple Computer/.test(navigator.vendor);\n  var mac_geMountainLion = /Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(navigator.userAgent);\n  var phantom = /PhantomJS/.test(navigator.userAgent);\n\n  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\\/\\w+/.test(navigator.userAgent);\n  // This is woefully incomplete. Suggestions for alternative methods welcome.\n  var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);\n  var mac = ios || /Mac/.test(navigator.platform);\n  var windows = /win/i.test(navigator.platform);\n\n  var presto_version = presto && navigator.userAgent.match(/Version\\/(\\d*\\.\\d*)/);\n  if (presto_version) presto_version = Number(presto_version[1]);\n  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }\n  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X\n  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));\n  var captureRightClick = gecko || (ie && ie_version >= 9);\n\n  // Optimize some code when these features are not used.\n  var sawReadOnlySpans = false, sawCollapsedSpans = false;\n\n  // EDITOR CONSTRUCTOR\n\n  // A CodeMirror instance represents an editor. This is the object\n  // that user code is usually dealing with.\n\n  function CodeMirror(place, options) {\n    if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);\n\n    this.options = options = options ? copyObj(options) : {};\n    // Determine effective options based on given values and defaults.\n    copyObj(defaults, options, false);\n    setGuttersForLineNumbers(options);\n\n    var doc = options.value;\n    if (typeof doc == \"string\") doc = new Doc(doc, options.mode);\n    this.doc = doc;\n\n    var input = new CodeMirror.inputStyles[options.inputStyle](this);\n    var display = this.display = new Display(place, doc, input);\n    display.wrapper.CodeMirror = this;\n    updateGutters(this);\n    themeChanged(this);\n    if (options.lineWrapping)\n      this.display.wrapper.className += \" CodeMirror-wrap\";\n    if (options.autofocus && !mobile) display.input.focus();\n    initScrollbars(this);\n\n    this.state = {\n      keyMaps: [],  // stores maps added by addKeyMap\n      overlays: [], // highlighting overlays, as added by addOverlay\n      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info\n      overwrite: false,\n      delayingBlurEvent: false,\n      focused: false,\n      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode\n      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll\n      draggingText: false,\n      highlight: new Delayed(), // stores highlight worker timeout\n      keySeq: null,  // Unfinished key sequence\n      specialChars: null\n    };\n\n    var cm = this;\n\n    // Override magic textarea content restore that IE sometimes does\n    // on our hidden textarea on reload\n    if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);\n\n    registerEventHandlers(this);\n    ensureGlobalHandlers();\n\n    startOperation(this);\n    this.curOp.forceUpdate = true;\n    attachDoc(this, doc);\n\n    if ((options.autofocus && !mobile) || cm.hasFocus())\n      setTimeout(bind(onFocus, this), 20);\n    else\n      onBlur(this);\n\n    for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))\n      optionHandlers[opt](this, options[opt], Init);\n    maybeUpdateLineNumberWidth(this);\n    if (options.finishInit) options.finishInit(this);\n    for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);\n    endOperation(this);\n    // Suppress optimizelegibility in Webkit, since it breaks text\n    // measuring on line wrapping boundaries.\n    if (webkit && options.lineWrapping &&\n        getComputedStyle(display.lineDiv).textRendering == \"optimizelegibility\")\n      display.lineDiv.style.textRendering = \"auto\";\n  }\n\n  // DISPLAY CONSTRUCTOR\n\n  // The display handles the DOM integration, both for input reading\n  // and content drawing. It holds references to DOM nodes and\n  // display-related state.\n\n  function Display(place, doc, input) {\n    var d = this;\n    this.input = input;\n\n    // Covers bottom-right square when both scrollbars are present.\n    d.scrollbarFiller = elt(\"div\", null, \"CodeMirror-scrollbar-filler\");\n    d.scrollbarFiller.setAttribute(\"cm-not-content\", \"true\");\n    // Covers bottom of gutter when coverGutterNextToScrollbar is on\n    // and h scrollbar is present.\n    d.gutterFiller = elt(\"div\", null, \"CodeMirror-gutter-filler\");\n    d.gutterFiller.setAttribute(\"cm-not-content\", \"true\");\n    // Will contain the actual code, positioned to cover the viewport.\n    d.lineDiv = elt(\"div\", null, \"CodeMirror-code\");\n    // Elements are added to these to represent selection and cursors.\n    d.selectionDiv = elt(\"div\", null, null, \"position: relative; z-index: 1\");\n    d.cursorDiv = elt(\"div\", null, \"CodeMirror-cursors\");\n    // A visibility: hidden element used to find the size of things.\n    d.measure = elt(\"div\", null, \"CodeMirror-measure\");\n    // When lines outside of the viewport are measured, they are drawn in this.\n    d.lineMeasure = elt(\"div\", null, \"CodeMirror-measure\");\n    // Wraps everything that needs to exist inside the vertically-padded coordinate system\n    d.lineSpace = elt(\"div\", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],\n                      null, \"position: relative; outline: none\");\n    // Moved around its parent to cover visible view.\n    d.mover = elt(\"div\", [elt(\"div\", [d.lineSpace], \"CodeMirror-lines\")], null, \"position: relative\");\n    // Set to the height of the document, allowing scrolling.\n    d.sizer = elt(\"div\", [d.mover], \"CodeMirror-sizer\");\n    d.sizerWidth = null;\n    // Behavior of elts with overflow: auto and padding is\n    // inconsistent across browsers. This is used to ensure the\n    // scrollable area is big enough.\n    d.heightForcer = elt(\"div\", null, null, \"position: absolute; height: \" + scrollerGap + \"px; width: 1px;\");\n    // Will contain the gutters, if any.\n    d.gutters = elt(\"div\", null, \"CodeMirror-gutters\");\n    d.lineGutter = null;\n    // Actual scrollable element.\n    d.scroller = elt(\"div\", [d.sizer, d.heightForcer, d.gutters], \"CodeMirror-scroll\");\n    d.scroller.setAttribute(\"tabIndex\", \"-1\");\n    // The element in which the editor lives.\n    d.wrapper = elt(\"div\", [d.scrollbarFiller, d.gutterFiller, d.scroller], \"CodeMirror\");\n\n    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)\n    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }\n    if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;\n\n    if (place) {\n      if (place.appendChild) place.appendChild(d.wrapper);\n      else place(d.wrapper);\n    }\n\n    // Current rendered range (may be bigger than the view window).\n    d.viewFrom = d.viewTo = doc.first;\n    d.reportedViewFrom = d.reportedViewTo = doc.first;\n    // Information about the rendered lines.\n    d.view = [];\n    d.renderedView = null;\n    // Holds info about a single rendered line when it was rendered\n    // for measurement, while not in view.\n    d.externalMeasured = null;\n    // Empty space (in pixels) above the view\n    d.viewOffset = 0;\n    d.lastWrapHeight = d.lastWrapWidth = 0;\n    d.updateLineNumbers = null;\n\n    d.nativeBarWidth = d.barHeight = d.barWidth = 0;\n    d.scrollbarsClipped = false;\n\n    // Used to only resize the line number gutter when necessary (when\n    // the amount of lines crosses a boundary that makes its width change)\n    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;\n    // Set to true when a non-horizontal-scrolling line widget is\n    // added. As an optimization, line widget aligning is skipped when\n    // this is false.\n    d.alignWidgets = false;\n\n    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n\n    // Tracks the maximum line length so that the horizontal scrollbar\n    // can be kept static when scrolling.\n    d.maxLine = null;\n    d.maxLineLength = 0;\n    d.maxLineChanged = false;\n\n    // Used for measuring wheel scrolling granularity\n    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;\n\n    // True when shift is held down.\n    d.shift = false;\n\n    // Used to track whether anything happened since the context menu\n    // was opened.\n    d.selForContextMenu = null;\n\n    d.activeTouch = null;\n\n    input.init(d);\n  }\n\n  // STATE UPDATES\n\n  // Used to get the editor into a consistent state again when options change.\n\n  function loadMode(cm) {\n    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);\n    resetModeState(cm);\n  }\n\n  function resetModeState(cm) {\n    cm.doc.iter(function(line) {\n      if (line.stateAfter) line.stateAfter = null;\n      if (line.styles) line.styles = null;\n    });\n    cm.doc.frontier = cm.doc.first;\n    startWorker(cm, 100);\n    cm.state.modeGen++;\n    if (cm.curOp) regChange(cm);\n  }\n\n  function wrappingChanged(cm) {\n    if (cm.options.lineWrapping) {\n      addClass(cm.display.wrapper, \"CodeMirror-wrap\");\n      cm.display.sizer.style.minWidth = \"\";\n      cm.display.sizerWidth = null;\n    } else {\n      rmClass(cm.display.wrapper, \"CodeMirror-wrap\");\n      findMaxLine(cm);\n    }\n    estimateLineHeights(cm);\n    regChange(cm);\n    clearCaches(cm);\n    setTimeout(function(){updateScrollbars(cm);}, 100);\n  }\n\n  // Returns a function that estimates the height of a line, to use as\n  // first approximation until the line becomes visible (and is thus\n  // properly measurable).\n  function estimateHeight(cm) {\n    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;\n    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);\n    return function(line) {\n      if (lineIsHidden(cm.doc, line)) return 0;\n\n      var widgetsHeight = 0;\n      if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {\n        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;\n      }\n\n      if (wrapping)\n        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;\n      else\n        return widgetsHeight + th;\n    };\n  }\n\n  function estimateLineHeights(cm) {\n    var doc = cm.doc, est = estimateHeight(cm);\n    doc.iter(function(line) {\n      var estHeight = est(line);\n      if (estHeight != line.height) updateLineHeight(line, estHeight);\n    });\n  }\n\n  function themeChanged(cm) {\n    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\\s*cm-s-\\S+/g, \"\") +\n      cm.options.theme.replace(/(^|\\s)\\s*/g, \" cm-s-\");\n    clearCaches(cm);\n  }\n\n  function guttersChanged(cm) {\n    updateGutters(cm);\n    regChange(cm);\n    setTimeout(function(){alignHorizontally(cm);}, 20);\n  }\n\n  // Rebuild the gutter elements, ensure the margin to the left of the\n  // code matches their width.\n  function updateGutters(cm) {\n    var gutters = cm.display.gutters, specs = cm.options.gutters;\n    removeChildren(gutters);\n    for (var i = 0; i < specs.length; ++i) {\n      var gutterClass = specs[i];\n      var gElt = gutters.appendChild(elt(\"div\", null, \"CodeMirror-gutter \" + gutterClass));\n      if (gutterClass == \"CodeMirror-linenumbers\") {\n        cm.display.lineGutter = gElt;\n        gElt.style.width = (cm.display.lineNumWidth || 1) + \"px\";\n      }\n    }\n    gutters.style.display = i ? \"\" : \"none\";\n    updateGutterSpace(cm);\n  }\n\n  function updateGutterSpace(cm) {\n    var width = cm.display.gutters.offsetWidth;\n    cm.display.sizer.style.marginLeft = width + \"px\";\n  }\n\n  // Compute the character length of a line, taking into account\n  // collapsed ranges (see markText) that might hide parts, and join\n  // other lines onto it.\n  function lineLength(line) {\n    if (line.height == 0) return 0;\n    var len = line.text.length, merged, cur = line;\n    while (merged = collapsedSpanAtStart(cur)) {\n      var found = merged.find(0, true);\n      cur = found.from.line;\n      len += found.from.ch - found.to.ch;\n    }\n    cur = line;\n    while (merged = collapsedSpanAtEnd(cur)) {\n      var found = merged.find(0, true);\n      len -= cur.text.length - found.from.ch;\n      cur = found.to.line;\n      len += cur.text.length - found.to.ch;\n    }\n    return len;\n  }\n\n  // Find the longest line in the document.\n  function findMaxLine(cm) {\n    var d = cm.display, doc = cm.doc;\n    d.maxLine = getLine(doc, doc.first);\n    d.maxLineLength = lineLength(d.maxLine);\n    d.maxLineChanged = true;\n    doc.iter(function(line) {\n      var len = lineLength(line);\n      if (len > d.maxLineLength) {\n        d.maxLineLength = len;\n        d.maxLine = line;\n      }\n    });\n  }\n\n  // Make sure the gutters options contains the element\n  // \"CodeMirror-linenumbers\" when the lineNumbers option is true.\n  function setGuttersForLineNumbers(options) {\n    var found = indexOf(options.gutters, \"CodeMirror-linenumbers\");\n    if (found == -1 && options.lineNumbers) {\n      options.gutters = options.gutters.concat([\"CodeMirror-linenumbers\"]);\n    } else if (found > -1 && !options.lineNumbers) {\n      options.gutters = options.gutters.slice(0);\n      options.gutters.splice(found, 1);\n    }\n  }\n\n  // SCROLLBARS\n\n  // Prepare DOM reads needed to update the scrollbars. Done in one\n  // shot to minimize update/measure roundtrips.\n  function measureForScrollbars(cm) {\n    var d = cm.display, gutterW = d.gutters.offsetWidth;\n    var docH = Math.round(cm.doc.height + paddingVert(cm.display));\n    return {\n      clientHeight: d.scroller.clientHeight,\n      viewHeight: d.wrapper.clientHeight,\n      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,\n      viewWidth: d.wrapper.clientWidth,\n      barLeft: cm.options.fixedGutter ? gutterW : 0,\n      docHeight: docH,\n      scrollHeight: docH + scrollGap(cm) + d.barHeight,\n      nativeBarWidth: d.nativeBarWidth,\n      gutterWidth: gutterW\n    };\n  }\n\n  function NativeScrollbars(place, scroll, cm) {\n    this.cm = cm;\n    var vert = this.vert = elt(\"div\", [elt(\"div\", null, null, \"min-width: 1px\")], \"CodeMirror-vscrollbar\");\n    var horiz = this.horiz = elt(\"div\", [elt(\"div\", null, null, \"height: 100%; min-height: 1px\")], \"CodeMirror-hscrollbar\");\n    place(vert); place(horiz);\n\n    on(vert, \"scroll\", function() {\n      if (vert.clientHeight) scroll(vert.scrollTop, \"vertical\");\n    });\n    on(horiz, \"scroll\", function() {\n      if (horiz.clientWidth) scroll(horiz.scrollLeft, \"horizontal\");\n    });\n\n    this.checkedOverlay = false;\n    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).\n    if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = \"18px\";\n  }\n\n  NativeScrollbars.prototype = copyObj({\n    update: function(measure) {\n      var needsH = measure.scrollWidth > measure.clientWidth + 1;\n      var needsV = measure.scrollHeight > measure.clientHeight + 1;\n      var sWidth = measure.nativeBarWidth;\n\n      if (needsV) {\n        this.vert.style.display = \"block\";\n        this.vert.style.bottom = needsH ? sWidth + \"px\" : \"0\";\n        var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);\n        // A bug in IE8 can cause this value to be negative, so guard it.\n        this.vert.firstChild.style.height =\n          Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + \"px\";\n      } else {\n        this.vert.style.display = \"\";\n        this.vert.firstChild.style.height = \"0\";\n      }\n\n      if (needsH) {\n        this.horiz.style.display = \"block\";\n        this.horiz.style.right = needsV ? sWidth + \"px\" : \"0\";\n        this.horiz.style.left = measure.barLeft + \"px\";\n        var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);\n        this.horiz.firstChild.style.width =\n          (measure.scrollWidth - measure.clientWidth + totalWidth) + \"px\";\n      } else {\n        this.horiz.style.display = \"\";\n        this.horiz.firstChild.style.width = \"0\";\n      }\n\n      if (!this.checkedOverlay && measure.clientHeight > 0) {\n        if (sWidth == 0) this.overlayHack();\n        this.checkedOverlay = true;\n      }\n\n      return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};\n    },\n    setScrollLeft: function(pos) {\n      if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;\n    },\n    setScrollTop: function(pos) {\n      if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;\n    },\n    overlayHack: function() {\n      var w = mac && !mac_geMountainLion ? \"12px\" : \"18px\";\n      this.horiz.style.minHeight = this.vert.style.minWidth = w;\n      var self = this;\n      var barMouseDown = function(e) {\n        if (e_target(e) != self.vert && e_target(e) != self.horiz)\n          operation(self.cm, onMouseDown)(e);\n      };\n      on(this.vert, \"mousedown\", barMouseDown);\n      on(this.horiz, \"mousedown\", barMouseDown);\n    },\n    clear: function() {\n      var parent = this.horiz.parentNode;\n      parent.removeChild(this.horiz);\n      parent.removeChild(this.vert);\n    }\n  }, NativeScrollbars.prototype);\n\n  function NullScrollbars() {}\n\n  NullScrollbars.prototype = copyObj({\n    update: function() { return {bottom: 0, right: 0}; },\n    setScrollLeft: function() {},\n    setScrollTop: function() {},\n    clear: function() {}\n  }, NullScrollbars.prototype);\n\n  CodeMirror.scrollbarModel = {\"native\": NativeScrollbars, \"null\": NullScrollbars};\n\n  function initScrollbars(cm) {\n    if (cm.display.scrollbars) {\n      cm.display.scrollbars.clear();\n      if (cm.display.scrollbars.addClass)\n        rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);\n    }\n\n    cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {\n      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);\n      // Prevent clicks in the scrollbars from killing focus\n      on(node, \"mousedown\", function() {\n        if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);\n      });\n      node.setAttribute(\"cm-not-content\", \"true\");\n    }, function(pos, axis) {\n      if (axis == \"horizontal\") setScrollLeft(cm, pos);\n      else setScrollTop(cm, pos);\n    }, cm);\n    if (cm.display.scrollbars.addClass)\n      addClass(cm.display.wrapper, cm.display.scrollbars.addClass);\n  }\n\n  function updateScrollbars(cm, measure) {\n    if (!measure) measure = measureForScrollbars(cm);\n    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;\n    updateScrollbarsInner(cm, measure);\n    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {\n      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)\n        updateHeightsInViewport(cm);\n      updateScrollbarsInner(cm, measureForScrollbars(cm));\n      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;\n    }\n  }\n\n  // Re-synchronize the fake scrollbars with the actual size of the\n  // content.\n  function updateScrollbarsInner(cm, measure) {\n    var d = cm.display;\n    var sizes = d.scrollbars.update(measure);\n\n    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + \"px\";\n    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + \"px\";\n\n    if (sizes.right && sizes.bottom) {\n      d.scrollbarFiller.style.display = \"block\";\n      d.scrollbarFiller.style.height = sizes.bottom + \"px\";\n      d.scrollbarFiller.style.width = sizes.right + \"px\";\n    } else d.scrollbarFiller.style.display = \"\";\n    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {\n      d.gutterFiller.style.display = \"block\";\n      d.gutterFiller.style.height = sizes.bottom + \"px\";\n      d.gutterFiller.style.width = measure.gutterWidth + \"px\";\n    } else d.gutterFiller.style.display = \"\";\n  }\n\n  // Compute the lines that are visible in a given viewport (defaults\n  // the the current scroll position). viewport may contain top,\n  // height, and ensure (see op.scrollToPos) properties.\n  function visibleLines(display, doc, viewport) {\n    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;\n    top = Math.floor(top - paddingTop(display));\n    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;\n\n    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);\n    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and\n    // forces those lines into the viewport (if possible).\n    if (viewport && viewport.ensure) {\n      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;\n      if (ensureFrom < from) {\n        from = ensureFrom;\n        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);\n      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {\n        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);\n        to = ensureTo;\n      }\n    }\n    return {from: from, to: Math.max(to, from + 1)};\n  }\n\n  // LINE NUMBERS\n\n  // Re-align line numbers and gutter marks to compensate for\n  // horizontal scrolling.\n  function alignHorizontally(cm) {\n    var display = cm.display, view = display.view;\n    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;\n    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;\n    var gutterW = display.gutters.offsetWidth, left = comp + \"px\";\n    for (var i = 0; i < view.length; i++) if (!view[i].hidden) {\n      if (cm.options.fixedGutter && view[i].gutter)\n        view[i].gutter.style.left = left;\n      var align = view[i].alignable;\n      if (align) for (var j = 0; j < align.length; j++)\n        align[j].style.left = left;\n    }\n    if (cm.options.fixedGutter)\n      display.gutters.style.left = (comp + gutterW) + \"px\";\n  }\n\n  // Used to ensure that the line number gutter is still the right\n  // size for the current document size. Returns true when an update\n  // is needed.\n  function maybeUpdateLineNumberWidth(cm) {\n    if (!cm.options.lineNumbers) return false;\n    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;\n    if (last.length != display.lineNumChars) {\n      var test = display.measure.appendChild(elt(\"div\", [elt(\"div\", last)],\n                                                 \"CodeMirror-linenumber CodeMirror-gutter-elt\"));\n      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;\n      display.lineGutter.style.width = \"\";\n      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;\n      display.lineNumWidth = display.lineNumInnerWidth + padding;\n      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;\n      display.lineGutter.style.width = display.lineNumWidth + \"px\";\n      updateGutterSpace(cm);\n      return true;\n    }\n    return false;\n  }\n\n  function lineNumberFor(options, i) {\n    return String(options.lineNumberFormatter(i + options.firstLineNumber));\n  }\n\n  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,\n  // but using getBoundingClientRect to get a sub-pixel-accurate\n  // result.\n  function compensateForHScroll(display) {\n    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;\n  }\n\n  // DISPLAY DRAWING\n\n  function DisplayUpdate(cm, viewport, force) {\n    var display = cm.display;\n\n    this.viewport = viewport;\n    // Store some values that we'll need later (but don't want to force a relayout for)\n    this.visible = visibleLines(display, cm.doc, viewport);\n    this.editorIsHidden = !display.wrapper.offsetWidth;\n    this.wrapperHeight = display.wrapper.clientHeight;\n    this.wrapperWidth = display.wrapper.clientWidth;\n    this.oldDisplayWidth = displayWidth(cm);\n    this.force = force;\n    this.dims = getDimensions(cm);\n    this.events = [];\n  }\n\n  DisplayUpdate.prototype.signal = function(emitter, type) {\n    if (hasHandler(emitter, type))\n      this.events.push(arguments);\n  };\n  DisplayUpdate.prototype.finish = function() {\n    for (var i = 0; i < this.events.length; i++)\n      signal.apply(null, this.events[i]);\n  };\n\n  function maybeClipScrollbars(cm) {\n    var display = cm.display;\n    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {\n      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;\n      display.heightForcer.style.height = scrollGap(cm) + \"px\";\n      display.sizer.style.marginBottom = -display.nativeBarWidth + \"px\";\n      display.sizer.style.borderRightWidth = scrollGap(cm) + \"px\";\n      display.scrollbarsClipped = true;\n    }\n  }\n\n  // Does the actual updating of the line display. Bails out\n  // (returning false) when there is nothing to be done and forced is\n  // false.\n  function updateDisplayIfNeeded(cm, update) {\n    var display = cm.display, doc = cm.doc;\n\n    if (update.editorIsHidden) {\n      resetView(cm);\n      return false;\n    }\n\n    // Bail out if the visible area is already rendered and nothing changed.\n    if (!update.force &&\n        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&\n        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&\n        display.renderedView == display.view && countDirtyView(cm) == 0)\n      return false;\n\n    if (maybeUpdateLineNumberWidth(cm)) {\n      resetView(cm);\n      update.dims = getDimensions(cm);\n    }\n\n    // Compute a suitable new viewport (from & to)\n    var end = doc.first + doc.size;\n    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);\n    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);\n    if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);\n    if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);\n    if (sawCollapsedSpans) {\n      from = visualLineNo(cm.doc, from);\n      to = visualLineEndNo(cm.doc, to);\n    }\n\n    var different = from != display.viewFrom || to != display.viewTo ||\n      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;\n    adjustView(cm, from, to);\n\n    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));\n    // Position the mover div to align with the current scroll position\n    cm.display.mover.style.top = display.viewOffset + \"px\";\n\n    var toUpdate = countDirtyView(cm);\n    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&\n        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))\n      return false;\n\n    // For big changes, we hide the enclosing element during the\n    // update, since that speeds up the operations on most browsers.\n    var focused = activeElt();\n    if (toUpdate > 4) display.lineDiv.style.display = \"none\";\n    patchDisplay(cm, display.updateLineNumbers, update.dims);\n    if (toUpdate > 4) display.lineDiv.style.display = \"\";\n    display.renderedView = display.view;\n    // There might have been a widget with a focused element that got\n    // hidden or updated, if so re-focus it.\n    if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();\n\n    // Prevent selection and cursors from interfering with the scroll\n    // width and height.\n    removeChildren(display.cursorDiv);\n    removeChildren(display.selectionDiv);\n    display.gutters.style.height = 0;\n\n    if (different) {\n      display.lastWrapHeight = update.wrapperHeight;\n      display.lastWrapWidth = update.wrapperWidth;\n      startWorker(cm, 400);\n    }\n\n    display.updateLineNumbers = null;\n\n    return true;\n  }\n\n  function postUpdateDisplay(cm, update) {\n    var viewport = update.viewport;\n    for (var first = true;; first = false) {\n      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {\n        // Clip forced viewport to actual scrollable area.\n        if (viewport && viewport.top != null)\n          viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};\n        // Updated line heights might result in the drawn area not\n        // actually covering the viewport. Keep looping until it does.\n        update.visible = visibleLines(cm.display, cm.doc, viewport);\n        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)\n          break;\n      }\n      if (!updateDisplayIfNeeded(cm, update)) break;\n      updateHeightsInViewport(cm);\n      var barMeasure = measureForScrollbars(cm);\n      updateSelection(cm);\n      setDocumentHeight(cm, barMeasure);\n      updateScrollbars(cm, barMeasure);\n    }\n\n    update.signal(cm, \"update\", cm);\n    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {\n      update.signal(cm, \"viewportChange\", cm, cm.display.viewFrom, cm.display.viewTo);\n      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;\n    }\n  }\n\n  function updateDisplaySimple(cm, viewport) {\n    var update = new DisplayUpdate(cm, viewport);\n    if (updateDisplayIfNeeded(cm, update)) {\n      updateHeightsInViewport(cm);\n      postUpdateDisplay(cm, update);\n      var barMeasure = measureForScrollbars(cm);\n      updateSelection(cm);\n      setDocumentHeight(cm, barMeasure);\n      updateScrollbars(cm, barMeasure);\n      update.finish();\n    }\n  }\n\n  function setDocumentHeight(cm, measure) {\n    cm.display.sizer.style.minHeight = measure.docHeight + \"px\";\n    var total = measure.docHeight + cm.display.barHeight;\n    cm.display.heightForcer.style.top = total + \"px\";\n    cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + \"px\";\n  }\n\n  // Read the actual heights of the rendered lines, and update their\n  // stored heights to match.\n  function updateHeightsInViewport(cm) {\n    var display = cm.display;\n    var prevBottom = display.lineDiv.offsetTop;\n    for (var i = 0; i < display.view.length; i++) {\n      var cur = display.view[i], height;\n      if (cur.hidden) continue;\n      if (ie && ie_version < 8) {\n        var bot = cur.node.offsetTop + cur.node.offsetHeight;\n        height = bot - prevBottom;\n        prevBottom = bot;\n      } else {\n        var box = cur.node.getBoundingClientRect();\n        height = box.bottom - box.top;\n      }\n      var diff = cur.line.height - height;\n      if (height < 2) height = textHeight(display);\n      if (diff > .001 || diff < -.001) {\n        updateLineHeight(cur.line, height);\n        updateWidgetHeight(cur.line);\n        if (cur.rest) for (var j = 0; j < cur.rest.length; j++)\n          updateWidgetHeight(cur.rest[j]);\n      }\n    }\n  }\n\n  // Read and store the height of line widgets associated with the\n  // given line.\n  function updateWidgetHeight(line) {\n    if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)\n      line.widgets[i].height = line.widgets[i].node.offsetHeight;\n  }\n\n  // Do a bulk-read of the DOM positions and sizes needed to draw the\n  // view, so that we don't interleave reading and writing to the DOM.\n  function getDimensions(cm) {\n    var d = cm.display, left = {}, width = {};\n    var gutterLeft = d.gutters.clientLeft;\n    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {\n      left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;\n      width[cm.options.gutters[i]] = n.clientWidth;\n    }\n    return {fixedPos: compensateForHScroll(d),\n            gutterTotalWidth: d.gutters.offsetWidth,\n            gutterLeft: left,\n            gutterWidth: width,\n            wrapperWidth: d.wrapper.clientWidth};\n  }\n\n  // Sync the actual display DOM structure with display.view, removing\n  // nodes for lines that are no longer in view, and creating the ones\n  // that are not there yet, and updating the ones that are out of\n  // date.\n  function patchDisplay(cm, updateNumbersFrom, dims) {\n    var display = cm.display, lineNumbers = cm.options.lineNumbers;\n    var container = display.lineDiv, cur = container.firstChild;\n\n    function rm(node) {\n      var next = node.nextSibling;\n      // Works around a throw-scroll bug in OS X Webkit\n      if (webkit && mac && cm.display.currentWheelTarget == node)\n        node.style.display = \"none\";\n      else\n        node.parentNode.removeChild(node);\n      return next;\n    }\n\n    var view = display.view, lineN = display.viewFrom;\n    // Loop over the elements in the view, syncing cur (the DOM nodes\n    // in display.lineDiv) with the view as we go.\n    for (var i = 0; i < view.length; i++) {\n      var lineView = view[i];\n      if (lineView.hidden) {\n      } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet\n        var node = buildLineElement(cm, lineView, lineN, dims);\n        container.insertBefore(node, cur);\n      } else { // Already drawn\n        while (cur != lineView.node) cur = rm(cur);\n        var updateNumber = lineNumbers && updateNumbersFrom != null &&\n          updateNumbersFrom <= lineN && lineView.lineNumber;\n        if (lineView.changes) {\n          if (indexOf(lineView.changes, \"gutter\") > -1) updateNumber = false;\n          updateLineForChanges(cm, lineView, lineN, dims);\n        }\n        if (updateNumber) {\n          removeChildren(lineView.lineNumber);\n          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));\n        }\n        cur = lineView.node.nextSibling;\n      }\n      lineN += lineView.size;\n    }\n    while (cur) cur = rm(cur);\n  }\n\n  // When an aspect of a line changes, a string is added to\n  // lineView.changes. This updates the relevant part of the line's\n  // DOM structure.\n  function updateLineForChanges(cm, lineView, lineN, dims) {\n    for (var j = 0; j < lineView.changes.length; j++) {\n      var type = lineView.changes[j];\n      if (type == \"text\") updateLineText(cm, lineView);\n      else if (type == \"gutter\") updateLineGutter(cm, lineView, lineN, dims);\n      else if (type == \"class\") updateLineClasses(lineView);\n      else if (type == \"widget\") updateLineWidgets(cm, lineView, dims);\n    }\n    lineView.changes = null;\n  }\n\n  // Lines with gutter elements, widgets or a background class need to\n  // be wrapped, and have the extra elements added to the wrapper div\n  function ensureLineWrapped(lineView) {\n    if (lineView.node == lineView.text) {\n      lineView.node = elt(\"div\", null, null, \"position: relative\");\n      if (lineView.text.parentNode)\n        lineView.text.parentNode.replaceChild(lineView.node, lineView.text);\n      lineView.node.appendChild(lineView.text);\n      if (ie && ie_version < 8) lineView.node.style.zIndex = 2;\n    }\n    return lineView.node;\n  }\n\n  function updateLineBackground(lineView) {\n    var cls = lineView.bgClass ? lineView.bgClass + \" \" + (lineView.line.bgClass || \"\") : lineView.line.bgClass;\n    if (cls) cls += \" CodeMirror-linebackground\";\n    if (lineView.background) {\n      if (cls) lineView.background.className = cls;\n      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }\n    } else if (cls) {\n      var wrap = ensureLineWrapped(lineView);\n      lineView.background = wrap.insertBefore(elt(\"div\", null, cls), wrap.firstChild);\n    }\n  }\n\n  // Wrapper around buildLineContent which will reuse the structure\n  // in display.externalMeasured when possible.\n  function getLineContent(cm, lineView) {\n    var ext = cm.display.externalMeasured;\n    if (ext && ext.line == lineView.line) {\n      cm.display.externalMeasured = null;\n      lineView.measure = ext.measure;\n      return ext.built;\n    }\n    return buildLineContent(cm, lineView);\n  }\n\n  // Redraw the line's text. Interacts with the background and text\n  // classes because the mode may output tokens that influence these\n  // classes.\n  function updateLineText(cm, lineView) {\n    var cls = lineView.text.className;\n    var built = getLineContent(cm, lineView);\n    if (lineView.text == lineView.node) lineView.node = built.pre;\n    lineView.text.parentNode.replaceChild(built.pre, lineView.text);\n    lineView.text = built.pre;\n    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {\n      lineView.bgClass = built.bgClass;\n      lineView.textClass = built.textClass;\n      updateLineClasses(lineView);\n    } else if (cls) {\n      lineView.text.className = cls;\n    }\n  }\n\n  function updateLineClasses(lineView) {\n    updateLineBackground(lineView);\n    if (lineView.line.wrapClass)\n      ensureLineWrapped(lineView).className = lineView.line.wrapClass;\n    else if (lineView.node != lineView.text)\n      lineView.node.className = \"\";\n    var textClass = lineView.textClass ? lineView.textClass + \" \" + (lineView.line.textClass || \"\") : lineView.line.textClass;\n    lineView.text.className = textClass || \"\";\n  }\n\n  function updateLineGutter(cm, lineView, lineN, dims) {\n    if (lineView.gutter) {\n      lineView.node.removeChild(lineView.gutter);\n      lineView.gutter = null;\n    }\n    var markers = lineView.line.gutterMarkers;\n    if (cm.options.lineNumbers || markers) {\n      var wrap = ensureLineWrapped(lineView);\n      var gutterWrap = lineView.gutter = elt(\"div\", null, \"CodeMirror-gutter-wrapper\", \"left: \" +\n                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +\n                                             \"px; width: \" + dims.gutterTotalWidth + \"px\");\n      cm.display.input.setUneditable(gutterWrap);\n      wrap.insertBefore(gutterWrap, lineView.text);\n      if (lineView.line.gutterClass)\n        gutterWrap.className += \" \" + lineView.line.gutterClass;\n      if (cm.options.lineNumbers && (!markers || !markers[\"CodeMirror-linenumbers\"]))\n        lineView.lineNumber = gutterWrap.appendChild(\n          elt(\"div\", lineNumberFor(cm.options, lineN),\n              \"CodeMirror-linenumber CodeMirror-gutter-elt\",\n              \"left: \" + dims.gutterLeft[\"CodeMirror-linenumbers\"] + \"px; width: \"\n              + cm.display.lineNumInnerWidth + \"px\"));\n      if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {\n        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];\n        if (found)\n          gutterWrap.appendChild(elt(\"div\", [found], \"CodeMirror-gutter-elt\", \"left: \" +\n                                     dims.gutterLeft[id] + \"px; width: \" + dims.gutterWidth[id] + \"px\"));\n      }\n    }\n  }\n\n  function updateLineWidgets(cm, lineView, dims) {\n    if (lineView.alignable) lineView.alignable = null;\n    for (var node = lineView.node.firstChild, next; node; node = next) {\n      var next = node.nextSibling;\n      if (node.className == \"CodeMirror-linewidget\")\n        lineView.node.removeChild(node);\n    }\n    insertLineWidgets(cm, lineView, dims);\n  }\n\n  // Build a line's DOM representation from scratch\n  function buildLineElement(cm, lineView, lineN, dims) {\n    var built = getLineContent(cm, lineView);\n    lineView.text = lineView.node = built.pre;\n    if (built.bgClass) lineView.bgClass = built.bgClass;\n    if (built.textClass) lineView.textClass = built.textClass;\n\n    updateLineClasses(lineView);\n    updateLineGutter(cm, lineView, lineN, dims);\n    insertLineWidgets(cm, lineView, dims);\n    return lineView.node;\n  }\n\n  // A lineView may contain multiple logical lines (when merged by\n  // collapsed spans). The widgets for all of them need to be drawn.\n  function insertLineWidgets(cm, lineView, dims) {\n    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);\n    if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)\n      insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);\n  }\n\n  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {\n    if (!line.widgets) return;\n    var wrap = ensureLineWrapped(lineView);\n    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {\n      var widget = ws[i], node = elt(\"div\", [widget.node], \"CodeMirror-linewidget\");\n      if (!widget.handleMouseEvents) node.setAttribute(\"cm-ignore-events\", \"true\");\n      positionLineWidget(widget, node, lineView, dims);\n      cm.display.input.setUneditable(node);\n      if (allowAbove && widget.above)\n        wrap.insertBefore(node, lineView.gutter || lineView.text);\n      else\n        wrap.appendChild(node);\n      signalLater(widget, \"redraw\");\n    }\n  }\n\n  function positionLineWidget(widget, node, lineView, dims) {\n    if (widget.noHScroll) {\n      (lineView.alignable || (lineView.alignable = [])).push(node);\n      var width = dims.wrapperWidth;\n      node.style.left = dims.fixedPos + \"px\";\n      if (!widget.coverGutter) {\n        width -= dims.gutterTotalWidth;\n        node.style.paddingLeft = dims.gutterTotalWidth + \"px\";\n      }\n      node.style.width = width + \"px\";\n    }\n    if (widget.coverGutter) {\n      node.style.zIndex = 5;\n      node.style.position = \"relative\";\n      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + \"px\";\n    }\n  }\n\n  // POSITION OBJECT\n\n  // A Pos instance represents a position within the text.\n  var Pos = CodeMirror.Pos = function(line, ch) {\n    if (!(this instanceof Pos)) return new Pos(line, ch);\n    this.line = line; this.ch = ch;\n  };\n\n  // Compare two positions, return 0 if they are the same, a negative\n  // number when a is less, and a positive number otherwise.\n  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };\n\n  function copyPos(x) {return Pos(x.line, x.ch);}\n  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }\n  function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }\n\n  // INPUT HANDLING\n\n  function ensureFocus(cm) {\n    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }\n  }\n\n  function isReadOnly(cm) {\n    return cm.options.readOnly || cm.doc.cantEdit;\n  }\n\n  // This will be set to an array of strings when copying, so that,\n  // when pasting, we know what kind of selections the copied text\n  // was made out of.\n  var lastCopied = null;\n\n  function applyTextInput(cm, inserted, deleted, sel, origin) {\n    var doc = cm.doc;\n    cm.display.shift = false;\n    if (!sel) sel = doc.sel;\n\n    var paste = cm.state.pasteIncoming || origin == \"paste\";\n    var textLines = splitLines(inserted), multiPaste = null;\n    // When pasing N lines into N selections, insert one line per selection\n    if (paste && sel.ranges.length > 1) {\n      if (lastCopied && lastCopied.join(\"\\n\") == inserted)\n        multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);\n      else if (textLines.length == sel.ranges.length)\n        multiPaste = map(textLines, function(l) { return [l]; });\n    }\n\n    // Normal behavior is to insert the new text into every selection\n    for (var i = sel.ranges.length - 1; i >= 0; i--) {\n      var range = sel.ranges[i];\n      var from = range.from(), to = range.to();\n      if (range.empty()) {\n        if (deleted && deleted > 0) // Handle deletion\n          from = Pos(from.line, from.ch - deleted);\n        else if (cm.state.overwrite && !paste) // Handle overwrite\n          to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));\n      }\n      var updateInput = cm.curOp.updateInput;\n      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,\n                         origin: origin || (paste ? \"paste\" : cm.state.cutIncoming ? \"cut\" : \"+input\")};\n      makeChange(cm.doc, changeEvent);\n      signalLater(cm, \"inputRead\", cm, changeEvent);\n    }\n    if (inserted && !paste)\n      triggerElectric(cm, inserted);\n\n    ensureCursorVisible(cm);\n    cm.curOp.updateInput = updateInput;\n    cm.curOp.typing = true;\n    cm.state.pasteIncoming = cm.state.cutIncoming = false;\n  }\n\n  function handlePaste(e, cm) {\n    var pasted = e.clipboardData && e.clipboardData.getData(\"text/plain\");\n    if (pasted) {\n      e.preventDefault();\n      runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, \"paste\"); });\n      return true;\n    }\n  }\n\n  function triggerElectric(cm, inserted) {\n    // When an 'electric' character is inserted, immediately trigger a reindent\n    if (!cm.options.electricChars || !cm.options.smartIndent) return;\n    var sel = cm.doc.sel;\n\n    for (var i = sel.ranges.length - 1; i >= 0; i--) {\n      var range = sel.ranges[i];\n      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;\n      var mode = cm.getModeAt(range.head);\n      var indented = false;\n      if (mode.electricChars) {\n        for (var j = 0; j < mode.electricChars.length; j++)\n          if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {\n            indented = indentLine(cm, range.head.line, \"smart\");\n            break;\n          }\n      } else if (mode.electricInput) {\n        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))\n          indented = indentLine(cm, range.head.line, \"smart\");\n      }\n      if (indented) signalLater(cm, \"electricInput\", cm, range.head.line);\n    }\n  }\n\n  function copyableRanges(cm) {\n    var text = [], ranges = [];\n    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {\n      var line = cm.doc.sel.ranges[i].head.line;\n      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};\n      ranges.push(lineRange);\n      text.push(cm.getRange(lineRange.anchor, lineRange.head));\n    }\n    return {text: text, ranges: ranges};\n  }\n\n  function disableBrowserMagic(field) {\n    field.setAttribute(\"autocorrect\", \"off\");\n    field.setAttribute(\"autocapitalize\", \"off\");\n    field.setAttribute(\"spellcheck\", \"false\");\n  }\n\n  // TEXTAREA INPUT STYLE\n\n  function TextareaInput(cm) {\n    this.cm = cm;\n    // See input.poll and input.reset\n    this.prevInput = \"\";\n\n    // Flag that indicates whether we expect input to appear real soon\n    // now (after some event like 'keypress' or 'input') and are\n    // polling intensively.\n    this.pollingFast = false;\n    // Self-resetting timeout for the poller\n    this.polling = new Delayed();\n    // Tracks when input.reset has punted to just putting a short\n    // string into the textarea instead of the full selection.\n    this.inaccurateSelection = false;\n    // Used to work around IE issue with selection being forgotten when focus moves away from textarea\n    this.hasSelection = false;\n    this.composing = null;\n  };\n\n  function hiddenTextarea() {\n    var te = elt(\"textarea\", null, null, \"position: absolute; padding: 0; width: 1px; height: 1em; outline: none\");\n    var div = elt(\"div\", [te], null, \"overflow: hidden; position: relative; width: 3px; height: 0px;\");\n    // The textarea is kept positioned near the cursor to prevent the\n    // fact that it'll be scrolled into view on input from scrolling\n    // our fake cursor out of view. On webkit, when wrap=off, paste is\n    // very slow. So make the area wide instead.\n    if (webkit) te.style.width = \"1000px\";\n    else te.setAttribute(\"wrap\", \"off\");\n    // If border: 0; -- iOS fails to open keyboard (issue #1287)\n    if (ios) te.style.border = \"1px solid black\";\n    disableBrowserMagic(te);\n    return div;\n  }\n\n  TextareaInput.prototype = copyObj({\n    init: function(display) {\n      var input = this, cm = this.cm;\n\n      // Wraps and hides input textarea\n      var div = this.wrapper = hiddenTextarea();\n      // The semihidden textarea that is focused when the editor is\n      // focused, and receives input.\n      var te = this.textarea = div.firstChild;\n      display.wrapper.insertBefore(div, display.wrapper.firstChild);\n\n      // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)\n      if (ios) te.style.width = \"0px\";\n\n      on(te, \"input\", function() {\n        if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;\n        input.poll();\n      });\n\n      on(te, \"paste\", function(e) {\n        if (handlePaste(e, cm)) return true;\n\n        cm.state.pasteIncoming = true;\n        input.fastPoll();\n      });\n\n      function prepareCopyCut(e) {\n        if (cm.somethingSelected()) {\n          lastCopied = cm.getSelections();\n          if (input.inaccurateSelection) {\n            input.prevInput = \"\";\n            input.inaccurateSelection = false;\n            te.value = lastCopied.join(\"\\n\");\n            selectInput(te);\n          }\n        } else if (!cm.options.lineWiseCopyCut) {\n          return;\n        } else {\n          var ranges = copyableRanges(cm);\n          lastCopied = ranges.text;\n          if (e.type == \"cut\") {\n            cm.setSelections(ranges.ranges, null, sel_dontScroll);\n          } else {\n            input.prevInput = \"\";\n            te.value = ranges.text.join(\"\\n\");\n            selectInput(te);\n          }\n        }\n        if (e.type == \"cut\") cm.state.cutIncoming = true;\n      }\n      on(te, \"cut\", prepareCopyCut);\n      on(te, \"copy\", prepareCopyCut);\n\n      on(display.scroller, \"paste\", function(e) {\n        if (eventInWidget(display, e)) return;\n        cm.state.pasteIncoming = true;\n        input.focus();\n      });\n\n      // Prevent normal selection in the editor (we handle our own)\n      on(display.lineSpace, \"selectstart\", function(e) {\n        if (!eventInWidget(display, e)) e_preventDefault(e);\n      });\n\n      on(te, \"compositionstart\", function() {\n        var start = cm.getCursor(\"from\");\n        input.composing = {\n          start: start,\n          range: cm.markText(start, cm.getCursor(\"to\"), {className: \"CodeMirror-composing\"})\n        };\n      });\n      on(te, \"compositionend\", function() {\n        if (input.composing) {\n          input.poll();\n          input.composing.range.clear();\n          input.composing = null;\n        }\n      });\n    },\n\n    prepareSelection: function() {\n      // Redraw the selection and/or cursor\n      var cm = this.cm, display = cm.display, doc = cm.doc;\n      var result = prepareSelection(cm);\n\n      // Move the hidden textarea near the cursor to prevent scrolling artifacts\n      if (cm.options.moveInputWithCursor) {\n        var headPos = cursorCoords(cm, doc.sel.primary().head, \"div\");\n        var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();\n        result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,\n                                            headPos.top + lineOff.top - wrapOff.top));\n        result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,\n                                             headPos.left + lineOff.left - wrapOff.left));\n      }\n\n      return result;\n    },\n\n    showSelection: function(drawn) {\n      var cm = this.cm, display = cm.display;\n      removeChildrenAndAdd(display.cursorDiv, drawn.cursors);\n      removeChildrenAndAdd(display.selectionDiv, drawn.selection);\n      if (drawn.teTop != null) {\n        this.wrapper.style.top = drawn.teTop + \"px\";\n        this.wrapper.style.left = drawn.teLeft + \"px\";\n      }\n    },\n\n    // Reset the input to correspond to the selection (or to be empty,\n    // when not typing and nothing is selected)\n    reset: function(typing) {\n      if (this.contextMenuPending) return;\n      var minimal, selected, cm = this.cm, doc = cm.doc;\n      if (cm.somethingSelected()) {\n        this.prevInput = \"\";\n        var range = doc.sel.primary();\n        minimal = hasCopyEvent &&\n          (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);\n        var content = minimal ? \"-\" : selected || cm.getSelection();\n        this.textarea.value = content;\n        if (cm.state.focused) selectInput(this.textarea);\n        if (ie && ie_version >= 9) this.hasSelection = content;\n      } else if (!typing) {\n        this.prevInput = this.textarea.value = \"\";\n        if (ie && ie_version >= 9) this.hasSelection = null;\n      }\n      this.inaccurateSelection = minimal;\n    },\n\n    getField: function() { return this.textarea; },\n\n    supportsTouch: function() { return false; },\n\n    focus: function() {\n      if (this.cm.options.readOnly != \"nocursor\" && (!mobile || activeElt() != this.textarea)) {\n        try { this.textarea.focus(); }\n        catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM\n      }\n    },\n\n    blur: function() { this.textarea.blur(); },\n\n    resetPosition: function() {\n      this.wrapper.style.top = this.wrapper.style.left = 0;\n    },\n\n    receivedFocus: function() { this.slowPoll(); },\n\n    // Poll for input changes, using the normal rate of polling. This\n    // runs as long as the editor is focused.\n    slowPoll: function() {\n      var input = this;\n      if (input.pollingFast) return;\n      input.polling.set(this.cm.options.pollInterval, function() {\n        input.poll();\n        if (input.cm.state.focused) input.slowPoll();\n      });\n    },\n\n    // When an event has just come in that is likely to add or change\n    // something in the input textarea, we poll faster, to ensure that\n    // the change appears on the screen quickly.\n    fastPoll: function() {\n      var missed = false, input = this;\n      input.pollingFast = true;\n      function p() {\n        var changed = input.poll();\n        if (!changed && !missed) {missed = true; input.polling.set(60, p);}\n        else {input.pollingFast = false; input.slowPoll();}\n      }\n      input.polling.set(20, p);\n    },\n\n    // Read input from the textarea, and update the document to match.\n    // When something is selected, it is present in the textarea, and\n    // selected (unless it is huge, in which case a placeholder is\n    // used). When nothing is selected, the cursor sits after previously\n    // seen text (can be empty), which is stored in prevInput (we must\n    // not reset the textarea when typing, because that breaks IME).\n    poll: function() {\n      var cm = this.cm, input = this.textarea, prevInput = this.prevInput;\n      // Since this is called a *lot*, try to bail out as cheaply as\n      // possible when it is clear that nothing happened. hasSelection\n      // will be the case when there is a lot of text in the textarea,\n      // in which case reading its value would be expensive.\n      if (this.contextMenuPending || !cm.state.focused ||\n          (hasSelection(input) && !prevInput) ||\n          isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)\n        return false;\n\n      var text = input.value;\n      // If nothing changed, bail.\n      if (text == prevInput && !cm.somethingSelected()) return false;\n      // Work around nonsensical selection resetting in IE9/10, and\n      // inexplicable appearance of private area unicode characters on\n      // some key combos in Mac (#2689).\n      if (ie && ie_version >= 9 && this.hasSelection === text ||\n          mac && /[\\uf700-\\uf7ff]/.test(text)) {\n        cm.display.input.reset();\n        return false;\n      }\n\n      if (cm.doc.sel == cm.display.selForContextMenu) {\n        var first = text.charCodeAt(0);\n        if (first == 0x200b && !prevInput) prevInput = \"\\u200b\";\n        if (first == 0x21da) { this.reset(); return this.cm.execCommand(\"undo\"); }\n      }\n      // Find the part of the input that is actually new\n      var same = 0, l = Math.min(prevInput.length, text.length);\n      while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;\n\n      var self = this;\n      runInOp(cm, function() {\n        applyTextInput(cm, text.slice(same), prevInput.length - same,\n                       null, self.composing ? \"*compose\" : null);\n\n        // Don't leave long text in the textarea, since it makes further polling slow\n        if (text.length > 1000 || text.indexOf(\"\\n\") > -1) input.value = self.prevInput = \"\";\n        else self.prevInput = text;\n\n        if (self.composing) {\n          self.composing.range.clear();\n          self.composing.range = cm.markText(self.composing.start, cm.getCursor(\"to\"),\n                                             {className: \"CodeMirror-composing\"});\n        }\n      });\n      return true;\n    },\n\n    ensurePolled: function() {\n      if (this.pollingFast && this.poll()) this.pollingFast = false;\n    },\n\n    onKeyPress: function() {\n      if (ie && ie_version >= 9) this.hasSelection = null;\n      this.fastPoll();\n    },\n\n    onContextMenu: function(e) {\n      var input = this, cm = input.cm, display = cm.display, te = input.textarea;\n      var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;\n      if (!pos || presto) return; // Opera is difficult.\n\n      // Reset the current text selection only if the click is done outside of the selection\n      // and 'resetSelectionOnContextMenu' option is true.\n      var reset = cm.options.resetSelectionOnContextMenu;\n      if (reset && cm.doc.sel.contains(pos) == -1)\n        operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);\n\n      var oldCSS = te.style.cssText;\n      input.wrapper.style.position = \"absolute\";\n      te.style.cssText = \"position: fixed; width: 30px; height: 30px; top: \" + (e.clientY - 5) +\n        \"px; left: \" + (e.clientX - 5) + \"px; z-index: 1000; background: \" +\n        (ie ? \"rgba(255, 255, 255, .05)\" : \"transparent\") +\n        \"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);\";\n      if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)\n      display.input.focus();\n      if (webkit) window.scrollTo(null, oldScrollY);\n      display.input.reset();\n      // Adds \"Select all\" to context menu in FF\n      if (!cm.somethingSelected()) te.value = input.prevInput = \" \";\n      input.contextMenuPending = true;\n      display.selForContextMenu = cm.doc.sel;\n      clearTimeout(display.detectingSelectAll);\n\n      // Select-all will be greyed out if there's nothing to select, so\n      // this adds a zero-width space so that we can later check whether\n      // it got selected.\n      function prepareSelectAllHack() {\n        if (te.selectionStart != null) {\n          var selected = cm.somethingSelected();\n          var extval = \"\\u200b\" + (selected ? te.value : \"\");\n          te.value = \"\\u21da\"; // Used to catch context-menu undo\n          te.value = extval;\n          input.prevInput = selected ? \"\" : \"\\u200b\";\n          te.selectionStart = 1; te.selectionEnd = extval.length;\n          // Re-set this, in case some other handler touched the\n          // selection in the meantime.\n          display.selForContextMenu = cm.doc.sel;\n        }\n      }\n      function rehide() {\n        input.contextMenuPending = false;\n        input.wrapper.style.position = \"relative\";\n        te.style.cssText = oldCSS;\n        if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);\n\n        // Try to detect the user choosing select-all\n        if (te.selectionStart != null) {\n          if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();\n          var i = 0, poll = function() {\n            if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&\n                te.selectionEnd > 0 && input.prevInput == \"\\u200b\")\n              operation(cm, commands.selectAll)(cm);\n            else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);\n            else display.input.reset();\n          };\n          display.detectingSelectAll = setTimeout(poll, 200);\n        }\n      }\n\n      if (ie && ie_version >= 9) prepareSelectAllHack();\n      if (captureRightClick) {\n        e_stop(e);\n        var mouseup = function() {\n          off(window, \"mouseup\", mouseup);\n          setTimeout(rehide, 20);\n        };\n        on(window, \"mouseup\", mouseup);\n      } else {\n        setTimeout(rehide, 50);\n      }\n    },\n\n    setUneditable: nothing,\n\n    needsContentAttribute: false\n  }, TextareaInput.prototype);\n\n  // CONTENTEDITABLE INPUT STYLE\n\n  function ContentEditableInput(cm) {\n    this.cm = cm;\n    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;\n    this.polling = new Delayed();\n    this.gracePeriod = false;\n  }\n\n  ContentEditableInput.prototype = copyObj({\n    init: function(display) {\n      var input = this, cm = input.cm;\n      var div = input.div = display.lineDiv;\n      div.contentEditable = \"true\";\n      disableBrowserMagic(div);\n\n      on(div, \"paste\", function(e) { handlePaste(e, cm); })\n\n      on(div, \"compositionstart\", function(e) {\n        var data = e.data;\n        input.composing = {sel: cm.doc.sel, data: data, startData: data};\n        if (!data) return;\n        var prim = cm.doc.sel.primary();\n        var line = cm.getLine(prim.head.line);\n        var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));\n        if (found > -1 && found <= prim.head.ch)\n          input.composing.sel = simpleSelection(Pos(prim.head.line, found),\n                                                Pos(prim.head.line, found + data.length));\n      });\n      on(div, \"compositionupdate\", function(e) {\n        input.composing.data = e.data;\n      });\n      on(div, \"compositionend\", function(e) {\n        var ours = input.composing;\n        if (!ours) return;\n        if (e.data != ours.startData && !/\\u200b/.test(e.data))\n          ours.data = e.data;\n        // Need a small delay to prevent other code (input event,\n        // selection polling) from doing damage when fired right after\n        // compositionend.\n        setTimeout(function() {\n          if (!ours.handled)\n            input.applyComposition(ours);\n          if (input.composing == ours)\n            input.composing = null;\n        }, 50);\n      });\n\n      on(div, \"touchstart\", function() {\n        input.forceCompositionEnd();\n      });\n\n      on(div, \"input\", function() {\n        if (input.composing) return;\n        if (!input.pollContent())\n          runInOp(input.cm, function() {regChange(cm);});\n      });\n\n      function onCopyCut(e) {\n        if (cm.somethingSelected()) {\n          lastCopied = cm.getSelections();\n          if (e.type == \"cut\") cm.replaceSelection(\"\", null, \"cut\");\n        } else if (!cm.options.lineWiseCopyCut) {\n          return;\n        } else {\n          var ranges = copyableRanges(cm);\n          lastCopied = ranges.text;\n          if (e.type == \"cut\") {\n            cm.operation(function() {\n              cm.setSelections(ranges.ranges, 0, sel_dontScroll);\n              cm.replaceSelection(\"\", null, \"cut\");\n            });\n          }\n        }\n        // iOS exposes the clipboard API, but seems to discard content inserted into it\n        if (e.clipboardData && !ios) {\n          e.preventDefault();\n          e.clipboardData.clearData();\n          e.clipboardData.setData(\"text/plain\", lastCopied.join(\"\\n\"));\n        } else {\n          // Old-fashioned briefly-focus-a-textarea hack\n          var kludge = hiddenTextarea(), te = kludge.firstChild;\n          cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);\n          te.value = lastCopied.join(\"\\n\");\n          var hadFocus = document.activeElement;\n          selectInput(te);\n          setTimeout(function() {\n            cm.display.lineSpace.removeChild(kludge);\n            hadFocus.focus();\n          }, 50);\n        }\n      }\n      on(div, \"copy\", onCopyCut);\n      on(div, \"cut\", onCopyCut);\n    },\n\n    prepareSelection: function() {\n      var result = prepareSelection(this.cm, false);\n      result.focus = this.cm.state.focused;\n      return result;\n    },\n\n    showSelection: function(info) {\n      if (!info || !this.cm.display.view.length) return;\n      if (info.focus) this.showPrimarySelection();\n      this.showMultipleSelections(info);\n    },\n\n    showPrimarySelection: function() {\n      var sel = window.getSelection(), prim = this.cm.doc.sel.primary();\n      var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);\n      var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);\n      if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&\n          cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&\n          cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)\n        return;\n\n      var start = posToDOM(this.cm, prim.from());\n      var end = posToDOM(this.cm, prim.to());\n      if (!start && !end) return;\n\n      var view = this.cm.display.view;\n      var old = sel.rangeCount && sel.getRangeAt(0);\n      if (!start) {\n        start = {node: view[0].measure.map[2], offset: 0};\n      } else if (!end) { // FIXME dangerously hacky\n        var measure = view[view.length - 1].measure;\n        var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;\n        end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};\n      }\n\n      try { var rng = range(start.node, start.offset, end.offset, end.node); }\n      catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible\n      if (rng) {\n        sel.removeAllRanges();\n        sel.addRange(rng);\n        if (old && sel.anchorNode == null) sel.addRange(old);\n        else if (gecko) this.startGracePeriod();\n      }\n      this.rememberSelection();\n    },\n\n    startGracePeriod: function() {\n      var input = this;\n      clearTimeout(this.gracePeriod);\n      this.gracePeriod = setTimeout(function() {\n        input.gracePeriod = false;\n        if (input.selectionChanged())\n          input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });\n      }, 20);\n    },\n\n    showMultipleSelections: function(info) {\n      removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);\n      removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);\n    },\n\n    rememberSelection: function() {\n      var sel = window.getSelection();\n      this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;\n      this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;\n    },\n\n    selectionInEditor: function() {\n      var sel = window.getSelection();\n      if (!sel.rangeCount) return false;\n      var node = sel.getRangeAt(0).commonAncestorContainer;\n      return contains(this.div, node);\n    },\n\n    focus: function() {\n      if (this.cm.options.readOnly != \"nocursor\") this.div.focus();\n    },\n    blur: function() { this.div.blur(); },\n    getField: function() { return this.div; },\n\n    supportsTouch: function() { return true; },\n\n    receivedFocus: function() {\n      var input = this;\n      if (this.selectionInEditor())\n        this.pollSelection();\n      else\n        runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });\n\n      function poll() {\n        if (input.cm.state.focused) {\n          input.pollSelection();\n          input.polling.set(input.cm.options.pollInterval, poll);\n        }\n      }\n      this.polling.set(this.cm.options.pollInterval, poll);\n    },\n\n    selectionChanged: function() {\n      var sel = window.getSelection();\n      return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||\n        sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;\n    },\n\n    pollSelection: function() {\n      if (!this.composing && !this.gracePeriod && this.selectionChanged()) {\n        var sel = window.getSelection(), cm = this.cm;\n        this.rememberSelection();\n        var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n        var head = domToPos(cm, sel.focusNode, sel.focusOffset);\n        if (anchor && head) runInOp(cm, function() {\n          setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);\n          if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;\n        });\n      }\n    },\n\n    pollContent: function() {\n      var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();\n      var from = sel.from(), to = sel.to();\n      if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;\n\n      var fromIndex;\n      if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {\n        var fromLine = lineNo(display.view[0].line);\n        var fromNode = display.view[0].node;\n      } else {\n        var fromLine = lineNo(display.view[fromIndex].line);\n        var fromNode = display.view[fromIndex - 1].node.nextSibling;\n      }\n      var toIndex = findViewIndex(cm, to.line);\n      if (toIndex == display.view.length - 1) {\n        var toLine = display.viewTo - 1;\n        var toNode = display.lineDiv.lastChild;\n      } else {\n        var toLine = lineNo(display.view[toIndex + 1].line) - 1;\n        var toNode = display.view[toIndex + 1].node.previousSibling;\n      }\n\n      var newText = splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));\n      var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));\n      while (newText.length > 1 && oldText.length > 1) {\n        if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }\n        else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }\n        else break;\n      }\n\n      var cutFront = 0, cutEnd = 0;\n      var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);\n      while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))\n        ++cutFront;\n      var newBot = lst(newText), oldBot = lst(oldText);\n      var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),\n                               oldBot.length - (oldText.length == 1 ? cutFront : 0));\n      while (cutEnd < maxCutEnd &&\n             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))\n        ++cutEnd;\n\n      newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);\n      newText[0] = newText[0].slice(cutFront);\n\n      var chFrom = Pos(fromLine, cutFront);\n      var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);\n      if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {\n        replaceRange(cm.doc, newText, chFrom, chTo, \"+input\");\n        return true;\n      }\n    },\n\n    ensurePolled: function() {\n      this.forceCompositionEnd();\n    },\n    reset: function() {\n      this.forceCompositionEnd();\n    },\n    forceCompositionEnd: function() {\n      if (!this.composing || this.composing.handled) return;\n      this.applyComposition(this.composing);\n      this.composing.handled = true;\n      this.div.blur();\n      this.div.focus();\n    },\n    applyComposition: function(composing) {\n      if (composing.data && composing.data != composing.startData)\n        operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);\n    },\n\n    setUneditable: function(node) {\n      node.setAttribute(\"contenteditable\", \"false\");\n    },\n\n    onKeyPress: function(e) {\n      e.preventDefault();\n      operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);\n    },\n\n    onContextMenu: nothing,\n    resetPosition: nothing,\n\n    needsContentAttribute: true\n  }, ContentEditableInput.prototype);\n\n  function posToDOM(cm, pos) {\n    var view = findViewForLine(cm, pos.line);\n    if (!view || view.hidden) return null;\n    var line = getLine(cm.doc, pos.line);\n    var info = mapFromLineView(view, line, pos.line);\n\n    var order = getOrder(line), side = \"left\";\n    if (order) {\n      var partPos = getBidiPartAt(order, pos.ch);\n      side = partPos % 2 ? \"right\" : \"left\";\n    }\n    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);\n    result.offset = result.collapse == \"right\" ? result.end : result.start;\n    return result;\n  }\n\n  function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }\n\n  function domToPos(cm, node, offset) {\n    var lineNode;\n    if (node == cm.display.lineDiv) {\n      lineNode = cm.display.lineDiv.childNodes[offset];\n      if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);\n      node = null; offset = 0;\n    } else {\n      for (lineNode = node;; lineNode = lineNode.parentNode) {\n        if (!lineNode || lineNode == cm.display.lineDiv) return null;\n        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;\n      }\n    }\n    for (var i = 0; i < cm.display.view.length; i++) {\n      var lineView = cm.display.view[i];\n      if (lineView.node == lineNode)\n        return locateNodeInLineView(lineView, node, offset);\n    }\n  }\n\n  function locateNodeInLineView(lineView, node, offset) {\n    var wrapper = lineView.text.firstChild, bad = false;\n    if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);\n    if (node == wrapper) {\n      bad = true;\n      node = wrapper.childNodes[offset];\n      offset = 0;\n      if (!node) {\n        var line = lineView.rest ? lst(lineView.rest) : lineView.line;\n        return badPos(Pos(lineNo(line), line.text.length), bad);\n      }\n    }\n\n    var textNode = node.nodeType == 3 ? node : null, topNode = node;\n    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {\n      textNode = node.firstChild;\n      if (offset) offset = textNode.nodeValue.length;\n    }\n    while (topNode.parentNode != wrapper) topNode = topNode.parentNode;\n    var measure = lineView.measure, maps = measure.maps;\n\n    function find(textNode, topNode, offset) {\n      for (var i = -1; i < (maps ? maps.length : 0); i++) {\n        var map = i < 0 ? measure.map : maps[i];\n        for (var j = 0; j < map.length; j += 3) {\n          var curNode = map[j + 2];\n          if (curNode == textNode || curNode == topNode) {\n            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);\n            var ch = map[j] + offset;\n            if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];\n            return Pos(line, ch);\n          }\n        }\n      }\n    }\n    var found = find(textNode, topNode, offset);\n    if (found) return badPos(found, bad);\n\n    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems\n    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {\n      found = find(after, after.firstChild, 0);\n      if (found)\n        return badPos(Pos(found.line, found.ch - dist), bad);\n      else\n        dist += after.textContent.length;\n    }\n    for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {\n      found = find(before, before.firstChild, -1);\n      if (found)\n        return badPos(Pos(found.line, found.ch + dist), bad);\n      else\n        dist += after.textContent.length;\n    }\n  }\n\n  function domTextBetween(cm, from, to, fromLine, toLine) {\n    var text = \"\", closing = false;\n    function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }\n    function walk(node) {\n      if (node.nodeType == 1) {\n        var cmText = node.getAttribute(\"cm-text\");\n        if (cmText != null) {\n          if (cmText == \"\") cmText = node.textContent.replace(/\\u200b/g, \"\");\n          text += cmText;\n          return;\n        }\n        var markerID = node.getAttribute(\"cm-marker\"), range;\n        if (markerID) {\n          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));\n          if (found.length && (range = found[0].find()))\n            text += getBetween(cm.doc, range.from, range.to).join(\"\\n\");\n          return;\n        }\n        if (node.getAttribute(\"contenteditable\") == \"false\") return;\n        for (var i = 0; i < node.childNodes.length; i++)\n          walk(node.childNodes[i]);\n        if (/^(pre|div|p)$/i.test(node.nodeName))\n          closing = true;\n      } else if (node.nodeType == 3) {\n        var val = node.nodeValue;\n        if (!val) return;\n        if (closing) {\n          text += \"\\n\";\n          closing = false;\n        }\n        text += val;\n      }\n    }\n    for (;;) {\n      walk(from);\n      if (from == to) break;\n      from = from.nextSibling;\n    }\n    return text;\n  }\n\n  CodeMirror.inputStyles = {\"textarea\": TextareaInput, \"contenteditable\": ContentEditableInput};\n\n  // SELECTION / CURSOR\n\n  // Selection objects are immutable. A new one is created every time\n  // the selection changes. A selection is one or more non-overlapping\n  // (and non-touching) ranges, sorted, and an integer that indicates\n  // which one is the primary selection (the one that's scrolled into\n  // view, that getCursor returns, etc).\n  function Selection(ranges, primIndex) {\n    this.ranges = ranges;\n    this.primIndex = primIndex;\n  }\n\n  Selection.prototype = {\n    primary: function() { return this.ranges[this.primIndex]; },\n    equals: function(other) {\n      if (other == this) return true;\n      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;\n      for (var i = 0; i < this.ranges.length; i++) {\n        var here = this.ranges[i], there = other.ranges[i];\n        if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;\n      }\n      return true;\n    },\n    deepCopy: function() {\n      for (var out = [], i = 0; i < this.ranges.length; i++)\n        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));\n      return new Selection(out, this.primIndex);\n    },\n    somethingSelected: function() {\n      for (var i = 0; i < this.ranges.length; i++)\n        if (!this.ranges[i].empty()) return true;\n      return false;\n    },\n    contains: function(pos, end) {\n      if (!end) end = pos;\n      for (var i = 0; i < this.ranges.length; i++) {\n        var range = this.ranges[i];\n        if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)\n          return i;\n      }\n      return -1;\n    }\n  };\n\n  function Range(anchor, head) {\n    this.anchor = anchor; this.head = head;\n  }\n\n  Range.prototype = {\n    from: function() { return minPos(this.anchor, this.head); },\n    to: function() { return maxPos(this.anchor, this.head); },\n    empty: function() {\n      return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;\n    }\n  };\n\n  // Take an unsorted, potentially overlapping set of ranges, and\n  // build a selection out of it. 'Consumes' ranges array (modifying\n  // it).\n  function normalizeSelection(ranges, primIndex) {\n    var prim = ranges[primIndex];\n    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });\n    primIndex = indexOf(ranges, prim);\n    for (var i = 1; i < ranges.length; i++) {\n      var cur = ranges[i], prev = ranges[i - 1];\n      if (cmp(prev.to(), cur.from()) >= 0) {\n        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());\n        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;\n        if (i <= primIndex) --primIndex;\n        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));\n      }\n    }\n    return new Selection(ranges, primIndex);\n  }\n\n  function simpleSelection(anchor, head) {\n    return new Selection([new Range(anchor, head || anchor)], 0);\n  }\n\n  // Most of the external API clips given positions to make sure they\n  // actually exist within the document.\n  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}\n  function clipPos(doc, pos) {\n    if (pos.line < doc.first) return Pos(doc.first, 0);\n    var last = doc.first + doc.size - 1;\n    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);\n    return clipToLen(pos, getLine(doc, pos.line).text.length);\n  }\n  function clipToLen(pos, linelen) {\n    var ch = pos.ch;\n    if (ch == null || ch > linelen) return Pos(pos.line, linelen);\n    else if (ch < 0) return Pos(pos.line, 0);\n    else return pos;\n  }\n  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}\n  function clipPosArray(doc, array) {\n    for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);\n    return out;\n  }\n\n  // SELECTION UPDATES\n\n  // The 'scroll' parameter given to many of these indicated whether\n  // the new cursor position should be scrolled into view after\n  // modifying the selection.\n\n  // If shift is held or the extend flag is set, extends a range to\n  // include a given position (and optionally a second position).\n  // Otherwise, simply returns the range between the given positions.\n  // Used for cursor motion and such.\n  function extendRange(doc, range, head, other) {\n    if (doc.cm && doc.cm.display.shift || doc.extend) {\n      var anchor = range.anchor;\n      if (other) {\n        var posBefore = cmp(head, anchor) < 0;\n        if (posBefore != (cmp(other, anchor) < 0)) {\n          anchor = head;\n          head = other;\n        } else if (posBefore != (cmp(head, other) < 0)) {\n          head = other;\n        }\n      }\n      return new Range(anchor, head);\n    } else {\n      return new Range(other || head, head);\n    }\n  }\n\n  // Extend the primary selection range, discard the rest.\n  function extendSelection(doc, head, other, options) {\n    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);\n  }\n\n  // Extend all selections (pos is an array of selections with length\n  // equal the number of selections)\n  function extendSelections(doc, heads, options) {\n    for (var out = [], i = 0; i < doc.sel.ranges.length; i++)\n      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);\n    var newSel = normalizeSelection(out, doc.sel.primIndex);\n    setSelection(doc, newSel, options);\n  }\n\n  // Updates a single range in the selection.\n  function replaceOneSelection(doc, i, range, options) {\n    var ranges = doc.sel.ranges.slice(0);\n    ranges[i] = range;\n    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);\n  }\n\n  // Reset the selection to a single range.\n  function setSimpleSelection(doc, anchor, head, options) {\n    setSelection(doc, simpleSelection(anchor, head), options);\n  }\n\n  // Give beforeSelectionChange handlers a change to influence a\n  // selection update.\n  function filterSelectionChange(doc, sel) {\n    var obj = {\n      ranges: sel.ranges,\n      update: function(ranges) {\n        this.ranges = [];\n        for (var i = 0; i < ranges.length; i++)\n          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),\n                                     clipPos(doc, ranges[i].head));\n      }\n    };\n    signal(doc, \"beforeSelectionChange\", doc, obj);\n    if (doc.cm) signal(doc.cm, \"beforeSelectionChange\", doc.cm, obj);\n    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);\n    else return sel;\n  }\n\n  function setSelectionReplaceHistory(doc, sel, options) {\n    var done = doc.history.done, last = lst(done);\n    if (last && last.ranges) {\n      done[done.length - 1] = sel;\n      setSelectionNoUndo(doc, sel, options);\n    } else {\n      setSelection(doc, sel, options);\n    }\n  }\n\n  // Set a new selection.\n  function setSelection(doc, sel, options) {\n    setSelectionNoUndo(doc, sel, options);\n    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);\n  }\n\n  function setSelectionNoUndo(doc, sel, options) {\n    if (hasHandler(doc, \"beforeSelectionChange\") || doc.cm && hasHandler(doc.cm, \"beforeSelectionChange\"))\n      sel = filterSelectionChange(doc, sel);\n\n    var bias = options && options.bias ||\n      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);\n    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));\n\n    if (!(options && options.scroll === false) && doc.cm)\n      ensureCursorVisible(doc.cm);\n  }\n\n  function setSelectionInner(doc, sel) {\n    if (sel.equals(doc.sel)) return;\n\n    doc.sel = sel;\n\n    if (doc.cm) {\n      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;\n      signalCursorActivity(doc.cm);\n    }\n    signalLater(doc, \"cursorActivity\", doc);\n  }\n\n  // Verify that the selection does not partially select any atomic\n  // marked ranges.\n  function reCheckSelection(doc) {\n    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);\n  }\n\n  // Return a selection that does not partially select any atomic\n  // ranges.\n  function skipAtomicInSelection(doc, sel, bias, mayClear) {\n    var out;\n    for (var i = 0; i < sel.ranges.length; i++) {\n      var range = sel.ranges[i];\n      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);\n      var newHead = skipAtomic(doc, range.head, bias, mayClear);\n      if (out || newAnchor != range.anchor || newHead != range.head) {\n        if (!out) out = sel.ranges.slice(0, i);\n        out[i] = new Range(newAnchor, newHead);\n      }\n    }\n    return out ? normalizeSelection(out, sel.primIndex) : sel;\n  }\n\n  // Ensure a given position is not inside an atomic range.\n  function skipAtomic(doc, pos, bias, mayClear) {\n    var flipped = false, curPos = pos;\n    var dir = bias || 1;\n    doc.cantEdit = false;\n    search: for (;;) {\n      var line = getLine(doc, curPos.line);\n      if (line.markedSpans) {\n        for (var i = 0; i < line.markedSpans.length; ++i) {\n          var sp = line.markedSpans[i], m = sp.marker;\n          if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&\n              (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {\n            if (mayClear) {\n              signal(m, \"beforeCursorEnter\");\n              if (m.explicitlyCleared) {\n                if (!line.markedSpans) break;\n                else {--i; continue;}\n              }\n            }\n            if (!m.atomic) continue;\n            var newPos = m.find(dir < 0 ? -1 : 1);\n            if (cmp(newPos, curPos) == 0) {\n              newPos.ch += dir;\n              if (newPos.ch < 0) {\n                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));\n                else newPos = null;\n              } else if (newPos.ch > line.text.length) {\n                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);\n                else newPos = null;\n              }\n              if (!newPos) {\n                if (flipped) {\n                  // Driven in a corner -- no valid cursor position found at all\n                  // -- try again *with* clearing, if we didn't already\n                  if (!mayClear) return skipAtomic(doc, pos, bias, true);\n                  // Otherwise, turn off editing until further notice, and return the start of the doc\n                  doc.cantEdit = true;\n                  return Pos(doc.first, 0);\n                }\n                flipped = true; newPos = pos; dir = -dir;\n              }\n            }\n            curPos = newPos;\n            continue search;\n          }\n        }\n      }\n      return curPos;\n    }\n  }\n\n  // SELECTION DRAWING\n\n  function updateSelection(cm) {\n    cm.display.input.showSelection(cm.display.input.prepareSelection());\n  }\n\n  function prepareSelection(cm, primary) {\n    var doc = cm.doc, result = {};\n    var curFragment = result.cursors = document.createDocumentFragment();\n    var selFragment = result.selection = document.createDocumentFragment();\n\n    for (var i = 0; i < doc.sel.ranges.length; i++) {\n      if (primary === false && i == doc.sel.primIndex) continue;\n      var range = doc.sel.ranges[i];\n      var collapsed = range.empty();\n      if (collapsed || cm.options.showCursorWhenSelecting)\n        drawSelectionCursor(cm, range, curFragment);\n      if (!collapsed)\n        drawSelectionRange(cm, range, selFragment);\n    }\n    return result;\n  }\n\n  // Draws a cursor for the given range\n  function drawSelectionCursor(cm, range, output) {\n    var pos = cursorCoords(cm, range.head, \"div\", null, null, !cm.options.singleCursorHeightPerLine);\n\n    var cursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor\"));\n    cursor.style.left = pos.left + \"px\";\n    cursor.style.top = pos.top + \"px\";\n    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + \"px\";\n\n    if (pos.other) {\n      // Secondary cursor, shown when on a 'jump' in bi-directional text\n      var otherCursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor CodeMirror-secondarycursor\"));\n      otherCursor.style.display = \"\";\n      otherCursor.style.left = pos.other.left + \"px\";\n      otherCursor.style.top = pos.other.top + \"px\";\n      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + \"px\";\n    }\n  }\n\n  // Draws the given range as a highlighted selection\n  function drawSelectionRange(cm, range, output) {\n    var display = cm.display, doc = cm.doc;\n    var fragment = document.createDocumentFragment();\n    var padding = paddingH(cm.display), leftSide = padding.left;\n    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;\n\n    function add(left, top, width, bottom) {\n      if (top < 0) top = 0;\n      top = Math.round(top);\n      bottom = Math.round(bottom);\n      fragment.appendChild(elt(\"div\", null, \"CodeMirror-selected\", \"position: absolute; left: \" + left +\n                               \"px; top: \" + top + \"px; width: \" + (width == null ? rightSide - left : width) +\n                               \"px; height: \" + (bottom - top) + \"px\"));\n    }\n\n    function drawForLine(line, fromArg, toArg) {\n      var lineObj = getLine(doc, line);\n      var lineLen = lineObj.text.length;\n      var start, end;\n      function coords(ch, bias) {\n        return charCoords(cm, Pos(line, ch), \"div\", lineObj, bias);\n      }\n\n      iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {\n        var leftPos = coords(from, \"left\"), rightPos, left, right;\n        if (from == to) {\n          rightPos = leftPos;\n          left = right = leftPos.left;\n        } else {\n          rightPos = coords(to - 1, \"right\");\n          if (dir == \"rtl\") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }\n          left = leftPos.left;\n          right = rightPos.right;\n        }\n        if (fromArg == null && from == 0) left = leftSide;\n        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part\n          add(left, leftPos.top, null, leftPos.bottom);\n          left = leftSide;\n          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);\n        }\n        if (toArg == null && to == lineLen) right = rightSide;\n        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)\n          start = leftPos;\n        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)\n          end = rightPos;\n        if (left < leftSide + 1) left = leftSide;\n        add(left, rightPos.top, right - left, rightPos.bottom);\n      });\n      return {start: start, end: end};\n    }\n\n    var sFrom = range.from(), sTo = range.to();\n    if (sFrom.line == sTo.line) {\n      drawForLine(sFrom.line, sFrom.ch, sTo.ch);\n    } else {\n      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);\n      var singleVLine = visualLine(fromLine) == visualLine(toLine);\n      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;\n      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;\n      if (singleVLine) {\n        if (leftEnd.top < rightStart.top - 2) {\n          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);\n          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);\n        } else {\n          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);\n        }\n      }\n      if (leftEnd.bottom < rightStart.top)\n        add(leftSide, leftEnd.bottom, null, rightStart.top);\n    }\n\n    output.appendChild(fragment);\n  }\n\n  // Cursor-blinking\n  function restartBlink(cm) {\n    if (!cm.state.focused) return;\n    var display = cm.display;\n    clearInterval(display.blinker);\n    var on = true;\n    display.cursorDiv.style.visibility = \"\";\n    if (cm.options.cursorBlinkRate > 0)\n      display.blinker = setInterval(function() {\n        display.cursorDiv.style.visibility = (on = !on) ? \"\" : \"hidden\";\n      }, cm.options.cursorBlinkRate);\n    else if (cm.options.cursorBlinkRate < 0)\n      display.cursorDiv.style.visibility = \"hidden\";\n  }\n\n  // HIGHLIGHT WORKER\n\n  function startWorker(cm, time) {\n    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)\n      cm.state.highlight.set(time, bind(highlightWorker, cm));\n  }\n\n  function highlightWorker(cm) {\n    var doc = cm.doc;\n    if (doc.frontier < doc.first) doc.frontier = doc.first;\n    if (doc.frontier >= cm.display.viewTo) return;\n    var end = +new Date + cm.options.workTime;\n    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));\n    var changedLines = [];\n\n    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {\n      if (doc.frontier >= cm.display.viewFrom) { // Visible\n        var oldStyles = line.styles;\n        var highlighted = highlightLine(cm, line, state, true);\n        line.styles = highlighted.styles;\n        var oldCls = line.styleClasses, newCls = highlighted.classes;\n        if (newCls) line.styleClasses = newCls;\n        else if (oldCls) line.styleClasses = null;\n        var ischange = !oldStyles || oldStyles.length != line.styles.length ||\n          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);\n        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];\n        if (ischange) changedLines.push(doc.frontier);\n        line.stateAfter = copyState(doc.mode, state);\n      } else {\n        processLine(cm, line.text, state);\n        line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;\n      }\n      ++doc.frontier;\n      if (+new Date > end) {\n        startWorker(cm, cm.options.workDelay);\n        return true;\n      }\n    });\n    if (changedLines.length) runInOp(cm, function() {\n      for (var i = 0; i < changedLines.length; i++)\n        regLineChange(cm, changedLines[i], \"text\");\n    });\n  }\n\n  // Finds the line to start with when starting a parse. Tries to\n  // find a line with a stateAfter, so that it can start with a\n  // valid state. If that fails, it returns the line with the\n  // smallest indentation, which tends to need the least context to\n  // parse correctly.\n  function findStartLine(cm, n, precise) {\n    var minindent, minline, doc = cm.doc;\n    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);\n    for (var search = n; search > lim; --search) {\n      if (search <= doc.first) return doc.first;\n      var line = getLine(doc, search - 1);\n      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;\n      var indented = countColumn(line.text, null, cm.options.tabSize);\n      if (minline == null || minindent > indented) {\n        minline = search - 1;\n        minindent = indented;\n      }\n    }\n    return minline;\n  }\n\n  function getStateBefore(cm, n, precise) {\n    var doc = cm.doc, display = cm.display;\n    if (!doc.mode.startState) return true;\n    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;\n    if (!state) state = startState(doc.mode);\n    else state = copyState(doc.mode, state);\n    doc.iter(pos, n, function(line) {\n      processLine(cm, line.text, state);\n      var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;\n      line.stateAfter = save ? copyState(doc.mode, state) : null;\n      ++pos;\n    });\n    if (precise) doc.frontier = pos;\n    return state;\n  }\n\n  // POSITION MEASUREMENT\n\n  function paddingTop(display) {return display.lineSpace.offsetTop;}\n  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}\n  function paddingH(display) {\n    if (display.cachedPaddingH) return display.cachedPaddingH;\n    var e = removeChildrenAndAdd(display.measure, elt(\"pre\", \"x\"));\n    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;\n    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};\n    if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;\n    return data;\n  }\n\n  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }\n  function displayWidth(cm) {\n    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;\n  }\n  function displayHeight(cm) {\n    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;\n  }\n\n  // Ensure the lineView.wrapping.heights array is populated. This is\n  // an array of bottom offsets for the lines that make up a drawn\n  // line. When lineWrapping is on, there might be more than one\n  // height.\n  function ensureLineHeights(cm, lineView, rect) {\n    var wrapping = cm.options.lineWrapping;\n    var curWidth = wrapping && displayWidth(cm);\n    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {\n      var heights = lineView.measure.heights = [];\n      if (wrapping) {\n        lineView.measure.width = curWidth;\n        var rects = lineView.text.firstChild.getClientRects();\n        for (var i = 0; i < rects.length - 1; i++) {\n          var cur = rects[i], next = rects[i + 1];\n          if (Math.abs(cur.bottom - next.bottom) > 2)\n            heights.push((cur.bottom + next.top) / 2 - rect.top);\n        }\n      }\n      heights.push(rect.bottom - rect.top);\n    }\n  }\n\n  // Find a line map (mapping character offsets to text nodes) and a\n  // measurement cache for the given line number. (A line view might\n  // contain multiple lines when collapsed ranges are present.)\n  function mapFromLineView(lineView, line, lineN) {\n    if (lineView.line == line)\n      return {map: lineView.measure.map, cache: lineView.measure.cache};\n    for (var i = 0; i < lineView.rest.length; i++)\n      if (lineView.rest[i] == line)\n        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};\n    for (var i = 0; i < lineView.rest.length; i++)\n      if (lineNo(lineView.rest[i]) > lineN)\n        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};\n  }\n\n  // Render a line into the hidden node display.externalMeasured. Used\n  // when measurement is needed for a line that's not in the viewport.\n  function updateExternalMeasurement(cm, line) {\n    line = visualLine(line);\n    var lineN = lineNo(line);\n    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);\n    view.lineN = lineN;\n    var built = view.built = buildLineContent(cm, view);\n    view.text = built.pre;\n    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);\n    return view;\n  }\n\n  // Get a {top, bottom, left, right} box (in line-local coordinates)\n  // for a given character.\n  function measureChar(cm, line, ch, bias) {\n    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);\n  }\n\n  // Find a line view that corresponds to the given line number.\n  function findViewForLine(cm, lineN) {\n    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)\n      return cm.display.view[findViewIndex(cm, lineN)];\n    var ext = cm.display.externalMeasured;\n    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)\n      return ext;\n  }\n\n  // Measurement can be split in two steps, the set-up work that\n  // applies to the whole line, and the measurement of the actual\n  // character. Functions like coordsChar, that need to do a lot of\n  // measurements in a row, can thus ensure that the set-up work is\n  // only done once.\n  function prepareMeasureForLine(cm, line) {\n    var lineN = lineNo(line);\n    var view = findViewForLine(cm, lineN);\n    if (view && !view.text)\n      view = null;\n    else if (view && view.changes)\n      updateLineForChanges(cm, view, lineN, getDimensions(cm));\n    if (!view)\n      view = updateExternalMeasurement(cm, line);\n\n    var info = mapFromLineView(view, line, lineN);\n    return {\n      line: line, view: view, rect: null,\n      map: info.map, cache: info.cache, before: info.before,\n      hasHeights: false\n    };\n  }\n\n  // Given a prepared measurement object, measures the position of an\n  // actual character (or fetches it from the cache).\n  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {\n    if (prepared.before) ch = -1;\n    var key = ch + (bias || \"\"), found;\n    if (prepared.cache.hasOwnProperty(key)) {\n      found = prepared.cache[key];\n    } else {\n      if (!prepared.rect)\n        prepared.rect = prepared.view.text.getBoundingClientRect();\n      if (!prepared.hasHeights) {\n        ensureLineHeights(cm, prepared.view, prepared.rect);\n        prepared.hasHeights = true;\n      }\n      found = measureCharInner(cm, prepared, ch, bias);\n      if (!found.bogus) prepared.cache[key] = found;\n    }\n    return {left: found.left, right: found.right,\n            top: varHeight ? found.rtop : found.top,\n            bottom: varHeight ? found.rbottom : found.bottom};\n  }\n\n  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};\n\n  function nodeAndOffsetInLineMap(map, ch, bias) {\n    var node, start, end, collapse;\n    // First, search the line map for the text node corresponding to,\n    // or closest to, the target character.\n    for (var i = 0; i < map.length; i += 3) {\n      var mStart = map[i], mEnd = map[i + 1];\n      if (ch < mStart) {\n        start = 0; end = 1;\n        collapse = \"left\";\n      } else if (ch < mEnd) {\n        start = ch - mStart;\n        end = start + 1;\n      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {\n        end = mEnd - mStart;\n        start = end - 1;\n        if (ch >= mEnd) collapse = \"right\";\n      }\n      if (start != null) {\n        node = map[i + 2];\n        if (mStart == mEnd && bias == (node.insertLeft ? \"left\" : \"right\"))\n          collapse = bias;\n        if (bias == \"left\" && start == 0)\n          while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {\n            node = map[(i -= 3) + 2];\n            collapse = \"left\";\n          }\n        if (bias == \"right\" && start == mEnd - mStart)\n          while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {\n            node = map[(i += 3) + 2];\n            collapse = \"right\";\n          }\n        break;\n      }\n    }\n    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};\n  }\n\n  function measureCharInner(cm, prepared, ch, bias) {\n    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);\n    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;\n\n    var rect;\n    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.\n      for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned\n        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;\n        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;\n        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {\n          rect = node.parentNode.getBoundingClientRect();\n        } else if (ie && cm.options.lineWrapping) {\n          var rects = range(node, start, end).getClientRects();\n          if (rects.length)\n            rect = rects[bias == \"right\" ? rects.length - 1 : 0];\n          else\n            rect = nullRect;\n        } else {\n          rect = range(node, start, end).getBoundingClientRect() || nullRect;\n        }\n        if (rect.left || rect.right || start == 0) break;\n        end = start;\n        start = start - 1;\n        collapse = \"right\";\n      }\n      if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);\n    } else { // If it is a widget, simply get the box for the whole widget.\n      if (start > 0) collapse = bias = \"right\";\n      var rects;\n      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)\n        rect = rects[bias == \"right\" ? rects.length - 1 : 0];\n      else\n        rect = node.getBoundingClientRect();\n    }\n    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {\n      var rSpan = node.parentNode.getClientRects()[0];\n      if (rSpan)\n        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};\n      else\n        rect = nullRect;\n    }\n\n    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;\n    var mid = (rtop + rbot) / 2;\n    var heights = prepared.view.measure.heights;\n    for (var i = 0; i < heights.length - 1; i++)\n      if (mid < heights[i]) break;\n    var top = i ? heights[i - 1] : 0, bot = heights[i];\n    var result = {left: (collapse == \"right\" ? rect.right : rect.left) - prepared.rect.left,\n                  right: (collapse == \"left\" ? rect.left : rect.right) - prepared.rect.left,\n                  top: top, bottom: bot};\n    if (!rect.left && !rect.right) result.bogus = true;\n    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }\n\n    return result;\n  }\n\n  // Work around problem with bounding client rects on ranges being\n  // returned incorrectly when zoomed on IE10 and below.\n  function maybeUpdateRectForZooming(measure, rect) {\n    if (!window.screen || screen.logicalXDPI == null ||\n        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))\n      return rect;\n    var scaleX = screen.logicalXDPI / screen.deviceXDPI;\n    var scaleY = screen.logicalYDPI / screen.deviceYDPI;\n    return {left: rect.left * scaleX, right: rect.right * scaleX,\n            top: rect.top * scaleY, bottom: rect.bottom * scaleY};\n  }\n\n  function clearLineMeasurementCacheFor(lineView) {\n    if (lineView.measure) {\n      lineView.measure.cache = {};\n      lineView.measure.heights = null;\n      if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)\n        lineView.measure.caches[i] = {};\n    }\n  }\n\n  function clearLineMeasurementCache(cm) {\n    cm.display.externalMeasure = null;\n    removeChildren(cm.display.lineMeasure);\n    for (var i = 0; i < cm.display.view.length; i++)\n      clearLineMeasurementCacheFor(cm.display.view[i]);\n  }\n\n  function clearCaches(cm) {\n    clearLineMeasurementCache(cm);\n    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;\n    if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;\n    cm.display.lineNumChars = null;\n  }\n\n  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }\n  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }\n\n  // Converts a {top, bottom, left, right} box from line-local\n  // coordinates into another coordinate system. Context may be one of\n  // \"line\", \"div\" (display.lineDiv), \"local\"/null (editor), \"window\",\n  // or \"page\".\n  function intoCoordSystem(cm, lineObj, rect, context) {\n    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {\n      var size = widgetHeight(lineObj.widgets[i]);\n      rect.top += size; rect.bottom += size;\n    }\n    if (context == \"line\") return rect;\n    if (!context) context = \"local\";\n    var yOff = heightAtLine(lineObj);\n    if (context == \"local\") yOff += paddingTop(cm.display);\n    else yOff -= cm.display.viewOffset;\n    if (context == \"page\" || context == \"window\") {\n      var lOff = cm.display.lineSpace.getBoundingClientRect();\n      yOff += lOff.top + (context == \"window\" ? 0 : pageScrollY());\n      var xOff = lOff.left + (context == \"window\" ? 0 : pageScrollX());\n      rect.left += xOff; rect.right += xOff;\n    }\n    rect.top += yOff; rect.bottom += yOff;\n    return rect;\n  }\n\n  // Coverts a box from \"div\" coords to another coordinate system.\n  // Context may be \"window\", \"page\", \"div\", or \"local\"/null.\n  function fromCoordSystem(cm, coords, context) {\n    if (context == \"div\") return coords;\n    var left = coords.left, top = coords.top;\n    // First move into \"page\" coordinate system\n    if (context == \"page\") {\n      left -= pageScrollX();\n      top -= pageScrollY();\n    } else if (context == \"local\" || !context) {\n      var localBox = cm.display.sizer.getBoundingClientRect();\n      left += localBox.left;\n      top += localBox.top;\n    }\n\n    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();\n    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};\n  }\n\n  function charCoords(cm, pos, context, lineObj, bias) {\n    if (!lineObj) lineObj = getLine(cm.doc, pos.line);\n    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);\n  }\n\n  // Returns a box for a given cursor position, which may have an\n  // 'other' property containing the position of the secondary cursor\n  // on a bidi boundary.\n  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {\n    lineObj = lineObj || getLine(cm.doc, pos.line);\n    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);\n    function get(ch, right) {\n      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? \"right\" : \"left\", varHeight);\n      if (right) m.left = m.right; else m.right = m.left;\n      return intoCoordSystem(cm, lineObj, m, context);\n    }\n    function getBidi(ch, partPos) {\n      var part = order[partPos], right = part.level % 2;\n      if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {\n        part = order[--partPos];\n        ch = bidiRight(part) - (part.level % 2 ? 0 : 1);\n        right = true;\n      } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {\n        part = order[++partPos];\n        ch = bidiLeft(part) - part.level % 2;\n        right = false;\n      }\n      if (right && ch == part.to && ch > part.from) return get(ch - 1);\n      return get(ch, right);\n    }\n    var order = getOrder(lineObj), ch = pos.ch;\n    if (!order) return get(ch);\n    var partPos = getBidiPartAt(order, ch);\n    var val = getBidi(ch, partPos);\n    if (bidiOther != null) val.other = getBidi(ch, bidiOther);\n    return val;\n  }\n\n  // Used to cheaply estimate the coordinates for a position. Used for\n  // intermediate scroll updates.\n  function estimateCoords(cm, pos) {\n    var left = 0, pos = clipPos(cm.doc, pos);\n    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;\n    var lineObj = getLine(cm.doc, pos.line);\n    var top = heightAtLine(lineObj) + paddingTop(cm.display);\n    return {left: left, right: left, top: top, bottom: top + lineObj.height};\n  }\n\n  // Positions returned by coordsChar contain some extra information.\n  // xRel is the relative x position of the input coordinates compared\n  // to the found position (so xRel > 0 means the coordinates are to\n  // the right of the character position, for example). When outside\n  // is true, that means the coordinates lie outside the line's\n  // vertical range.\n  function PosWithInfo(line, ch, outside, xRel) {\n    var pos = Pos(line, ch);\n    pos.xRel = xRel;\n    if (outside) pos.outside = true;\n    return pos;\n  }\n\n  // Compute the character position closest to the given coordinates.\n  // Input must be lineSpace-local (\"div\" coordinate system).\n  function coordsChar(cm, x, y) {\n    var doc = cm.doc;\n    y += cm.display.viewOffset;\n    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);\n    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;\n    if (lineN > last)\n      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);\n    if (x < 0) x = 0;\n\n    var lineObj = getLine(doc, lineN);\n    for (;;) {\n      var found = coordsCharInner(cm, lineObj, lineN, x, y);\n      var merged = collapsedSpanAtEnd(lineObj);\n      var mergedPos = merged && merged.find(0, true);\n      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))\n        lineN = lineNo(lineObj = mergedPos.to.line);\n      else\n        return found;\n    }\n  }\n\n  function coordsCharInner(cm, lineObj, lineNo, x, y) {\n    var innerOff = y - heightAtLine(lineObj);\n    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;\n    var preparedMeasure = prepareMeasureForLine(cm, lineObj);\n\n    function getX(ch) {\n      var sp = cursorCoords(cm, Pos(lineNo, ch), \"line\", lineObj, preparedMeasure);\n      wrongLine = true;\n      if (innerOff > sp.bottom) return sp.left - adjust;\n      else if (innerOff < sp.top) return sp.left + adjust;\n      else wrongLine = false;\n      return sp.left;\n    }\n\n    var bidi = getOrder(lineObj), dist = lineObj.text.length;\n    var from = lineLeft(lineObj), to = lineRight(lineObj);\n    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;\n\n    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);\n    // Do a binary search between these bounds.\n    for (;;) {\n      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {\n        var ch = x < fromX || x - fromX <= toX - x ? from : to;\n        var xDiff = x - (ch == from ? fromX : toX);\n        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;\n        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,\n                              xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);\n        return pos;\n      }\n      var step = Math.ceil(dist / 2), middle = from + step;\n      if (bidi) {\n        middle = from;\n        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);\n      }\n      var middleX = getX(middle);\n      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}\n      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}\n    }\n  }\n\n  var measureText;\n  // Compute the default text height.\n  function textHeight(display) {\n    if (display.cachedTextHeight != null) return display.cachedTextHeight;\n    if (measureText == null) {\n      measureText = elt(\"pre\");\n      // Measure a bunch of lines, for browsers that compute\n      // fractional heights.\n      for (var i = 0; i < 49; ++i) {\n        measureText.appendChild(document.createTextNode(\"x\"));\n        measureText.appendChild(elt(\"br\"));\n      }\n      measureText.appendChild(document.createTextNode(\"x\"));\n    }\n    removeChildrenAndAdd(display.measure, measureText);\n    var height = measureText.offsetHeight / 50;\n    if (height > 3) display.cachedTextHeight = height;\n    removeChildren(display.measure);\n    return height || 1;\n  }\n\n  // Compute the default character width.\n  function charWidth(display) {\n    if (display.cachedCharWidth != null) return display.cachedCharWidth;\n    var anchor = elt(\"span\", \"xxxxxxxxxx\");\n    var pre = elt(\"pre\", [anchor]);\n    removeChildrenAndAdd(display.measure, pre);\n    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;\n    if (width > 2) display.cachedCharWidth = width;\n    return width || 10;\n  }\n\n  // OPERATIONS\n\n  // Operations are used to wrap a series of changes to the editor\n  // state in such a way that each change won't have to update the\n  // cursor and display (which would be awkward, slow, and\n  // error-prone). Instead, display updates are batched and then all\n  // combined and executed at once.\n\n  var operationGroup = null;\n\n  var nextOpId = 0;\n  // Start a new operation.\n  function startOperation(cm) {\n    cm.curOp = {\n      cm: cm,\n      viewChanged: false,      // Flag that indicates that lines might need to be redrawn\n      startHeight: cm.doc.height, // Used to detect need to update scrollbar\n      forceUpdate: false,      // Used to force a redraw\n      updateInput: null,       // Whether to reset the input textarea\n      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)\n      changeObjs: null,        // Accumulated changes, for firing change events\n      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on\n      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already\n      selectionChanged: false, // Whether the selection needs to be redrawn\n      updateMaxLine: false,    // Set when the widest line needs to be determined anew\n      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet\n      scrollToPos: null,       // Used to scroll to a specific position\n      focus: false,\n      id: ++nextOpId           // Unique ID\n    };\n    if (operationGroup) {\n      operationGroup.ops.push(cm.curOp);\n    } else {\n      cm.curOp.ownsGroup = operationGroup = {\n        ops: [cm.curOp],\n        delayedCallbacks: []\n      };\n    }\n  }\n\n  function fireCallbacksForOps(group) {\n    // Calls delayed callbacks and cursorActivity handlers until no\n    // new ones appear\n    var callbacks = group.delayedCallbacks, i = 0;\n    do {\n      for (; i < callbacks.length; i++)\n        callbacks[i]();\n      for (var j = 0; j < group.ops.length; j++) {\n        var op = group.ops[j];\n        if (op.cursorActivityHandlers)\n          while (op.cursorActivityCalled < op.cursorActivityHandlers.length)\n            op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);\n      }\n    } while (i < callbacks.length);\n  }\n\n  // Finish an operation, updating the display and signalling delayed events\n  function endOperation(cm) {\n    var op = cm.curOp, group = op.ownsGroup;\n    if (!group) return;\n\n    try { fireCallbacksForOps(group); }\n    finally {\n      operationGroup = null;\n      for (var i = 0; i < group.ops.length; i++)\n        group.ops[i].cm.curOp = null;\n      endOperations(group);\n    }\n  }\n\n  // The DOM updates done when an operation finishes are batched so\n  // that the minimum number of relayouts are required.\n  function endOperations(group) {\n    var ops = group.ops;\n    for (var i = 0; i < ops.length; i++) // Read DOM\n      endOperation_R1(ops[i]);\n    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)\n      endOperation_W1(ops[i]);\n    for (var i = 0; i < ops.length; i++) // Read DOM\n      endOperation_R2(ops[i]);\n    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)\n      endOperation_W2(ops[i]);\n    for (var i = 0; i < ops.length; i++) // Read DOM\n      endOperation_finish(ops[i]);\n  }\n\n  function endOperation_R1(op) {\n    var cm = op.cm, display = cm.display;\n    maybeClipScrollbars(cm);\n    if (op.updateMaxLine) findMaxLine(cm);\n\n    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||\n      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||\n                         op.scrollToPos.to.line >= display.viewTo) ||\n      display.maxLineChanged && cm.options.lineWrapping;\n    op.update = op.mustUpdate &&\n      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);\n  }\n\n  function endOperation_W1(op) {\n    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);\n  }\n\n  function endOperation_R2(op) {\n    var cm = op.cm, display = cm.display;\n    if (op.updatedDisplay) updateHeightsInViewport(cm);\n\n    op.barMeasure = measureForScrollbars(cm);\n\n    // If the max line changed since it was last measured, measure it,\n    // and ensure the document's width matches it.\n    // updateDisplay_W2 will use these properties to do the actual resizing\n    if (display.maxLineChanged && !cm.options.lineWrapping) {\n      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;\n      cm.display.sizerWidth = op.adjustWidthTo;\n      op.barMeasure.scrollWidth =\n        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);\n      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));\n    }\n\n    if (op.updatedDisplay || op.selectionChanged)\n      op.preparedSelection = display.input.prepareSelection();\n  }\n\n  function endOperation_W2(op) {\n    var cm = op.cm;\n\n    if (op.adjustWidthTo != null) {\n      cm.display.sizer.style.minWidth = op.adjustWidthTo + \"px\";\n      if (op.maxScrollLeft < cm.doc.scrollLeft)\n        setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);\n      cm.display.maxLineChanged = false;\n    }\n\n    if (op.preparedSelection)\n      cm.display.input.showSelection(op.preparedSelection);\n    if (op.updatedDisplay)\n      setDocumentHeight(cm, op.barMeasure);\n    if (op.updatedDisplay || op.startHeight != cm.doc.height)\n      updateScrollbars(cm, op.barMeasure);\n\n    if (op.selectionChanged) restartBlink(cm);\n\n    if (cm.state.focused && op.updateInput)\n      cm.display.input.reset(op.typing);\n    if (op.focus && op.focus == activeElt()) ensureFocus(op.cm);\n  }\n\n  function endOperation_finish(op) {\n    var cm = op.cm, display = cm.display, doc = cm.doc;\n\n    if (op.updatedDisplay) postUpdateDisplay(cm, op.update);\n\n    // Abort mouse wheel delta measurement, when scrolling explicitly\n    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))\n      display.wheelStartX = display.wheelStartY = null;\n\n    // Propagate the scroll position to the actual DOM scroller\n    if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {\n      doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));\n      display.scrollbars.setScrollTop(doc.scrollTop);\n      display.scroller.scrollTop = doc.scrollTop;\n    }\n    if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {\n      doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));\n      display.scrollbars.setScrollLeft(doc.scrollLeft);\n      display.scroller.scrollLeft = doc.scrollLeft;\n      alignHorizontally(cm);\n    }\n    // If we need to scroll a specific position into view, do so.\n    if (op.scrollToPos) {\n      var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),\n                                     clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);\n      if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);\n    }\n\n    // Fire events for markers that are hidden/unidden by editing or\n    // undoing\n    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;\n    if (hidden) for (var i = 0; i < hidden.length; ++i)\n      if (!hidden[i].lines.length) signal(hidden[i], \"hide\");\n    if (unhidden) for (var i = 0; i < unhidden.length; ++i)\n      if (unhidden[i].lines.length) signal(unhidden[i], \"unhide\");\n\n    if (display.wrapper.offsetHeight)\n      doc.scrollTop = cm.display.scroller.scrollTop;\n\n    // Fire change events, and delayed event handlers\n    if (op.changeObjs)\n      signal(cm, \"changes\", cm, op.changeObjs);\n    if (op.update)\n      op.update.finish();\n  }\n\n  // Run the given function in an operation\n  function runInOp(cm, f) {\n    if (cm.curOp) return f();\n    startOperation(cm);\n    try { return f(); }\n    finally { endOperation(cm); }\n  }\n  // Wraps a function in an operation. Returns the wrapped function.\n  function operation(cm, f) {\n    return function() {\n      if (cm.curOp) return f.apply(cm, arguments);\n      startOperation(cm);\n      try { return f.apply(cm, arguments); }\n      finally { endOperation(cm); }\n    };\n  }\n  // Used to add methods to editor and doc instances, wrapping them in\n  // operations.\n  function methodOp(f) {\n    return function() {\n      if (this.curOp) return f.apply(this, arguments);\n      startOperation(this);\n      try { return f.apply(this, arguments); }\n      finally { endOperation(this); }\n    };\n  }\n  function docMethodOp(f) {\n    return function() {\n      var cm = this.cm;\n      if (!cm || cm.curOp) return f.apply(this, arguments);\n      startOperation(cm);\n      try { return f.apply(this, arguments); }\n      finally { endOperation(cm); }\n    };\n  }\n\n  // VIEW TRACKING\n\n  // These objects are used to represent the visible (currently drawn)\n  // part of the document. A LineView may correspond to multiple\n  // logical lines, if those are connected by collapsed ranges.\n  function LineView(doc, line, lineN) {\n    // The starting line\n    this.line = line;\n    // Continuing lines, if any\n    this.rest = visualLineContinued(line);\n    // Number of logical lines in this visual line\n    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;\n    this.node = this.text = null;\n    this.hidden = lineIsHidden(doc, line);\n  }\n\n  // Create a range of LineView objects for the given lines.\n  function buildViewArray(cm, from, to) {\n    var array = [], nextPos;\n    for (var pos = from; pos < to; pos = nextPos) {\n      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);\n      nextPos = pos + view.size;\n      array.push(view);\n    }\n    return array;\n  }\n\n  // Updates the display.view data structure for a given change to the\n  // document. From and to are in pre-change coordinates. Lendiff is\n  // the amount of lines added or subtracted by the change. This is\n  // used for changes that span multiple lines, or change the way\n  // lines are divided into visual lines. regLineChange (below)\n  // registers single-line changes.\n  function regChange(cm, from, to, lendiff) {\n    if (from == null) from = cm.doc.first;\n    if (to == null) to = cm.doc.first + cm.doc.size;\n    if (!lendiff) lendiff = 0;\n\n    var display = cm.display;\n    if (lendiff && to < display.viewTo &&\n        (display.updateLineNumbers == null || display.updateLineNumbers > from))\n      display.updateLineNumbers = from;\n\n    cm.curOp.viewChanged = true;\n\n    if (from >= display.viewTo) { // Change after\n      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)\n        resetView(cm);\n    } else if (to <= display.viewFrom) { // Change before\n      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {\n        resetView(cm);\n      } else {\n        display.viewFrom += lendiff;\n        display.viewTo += lendiff;\n      }\n    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap\n      resetView(cm);\n    } else if (from <= display.viewFrom) { // Top overlap\n      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);\n      if (cut) {\n        display.view = display.view.slice(cut.index);\n        display.viewFrom = cut.lineN;\n        display.viewTo += lendiff;\n      } else {\n        resetView(cm);\n      }\n    } else if (to >= display.viewTo) { // Bottom overlap\n      var cut = viewCuttingPoint(cm, from, from, -1);\n      if (cut) {\n        display.view = display.view.slice(0, cut.index);\n        display.viewTo = cut.lineN;\n      } else {\n        resetView(cm);\n      }\n    } else { // Gap in the middle\n      var cutTop = viewCuttingPoint(cm, from, from, -1);\n      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);\n      if (cutTop && cutBot) {\n        display.view = display.view.slice(0, cutTop.index)\n          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))\n          .concat(display.view.slice(cutBot.index));\n        display.viewTo += lendiff;\n      } else {\n        resetView(cm);\n      }\n    }\n\n    var ext = display.externalMeasured;\n    if (ext) {\n      if (to < ext.lineN)\n        ext.lineN += lendiff;\n      else if (from < ext.lineN + ext.size)\n        display.externalMeasured = null;\n    }\n  }\n\n  // Register a change to a single line. Type must be one of \"text\",\n  // \"gutter\", \"class\", \"widget\"\n  function regLineChange(cm, line, type) {\n    cm.curOp.viewChanged = true;\n    var display = cm.display, ext = cm.display.externalMeasured;\n    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)\n      display.externalMeasured = null;\n\n    if (line < display.viewFrom || line >= display.viewTo) return;\n    var lineView = display.view[findViewIndex(cm, line)];\n    if (lineView.node == null) return;\n    var arr = lineView.changes || (lineView.changes = []);\n    if (indexOf(arr, type) == -1) arr.push(type);\n  }\n\n  // Clear the view.\n  function resetView(cm) {\n    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;\n    cm.display.view = [];\n    cm.display.viewOffset = 0;\n  }\n\n  // Find the view element corresponding to a given line. Return null\n  // when the line isn't visible.\n  function findViewIndex(cm, n) {\n    if (n >= cm.display.viewTo) return null;\n    n -= cm.display.viewFrom;\n    if (n < 0) return null;\n    var view = cm.display.view;\n    for (var i = 0; i < view.length; i++) {\n      n -= view[i].size;\n      if (n < 0) return i;\n    }\n  }\n\n  function viewCuttingPoint(cm, oldN, newN, dir) {\n    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;\n    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)\n      return {index: index, lineN: newN};\n    for (var i = 0, n = cm.display.viewFrom; i < index; i++)\n      n += view[i].size;\n    if (n != oldN) {\n      if (dir > 0) {\n        if (index == view.length - 1) return null;\n        diff = (n + view[index].size) - oldN;\n        index++;\n      } else {\n        diff = n - oldN;\n      }\n      oldN += diff; newN += diff;\n    }\n    while (visualLineNo(cm.doc, newN) != newN) {\n      if (index == (dir < 0 ? 0 : view.length - 1)) return null;\n      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;\n      index += dir;\n    }\n    return {index: index, lineN: newN};\n  }\n\n  // Force the view to cover a given range, adding empty view element\n  // or clipping off existing ones as needed.\n  function adjustView(cm, from, to) {\n    var display = cm.display, view = display.view;\n    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {\n      display.view = buildViewArray(cm, from, to);\n      display.viewFrom = from;\n    } else {\n      if (display.viewFrom > from)\n        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);\n      else if (display.viewFrom < from)\n        display.view = display.view.slice(findViewIndex(cm, from));\n      display.viewFrom = from;\n      if (display.viewTo < to)\n        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));\n      else if (display.viewTo > to)\n        display.view = display.view.slice(0, findViewIndex(cm, to));\n    }\n    display.viewTo = to;\n  }\n\n  // Count the number of lines in the view whose DOM representation is\n  // out of date (or nonexistent).\n  function countDirtyView(cm) {\n    var view = cm.display.view, dirty = 0;\n    for (var i = 0; i < view.length; i++) {\n      var lineView = view[i];\n      if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;\n    }\n    return dirty;\n  }\n\n  // EVENT HANDLERS\n\n  // Attach the necessary event handlers when initializing the editor\n  function registerEventHandlers(cm) {\n    var d = cm.display;\n    on(d.scroller, \"mousedown\", operation(cm, onMouseDown));\n    // Older IE's will not fire a second mousedown for a double click\n    if (ie && ie_version < 11)\n      on(d.scroller, \"dblclick\", operation(cm, function(e) {\n        if (signalDOMEvent(cm, e)) return;\n        var pos = posFromMouse(cm, e);\n        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;\n        e_preventDefault(e);\n        var word = cm.findWordAt(pos);\n        extendSelection(cm.doc, word.anchor, word.head);\n      }));\n    else\n      on(d.scroller, \"dblclick\", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });\n    // Some browsers fire contextmenu *after* opening the menu, at\n    // which point we can't mess with it anymore. Context menu is\n    // handled in onMouseDown for these browsers.\n    if (!captureRightClick) on(d.scroller, \"contextmenu\", function(e) {onContextMenu(cm, e);});\n\n    // Used to suppress mouse event handling when a touch happens\n    var touchFinished, prevTouch = {end: 0};\n    function finishTouch() {\n      if (d.activeTouch) {\n        touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);\n        prevTouch = d.activeTouch;\n        prevTouch.end = +new Date;\n      }\n    };\n    function isMouseLikeTouchEvent(e) {\n      if (e.touches.length != 1) return false;\n      var touch = e.touches[0];\n      return touch.radiusX <= 1 && touch.radiusY <= 1;\n    }\n    function farAway(touch, other) {\n      if (other.left == null) return true;\n      var dx = other.left - touch.left, dy = other.top - touch.top;\n      return dx * dx + dy * dy > 20 * 20;\n    }\n    on(d.scroller, \"touchstart\", function(e) {\n      if (!isMouseLikeTouchEvent(e)) {\n        clearTimeout(touchFinished);\n        var now = +new Date;\n        d.activeTouch = {start: now, moved: false,\n                         prev: now - prevTouch.end <= 300 ? prevTouch : null};\n        if (e.touches.length == 1) {\n          d.activeTouch.left = e.touches[0].pageX;\n          d.activeTouch.top = e.touches[0].pageY;\n        }\n      }\n    });\n    on(d.scroller, \"touchmove\", function() {\n      if (d.activeTouch) d.activeTouch.moved = true;\n    });\n    on(d.scroller, \"touchend\", function(e) {\n      var touch = d.activeTouch;\n      if (touch && !eventInWidget(d, e) && touch.left != null &&\n          !touch.moved && new Date - touch.start < 300) {\n        var pos = cm.coordsChar(d.activeTouch, \"page\"), range;\n        if (!touch.prev || farAway(touch, touch.prev)) // Single tap\n          range = new Range(pos, pos);\n        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap\n          range = cm.findWordAt(pos);\n        else // Triple tap\n          range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));\n        cm.setSelection(range.anchor, range.head);\n        cm.focus();\n        e_preventDefault(e);\n      }\n      finishTouch();\n    });\n    on(d.scroller, \"touchcancel\", finishTouch);\n\n    // Sync scrolling between fake scrollbars and real scrollable\n    // area, ensure viewport is updated when scrolling.\n    on(d.scroller, \"scroll\", function() {\n      if (d.scroller.clientHeight) {\n        setScrollTop(cm, d.scroller.scrollTop);\n        setScrollLeft(cm, d.scroller.scrollLeft, true);\n        signal(cm, \"scroll\", cm);\n      }\n    });\n\n    // Listen to wheel events in order to try and update the viewport on time.\n    on(d.scroller, \"mousewheel\", function(e){onScrollWheel(cm, e);});\n    on(d.scroller, \"DOMMouseScroll\", function(e){onScrollWheel(cm, e);});\n\n    // Prevent wrapper from ever scrolling\n    on(d.wrapper, \"scroll\", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });\n\n    d.dragFunctions = {\n      simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},\n      start: function(e){onDragStart(cm, e);},\n      drop: operation(cm, onDrop)\n    };\n\n    var inp = d.input.getField();\n    on(inp, \"keyup\", function(e) { onKeyUp.call(cm, e); });\n    on(inp, \"keydown\", operation(cm, onKeyDown));\n    on(inp, \"keypress\", operation(cm, onKeyPress));\n    on(inp, \"focus\", bind(onFocus, cm));\n    on(inp, \"blur\", bind(onBlur, cm));\n  }\n\n  function dragDropChanged(cm, value, old) {\n    var wasOn = old && old != CodeMirror.Init;\n    if (!value != !wasOn) {\n      var funcs = cm.display.dragFunctions;\n      var toggle = value ? on : off;\n      toggle(cm.display.scroller, \"dragstart\", funcs.start);\n      toggle(cm.display.scroller, \"dragenter\", funcs.simple);\n      toggle(cm.display.scroller, \"dragover\", funcs.simple);\n      toggle(cm.display.scroller, \"drop\", funcs.drop);\n    }\n  }\n\n  // Called when the window resizes\n  function onResize(cm) {\n    var d = cm.display;\n    if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)\n      return;\n    // Might be a text scaling operation, clear size caches.\n    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n    d.scrollbarsClipped = false;\n    cm.setSize();\n  }\n\n  // MOUSE EVENTS\n\n  // Return true when the given mouse event happened in a widget\n  function eventInWidget(display, e) {\n    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {\n      if (!n || (n.nodeType == 1 && n.getAttribute(\"cm-ignore-events\") == \"true\") ||\n          (n.parentNode == display.sizer && n != display.mover))\n        return true;\n    }\n  }\n\n  // Given a mouse event, find the corresponding position. If liberal\n  // is false, it checks whether a gutter or scrollbar was clicked,\n  // and returns null if it was. forRect is used by rectangular\n  // selections, and tries to estimate a character position even for\n  // coordinates beyond the right of the text.\n  function posFromMouse(cm, e, liberal, forRect) {\n    var display = cm.display;\n    if (!liberal && e_target(e).getAttribute(\"cm-not-content\") == \"true\") return null;\n\n    var x, y, space = display.lineSpace.getBoundingClientRect();\n    // Fails unpredictably on IE[67] when mouse is dragged around quickly.\n    try { x = e.clientX - space.left; y = e.clientY - space.top; }\n    catch (e) { return null; }\n    var coords = coordsChar(cm, x, y), line;\n    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {\n      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;\n      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));\n    }\n    return coords;\n  }\n\n  // A mouse down can be a single click, double click, triple click,\n  // start of selection drag, start of text drag, new cursor\n  // (ctrl-click), rectangle drag (alt-drag), or xwin\n  // middle-click-paste. Or it might be a click on something we should\n  // not interfere with, such as a scrollbar or widget.\n  function onMouseDown(e) {\n    var cm = this, display = cm.display;\n    if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;\n    display.shift = e.shiftKey;\n\n    if (eventInWidget(display, e)) {\n      if (!webkit) {\n        // Briefly turn off draggability, to allow widgets to do\n        // normal dragging things.\n        display.scroller.draggable = false;\n        setTimeout(function(){display.scroller.draggable = true;}, 100);\n      }\n      return;\n    }\n    if (clickInGutter(cm, e)) return;\n    var start = posFromMouse(cm, e);\n    window.focus();\n\n    switch (e_button(e)) {\n    case 1:\n      if (start)\n        leftButtonDown(cm, e, start);\n      else if (e_target(e) == display.scroller)\n        e_preventDefault(e);\n      break;\n    case 2:\n      if (webkit) cm.state.lastMiddleDown = +new Date;\n      if (start) extendSelection(cm.doc, start);\n      setTimeout(function() {display.input.focus();}, 20);\n      e_preventDefault(e);\n      break;\n    case 3:\n      if (captureRightClick) onContextMenu(cm, e);\n      else delayBlurEvent(cm);\n      break;\n    }\n  }\n\n  var lastClick, lastDoubleClick;\n  function leftButtonDown(cm, e, start) {\n    if (ie) setTimeout(bind(ensureFocus, cm), 0);\n    else cm.curOp.focus = activeElt();\n\n    var now = +new Date, type;\n    if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {\n      type = \"triple\";\n    } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {\n      type = \"double\";\n      lastDoubleClick = {time: now, pos: start};\n    } else {\n      type = \"single\";\n      lastClick = {time: now, pos: start};\n    }\n\n    var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;\n    if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&\n        type == \"single\" && (contained = sel.contains(start)) > -1 &&\n        (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&\n        (cmp(contained.to(), start) > 0 || start.xRel < 0))\n      leftButtonStartDrag(cm, e, start, modifier);\n    else\n      leftButtonSelect(cm, e, start, type, modifier);\n  }\n\n  // Start a text drag. When it ends, see if any dragging actually\n  // happen, and treat as a click if it didn't.\n  function leftButtonStartDrag(cm, e, start, modifier) {\n    var display = cm.display, startTime = +new Date;\n    var dragEnd = operation(cm, function(e2) {\n      if (webkit) display.scroller.draggable = false;\n      cm.state.draggingText = false;\n      off(document, \"mouseup\", dragEnd);\n      off(display.scroller, \"drop\", dragEnd);\n      if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {\n        e_preventDefault(e2);\n        if (!modifier && +new Date - 200 < startTime)\n          extendSelection(cm.doc, start);\n        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)\n        if (webkit || ie && ie_version == 9)\n          setTimeout(function() {document.body.focus(); display.input.focus();}, 20);\n        else\n          display.input.focus();\n      }\n    });\n    // Let the drag handler handle this.\n    if (webkit) display.scroller.draggable = true;\n    cm.state.draggingText = dragEnd;\n    // IE's approach to draggable\n    if (display.scroller.dragDrop) display.scroller.dragDrop();\n    on(document, \"mouseup\", dragEnd);\n    on(display.scroller, \"drop\", dragEnd);\n  }\n\n  // Normal selection, as opposed to text dragging.\n  function leftButtonSelect(cm, e, start, type, addNew) {\n    var display = cm.display, doc = cm.doc;\n    e_preventDefault(e);\n\n    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;\n    if (addNew && !e.shiftKey) {\n      ourIndex = doc.sel.contains(start);\n      if (ourIndex > -1)\n        ourRange = ranges[ourIndex];\n      else\n        ourRange = new Range(start, start);\n    } else {\n      ourRange = doc.sel.primary();\n      ourIndex = doc.sel.primIndex;\n    }\n\n    if (e.altKey) {\n      type = \"rect\";\n      if (!addNew) ourRange = new Range(start, start);\n      start = posFromMouse(cm, e, true, true);\n      ourIndex = -1;\n    } else if (type == \"double\") {\n      var word = cm.findWordAt(start);\n      if (cm.display.shift || doc.extend)\n        ourRange = extendRange(doc, ourRange, word.anchor, word.head);\n      else\n        ourRange = word;\n    } else if (type == \"triple\") {\n      var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));\n      if (cm.display.shift || doc.extend)\n        ourRange = extendRange(doc, ourRange, line.anchor, line.head);\n      else\n        ourRange = line;\n    } else {\n      ourRange = extendRange(doc, ourRange, start);\n    }\n\n    if (!addNew) {\n      ourIndex = 0;\n      setSelection(doc, new Selection([ourRange], 0), sel_mouse);\n      startSel = doc.sel;\n    } else if (ourIndex == -1) {\n      ourIndex = ranges.length;\n      setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),\n                   {scroll: false, origin: \"*mouse\"});\n    } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == \"single\" && !e.shiftKey) {\n      setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));\n      startSel = doc.sel;\n    } else {\n      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);\n    }\n\n    var lastPos = start;\n    function extendTo(pos) {\n      if (cmp(lastPos, pos) == 0) return;\n      lastPos = pos;\n\n      if (type == \"rect\") {\n        var ranges = [], tabSize = cm.options.tabSize;\n        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);\n        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);\n        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);\n        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));\n             line <= end; line++) {\n          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);\n          if (left == right)\n            ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));\n          else if (text.length > leftPos)\n            ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));\n        }\n        if (!ranges.length) ranges.push(new Range(start, start));\n        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),\n                     {origin: \"*mouse\", scroll: false});\n        cm.scrollIntoView(pos);\n      } else {\n        var oldRange = ourRange;\n        var anchor = oldRange.anchor, head = pos;\n        if (type != \"single\") {\n          if (type == \"double\")\n            var range = cm.findWordAt(pos);\n          else\n            var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));\n          if (cmp(range.anchor, anchor) > 0) {\n            head = range.head;\n            anchor = minPos(oldRange.from(), range.anchor);\n          } else {\n            head = range.anchor;\n            anchor = maxPos(oldRange.to(), range.head);\n          }\n        }\n        var ranges = startSel.ranges.slice(0);\n        ranges[ourIndex] = new Range(clipPos(doc, anchor), head);\n        setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);\n      }\n    }\n\n    var editorSize = display.wrapper.getBoundingClientRect();\n    // Used to ensure timeout re-tries don't fire when another extend\n    // happened in the meantime (clearTimeout isn't reliable -- at\n    // least on Chrome, the timeouts still happen even when cleared,\n    // if the clear happens after their scheduled firing time).\n    var counter = 0;\n\n    function extend(e) {\n      var curCount = ++counter;\n      var cur = posFromMouse(cm, e, true, type == \"rect\");\n      if (!cur) return;\n      if (cmp(cur, lastPos) != 0) {\n        cm.curOp.focus = activeElt();\n        extendTo(cur);\n        var visible = visibleLines(display, doc);\n        if (cur.line >= visible.to || cur.line < visible.from)\n          setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);\n      } else {\n        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;\n        if (outside) setTimeout(operation(cm, function() {\n          if (counter != curCount) return;\n          display.scroller.scrollTop += outside;\n          extend(e);\n        }), 50);\n      }\n    }\n\n    function done(e) {\n      counter = Infinity;\n      e_preventDefault(e);\n      display.input.focus();\n      off(document, \"mousemove\", move);\n      off(document, \"mouseup\", up);\n      doc.history.lastSelOrigin = null;\n    }\n\n    var move = operation(cm, function(e) {\n      if (!e_button(e)) done(e);\n      else extend(e);\n    });\n    var up = operation(cm, done);\n    on(document, \"mousemove\", move);\n    on(document, \"mouseup\", up);\n  }\n\n  // Determines whether an event happened in the gutter, and fires the\n  // handlers for the corresponding event.\n  function gutterEvent(cm, e, type, prevent, signalfn) {\n    try { var mX = e.clientX, mY = e.clientY; }\n    catch(e) { return false; }\n    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;\n    if (prevent) e_preventDefault(e);\n\n    var display = cm.display;\n    var lineBox = display.lineDiv.getBoundingClientRect();\n\n    if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);\n    mY -= lineBox.top - display.viewOffset;\n\n    for (var i = 0; i < cm.options.gutters.length; ++i) {\n      var g = display.gutters.childNodes[i];\n      if (g && g.getBoundingClientRect().right >= mX) {\n        var line = lineAtHeight(cm.doc, mY);\n        var gutter = cm.options.gutters[i];\n        signalfn(cm, type, cm, line, gutter, e);\n        return e_defaultPrevented(e);\n      }\n    }\n  }\n\n  function clickInGutter(cm, e) {\n    return gutterEvent(cm, e, \"gutterClick\", true, signalLater);\n  }\n\n  // Kludge to work around strange IE behavior where it'll sometimes\n  // re-fire a series of drag-related events right after the drop (#1551)\n  var lastDrop = 0;\n\n  function onDrop(e) {\n    var cm = this;\n    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))\n      return;\n    e_preventDefault(e);\n    if (ie) lastDrop = +new Date;\n    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;\n    if (!pos || isReadOnly(cm)) return;\n    // Might be a file drop, in which case we simply extract the text\n    // and insert it.\n    if (files && files.length && window.FileReader && window.File) {\n      var n = files.length, text = Array(n), read = 0;\n      var loadFile = function(file, i) {\n        var reader = new FileReader;\n        reader.onload = operation(cm, function() {\n          text[i] = reader.result;\n          if (++read == n) {\n            pos = clipPos(cm.doc, pos);\n            var change = {from: pos, to: pos, text: splitLines(text.join(\"\\n\")), origin: \"paste\"};\n            makeChange(cm.doc, change);\n            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));\n          }\n        });\n        reader.readAsText(file);\n      };\n      for (var i = 0; i < n; ++i) loadFile(files[i], i);\n    } else { // Normal drop\n      // Don't do a replace if the drop happened inside of the selected text.\n      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {\n        cm.state.draggingText(e);\n        // Ensure the editor is re-focused\n        setTimeout(function() {cm.display.input.focus();}, 20);\n        return;\n      }\n      try {\n        var text = e.dataTransfer.getData(\"Text\");\n        if (text) {\n          if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))\n            var selected = cm.listSelections();\n          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));\n          if (selected) for (var i = 0; i < selected.length; ++i)\n            replaceRange(cm.doc, \"\", selected[i].anchor, selected[i].head, \"drag\");\n          cm.replaceSelection(text, \"around\", \"paste\");\n          cm.display.input.focus();\n        }\n      }\n      catch(e){}\n    }\n  }\n\n  function onDragStart(cm, e) {\n    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }\n    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;\n\n    e.dataTransfer.setData(\"Text\", cm.getSelection());\n\n    // Use dummy image instead of default browsers image.\n    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.\n    if (e.dataTransfer.setDragImage && !safari) {\n      var img = elt(\"img\", null, null, \"position: fixed; left: 0; top: 0;\");\n      img.src = \"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\";\n      if (presto) {\n        img.width = img.height = 1;\n        cm.display.wrapper.appendChild(img);\n        // Force a relayout, or Opera won't use our image for some obscure reason\n        img._top = img.offsetTop;\n      }\n      e.dataTransfer.setDragImage(img, 0, 0);\n      if (presto) img.parentNode.removeChild(img);\n    }\n  }\n\n  // SCROLL EVENTS\n\n  // Sync the scrollable area and scrollbars, ensure the viewport\n  // covers the visible area.\n  function setScrollTop(cm, val) {\n    if (Math.abs(cm.doc.scrollTop - val) < 2) return;\n    cm.doc.scrollTop = val;\n    if (!gecko) updateDisplaySimple(cm, {top: val});\n    if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;\n    cm.display.scrollbars.setScrollTop(val);\n    if (gecko) updateDisplaySimple(cm);\n    startWorker(cm, 100);\n  }\n  // Sync scroller and scrollbar, ensure the gutter elements are\n  // aligned.\n  function setScrollLeft(cm, val, isScroller) {\n    if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;\n    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);\n    cm.doc.scrollLeft = val;\n    alignHorizontally(cm);\n    if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;\n    cm.display.scrollbars.setScrollLeft(val);\n  }\n\n  // Since the delta values reported on mouse wheel events are\n  // unstandardized between browsers and even browser versions, and\n  // generally horribly unpredictable, this code starts by measuring\n  // the scroll effect that the first few mouse wheel events have,\n  // and, from that, detects the way it can convert deltas to pixel\n  // offsets afterwards.\n  //\n  // The reason we want to know the amount a wheel event will scroll\n  // is that it gives us a chance to update the display before the\n  // actual scrolling happens, reducing flickering.\n\n  var wheelSamples = 0, wheelPixelsPerUnit = null;\n  // Fill in a browser-detected starting value on browsers where we\n  // know one. These don't have to be accurate -- the result of them\n  // being wrong would just be a slight flicker on the first wheel\n  // scroll (if it is large enough).\n  if (ie) wheelPixelsPerUnit = -.53;\n  else if (gecko) wheelPixelsPerUnit = 15;\n  else if (chrome) wheelPixelsPerUnit = -.7;\n  else if (safari) wheelPixelsPerUnit = -1/3;\n\n  var wheelEventDelta = function(e) {\n    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;\n    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;\n    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;\n    else if (dy == null) dy = e.wheelDelta;\n    return {x: dx, y: dy};\n  };\n  CodeMirror.wheelEventPixels = function(e) {\n    var delta = wheelEventDelta(e);\n    delta.x *= wheelPixelsPerUnit;\n    delta.y *= wheelPixelsPerUnit;\n    return delta;\n  };\n\n  function onScrollWheel(cm, e) {\n    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;\n\n    var display = cm.display, scroll = display.scroller;\n    // Quit if there's nothing to scroll here\n    if (!(dx && scroll.scrollWidth > scroll.clientWidth ||\n          dy && scroll.scrollHeight > scroll.clientHeight)) return;\n\n    // Webkit browsers on OS X abort momentum scrolls when the target\n    // of the scroll event is removed from the scrollable element.\n    // This hack (see related code in patchDisplay) makes sure the\n    // element is kept around.\n    if (dy && mac && webkit) {\n      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {\n        for (var i = 0; i < view.length; i++) {\n          if (view[i].node == cur) {\n            cm.display.currentWheelTarget = cur;\n            break outer;\n          }\n        }\n      }\n    }\n\n    // On some browsers, horizontal scrolling will cause redraws to\n    // happen before the gutter has been realigned, causing it to\n    // wriggle around in a most unseemly way. When we have an\n    // estimated pixels/delta value, we just handle horizontal\n    // scrolling entirely here. It'll be slightly off from native, but\n    // better than glitching out.\n    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {\n      if (dy)\n        setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));\n      setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));\n      e_preventDefault(e);\n      display.wheelStartX = null; // Abort measurement, if in progress\n      return;\n    }\n\n    // 'Project' the visible viewport to cover the area that is being\n    // scrolled into view (if we know enough to estimate it).\n    if (dy && wheelPixelsPerUnit != null) {\n      var pixels = dy * wheelPixelsPerUnit;\n      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;\n      if (pixels < 0) top = Math.max(0, top + pixels - 50);\n      else bot = Math.min(cm.doc.height, bot + pixels + 50);\n      updateDisplaySimple(cm, {top: top, bottom: bot});\n    }\n\n    if (wheelSamples < 20) {\n      if (display.wheelStartX == null) {\n        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;\n        display.wheelDX = dx; display.wheelDY = dy;\n        setTimeout(function() {\n          if (display.wheelStartX == null) return;\n          var movedX = scroll.scrollLeft - display.wheelStartX;\n          var movedY = scroll.scrollTop - display.wheelStartY;\n          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||\n            (movedX && display.wheelDX && movedX / display.wheelDX);\n          display.wheelStartX = display.wheelStartY = null;\n          if (!sample) return;\n          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);\n          ++wheelSamples;\n        }, 200);\n      } else {\n        display.wheelDX += dx; display.wheelDY += dy;\n      }\n    }\n  }\n\n  // KEY EVENTS\n\n  // Run a handler that was bound to a key.\n  function doHandleBinding(cm, bound, dropShift) {\n    if (typeof bound == \"string\") {\n      bound = commands[bound];\n      if (!bound) return false;\n    }\n    // Ensure previous input has been read, so that the handler sees a\n    // consistent view of the document\n    cm.display.input.ensurePolled();\n    var prevShift = cm.display.shift, done = false;\n    try {\n      if (isReadOnly(cm)) cm.state.suppressEdits = true;\n      if (dropShift) cm.display.shift = false;\n      done = bound(cm) != Pass;\n    } finally {\n      cm.display.shift = prevShift;\n      cm.state.suppressEdits = false;\n    }\n    return done;\n  }\n\n  function lookupKeyForEditor(cm, name, handle) {\n    for (var i = 0; i < cm.state.keyMaps.length; i++) {\n      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);\n      if (result) return result;\n    }\n    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))\n      || lookupKey(name, cm.options.keyMap, handle, cm);\n  }\n\n  var stopSeq = new Delayed;\n  function dispatchKey(cm, name, e, handle) {\n    var seq = cm.state.keySeq;\n    if (seq) {\n      if (isModifierKey(name)) return \"handled\";\n      stopSeq.set(50, function() {\n        if (cm.state.keySeq == seq) {\n          cm.state.keySeq = null;\n          cm.display.input.reset();\n        }\n      });\n      name = seq + \" \" + name;\n    }\n    var result = lookupKeyForEditor(cm, name, handle);\n\n    if (result == \"multi\")\n      cm.state.keySeq = name;\n    if (result == \"handled\")\n      signalLater(cm, \"keyHandled\", cm, name, e);\n\n    if (result == \"handled\" || result == \"multi\") {\n      e_preventDefault(e);\n      restartBlink(cm);\n    }\n\n    if (seq && !result && /\\'$/.test(name)) {\n      e_preventDefault(e);\n      return true;\n    }\n    return !!result;\n  }\n\n  // Handle a key from the keydown event.\n  function handleKeyBinding(cm, e) {\n    var name = keyName(e, true);\n    if (!name) return false;\n\n    if (e.shiftKey && !cm.state.keySeq) {\n      // First try to resolve full name (including 'Shift-'). Failing\n      // that, see if there is a cursor-motion command (starting with\n      // 'go') bound to the keyname without 'Shift-'.\n      return dispatchKey(cm, \"Shift-\" + name, e, function(b) {return doHandleBinding(cm, b, true);})\n          || dispatchKey(cm, name, e, function(b) {\n               if (typeof b == \"string\" ? /^go[A-Z]/.test(b) : b.motion)\n                 return doHandleBinding(cm, b);\n             });\n    } else {\n      return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });\n    }\n  }\n\n  // Handle a key from the keypress event\n  function handleCharBinding(cm, e, ch) {\n    return dispatchKey(cm, \"'\" + ch + \"'\", e,\n                       function(b) { return doHandleBinding(cm, b, true); });\n  }\n\n  var lastStoppedKey = null;\n  function onKeyDown(e) {\n    var cm = this;\n    cm.curOp.focus = activeElt();\n    if (signalDOMEvent(cm, e)) return;\n    // IE does strange things with escape.\n    if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;\n    var code = e.keyCode;\n    cm.display.shift = code == 16 || e.shiftKey;\n    var handled = handleKeyBinding(cm, e);\n    if (presto) {\n      lastStoppedKey = handled ? code : null;\n      // Opera has no cut event... we try to at least catch the key combo\n      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))\n        cm.replaceSelection(\"\", null, \"cut\");\n    }\n\n    // Turn mouse into crosshair when Alt is held on Mac.\n    if (code == 18 && !/\\bCodeMirror-crosshair\\b/.test(cm.display.lineDiv.className))\n      showCrossHair(cm);\n  }\n\n  function showCrossHair(cm) {\n    var lineDiv = cm.display.lineDiv;\n    addClass(lineDiv, \"CodeMirror-crosshair\");\n\n    function up(e) {\n      if (e.keyCode == 18 || !e.altKey) {\n        rmClass(lineDiv, \"CodeMirror-crosshair\");\n        off(document, \"keyup\", up);\n        off(document, \"mouseover\", up);\n      }\n    }\n    on(document, \"keyup\", up);\n    on(document, \"mouseover\", up);\n  }\n\n  function onKeyUp(e) {\n    if (e.keyCode == 16) this.doc.sel.shift = false;\n    signalDOMEvent(this, e);\n  }\n\n  function onKeyPress(e) {\n    var cm = this;\n    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;\n    var keyCode = e.keyCode, charCode = e.charCode;\n    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}\n    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;\n    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);\n    if (handleCharBinding(cm, e, ch)) return;\n    cm.display.input.onKeyPress(e);\n  }\n\n  // FOCUS/BLUR EVENTS\n\n  function delayBlurEvent(cm) {\n    cm.state.delayingBlurEvent = true;\n    setTimeout(function() {\n      if (cm.state.delayingBlurEvent) {\n        cm.state.delayingBlurEvent = false;\n        onBlur(cm);\n      }\n    }, 100);\n  }\n\n  function onFocus(cm) {\n    if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;\n\n    if (cm.options.readOnly == \"nocursor\") return;\n    if (!cm.state.focused) {\n      signal(cm, \"focus\", cm);\n      cm.state.focused = true;\n      addClass(cm.display.wrapper, \"CodeMirror-focused\");\n      // This test prevents this from firing when a context\n      // menu is closed (since the input reset would kill the\n      // select-all detection hack)\n      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {\n        cm.display.input.reset();\n        if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730\n      }\n      cm.display.input.receivedFocus();\n    }\n    restartBlink(cm);\n  }\n  function onBlur(cm) {\n    if (cm.state.delayingBlurEvent) return;\n\n    if (cm.state.focused) {\n      signal(cm, \"blur\", cm);\n      cm.state.focused = false;\n      rmClass(cm.display.wrapper, \"CodeMirror-focused\");\n    }\n    clearInterval(cm.display.blinker);\n    setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);\n  }\n\n  // CONTEXT MENU HANDLING\n\n  // To make the context menu work, we need to briefly unhide the\n  // textarea (making it as unobtrusive as possible) to let the\n  // right-click take effect on it.\n  function onContextMenu(cm, e) {\n    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;\n    cm.display.input.onContextMenu(e);\n  }\n\n  function contextMenuInGutter(cm, e) {\n    if (!hasHandler(cm, \"gutterContextMenu\")) return false;\n    return gutterEvent(cm, e, \"gutterContextMenu\", false, signal);\n  }\n\n  // UPDATING\n\n  // Compute the position of the end of a change (its 'to' property\n  // refers to the pre-change end).\n  var changeEnd = CodeMirror.changeEnd = function(change) {\n    if (!change.text) return change.to;\n    return Pos(change.from.line + change.text.length - 1,\n               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));\n  };\n\n  // Adjust a position to refer to the post-change position of the\n  // same text, or the end of the change if the change covers it.\n  function adjustForChange(pos, change) {\n    if (cmp(pos, change.from) < 0) return pos;\n    if (cmp(pos, change.to) <= 0) return changeEnd(change);\n\n    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;\n    if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;\n    return Pos(line, ch);\n  }\n\n  function computeSelAfterChange(doc, change) {\n    var out = [];\n    for (var i = 0; i < doc.sel.ranges.length; i++) {\n      var range = doc.sel.ranges[i];\n      out.push(new Range(adjustForChange(range.anchor, change),\n                         adjustForChange(range.head, change)));\n    }\n    return normalizeSelection(out, doc.sel.primIndex);\n  }\n\n  function offsetPos(pos, old, nw) {\n    if (pos.line == old.line)\n      return Pos(nw.line, pos.ch - old.ch + nw.ch);\n    else\n      return Pos(nw.line + (pos.line - old.line), pos.ch);\n  }\n\n  // Used by replaceSelections to allow moving the selection to the\n  // start or around the replaced test. Hint may be \"start\" or \"around\".\n  function computeReplacedSel(doc, changes, hint) {\n    var out = [];\n    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;\n    for (var i = 0; i < changes.length; i++) {\n      var change = changes[i];\n      var from = offsetPos(change.from, oldPrev, newPrev);\n      var to = offsetPos(changeEnd(change), oldPrev, newPrev);\n      oldPrev = change.to;\n      newPrev = to;\n      if (hint == \"around\") {\n        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;\n        out[i] = new Range(inv ? to : from, inv ? from : to);\n      } else {\n        out[i] = new Range(from, from);\n      }\n    }\n    return new Selection(out, doc.sel.primIndex);\n  }\n\n  // Allow \"beforeChange\" event handlers to influence a change\n  function filterChange(doc, change, update) {\n    var obj = {\n      canceled: false,\n      from: change.from,\n      to: change.to,\n      text: change.text,\n      origin: change.origin,\n      cancel: function() { this.canceled = true; }\n    };\n    if (update) obj.update = function(from, to, text, origin) {\n      if (from) this.from = clipPos(doc, from);\n      if (to) this.to = clipPos(doc, to);\n      if (text) this.text = text;\n      if (origin !== undefined) this.origin = origin;\n    };\n    signal(doc, \"beforeChange\", doc, obj);\n    if (doc.cm) signal(doc.cm, \"beforeChange\", doc.cm, obj);\n\n    if (obj.canceled) return null;\n    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};\n  }\n\n  // Apply a change to a document, and add it to the document's\n  // history, and propagating it to all linked documents.\n  function makeChange(doc, change, ignoreReadOnly) {\n    if (doc.cm) {\n      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);\n      if (doc.cm.state.suppressEdits) return;\n    }\n\n    if (hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")) {\n      change = filterChange(doc, change, true);\n      if (!change) return;\n    }\n\n    // Possibly split or suppress the update based on the presence\n    // of read-only spans in its range.\n    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);\n    if (split) {\n      for (var i = split.length - 1; i >= 0; --i)\n        makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [\"\"] : change.text});\n    } else {\n      makeChangeInner(doc, change);\n    }\n  }\n\n  function makeChangeInner(doc, change) {\n    if (change.text.length == 1 && change.text[0] == \"\" && cmp(change.from, change.to) == 0) return;\n    var selAfter = computeSelAfterChange(doc, change);\n    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);\n\n    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));\n    var rebased = [];\n\n    linkedDocs(doc, function(doc, sharedHist) {\n      if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n        rebaseHist(doc.history, change);\n        rebased.push(doc.history);\n      }\n      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));\n    });\n  }\n\n  // Revert a change stored in a document's history.\n  function makeChangeFromHistory(doc, type, allowSelectionOnly) {\n    if (doc.cm && doc.cm.state.suppressEdits) return;\n\n    var hist = doc.history, event, selAfter = doc.sel;\n    var source = type == \"undo\" ? hist.done : hist.undone, dest = type == \"undo\" ? hist.undone : hist.done;\n\n    // Verify that there is a useable event (so that ctrl-z won't\n    // needlessly clear selection events)\n    for (var i = 0; i < source.length; i++) {\n      event = source[i];\n      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)\n        break;\n    }\n    if (i == source.length) return;\n    hist.lastOrigin = hist.lastSelOrigin = null;\n\n    for (;;) {\n      event = source.pop();\n      if (event.ranges) {\n        pushSelectionToHistory(event, dest);\n        if (allowSelectionOnly && !event.equals(doc.sel)) {\n          setSelection(doc, event, {clearRedo: false});\n          return;\n        }\n        selAfter = event;\n      }\n      else break;\n    }\n\n    // Build up a reverse change object to add to the opposite history\n    // stack (redo when undoing, and vice versa).\n    var antiChanges = [];\n    pushSelectionToHistory(selAfter, dest);\n    dest.push({changes: antiChanges, generation: hist.generation});\n    hist.generation = event.generation || ++hist.maxGeneration;\n\n    var filter = hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\");\n\n    for (var i = event.changes.length - 1; i >= 0; --i) {\n      var change = event.changes[i];\n      change.origin = type;\n      if (filter && !filterChange(doc, change, false)) {\n        source.length = 0;\n        return;\n      }\n\n      antiChanges.push(historyChangeFromChange(doc, change));\n\n      var after = i ? computeSelAfterChange(doc, change) : lst(source);\n      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));\n      if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});\n      var rebased = [];\n\n      // Propagate to the linked documents\n      linkedDocs(doc, function(doc, sharedHist) {\n        if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n          rebaseHist(doc.history, change);\n          rebased.push(doc.history);\n        }\n        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));\n      });\n    }\n  }\n\n  // Sub-views need their line numbers shifted when text is added\n  // above or below them in the parent document.\n  function shiftDoc(doc, distance) {\n    if (distance == 0) return;\n    doc.first += distance;\n    doc.sel = new Selection(map(doc.sel.ranges, function(range) {\n      return new Range(Pos(range.anchor.line + distance, range.anchor.ch),\n                       Pos(range.head.line + distance, range.head.ch));\n    }), doc.sel.primIndex);\n    if (doc.cm) {\n      regChange(doc.cm, doc.first, doc.first - distance, distance);\n      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)\n        regLineChange(doc.cm, l, \"gutter\");\n    }\n  }\n\n  // More lower-level change function, handling only a single document\n  // (not linked ones).\n  function makeChangeSingleDoc(doc, change, selAfter, spans) {\n    if (doc.cm && !doc.cm.curOp)\n      return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);\n\n    if (change.to.line < doc.first) {\n      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));\n      return;\n    }\n    if (change.from.line > doc.lastLine()) return;\n\n    // Clip the change to the size of this doc\n    if (change.from.line < doc.first) {\n      var shift = change.text.length - 1 - (doc.first - change.from.line);\n      shiftDoc(doc, shift);\n      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),\n                text: [lst(change.text)], origin: change.origin};\n    }\n    var last = doc.lastLine();\n    if (change.to.line > last) {\n      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),\n                text: [change.text[0]], origin: change.origin};\n    }\n\n    change.removed = getBetween(doc, change.from, change.to);\n\n    if (!selAfter) selAfter = computeSelAfterChange(doc, change);\n    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);\n    else updateDoc(doc, change, spans);\n    setSelectionNoUndo(doc, selAfter, sel_dontScroll);\n  }\n\n  // Handle the interaction of a change to a document with the editor\n  // that this document is part of.\n  function makeChangeSingleDocInEditor(cm, change, spans) {\n    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;\n\n    var recomputeMaxLength = false, checkWidthStart = from.line;\n    if (!cm.options.lineWrapping) {\n      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));\n      doc.iter(checkWidthStart, to.line + 1, function(line) {\n        if (line == display.maxLine) {\n          recomputeMaxLength = true;\n          return true;\n        }\n      });\n    }\n\n    if (doc.sel.contains(change.from, change.to) > -1)\n      signalCursorActivity(cm);\n\n    updateDoc(doc, change, spans, estimateHeight(cm));\n\n    if (!cm.options.lineWrapping) {\n      doc.iter(checkWidthStart, from.line + change.text.length, function(line) {\n        var len = lineLength(line);\n        if (len > display.maxLineLength) {\n          display.maxLine = line;\n          display.maxLineLength = len;\n          display.maxLineChanged = true;\n          recomputeMaxLength = false;\n        }\n      });\n      if (recomputeMaxLength) cm.curOp.updateMaxLine = true;\n    }\n\n    // Adjust frontier, schedule worker\n    doc.frontier = Math.min(doc.frontier, from.line);\n    startWorker(cm, 400);\n\n    var lendiff = change.text.length - (to.line - from.line) - 1;\n    // Remember that these lines changed, for updating the display\n    if (change.full)\n      regChange(cm);\n    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))\n      regLineChange(cm, from.line, \"text\");\n    else\n      regChange(cm, from.line, to.line + 1, lendiff);\n\n    var changesHandler = hasHandler(cm, \"changes\"), changeHandler = hasHandler(cm, \"change\");\n    if (changeHandler || changesHandler) {\n      var obj = {\n        from: from, to: to,\n        text: change.text,\n        removed: change.removed,\n        origin: change.origin\n      };\n      if (changeHandler) signalLater(cm, \"change\", cm, obj);\n      if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);\n    }\n    cm.display.selForContextMenu = null;\n  }\n\n  function replaceRange(doc, code, from, to, origin) {\n    if (!to) to = from;\n    if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }\n    if (typeof code == \"string\") code = splitLines(code);\n    makeChange(doc, {from: from, to: to, text: code, origin: origin});\n  }\n\n  // SCROLLING THINGS INTO VIEW\n\n  // If an editor sits on the top or bottom of the window, partially\n  // scrolled out of view, this ensures that the cursor is visible.\n  function maybeScrollWindow(cm, coords) {\n    if (signalDOMEvent(cm, \"scrollCursorIntoView\")) return;\n\n    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;\n    if (coords.top + box.top < 0) doScroll = true;\n    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;\n    if (doScroll != null && !phantom) {\n      var scrollNode = elt(\"div\", \"\\u200b\", null, \"position: absolute; top: \" +\n                           (coords.top - display.viewOffset - paddingTop(cm.display)) + \"px; height: \" +\n                           (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + \"px; left: \" +\n                           coords.left + \"px; width: 2px;\");\n      cm.display.lineSpace.appendChild(scrollNode);\n      scrollNode.scrollIntoView(doScroll);\n      cm.display.lineSpace.removeChild(scrollNode);\n    }\n  }\n\n  // Scroll a given position into view (immediately), verifying that\n  // it actually became visible (as line heights are accurately\n  // measured, the position of something may 'drift' during drawing).\n  function scrollPosIntoView(cm, pos, end, margin) {\n    if (margin == null) margin = 0;\n    for (var limit = 0; limit < 5; limit++) {\n      var changed = false, coords = cursorCoords(cm, pos);\n      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);\n      var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),\n                                         Math.min(coords.top, endCoords.top) - margin,\n                                         Math.max(coords.left, endCoords.left),\n                                         Math.max(coords.bottom, endCoords.bottom) + margin);\n      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;\n      if (scrollPos.scrollTop != null) {\n        setScrollTop(cm, scrollPos.scrollTop);\n        if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;\n      }\n      if (scrollPos.scrollLeft != null) {\n        setScrollLeft(cm, scrollPos.scrollLeft);\n        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;\n      }\n      if (!changed) break;\n    }\n    return coords;\n  }\n\n  // Scroll a given set of coordinates into view (immediately).\n  function scrollIntoView(cm, x1, y1, x2, y2) {\n    var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);\n    if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);\n    if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);\n  }\n\n  // Calculate a new scroll position needed to scroll the given\n  // rectangle into view. Returns an object with scrollTop and\n  // scrollLeft properties. When these are undefined, the\n  // vertical/horizontal position does not need to be adjusted.\n  function calculateScrollPos(cm, x1, y1, x2, y2) {\n    var display = cm.display, snapMargin = textHeight(cm.display);\n    if (y1 < 0) y1 = 0;\n    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;\n    var screen = displayHeight(cm), result = {};\n    if (y2 - y1 > screen) y2 = y1 + screen;\n    var docBottom = cm.doc.height + paddingVert(display);\n    var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;\n    if (y1 < screentop) {\n      result.scrollTop = atTop ? 0 : y1;\n    } else if (y2 > screentop + screen) {\n      var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);\n      if (newTop != screentop) result.scrollTop = newTop;\n    }\n\n    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;\n    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);\n    var tooWide = x2 - x1 > screenw;\n    if (tooWide) x2 = x1 + screenw;\n    if (x1 < 10)\n      result.scrollLeft = 0;\n    else if (x1 < screenleft)\n      result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));\n    else if (x2 > screenw + screenleft - 3)\n      result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;\n    return result;\n  }\n\n  // Store a relative adjustment to the scroll position in the current\n  // operation (to be applied when the operation finishes).\n  function addToScrollPos(cm, left, top) {\n    if (left != null || top != null) resolveScrollToPos(cm);\n    if (left != null)\n      cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;\n    if (top != null)\n      cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;\n  }\n\n  // Make sure that at the end of the operation the current cursor is\n  // shown.\n  function ensureCursorVisible(cm) {\n    resolveScrollToPos(cm);\n    var cur = cm.getCursor(), from = cur, to = cur;\n    if (!cm.options.lineWrapping) {\n      from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;\n      to = Pos(cur.line, cur.ch + 1);\n    }\n    cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};\n  }\n\n  // When an operation has its scrollToPos property set, and another\n  // scroll action is applied before the end of the operation, this\n  // 'simulates' scrolling that position into view in a cheap way, so\n  // that the effect of intermediate scroll commands is not ignored.\n  function resolveScrollToPos(cm) {\n    var range = cm.curOp.scrollToPos;\n    if (range) {\n      cm.curOp.scrollToPos = null;\n      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);\n      var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),\n                                    Math.min(from.top, to.top) - range.margin,\n                                    Math.max(from.right, to.right),\n                                    Math.max(from.bottom, to.bottom) + range.margin);\n      cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);\n    }\n  }\n\n  // API UTILITIES\n\n  // Indent the given line. The how parameter can be \"smart\",\n  // \"add\"/null, \"subtract\", or \"prev\". When aggressive is false\n  // (typically set to true for forced single-line indents), empty\n  // lines are not indented, and places where the mode returns Pass\n  // are left alone.\n  function indentLine(cm, n, how, aggressive) {\n    var doc = cm.doc, state;\n    if (how == null) how = \"add\";\n    if (how == \"smart\") {\n      // Fall back to \"prev\" when the mode doesn't have an indentation\n      // method.\n      if (!doc.mode.indent) how = \"prev\";\n      else state = getStateBefore(cm, n);\n    }\n\n    var tabSize = cm.options.tabSize;\n    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);\n    if (line.stateAfter) line.stateAfter = null;\n    var curSpaceString = line.text.match(/^\\s*/)[0], indentation;\n    if (!aggressive && !/\\S/.test(line.text)) {\n      indentation = 0;\n      how = \"not\";\n    } else if (how == \"smart\") {\n      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);\n      if (indentation == Pass || indentation > 150) {\n        if (!aggressive) return;\n        how = \"prev\";\n      }\n    }\n    if (how == \"prev\") {\n      if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);\n      else indentation = 0;\n    } else if (how == \"add\") {\n      indentation = curSpace + cm.options.indentUnit;\n    } else if (how == \"subtract\") {\n      indentation = curSpace - cm.options.indentUnit;\n    } else if (typeof how == \"number\") {\n      indentation = curSpace + how;\n    }\n    indentation = Math.max(0, indentation);\n\n    var indentString = \"\", pos = 0;\n    if (cm.options.indentWithTabs)\n      for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += \"\\t\";}\n    if (pos < indentation) indentString += spaceStr(indentation - pos);\n\n    if (indentString != curSpaceString) {\n      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), \"+input\");\n      line.stateAfter = null;\n      return true;\n    } else {\n      // Ensure that, if the cursor was in the whitespace at the start\n      // of the line, it is moved to the end of that space.\n      for (var i = 0; i < doc.sel.ranges.length; i++) {\n        var range = doc.sel.ranges[i];\n        if (range.head.line == n && range.head.ch < curSpaceString.length) {\n          var pos = Pos(n, curSpaceString.length);\n          replaceOneSelection(doc, i, new Range(pos, pos));\n          break;\n        }\n      }\n    }\n  }\n\n  // Utility for applying a change to a line by handle or number,\n  // returning the number and optionally registering the line as\n  // changed.\n  function changeLine(doc, handle, changeType, op) {\n    var no = handle, line = handle;\n    if (typeof handle == \"number\") line = getLine(doc, clipLine(doc, handle));\n    else no = lineNo(handle);\n    if (no == null) return null;\n    if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);\n    return line;\n  }\n\n  // Helper for deleting text near the selection(s), used to implement\n  // backspace, delete, and similar functionality.\n  function deleteNearSelection(cm, compute) {\n    var ranges = cm.doc.sel.ranges, kill = [];\n    // Build up a set of ranges to kill first, merging overlapping\n    // ranges.\n    for (var i = 0; i < ranges.length; i++) {\n      var toKill = compute(ranges[i]);\n      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {\n        var replaced = kill.pop();\n        if (cmp(replaced.from, toKill.from) < 0) {\n          toKill.from = replaced.from;\n          break;\n        }\n      }\n      kill.push(toKill);\n    }\n    // Next, remove those actual ranges.\n    runInOp(cm, function() {\n      for (var i = kill.length - 1; i >= 0; i--)\n        replaceRange(cm.doc, \"\", kill[i].from, kill[i].to, \"+delete\");\n      ensureCursorVisible(cm);\n    });\n  }\n\n  // Used for horizontal relative motion. Dir is -1 or 1 (left or\n  // right), unit can be \"char\", \"column\" (like char, but doesn't\n  // cross line boundaries), \"word\" (across next word), or \"group\" (to\n  // the start of next group of word or non-word-non-whitespace\n  // chars). The visually param controls whether, in right-to-left\n  // text, direction 1 means to move towards the next index in the\n  // string, or towards the character to the right of the current\n  // position. The resulting position will have a hitSide=true\n  // property if it reached the end of the document.\n  function findPosH(doc, pos, dir, unit, visually) {\n    var line = pos.line, ch = pos.ch, origDir = dir;\n    var lineObj = getLine(doc, line);\n    var possible = true;\n    function findNextLine() {\n      var l = line + dir;\n      if (l < doc.first || l >= doc.first + doc.size) return (possible = false);\n      line = l;\n      return lineObj = getLine(doc, l);\n    }\n    function moveOnce(boundToLine) {\n      var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);\n      if (next == null) {\n        if (!boundToLine && findNextLine()) {\n          if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);\n          else ch = dir < 0 ? lineObj.text.length : 0;\n        } else return (possible = false);\n      } else ch = next;\n      return true;\n    }\n\n    if (unit == \"char\") moveOnce();\n    else if (unit == \"column\") moveOnce(true);\n    else if (unit == \"word\" || unit == \"group\") {\n      var sawType = null, group = unit == \"group\";\n      var helper = doc.cm && doc.cm.getHelper(pos, \"wordChars\");\n      for (var first = true;; first = false) {\n        if (dir < 0 && !moveOnce(!first)) break;\n        var cur = lineObj.text.charAt(ch) || \"\\n\";\n        var type = isWordChar(cur, helper) ? \"w\"\n          : group && cur == \"\\n\" ? \"n\"\n          : !group || /\\s/.test(cur) ? null\n          : \"p\";\n        if (group && !first && !type) type = \"s\";\n        if (sawType && sawType != type) {\n          if (dir < 0) {dir = 1; moveOnce();}\n          break;\n        }\n\n        if (type) sawType = type;\n        if (dir > 0 && !moveOnce(!first)) break;\n      }\n    }\n    var result = skipAtomic(doc, Pos(line, ch), origDir, true);\n    if (!possible) result.hitSide = true;\n    return result;\n  }\n\n  // For relative vertical movement. Dir may be -1 or 1. Unit can be\n  // \"page\" or \"line\". The resulting position will have a hitSide=true\n  // property if it reached the end of the document.\n  function findPosV(cm, pos, dir, unit) {\n    var doc = cm.doc, x = pos.left, y;\n    if (unit == \"page\") {\n      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);\n      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));\n    } else if (unit == \"line\") {\n      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;\n    }\n    for (;;) {\n      var target = coordsChar(cm, x, y);\n      if (!target.outside) break;\n      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }\n      y += dir * 5;\n    }\n    return target;\n  }\n\n  // EDITOR METHODS\n\n  // The publicly visible API. Note that methodOp(f) means\n  // 'wrap f in an operation, performed on its `this` parameter'.\n\n  // This is not the complete set of editor methods. Most of the\n  // methods defined on the Doc type are also injected into\n  // CodeMirror.prototype, for backwards compatibility and\n  // convenience.\n\n  CodeMirror.prototype = {\n    constructor: CodeMirror,\n    focus: function(){window.focus(); this.display.input.focus();},\n\n    setOption: function(option, value) {\n      var options = this.options, old = options[option];\n      if (options[option] == value && option != \"mode\") return;\n      options[option] = value;\n      if (optionHandlers.hasOwnProperty(option))\n        operation(this, optionHandlers[option])(this, value, old);\n    },\n\n    getOption: function(option) {return this.options[option];},\n    getDoc: function() {return this.doc;},\n\n    addKeyMap: function(map, bottom) {\n      this.state.keyMaps[bottom ? \"push\" : \"unshift\"](getKeyMap(map));\n    },\n    removeKeyMap: function(map) {\n      var maps = this.state.keyMaps;\n      for (var i = 0; i < maps.length; ++i)\n        if (maps[i] == map || maps[i].name == map) {\n          maps.splice(i, 1);\n          return true;\n        }\n    },\n\n    addOverlay: methodOp(function(spec, options) {\n      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);\n      if (mode.startState) throw new Error(\"Overlays may not be stateful.\");\n      this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});\n      this.state.modeGen++;\n      regChange(this);\n    }),\n    removeOverlay: methodOp(function(spec) {\n      var overlays = this.state.overlays;\n      for (var i = 0; i < overlays.length; ++i) {\n        var cur = overlays[i].modeSpec;\n        if (cur == spec || typeof spec == \"string\" && cur.name == spec) {\n          overlays.splice(i, 1);\n          this.state.modeGen++;\n          regChange(this);\n          return;\n        }\n      }\n    }),\n\n    indentLine: methodOp(function(n, dir, aggressive) {\n      if (typeof dir != \"string\" && typeof dir != \"number\") {\n        if (dir == null) dir = this.options.smartIndent ? \"smart\" : \"prev\";\n        else dir = dir ? \"add\" : \"subtract\";\n      }\n      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);\n    }),\n    indentSelection: methodOp(function(how) {\n      var ranges = this.doc.sel.ranges, end = -1;\n      for (var i = 0; i < ranges.length; i++) {\n        var range = ranges[i];\n        if (!range.empty()) {\n          var from = range.from(), to = range.to();\n          var start = Math.max(end, from.line);\n          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;\n          for (var j = start; j < end; ++j)\n            indentLine(this, j, how);\n          var newRanges = this.doc.sel.ranges;\n          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)\n            replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);\n        } else if (range.head.line > end) {\n          indentLine(this, range.head.line, how, true);\n          end = range.head.line;\n          if (i == this.doc.sel.primIndex) ensureCursorVisible(this);\n        }\n      }\n    }),\n\n    // Fetch the parser token for a given character. Useful for hacks\n    // that want to inspect the mode state (say, for completion).\n    getTokenAt: function(pos, precise) {\n      return takeToken(this, pos, precise);\n    },\n\n    getLineTokens: function(line, precise) {\n      return takeToken(this, Pos(line), precise, true);\n    },\n\n    getTokenTypeAt: function(pos) {\n      pos = clipPos(this.doc, pos);\n      var styles = getLineStyles(this, getLine(this.doc, pos.line));\n      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;\n      var type;\n      if (ch == 0) type = styles[2];\n      else for (;;) {\n        var mid = (before + after) >> 1;\n        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;\n        else if (styles[mid * 2 + 1] < ch) before = mid + 1;\n        else { type = styles[mid * 2 + 2]; break; }\n      }\n      var cut = type ? type.indexOf(\"cm-overlay \") : -1;\n      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);\n    },\n\n    getModeAt: function(pos) {\n      var mode = this.doc.mode;\n      if (!mode.innerMode) return mode;\n      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;\n    },\n\n    getHelper: function(pos, type) {\n      return this.getHelpers(pos, type)[0];\n    },\n\n    getHelpers: function(pos, type) {\n      var found = [];\n      if (!helpers.hasOwnProperty(type)) return found;\n      var help = helpers[type], mode = this.getModeAt(pos);\n      if (typeof mode[type] == \"string\") {\n        if (help[mode[type]]) found.push(help[mode[type]]);\n      } else if (mode[type]) {\n        for (var i = 0; i < mode[type].length; i++) {\n          var val = help[mode[type][i]];\n          if (val) found.push(val);\n        }\n      } else if (mode.helperType && help[mode.helperType]) {\n        found.push(help[mode.helperType]);\n      } else if (help[mode.name]) {\n        found.push(help[mode.name]);\n      }\n      for (var i = 0; i < help._global.length; i++) {\n        var cur = help._global[i];\n        if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)\n          found.push(cur.val);\n      }\n      return found;\n    },\n\n    getStateAfter: function(line, precise) {\n      var doc = this.doc;\n      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);\n      return getStateBefore(this, line + 1, precise);\n    },\n\n    cursorCoords: function(start, mode) {\n      var pos, range = this.doc.sel.primary();\n      if (start == null) pos = range.head;\n      else if (typeof start == \"object\") pos = clipPos(this.doc, start);\n      else pos = start ? range.from() : range.to();\n      return cursorCoords(this, pos, mode || \"page\");\n    },\n\n    charCoords: function(pos, mode) {\n      return charCoords(this, clipPos(this.doc, pos), mode || \"page\");\n    },\n\n    coordsChar: function(coords, mode) {\n      coords = fromCoordSystem(this, coords, mode || \"page\");\n      return coordsChar(this, coords.left, coords.top);\n    },\n\n    lineAtHeight: function(height, mode) {\n      height = fromCoordSystem(this, {top: height, left: 0}, mode || \"page\").top;\n      return lineAtHeight(this.doc, height + this.display.viewOffset);\n    },\n    heightAtLine: function(line, mode) {\n      var end = false, lineObj;\n      if (typeof line == \"number\") {\n        var last = this.doc.first + this.doc.size - 1;\n        if (line < this.doc.first) line = this.doc.first;\n        else if (line > last) { line = last; end = true; }\n        lineObj = getLine(this.doc, line);\n      } else {\n        lineObj = line;\n      }\n      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || \"page\").top +\n        (end ? this.doc.height - heightAtLine(lineObj) : 0);\n    },\n\n    defaultTextHeight: function() { return textHeight(this.display); },\n    defaultCharWidth: function() { return charWidth(this.display); },\n\n    setGutterMarker: methodOp(function(line, gutterID, value) {\n      return changeLine(this.doc, line, \"gutter\", function(line) {\n        var markers = line.gutterMarkers || (line.gutterMarkers = {});\n        markers[gutterID] = value;\n        if (!value && isEmpty(markers)) line.gutterMarkers = null;\n        return true;\n      });\n    }),\n\n    clearGutter: methodOp(function(gutterID) {\n      var cm = this, doc = cm.doc, i = doc.first;\n      doc.iter(function(line) {\n        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {\n          line.gutterMarkers[gutterID] = null;\n          regLineChange(cm, i, \"gutter\");\n          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;\n        }\n        ++i;\n      });\n    }),\n\n    lineInfo: function(line) {\n      if (typeof line == \"number\") {\n        if (!isLine(this.doc, line)) return null;\n        var n = line;\n        line = getLine(this.doc, line);\n        if (!line) return null;\n      } else {\n        var n = lineNo(line);\n        if (n == null) return null;\n      }\n      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,\n              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,\n              widgets: line.widgets};\n    },\n\n    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},\n\n    addWidget: function(pos, node, scroll, vert, horiz) {\n      var display = this.display;\n      pos = cursorCoords(this, clipPos(this.doc, pos));\n      var top = pos.bottom, left = pos.left;\n      node.style.position = \"absolute\";\n      node.setAttribute(\"cm-ignore-events\", \"true\");\n      this.display.input.setUneditable(node);\n      display.sizer.appendChild(node);\n      if (vert == \"over\") {\n        top = pos.top;\n      } else if (vert == \"above\" || vert == \"near\") {\n        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),\n        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);\n        // Default to positioning above (if specified and possible); otherwise default to positioning below\n        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)\n          top = pos.top - node.offsetHeight;\n        else if (pos.bottom + node.offsetHeight <= vspace)\n          top = pos.bottom;\n        if (left + node.offsetWidth > hspace)\n          left = hspace - node.offsetWidth;\n      }\n      node.style.top = top + \"px\";\n      node.style.left = node.style.right = \"\";\n      if (horiz == \"right\") {\n        left = display.sizer.clientWidth - node.offsetWidth;\n        node.style.right = \"0px\";\n      } else {\n        if (horiz == \"left\") left = 0;\n        else if (horiz == \"middle\") left = (display.sizer.clientWidth - node.offsetWidth) / 2;\n        node.style.left = left + \"px\";\n      }\n      if (scroll)\n        scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);\n    },\n\n    triggerOnKeyDown: methodOp(onKeyDown),\n    triggerOnKeyPress: methodOp(onKeyPress),\n    triggerOnKeyUp: onKeyUp,\n\n    execCommand: function(cmd) {\n      if (commands.hasOwnProperty(cmd))\n        return commands[cmd](this);\n    },\n\n    triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),\n\n    findPosH: function(from, amount, unit, visually) {\n      var dir = 1;\n      if (amount < 0) { dir = -1; amount = -amount; }\n      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {\n        cur = findPosH(this.doc, cur, dir, unit, visually);\n        if (cur.hitSide) break;\n      }\n      return cur;\n    },\n\n    moveH: methodOp(function(dir, unit) {\n      var cm = this;\n      cm.extendSelectionsBy(function(range) {\n        if (cm.display.shift || cm.doc.extend || range.empty())\n          return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);\n        else\n          return dir < 0 ? range.from() : range.to();\n      }, sel_move);\n    }),\n\n    deleteH: methodOp(function(dir, unit) {\n      var sel = this.doc.sel, doc = this.doc;\n      if (sel.somethingSelected())\n        doc.replaceSelection(\"\", null, \"+delete\");\n      else\n        deleteNearSelection(this, function(range) {\n          var other = findPosH(doc, range.head, dir, unit, false);\n          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};\n        });\n    }),\n\n    findPosV: function(from, amount, unit, goalColumn) {\n      var dir = 1, x = goalColumn;\n      if (amount < 0) { dir = -1; amount = -amount; }\n      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {\n        var coords = cursorCoords(this, cur, \"div\");\n        if (x == null) x = coords.left;\n        else coords.left = x;\n        cur = findPosV(this, coords, dir, unit);\n        if (cur.hitSide) break;\n      }\n      return cur;\n    },\n\n    moveV: methodOp(function(dir, unit) {\n      var cm = this, doc = this.doc, goals = [];\n      var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();\n      doc.extendSelectionsBy(function(range) {\n        if (collapse)\n          return dir < 0 ? range.from() : range.to();\n        var headPos = cursorCoords(cm, range.head, \"div\");\n        if (range.goalColumn != null) headPos.left = range.goalColumn;\n        goals.push(headPos.left);\n        var pos = findPosV(cm, headPos, dir, unit);\n        if (unit == \"page\" && range == doc.sel.primary())\n          addToScrollPos(cm, null, charCoords(cm, pos, \"div\").top - headPos.top);\n        return pos;\n      }, sel_move);\n      if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)\n        doc.sel.ranges[i].goalColumn = goals[i];\n    }),\n\n    // Find the word at the given position (as returned by coordsChar).\n    findWordAt: function(pos) {\n      var doc = this.doc, line = getLine(doc, pos.line).text;\n      var start = pos.ch, end = pos.ch;\n      if (line) {\n        var helper = this.getHelper(pos, \"wordChars\");\n        if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;\n        var startChar = line.charAt(start);\n        var check = isWordChar(startChar, helper)\n          ? function(ch) { return isWordChar(ch, helper); }\n          : /\\s/.test(startChar) ? function(ch) {return /\\s/.test(ch);}\n          : function(ch) {return !/\\s/.test(ch) && !isWordChar(ch);};\n        while (start > 0 && check(line.charAt(start - 1))) --start;\n        while (end < line.length && check(line.charAt(end))) ++end;\n      }\n      return new Range(Pos(pos.line, start), Pos(pos.line, end));\n    },\n\n    toggleOverwrite: function(value) {\n      if (value != null && value == this.state.overwrite) return;\n      if (this.state.overwrite = !this.state.overwrite)\n        addClass(this.display.cursorDiv, \"CodeMirror-overwrite\");\n      else\n        rmClass(this.display.cursorDiv, \"CodeMirror-overwrite\");\n\n      signal(this, \"overwriteToggle\", this, this.state.overwrite);\n    },\n    hasFocus: function() { return this.display.input.getField() == activeElt(); },\n\n    scrollTo: methodOp(function(x, y) {\n      if (x != null || y != null) resolveScrollToPos(this);\n      if (x != null) this.curOp.scrollLeft = x;\n      if (y != null) this.curOp.scrollTop = y;\n    }),\n    getScrollInfo: function() {\n      var scroller = this.display.scroller;\n      return {left: scroller.scrollLeft, top: scroller.scrollTop,\n              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,\n              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,\n              clientHeight: displayHeight(this), clientWidth: displayWidth(this)};\n    },\n\n    scrollIntoView: methodOp(function(range, margin) {\n      if (range == null) {\n        range = {from: this.doc.sel.primary().head, to: null};\n        if (margin == null) margin = this.options.cursorScrollMargin;\n      } else if (typeof range == \"number\") {\n        range = {from: Pos(range, 0), to: null};\n      } else if (range.from == null) {\n        range = {from: range, to: null};\n      }\n      if (!range.to) range.to = range.from;\n      range.margin = margin || 0;\n\n      if (range.from.line != null) {\n        resolveScrollToPos(this);\n        this.curOp.scrollToPos = range;\n      } else {\n        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),\n                                      Math.min(range.from.top, range.to.top) - range.margin,\n                                      Math.max(range.from.right, range.to.right),\n                                      Math.max(range.from.bottom, range.to.bottom) + range.margin);\n        this.scrollTo(sPos.scrollLeft, sPos.scrollTop);\n      }\n    }),\n\n    setSize: methodOp(function(width, height) {\n      var cm = this;\n      function interpret(val) {\n        return typeof val == \"number\" || /^\\d+$/.test(String(val)) ? val + \"px\" : val;\n      }\n      if (width != null) cm.display.wrapper.style.width = interpret(width);\n      if (height != null) cm.display.wrapper.style.height = interpret(height);\n      if (cm.options.lineWrapping) clearLineMeasurementCache(this);\n      var lineNo = cm.display.viewFrom;\n      cm.doc.iter(lineNo, cm.display.viewTo, function(line) {\n        if (line.widgets) for (var i = 0; i < line.widgets.length; i++)\n          if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, \"widget\"); break; }\n        ++lineNo;\n      });\n      cm.curOp.forceUpdate = true;\n      signal(cm, \"refresh\", this);\n    }),\n\n    operation: function(f){return runInOp(this, f);},\n\n    refresh: methodOp(function() {\n      var oldHeight = this.display.cachedTextHeight;\n      regChange(this);\n      this.curOp.forceUpdate = true;\n      clearCaches(this);\n      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);\n      updateGutterSpace(this);\n      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)\n        estimateLineHeights(this);\n      signal(this, \"refresh\", this);\n    }),\n\n    swapDoc: methodOp(function(doc) {\n      var old = this.doc;\n      old.cm = null;\n      attachDoc(this, doc);\n      clearCaches(this);\n      this.display.input.reset();\n      this.scrollTo(doc.scrollLeft, doc.scrollTop);\n      this.curOp.forceScroll = true;\n      signalLater(this, \"swapDoc\", this, old);\n      return old;\n    }),\n\n    getInputField: function(){return this.display.input.getField();},\n    getWrapperElement: function(){return this.display.wrapper;},\n    getScrollerElement: function(){return this.display.scroller;},\n    getGutterElement: function(){return this.display.gutters;}\n  };\n  eventMixin(CodeMirror);\n\n  // OPTION DEFAULTS\n\n  // The default configuration options.\n  var defaults = CodeMirror.defaults = {};\n  // Functions to run when options are changed.\n  var optionHandlers = CodeMirror.optionHandlers = {};\n\n  function option(name, deflt, handle, notOnInit) {\n    CodeMirror.defaults[name] = deflt;\n    if (handle) optionHandlers[name] =\n      notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;\n  }\n\n  // Passed to option handlers when there is no old value.\n  var Init = CodeMirror.Init = {toString: function(){return \"CodeMirror.Init\";}};\n\n  // These two are, on init, called from the constructor because they\n  // have to be initialized before the editor can start at all.\n  option(\"value\", \"\", function(cm, val) {\n    cm.setValue(val);\n  }, true);\n  option(\"mode\", null, function(cm, val) {\n    cm.doc.modeOption = val;\n    loadMode(cm);\n  }, true);\n\n  option(\"indentUnit\", 2, loadMode, true);\n  option(\"indentWithTabs\", false);\n  option(\"smartIndent\", true);\n  option(\"tabSize\", 4, function(cm) {\n    resetModeState(cm);\n    clearCaches(cm);\n    regChange(cm);\n  }, true);\n  option(\"specialChars\", /[\\t\\u0000-\\u0019\\u00ad\\u200b-\\u200f\\u2028\\u2029\\ufeff]/g, function(cm, val, old) {\n    cm.state.specialChars = new RegExp(val.source + (val.test(\"\\t\") ? \"\" : \"|\\t\"), \"g\");\n    if (old != CodeMirror.Init) cm.refresh();\n  });\n  option(\"specialCharPlaceholder\", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);\n  option(\"electricChars\", true);\n  option(\"inputStyle\", mobile ? \"contenteditable\" : \"textarea\", function() {\n    throw new Error(\"inputStyle can not (yet) be changed in a running editor\"); // FIXME\n  }, true);\n  option(\"rtlMoveVisually\", !windows);\n  option(\"wholeLineUpdateBefore\", true);\n\n  option(\"theme\", \"default\", function(cm) {\n    themeChanged(cm);\n    guttersChanged(cm);\n  }, true);\n  option(\"keyMap\", \"default\", function(cm, val, old) {\n    var next = getKeyMap(val);\n    var prev = old != CodeMirror.Init && getKeyMap(old);\n    if (prev && prev.detach) prev.detach(cm, next);\n    if (next.attach) next.attach(cm, prev || null);\n  });\n  option(\"extraKeys\", null);\n\n  option(\"lineWrapping\", false, wrappingChanged, true);\n  option(\"gutters\", [], function(cm) {\n    setGuttersForLineNumbers(cm.options);\n    guttersChanged(cm);\n  }, true);\n  option(\"fixedGutter\", true, function(cm, val) {\n    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + \"px\" : \"0\";\n    cm.refresh();\n  }, true);\n  option(\"coverGutterNextToScrollbar\", false, function(cm) {updateScrollbars(cm);}, true);\n  option(\"scrollbarStyle\", \"native\", function(cm) {\n    initScrollbars(cm);\n    updateScrollbars(cm);\n    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);\n    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);\n  }, true);\n  option(\"lineNumbers\", false, function(cm) {\n    setGuttersForLineNumbers(cm.options);\n    guttersChanged(cm);\n  }, true);\n  option(\"firstLineNumber\", 1, guttersChanged, true);\n  option(\"lineNumberFormatter\", function(integer) {return integer;}, guttersChanged, true);\n  option(\"showCursorWhenSelecting\", false, updateSelection, true);\n\n  option(\"resetSelectionOnContextMenu\", true);\n  option(\"lineWiseCopyCut\", true);\n\n  option(\"readOnly\", false, function(cm, val) {\n    if (val == \"nocursor\") {\n      onBlur(cm);\n      cm.display.input.blur();\n      cm.display.disabled = true;\n    } else {\n      cm.display.disabled = false;\n      if (!val) cm.display.input.reset();\n    }\n  });\n  option(\"disableInput\", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);\n  option(\"dragDrop\", true, dragDropChanged);\n\n  option(\"cursorBlinkRate\", 530);\n  option(\"cursorScrollMargin\", 0);\n  option(\"cursorHeight\", 1, updateSelection, true);\n  option(\"singleCursorHeightPerLine\", true, updateSelection, true);\n  option(\"workTime\", 100);\n  option(\"workDelay\", 100);\n  option(\"flattenSpans\", true, resetModeState, true);\n  option(\"addModeClass\", false, resetModeState, true);\n  option(\"pollInterval\", 100);\n  option(\"undoDepth\", 200, function(cm, val){cm.doc.history.undoDepth = val;});\n  option(\"historyEventDelay\", 1250);\n  option(\"viewportMargin\", 10, function(cm){cm.refresh();}, true);\n  option(\"maxHighlightLength\", 10000, resetModeState, true);\n  option(\"moveInputWithCursor\", true, function(cm, val) {\n    if (!val) cm.display.input.resetPosition();\n  });\n\n  option(\"tabindex\", null, function(cm, val) {\n    cm.display.input.getField().tabIndex = val || \"\";\n  });\n  option(\"autofocus\", null);\n\n  // MODE DEFINITION AND QUERYING\n\n  // Known modes, by name and by MIME\n  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};\n\n  // Extra arguments are stored as the mode's dependencies, which is\n  // used by (legacy) mechanisms like loadmode.js to automatically\n  // load a mode. (Preferred mechanism is the require/define calls.)\n  CodeMirror.defineMode = function(name, mode) {\n    if (!CodeMirror.defaults.mode && name != \"null\") CodeMirror.defaults.mode = name;\n    if (arguments.length > 2)\n      mode.dependencies = Array.prototype.slice.call(arguments, 2);\n    modes[name] = mode;\n  };\n\n  CodeMirror.defineMIME = function(mime, spec) {\n    mimeModes[mime] = spec;\n  };\n\n  // Given a MIME type, a {name, ...options} config object, or a name\n  // string, return a mode config object.\n  CodeMirror.resolveMode = function(spec) {\n    if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n      spec = mimeModes[spec];\n    } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n      var found = mimeModes[spec.name];\n      if (typeof found == \"string\") found = {name: found};\n      spec = createObj(found, spec);\n      spec.name = found.name;\n    } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n      return CodeMirror.resolveMode(\"application/xml\");\n    }\n    if (typeof spec == \"string\") return {name: spec};\n    else return spec || {name: \"null\"};\n  };\n\n  // Given a mode spec (anything that resolveMode accepts), find and\n  // initialize an actual mode object.\n  CodeMirror.getMode = function(options, spec) {\n    var spec = CodeMirror.resolveMode(spec);\n    var mfactory = modes[spec.name];\n    if (!mfactory) return CodeMirror.getMode(options, \"text/plain\");\n    var modeObj = mfactory(options, spec);\n    if (modeExtensions.hasOwnProperty(spec.name)) {\n      var exts = modeExtensions[spec.name];\n      for (var prop in exts) {\n        if (!exts.hasOwnProperty(prop)) continue;\n        if (modeObj.hasOwnProperty(prop)) modeObj[\"_\" + prop] = modeObj[prop];\n        modeObj[prop] = exts[prop];\n      }\n    }\n    modeObj.name = spec.name;\n    if (spec.helperType) modeObj.helperType = spec.helperType;\n    if (spec.modeProps) for (var prop in spec.modeProps)\n      modeObj[prop] = spec.modeProps[prop];\n\n    return modeObj;\n  };\n\n  // Minimal default mode.\n  CodeMirror.defineMode(\"null\", function() {\n    return {token: function(stream) {stream.skipToEnd();}};\n  });\n  CodeMirror.defineMIME(\"text/plain\", \"null\");\n\n  // This can be used to attach properties to mode objects from\n  // outside the actual mode definition.\n  var modeExtensions = CodeMirror.modeExtensions = {};\n  CodeMirror.extendMode = function(mode, properties) {\n    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});\n    copyObj(properties, exts);\n  };\n\n  // EXTENSIONS\n\n  CodeMirror.defineExtension = function(name, func) {\n    CodeMirror.prototype[name] = func;\n  };\n  CodeMirror.defineDocExtension = function(name, func) {\n    Doc.prototype[name] = func;\n  };\n  CodeMirror.defineOption = option;\n\n  var initHooks = [];\n  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};\n\n  var helpers = CodeMirror.helpers = {};\n  CodeMirror.registerHelper = function(type, name, value) {\n    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};\n    helpers[type][name] = value;\n  };\n  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {\n    CodeMirror.registerHelper(type, name, value);\n    helpers[type]._global.push({pred: predicate, val: value});\n  };\n\n  // MODE STATE HANDLING\n\n  // Utility functions for working with state. Exported because nested\n  // modes need to do this for their inner modes.\n\n  var copyState = CodeMirror.copyState = function(mode, state) {\n    if (state === true) return state;\n    if (mode.copyState) return mode.copyState(state);\n    var nstate = {};\n    for (var n in state) {\n      var val = state[n];\n      if (val instanceof Array) val = val.concat([]);\n      nstate[n] = val;\n    }\n    return nstate;\n  };\n\n  var startState = CodeMirror.startState = function(mode, a1, a2) {\n    return mode.startState ? mode.startState(a1, a2) : true;\n  };\n\n  // Given a mode and a state (for that mode), find the inner mode and\n  // state at the position that the state refers to.\n  CodeMirror.innerMode = function(mode, state) {\n    while (mode.innerMode) {\n      var info = mode.innerMode(state);\n      if (!info || info.mode == mode) break;\n      state = info.state;\n      mode = info.mode;\n    }\n    return info || {mode: mode, state: state};\n  };\n\n  // STANDARD COMMANDS\n\n  // Commands are parameter-less actions that can be performed on an\n  // editor, mostly used for keybindings.\n  var commands = CodeMirror.commands = {\n    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},\n    singleSelection: function(cm) {\n      cm.setSelection(cm.getCursor(\"anchor\"), cm.getCursor(\"head\"), sel_dontScroll);\n    },\n    killLine: function(cm) {\n      deleteNearSelection(cm, function(range) {\n        if (range.empty()) {\n          var len = getLine(cm.doc, range.head.line).text.length;\n          if (range.head.ch == len && range.head.line < cm.lastLine())\n            return {from: range.head, to: Pos(range.head.line + 1, 0)};\n          else\n            return {from: range.head, to: Pos(range.head.line, len)};\n        } else {\n          return {from: range.from(), to: range.to()};\n        }\n      });\n    },\n    deleteLine: function(cm) {\n      deleteNearSelection(cm, function(range) {\n        return {from: Pos(range.from().line, 0),\n                to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};\n      });\n    },\n    delLineLeft: function(cm) {\n      deleteNearSelection(cm, function(range) {\n        return {from: Pos(range.from().line, 0), to: range.from()};\n      });\n    },\n    delWrappedLineLeft: function(cm) {\n      deleteNearSelection(cm, function(range) {\n        var top = cm.charCoords(range.head, \"div\").top + 5;\n        var leftPos = cm.coordsChar({left: 0, top: top}, \"div\");\n        return {from: leftPos, to: range.from()};\n      });\n    },\n    delWrappedLineRight: function(cm) {\n      deleteNearSelection(cm, function(range) {\n        var top = cm.charCoords(range.head, \"div\").top + 5;\n        var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\");\n        return {from: range.from(), to: rightPos };\n      });\n    },\n    undo: function(cm) {cm.undo();},\n    redo: function(cm) {cm.redo();},\n    undoSelection: function(cm) {cm.undoSelection();},\n    redoSelection: function(cm) {cm.redoSelection();},\n    goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},\n    goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},\n    goLineStart: function(cm) {\n      cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },\n                            {origin: \"+move\", bias: 1});\n    },\n    goLineStartSmart: function(cm) {\n      cm.extendSelectionsBy(function(range) {\n        return lineStartSmart(cm, range.head);\n      }, {origin: \"+move\", bias: 1});\n    },\n    goLineEnd: function(cm) {\n      cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },\n                            {origin: \"+move\", bias: -1});\n    },\n    goLineRight: function(cm) {\n      cm.extendSelectionsBy(function(range) {\n        var top = cm.charCoords(range.head, \"div\").top + 5;\n        return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\");\n      }, sel_move);\n    },\n    goLineLeft: function(cm) {\n      cm.extendSelectionsBy(function(range) {\n        var top = cm.charCoords(range.head, \"div\").top + 5;\n        return cm.coordsChar({left: 0, top: top}, \"div\");\n      }, sel_move);\n    },\n    goLineLeftSmart: function(cm) {\n      cm.extendSelectionsBy(function(range) {\n        var top = cm.charCoords(range.head, \"div\").top + 5;\n        var pos = cm.coordsChar({left: 0, top: top}, \"div\");\n        if (pos.ch < cm.getLine(pos.line).search(/\\S/)) return lineStartSmart(cm, range.head);\n        return pos;\n      }, sel_move);\n    },\n    goLineUp: function(cm) {cm.moveV(-1, \"line\");},\n    goLineDown: function(cm) {cm.moveV(1, \"line\");},\n    goPageUp: function(cm) {cm.moveV(-1, \"page\");},\n    goPageDown: function(cm) {cm.moveV(1, \"page\");},\n    goCharLeft: function(cm) {cm.moveH(-1, \"char\");},\n    goCharRight: function(cm) {cm.moveH(1, \"char\");},\n    goColumnLeft: function(cm) {cm.moveH(-1, \"column\");},\n    goColumnRight: function(cm) {cm.moveH(1, \"column\");},\n    goWordLeft: function(cm) {cm.moveH(-1, \"word\");},\n    goGroupRight: function(cm) {cm.moveH(1, \"group\");},\n    goGroupLeft: function(cm) {cm.moveH(-1, \"group\");},\n    goWordRight: function(cm) {cm.moveH(1, \"word\");},\n    delCharBefore: function(cm) {cm.deleteH(-1, \"char\");},\n    delCharAfter: function(cm) {cm.deleteH(1, \"char\");},\n    delWordBefore: function(cm) {cm.deleteH(-1, \"word\");},\n    delWordAfter: function(cm) {cm.deleteH(1, \"word\");},\n    delGroupBefore: function(cm) {cm.deleteH(-1, \"group\");},\n    delGroupAfter: function(cm) {cm.deleteH(1, \"group\");},\n    indentAuto: function(cm) {cm.indentSelection(\"smart\");},\n    indentMore: function(cm) {cm.indentSelection(\"add\");},\n    indentLess: function(cm) {cm.indentSelection(\"subtract\");},\n    insertTab: function(cm) {cm.replaceSelection(\"\\t\");},\n    insertSoftTab: function(cm) {\n      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;\n      for (var i = 0; i < ranges.length; i++) {\n        var pos = ranges[i].from();\n        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);\n        spaces.push(new Array(tabSize - col % tabSize + 1).join(\" \"));\n      }\n      cm.replaceSelections(spaces);\n    },\n    defaultTab: function(cm) {\n      if (cm.somethingSelected()) cm.indentSelection(\"add\");\n      else cm.execCommand(\"insertTab\");\n    },\n    transposeChars: function(cm) {\n      runInOp(cm, function() {\n        var ranges = cm.listSelections(), newSel = [];\n        for (var i = 0; i < ranges.length; i++) {\n          var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;\n          if (line) {\n            if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);\n            if (cur.ch > 0) {\n              cur = new Pos(cur.line, cur.ch + 1);\n              cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),\n                              Pos(cur.line, cur.ch - 2), cur, \"+transpose\");\n            } else if (cur.line > cm.doc.first) {\n              var prev = getLine(cm.doc, cur.line - 1).text;\n              if (prev)\n                cm.replaceRange(line.charAt(0) + \"\\n\" + prev.charAt(prev.length - 1),\n                                Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), \"+transpose\");\n            }\n          }\n          newSel.push(new Range(cur, cur));\n        }\n        cm.setSelections(newSel);\n      });\n    },\n    newlineAndIndent: function(cm) {\n      runInOp(cm, function() {\n        var len = cm.listSelections().length;\n        for (var i = 0; i < len; i++) {\n          var range = cm.listSelections()[i];\n          cm.replaceRange(\"\\n\", range.anchor, range.head, \"+input\");\n          cm.indentLine(range.from().line + 1, null, true);\n          ensureCursorVisible(cm);\n        }\n      });\n    },\n    toggleOverwrite: function(cm) {cm.toggleOverwrite();}\n  };\n\n\n  // STANDARD KEYMAPS\n\n  var keyMap = CodeMirror.keyMap = {};\n\n  keyMap.basic = {\n    \"Left\": \"goCharLeft\", \"Right\": \"goCharRight\", \"Up\": \"goLineUp\", \"Down\": \"goLineDown\",\n    \"End\": \"goLineEnd\", \"Home\": \"goLineStartSmart\", \"PageUp\": \"goPageUp\", \"PageDown\": \"goPageDown\",\n    \"Delete\": \"delCharAfter\", \"Backspace\": \"delCharBefore\", \"Shift-Backspace\": \"delCharBefore\",\n    \"Tab\": \"defaultTab\", \"Shift-Tab\": \"indentAuto\",\n    \"Enter\": \"newlineAndIndent\", \"Insert\": \"toggleOverwrite\",\n    \"Esc\": \"singleSelection\"\n  };\n  // Note that the save and find-related commands aren't defined by\n  // default. User code or addons can define them. Unknown commands\n  // are simply ignored.\n  keyMap.pcDefault = {\n    \"Ctrl-A\": \"selectAll\", \"Ctrl-D\": \"deleteLine\", \"Ctrl-Z\": \"undo\", \"Shift-Ctrl-Z\": \"redo\", \"Ctrl-Y\": \"redo\",\n    \"Ctrl-Home\": \"goDocStart\", \"Ctrl-End\": \"goDocEnd\", \"Ctrl-Up\": \"goLineUp\", \"Ctrl-Down\": \"goLineDown\",\n    \"Ctrl-Left\": \"goGroupLeft\", \"Ctrl-Right\": \"goGroupRight\", \"Alt-Left\": \"goLineStart\", \"Alt-Right\": \"goLineEnd\",\n    \"Ctrl-Backspace\": \"delGroupBefore\", \"Ctrl-Delete\": \"delGroupAfter\", \"Ctrl-S\": \"save\", \"Ctrl-F\": \"find\",\n    \"Ctrl-G\": \"findNext\", \"Shift-Ctrl-G\": \"findPrev\", \"Shift-Ctrl-F\": \"replace\", \"Shift-Ctrl-R\": \"replaceAll\",\n    \"Ctrl-[\": \"indentLess\", \"Ctrl-]\": \"indentMore\",\n    \"Ctrl-U\": \"undoSelection\", \"Shift-Ctrl-U\": \"redoSelection\", \"Alt-U\": \"redoSelection\",\n    fallthrough: \"basic\"\n  };\n  // Very basic readline/emacs-style bindings, which are standard on Mac.\n  keyMap.emacsy = {\n    \"Ctrl-F\": \"goCharRight\", \"Ctrl-B\": \"goCharLeft\", \"Ctrl-P\": \"goLineUp\", \"Ctrl-N\": \"goLineDown\",\n    \"Alt-F\": \"goWordRight\", \"Alt-B\": \"goWordLeft\", \"Ctrl-A\": \"goLineStart\", \"Ctrl-E\": \"goLineEnd\",\n    \"Ctrl-V\": \"goPageDown\", \"Shift-Ctrl-V\": \"goPageUp\", \"Ctrl-D\": \"delCharAfter\", \"Ctrl-H\": \"delCharBefore\",\n    \"Alt-D\": \"delWordAfter\", \"Alt-Backspace\": \"delWordBefore\", \"Ctrl-K\": \"killLine\", \"Ctrl-T\": \"transposeChars\"\n  };\n  keyMap.macDefault = {\n    \"Cmd-A\": \"selectAll\", \"Cmd-D\": \"deleteLine\", \"Cmd-Z\": \"undo\", \"Shift-Cmd-Z\": \"redo\", \"Cmd-Y\": \"redo\",\n    \"Cmd-Home\": \"goDocStart\", \"Cmd-Up\": \"goDocStart\", \"Cmd-End\": \"goDocEnd\", \"Cmd-Down\": \"goDocEnd\", \"Alt-Left\": \"goGroupLeft\",\n    \"Alt-Right\": \"goGroupRight\", \"Cmd-Left\": \"goLineLeft\", \"Cmd-Right\": \"goLineRight\", \"Alt-Backspace\": \"delGroupBefore\",\n    \"Ctrl-Alt-Backspace\": \"delGroupAfter\", \"Alt-Delete\": \"delGroupAfter\", \"Cmd-S\": \"save\", \"Cmd-F\": \"find\",\n    \"Cmd-G\": \"findNext\", \"Shift-Cmd-G\": \"findPrev\", \"Cmd-Alt-F\": \"replace\", \"Shift-Cmd-Alt-F\": \"replaceAll\",\n    \"Cmd-[\": \"indentLess\", \"Cmd-]\": \"indentMore\", \"Cmd-Backspace\": \"delWrappedLineLeft\", \"Cmd-Delete\": \"delWrappedLineRight\",\n    \"Cmd-U\": \"undoSelection\", \"Shift-Cmd-U\": \"redoSelection\", \"Ctrl-Up\": \"goDocStart\", \"Ctrl-Down\": \"goDocEnd\",\n    fallthrough: [\"basic\", \"emacsy\"]\n  };\n  keyMap[\"default\"] = mac ? keyMap.macDefault : keyMap.pcDefault;\n\n  // KEYMAP DISPATCH\n\n  function normalizeKeyName(name) {\n    var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];\n    var alt, ctrl, shift, cmd;\n    for (var i = 0; i < parts.length - 1; i++) {\n      var mod = parts[i];\n      if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;\n      else if (/^a(lt)?$/i.test(mod)) alt = true;\n      else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;\n      else if (/^s(hift)$/i.test(mod)) shift = true;\n      else throw new Error(\"Unrecognized modifier name: \" + mod);\n    }\n    if (alt) name = \"Alt-\" + name;\n    if (ctrl) name = \"Ctrl-\" + name;\n    if (cmd) name = \"Cmd-\" + name;\n    if (shift) name = \"Shift-\" + name;\n    return name;\n  }\n\n  // This is a kludge to keep keymaps mostly working as raw objects\n  // (backwards compatibility) while at the same time support features\n  // like normalization and multi-stroke key bindings. It compiles a\n  // new normalized keymap, and then updates the old object to reflect\n  // this.\n  CodeMirror.normalizeKeyMap = function(keymap) {\n    var copy = {};\n    for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {\n      var value = keymap[keyname];\n      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;\n      if (value == \"...\") { delete keymap[keyname]; continue; }\n\n      var keys = map(keyname.split(\" \"), normalizeKeyName);\n      for (var i = 0; i < keys.length; i++) {\n        var val, name;\n        if (i == keys.length - 1) {\n          name = keys.join(\" \");\n          val = value;\n        } else {\n          name = keys.slice(0, i + 1).join(\" \");\n          val = \"...\";\n        }\n        var prev = copy[name];\n        if (!prev) copy[name] = val;\n        else if (prev != val) throw new Error(\"Inconsistent bindings for \" + name);\n      }\n      delete keymap[keyname];\n    }\n    for (var prop in copy) keymap[prop] = copy[prop];\n    return keymap;\n  };\n\n  var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {\n    map = getKeyMap(map);\n    var found = map.call ? map.call(key, context) : map[key];\n    if (found === false) return \"nothing\";\n    if (found === \"...\") return \"multi\";\n    if (found != null && handle(found)) return \"handled\";\n\n    if (map.fallthrough) {\n      if (Object.prototype.toString.call(map.fallthrough) != \"[object Array]\")\n        return lookupKey(key, map.fallthrough, handle, context);\n      for (var i = 0; i < map.fallthrough.length; i++) {\n        var result = lookupKey(key, map.fallthrough[i], handle, context);\n        if (result) return result;\n      }\n    }\n  };\n\n  // Modifier key presses don't count as 'real' key presses for the\n  // purpose of keymap fallthrough.\n  var isModifierKey = CodeMirror.isModifierKey = function(value) {\n    var name = typeof value == \"string\" ? value : keyNames[value.keyCode];\n    return name == \"Ctrl\" || name == \"Alt\" || name == \"Shift\" || name == \"Mod\";\n  };\n\n  // Look up the name of a key as indicated by an event object.\n  var keyName = CodeMirror.keyName = function(event, noShift) {\n    if (presto && event.keyCode == 34 && event[\"char\"]) return false;\n    var base = keyNames[event.keyCode], name = base;\n    if (name == null || event.altGraphKey) return false;\n    if (event.altKey && base != \"Alt\") name = \"Alt-\" + name;\n    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != \"Ctrl\") name = \"Ctrl-\" + name;\n    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != \"Cmd\") name = \"Cmd-\" + name;\n    if (!noShift && event.shiftKey && base != \"Shift\") name = \"Shift-\" + name;\n    return name;\n  };\n\n  function getKeyMap(val) {\n    return typeof val == \"string\" ? keyMap[val] : val;\n  }\n\n  // FROMTEXTAREA\n\n  CodeMirror.fromTextArea = function(textarea, options) {\n    options = options ? copyObj(options) : {};\n    options.value = textarea.value;\n    if (!options.tabindex && textarea.tabIndex)\n      options.tabindex = textarea.tabIndex;\n    if (!options.placeholder && textarea.placeholder)\n      options.placeholder = textarea.placeholder;\n    // Set autofocus to true if this textarea is focused, or if it has\n    // autofocus and no other element is focused.\n    if (options.autofocus == null) {\n      var hasFocus = activeElt();\n      options.autofocus = hasFocus == textarea ||\n        textarea.getAttribute(\"autofocus\") != null && hasFocus == document.body;\n    }\n\n    function save() {textarea.value = cm.getValue();}\n    if (textarea.form) {\n      on(textarea.form, \"submit\", save);\n      // Deplorable hack to make the submit method do the right thing.\n      if (!options.leaveSubmitMethodAlone) {\n        var form = textarea.form, realSubmit = form.submit;\n        try {\n          var wrappedSubmit = form.submit = function() {\n            save();\n            form.submit = realSubmit;\n            form.submit();\n            form.submit = wrappedSubmit;\n          };\n        } catch(e) {}\n      }\n    }\n\n    options.finishInit = function(cm) {\n      cm.save = save;\n      cm.getTextArea = function() { return textarea; };\n      cm.toTextArea = function() {\n        cm.toTextArea = isNaN; // Prevent this from being ran twice\n        save();\n        textarea.parentNode.removeChild(cm.getWrapperElement());\n        textarea.style.display = \"\";\n        if (textarea.form) {\n          off(textarea.form, \"submit\", save);\n          if (typeof textarea.form.submit == \"function\")\n            textarea.form.submit = realSubmit;\n        }\n      };\n    };\n\n    textarea.style.display = \"none\";\n    var cm = CodeMirror(function(node) {\n      textarea.parentNode.insertBefore(node, textarea.nextSibling);\n    }, options);\n    return cm;\n  };\n\n  // STRING STREAM\n\n  // Fed to the mode parsers, provides helper functions to make\n  // parsers more succinct.\n\n  var StringStream = CodeMirror.StringStream = function(string, tabSize) {\n    this.pos = this.start = 0;\n    this.string = string;\n    this.tabSize = tabSize || 8;\n    this.lastColumnPos = this.lastColumnValue = 0;\n    this.lineStart = 0;\n  };\n\n  StringStream.prototype = {\n    eol: function() {return this.pos >= this.string.length;},\n    sol: function() {return this.pos == this.lineStart;},\n    peek: function() {return this.string.charAt(this.pos) || undefined;},\n    next: function() {\n      if (this.pos < this.string.length)\n        return this.string.charAt(this.pos++);\n    },\n    eat: function(match) {\n      var ch = this.string.charAt(this.pos);\n      if (typeof match == \"string\") var ok = ch == match;\n      else var ok = ch && (match.test ? match.test(ch) : match(ch));\n      if (ok) {++this.pos; return ch;}\n    },\n    eatWhile: function(match) {\n      var start = this.pos;\n      while (this.eat(match)){}\n      return this.pos > start;\n    },\n    eatSpace: function() {\n      var start = this.pos;\n      while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;\n      return this.pos > start;\n    },\n    skipToEnd: function() {this.pos = this.string.length;},\n    skipTo: function(ch) {\n      var found = this.string.indexOf(ch, this.pos);\n      if (found > -1) {this.pos = found; return true;}\n    },\n    backUp: function(n) {this.pos -= n;},\n    column: function() {\n      if (this.lastColumnPos < this.start) {\n        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);\n        this.lastColumnPos = this.start;\n      }\n      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);\n    },\n    indentation: function() {\n      return countColumn(this.string, null, this.tabSize) -\n        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);\n    },\n    match: function(pattern, consume, caseInsensitive) {\n      if (typeof pattern == \"string\") {\n        var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};\n        var substr = this.string.substr(this.pos, pattern.length);\n        if (cased(substr) == cased(pattern)) {\n          if (consume !== false) this.pos += pattern.length;\n          return true;\n        }\n      } else {\n        var match = this.string.slice(this.pos).match(pattern);\n        if (match && match.index > 0) return null;\n        if (match && consume !== false) this.pos += match[0].length;\n        return match;\n      }\n    },\n    current: function(){return this.string.slice(this.start, this.pos);},\n    hideFirstChars: function(n, inner) {\n      this.lineStart += n;\n      try { return inner(); }\n      finally { this.lineStart -= n; }\n    }\n  };\n\n  // TEXTMARKERS\n\n  // Created with markText and setBookmark methods. A TextMarker is a\n  // handle that can be used to clear or find a marked position in the\n  // document. Line objects hold arrays (markedSpans) containing\n  // {from, to, marker} object pointing to such marker objects, and\n  // indicating that such a marker is present on that line. Multiple\n  // lines may point to the same marker when it spans across lines.\n  // The spans will have null for their from/to properties when the\n  // marker continues beyond the start/end of the line. Markers have\n  // links back to the lines they currently touch.\n\n  var nextMarkerId = 0;\n\n  var TextMarker = CodeMirror.TextMarker = function(doc, type) {\n    this.lines = [];\n    this.type = type;\n    this.doc = doc;\n    this.id = ++nextMarkerId;\n  };\n  eventMixin(TextMarker);\n\n  // Clear the marker.\n  TextMarker.prototype.clear = function() {\n    if (this.explicitlyCleared) return;\n    var cm = this.doc.cm, withOp = cm && !cm.curOp;\n    if (withOp) startOperation(cm);\n    if (hasHandler(this, \"clear\")) {\n      var found = this.find();\n      if (found) signalLater(this, \"clear\", found.from, found.to);\n    }\n    var min = null, max = null;\n    for (var i = 0; i < this.lines.length; ++i) {\n      var line = this.lines[i];\n      var span = getMarkedSpanFor(line.markedSpans, this);\n      if (cm && !this.collapsed) regLineChange(cm, lineNo(line), \"text\");\n      else if (cm) {\n        if (span.to != null) max = lineNo(line);\n        if (span.from != null) min = lineNo(line);\n      }\n      line.markedSpans = removeMarkedSpan(line.markedSpans, span);\n      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)\n        updateLineHeight(line, textHeight(cm.display));\n    }\n    if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {\n      var visual = visualLine(this.lines[i]), len = lineLength(visual);\n      if (len > cm.display.maxLineLength) {\n        cm.display.maxLine = visual;\n        cm.display.maxLineLength = len;\n        cm.display.maxLineChanged = true;\n      }\n    }\n\n    if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);\n    this.lines.length = 0;\n    this.explicitlyCleared = true;\n    if (this.atomic && this.doc.cantEdit) {\n      this.doc.cantEdit = false;\n      if (cm) reCheckSelection(cm.doc);\n    }\n    if (cm) signalLater(cm, \"markerCleared\", cm, this);\n    if (withOp) endOperation(cm);\n    if (this.parent) this.parent.clear();\n  };\n\n  // Find the position of the marker in the document. Returns a {from,\n  // to} object by default. Side can be passed to get a specific side\n  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the\n  // Pos objects returned contain a line object, rather than a line\n  // number (used to prevent looking up the same line twice).\n  TextMarker.prototype.find = function(side, lineObj) {\n    if (side == null && this.type == \"bookmark\") side = 1;\n    var from, to;\n    for (var i = 0; i < this.lines.length; ++i) {\n      var line = this.lines[i];\n      var span = getMarkedSpanFor(line.markedSpans, this);\n      if (span.from != null) {\n        from = Pos(lineObj ? line : lineNo(line), span.from);\n        if (side == -1) return from;\n      }\n      if (span.to != null) {\n        to = Pos(lineObj ? line : lineNo(line), span.to);\n        if (side == 1) return to;\n      }\n    }\n    return from && {from: from, to: to};\n  };\n\n  // Signals that the marker's widget changed, and surrounding layout\n  // should be recomputed.\n  TextMarker.prototype.changed = function() {\n    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;\n    if (!pos || !cm) return;\n    runInOp(cm, function() {\n      var line = pos.line, lineN = lineNo(pos.line);\n      var view = findViewForLine(cm, lineN);\n      if (view) {\n        clearLineMeasurementCacheFor(view);\n        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;\n      }\n      cm.curOp.updateMaxLine = true;\n      if (!lineIsHidden(widget.doc, line) && widget.height != null) {\n        var oldHeight = widget.height;\n        widget.height = null;\n        var dHeight = widgetHeight(widget) - oldHeight;\n        if (dHeight)\n          updateLineHeight(line, line.height + dHeight);\n      }\n    });\n  };\n\n  TextMarker.prototype.attachLine = function(line) {\n    if (!this.lines.length && this.doc.cm) {\n      var op = this.doc.cm.curOp;\n      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)\n        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);\n    }\n    this.lines.push(line);\n  };\n  TextMarker.prototype.detachLine = function(line) {\n    this.lines.splice(indexOf(this.lines, line), 1);\n    if (!this.lines.length && this.doc.cm) {\n      var op = this.doc.cm.curOp;\n      (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);\n    }\n  };\n\n  // Collapsed markers have unique ids, in order to be able to order\n  // them, which is needed for uniquely determining an outer marker\n  // when they overlap (they may nest, but not partially overlap).\n  var nextMarkerId = 0;\n\n  // Create a marker, wire it up to the right lines, and\n  function markText(doc, from, to, options, type) {\n    // Shared markers (across linked documents) are handled separately\n    // (markTextShared will call out to this again, once per\n    // document).\n    if (options && options.shared) return markTextShared(doc, from, to, options, type);\n    // Ensure we are in an operation.\n    if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);\n\n    var marker = new TextMarker(doc, type), diff = cmp(from, to);\n    if (options) copyObj(options, marker, false);\n    // Don't connect empty markers unless clearWhenEmpty is false\n    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)\n      return marker;\n    if (marker.replacedWith) {\n      // Showing up as a widget implies collapsed (widget replaces text)\n      marker.collapsed = true;\n      marker.widgetNode = elt(\"span\", [marker.replacedWith], \"CodeMirror-widget\");\n      if (!options.handleMouseEvents) marker.widgetNode.setAttribute(\"cm-ignore-events\", \"true\");\n      if (options.insertLeft) marker.widgetNode.insertLeft = true;\n    }\n    if (marker.collapsed) {\n      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||\n          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))\n        throw new Error(\"Inserting collapsed marker partially overlapping an existing one\");\n      sawCollapsedSpans = true;\n    }\n\n    if (marker.addToHistory)\n      addChangeToHistory(doc, {from: from, to: to, origin: \"markText\"}, doc.sel, NaN);\n\n    var curLine = from.line, cm = doc.cm, updateMaxLine;\n    doc.iter(curLine, to.line + 1, function(line) {\n      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)\n        updateMaxLine = true;\n      if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);\n      addMarkedSpan(line, new MarkedSpan(marker,\n                                         curLine == from.line ? from.ch : null,\n                                         curLine == to.line ? to.ch : null));\n      ++curLine;\n    });\n    // lineIsHidden depends on the presence of the spans, so needs a second pass\n    if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {\n      if (lineIsHidden(doc, line)) updateLineHeight(line, 0);\n    });\n\n    if (marker.clearOnEnter) on(marker, \"beforeCursorEnter\", function() { marker.clear(); });\n\n    if (marker.readOnly) {\n      sawReadOnlySpans = true;\n      if (doc.history.done.length || doc.history.undone.length)\n        doc.clearHistory();\n    }\n    if (marker.collapsed) {\n      marker.id = ++nextMarkerId;\n      marker.atomic = true;\n    }\n    if (cm) {\n      // Sync editor state\n      if (updateMaxLine) cm.curOp.updateMaxLine = true;\n      if (marker.collapsed)\n        regChange(cm, from.line, to.line + 1);\n      else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)\n        for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, \"text\");\n      if (marker.atomic) reCheckSelection(cm.doc);\n      signalLater(cm, \"markerAdded\", cm, marker);\n    }\n    return marker;\n  }\n\n  // SHARED TEXTMARKERS\n\n  // A shared marker spans multiple linked documents. It is\n  // implemented as a meta-marker-object controlling multiple normal\n  // markers.\n  var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {\n    this.markers = markers;\n    this.primary = primary;\n    for (var i = 0; i < markers.length; ++i)\n      markers[i].parent = this;\n  };\n  eventMixin(SharedTextMarker);\n\n  SharedTextMarker.prototype.clear = function() {\n    if (this.explicitlyCleared) return;\n    this.explicitlyCleared = true;\n    for (var i = 0; i < this.markers.length; ++i)\n      this.markers[i].clear();\n    signalLater(this, \"clear\");\n  };\n  SharedTextMarker.prototype.find = function(side, lineObj) {\n    return this.primary.find(side, lineObj);\n  };\n\n  function markTextShared(doc, from, to, options, type) {\n    options = copyObj(options);\n    options.shared = false;\n    var markers = [markText(doc, from, to, options, type)], primary = markers[0];\n    var widget = options.widgetNode;\n    linkedDocs(doc, function(doc) {\n      if (widget) options.widgetNode = widget.cloneNode(true);\n      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));\n      for (var i = 0; i < doc.linked.length; ++i)\n        if (doc.linked[i].isParent) return;\n      primary = lst(markers);\n    });\n    return new SharedTextMarker(markers, primary);\n  }\n\n  function findSharedMarkers(doc) {\n    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),\n                         function(m) { return m.parent; });\n  }\n\n  function copySharedMarkers(doc, markers) {\n    for (var i = 0; i < markers.length; i++) {\n      var marker = markers[i], pos = marker.find();\n      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);\n      if (cmp(mFrom, mTo)) {\n        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);\n        marker.markers.push(subMark);\n        subMark.parent = marker;\n      }\n    }\n  }\n\n  function detachSharedMarkers(markers) {\n    for (var i = 0; i < markers.length; i++) {\n      var marker = markers[i], linked = [marker.primary.doc];;\n      linkedDocs(marker.primary.doc, function(d) { linked.push(d); });\n      for (var j = 0; j < marker.markers.length; j++) {\n        var subMarker = marker.markers[j];\n        if (indexOf(linked, subMarker.doc) == -1) {\n          subMarker.parent = null;\n          marker.markers.splice(j--, 1);\n        }\n      }\n    }\n  }\n\n  // TEXTMARKER SPANS\n\n  function MarkedSpan(marker, from, to) {\n    this.marker = marker;\n    this.from = from; this.to = to;\n  }\n\n  // Search an array of spans for a span matching the given marker.\n  function getMarkedSpanFor(spans, marker) {\n    if (spans) for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i];\n      if (span.marker == marker) return span;\n    }\n  }\n  // Remove a span from an array, returning undefined if no spans are\n  // left (we don't store arrays for lines without spans).\n  function removeMarkedSpan(spans, span) {\n    for (var r, i = 0; i < spans.length; ++i)\n      if (spans[i] != span) (r || (r = [])).push(spans[i]);\n    return r;\n  }\n  // Add a span to a line.\n  function addMarkedSpan(line, span) {\n    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];\n    span.marker.attachLine(line);\n  }\n\n  // Used for the algorithm that adjusts markers for a change in the\n  // document. These functions cut an array of spans at a given\n  // character position, returning an array of remaining chunks (or\n  // undefined if nothing remains).\n  function markedSpansBefore(old, startCh, isInsert) {\n    if (old) for (var i = 0, nw; i < old.length; ++i) {\n      var span = old[i], marker = span.marker;\n      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);\n      if (startsBefore || span.from == startCh && marker.type == \"bookmark\" && (!isInsert || !span.marker.insertLeft)) {\n        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);\n        (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));\n      }\n    }\n    return nw;\n  }\n  function markedSpansAfter(old, endCh, isInsert) {\n    if (old) for (var i = 0, nw; i < old.length; ++i) {\n      var span = old[i], marker = span.marker;\n      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);\n      if (endsAfter || span.from == endCh && marker.type == \"bookmark\" && (!isInsert || span.marker.insertLeft)) {\n        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);\n        (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,\n                                              span.to == null ? null : span.to - endCh));\n      }\n    }\n    return nw;\n  }\n\n  // Given a change object, compute the new set of marker spans that\n  // cover the line in which the change took place. Removes spans\n  // entirely within the change, reconnects spans belonging to the\n  // same marker that appear on both sides of the change, and cuts off\n  // spans partially within the change. Returns an array of span\n  // arrays with one element for each line in (after) the change.\n  function stretchSpansOverChange(doc, change) {\n    if (change.full) return null;\n    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;\n    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;\n    if (!oldFirst && !oldLast) return null;\n\n    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;\n    // Get the spans that 'stick out' on both sides\n    var first = markedSpansBefore(oldFirst, startCh, isInsert);\n    var last = markedSpansAfter(oldLast, endCh, isInsert);\n\n    // Next, merge those two ends\n    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);\n    if (first) {\n      // Fix up .to properties of first\n      for (var i = 0; i < first.length; ++i) {\n        var span = first[i];\n        if (span.to == null) {\n          var found = getMarkedSpanFor(last, span.marker);\n          if (!found) span.to = startCh;\n          else if (sameLine) span.to = found.to == null ? null : found.to + offset;\n        }\n      }\n    }\n    if (last) {\n      // Fix up .from in last (or move them into first in case of sameLine)\n      for (var i = 0; i < last.length; ++i) {\n        var span = last[i];\n        if (span.to != null) span.to += offset;\n        if (span.from == null) {\n          var found = getMarkedSpanFor(first, span.marker);\n          if (!found) {\n            span.from = offset;\n            if (sameLine) (first || (first = [])).push(span);\n          }\n        } else {\n          span.from += offset;\n          if (sameLine) (first || (first = [])).push(span);\n        }\n      }\n    }\n    // Make sure we didn't create any zero-length spans\n    if (first) first = clearEmptySpans(first);\n    if (last && last != first) last = clearEmptySpans(last);\n\n    var newMarkers = [first];\n    if (!sameLine) {\n      // Fill gap with whole-line-spans\n      var gap = change.text.length - 2, gapMarkers;\n      if (gap > 0 && first)\n        for (var i = 0; i < first.length; ++i)\n          if (first[i].to == null)\n            (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));\n      for (var i = 0; i < gap; ++i)\n        newMarkers.push(gapMarkers);\n      newMarkers.push(last);\n    }\n    return newMarkers;\n  }\n\n  // Remove spans that are empty and don't have a clearWhenEmpty\n  // option of false.\n  function clearEmptySpans(spans) {\n    for (var i = 0; i < spans.length; ++i) {\n      var span = spans[i];\n      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)\n        spans.splice(i--, 1);\n    }\n    if (!spans.length) return null;\n    return spans;\n  }\n\n  // Used for un/re-doing changes from the history. Combines the\n  // result of computing the existing spans with the set of spans that\n  // existed in the history (so that deleting around a span and then\n  // undoing brings back the span).\n  function mergeOldSpans(doc, change) {\n    var old = getOldSpans(doc, change);\n    var stretched = stretchSpansOverChange(doc, change);\n    if (!old) return stretched;\n    if (!stretched) return old;\n\n    for (var i = 0; i < old.length; ++i) {\n      var oldCur = old[i], stretchCur = stretched[i];\n      if (oldCur && stretchCur) {\n        spans: for (var j = 0; j < stretchCur.length; ++j) {\n          var span = stretchCur[j];\n          for (var k = 0; k < oldCur.length; ++k)\n            if (oldCur[k].marker == span.marker) continue spans;\n          oldCur.push(span);\n        }\n      } else if (stretchCur) {\n        old[i] = stretchCur;\n      }\n    }\n    return old;\n  }\n\n  // Used to 'clip' out readOnly ranges when making a change.\n  function removeReadOnlyRanges(doc, from, to) {\n    var markers = null;\n    doc.iter(from.line, to.line + 1, function(line) {\n      if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {\n        var mark = line.markedSpans[i].marker;\n        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))\n          (markers || (markers = [])).push(mark);\n      }\n    });\n    if (!markers) return null;\n    var parts = [{from: from, to: to}];\n    for (var i = 0; i < markers.length; ++i) {\n      var mk = markers[i], m = mk.find(0);\n      for (var j = 0; j < parts.length; ++j) {\n        var p = parts[j];\n        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;\n        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);\n        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)\n          newParts.push({from: p.from, to: m.from});\n        if (dto > 0 || !mk.inclusiveRight && !dto)\n          newParts.push({from: m.to, to: p.to});\n        parts.splice.apply(parts, newParts);\n        j += newParts.length - 1;\n      }\n    }\n    return parts;\n  }\n\n  // Connect or disconnect spans from a line.\n  function detachMarkedSpans(line) {\n    var spans = line.markedSpans;\n    if (!spans) return;\n    for (var i = 0; i < spans.length; ++i)\n      spans[i].marker.detachLine(line);\n    line.markedSpans = null;\n  }\n  function attachMarkedSpans(line, spans) {\n    if (!spans) return;\n    for (var i = 0; i < spans.length; ++i)\n      spans[i].marker.attachLine(line);\n    line.markedSpans = spans;\n  }\n\n  // Helpers used when computing which overlapping collapsed span\n  // counts as the larger one.\n  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }\n  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }\n\n  // Returns a number indicating which of two overlapping collapsed\n  // spans is larger (and thus includes the other). Falls back to\n  // comparing ids when the spans cover exactly the same range.\n  function compareCollapsedMarkers(a, b) {\n    var lenDiff = a.lines.length - b.lines.length;\n    if (lenDiff != 0) return lenDiff;\n    var aPos = a.find(), bPos = b.find();\n    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);\n    if (fromCmp) return -fromCmp;\n    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);\n    if (toCmp) return toCmp;\n    return b.id - a.id;\n  }\n\n  // Find out whether a line ends or starts in a collapsed span. If\n  // so, return the marker for that span.\n  function collapsedSpanAtSide(line, start) {\n    var sps = sawCollapsedSpans && line.markedSpans, found;\n    if (sps) for (var sp, i = 0; i < sps.length; ++i) {\n      sp = sps[i];\n      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&\n          (!found || compareCollapsedMarkers(found, sp.marker) < 0))\n        found = sp.marker;\n    }\n    return found;\n  }\n  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }\n  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }\n\n  // Test whether there exists a collapsed span that partially\n  // overlaps (covers the start or end, but not both) of a new span.\n  // Such overlap is not allowed.\n  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {\n    var line = getLine(doc, lineNo);\n    var sps = sawCollapsedSpans && line.markedSpans;\n    if (sps) for (var i = 0; i < sps.length; ++i) {\n      var sp = sps[i];\n      if (!sp.marker.collapsed) continue;\n      var found = sp.marker.find(0);\n      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);\n      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);\n      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;\n      if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||\n          fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))\n        return true;\n    }\n  }\n\n  // A visual line is a line as drawn on the screen. Folding, for\n  // example, can cause multiple logical lines to appear on the same\n  // visual line. This finds the start of the visual line that the\n  // given line is part of (usually that is the line itself).\n  function visualLine(line) {\n    var merged;\n    while (merged = collapsedSpanAtStart(line))\n      line = merged.find(-1, true).line;\n    return line;\n  }\n\n  // Returns an array of logical lines that continue the visual line\n  // started by the argument, or undefined if there are no such lines.\n  function visualLineContinued(line) {\n    var merged, lines;\n    while (merged = collapsedSpanAtEnd(line)) {\n      line = merged.find(1, true).line;\n      (lines || (lines = [])).push(line);\n    }\n    return lines;\n  }\n\n  // Get the line number of the start of the visual line that the\n  // given line number is part of.\n  function visualLineNo(doc, lineN) {\n    var line = getLine(doc, lineN), vis = visualLine(line);\n    if (line == vis) return lineN;\n    return lineNo(vis);\n  }\n  // Get the line number of the start of the next visual line after\n  // the given line.\n  function visualLineEndNo(doc, lineN) {\n    if (lineN > doc.lastLine()) return lineN;\n    var line = getLine(doc, lineN), merged;\n    if (!lineIsHidden(doc, line)) return lineN;\n    while (merged = collapsedSpanAtEnd(line))\n      line = merged.find(1, true).line;\n    return lineNo(line) + 1;\n  }\n\n  // Compute whether a line is hidden. Lines count as hidden when they\n  // are part of a visual line that starts with another line, or when\n  // they are entirely covered by collapsed, non-widget span.\n  function lineIsHidden(doc, line) {\n    var sps = sawCollapsedSpans && line.markedSpans;\n    if (sps) for (var sp, i = 0; i < sps.length; ++i) {\n      sp = sps[i];\n      if (!sp.marker.collapsed) continue;\n      if (sp.from == null) return true;\n      if (sp.marker.widgetNode) continue;\n      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))\n        return true;\n    }\n  }\n  function lineIsHiddenInner(doc, line, span) {\n    if (span.to == null) {\n      var end = span.marker.find(1, true);\n      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));\n    }\n    if (span.marker.inclusiveRight && span.to == line.text.length)\n      return true;\n    for (var sp, i = 0; i < line.markedSpans.length; ++i) {\n      sp = line.markedSpans[i];\n      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&\n          (sp.to == null || sp.to != span.from) &&\n          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&\n          lineIsHiddenInner(doc, line, sp)) return true;\n    }\n  }\n\n  // LINE WIDGETS\n\n  // Line widgets are block elements displayed above or below a line.\n\n  var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {\n    if (options) for (var opt in options) if (options.hasOwnProperty(opt))\n      this[opt] = options[opt];\n    this.doc = doc;\n    this.node = node;\n  };\n  eventMixin(LineWidget);\n\n  function adjustScrollWhenAboveVisible(cm, line, diff) {\n    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))\n      addToScrollPos(cm, null, diff);\n  }\n\n  LineWidget.prototype.clear = function() {\n    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);\n    if (no == null || !ws) return;\n    for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);\n    if (!ws.length) line.widgets = null;\n    var height = widgetHeight(this);\n    updateLineHeight(line, Math.max(0, line.height - height));\n    if (cm) runInOp(cm, function() {\n      adjustScrollWhenAboveVisible(cm, line, -height);\n      regLineChange(cm, no, \"widget\");\n    });\n  };\n  LineWidget.prototype.changed = function() {\n    var oldH = this.height, cm = this.doc.cm, line = this.line;\n    this.height = null;\n    var diff = widgetHeight(this) - oldH;\n    if (!diff) return;\n    updateLineHeight(line, line.height + diff);\n    if (cm) runInOp(cm, function() {\n      cm.curOp.forceUpdate = true;\n      adjustScrollWhenAboveVisible(cm, line, diff);\n    });\n  };\n\n  function widgetHeight(widget) {\n    if (widget.height != null) return widget.height;\n    var cm = widget.doc.cm;\n    if (!cm) return 0;\n    if (!contains(document.body, widget.node)) {\n      var parentStyle = \"position: relative;\";\n      if (widget.coverGutter)\n        parentStyle += \"margin-left: -\" + cm.display.gutters.offsetWidth + \"px;\";\n      if (widget.noHScroll)\n        parentStyle += \"width: \" + cm.display.wrapper.clientWidth + \"px;\";\n      removeChildrenAndAdd(cm.display.measure, elt(\"div\", [widget.node], null, parentStyle));\n    }\n    return widget.height = widget.node.offsetHeight;\n  }\n\n  function addLineWidget(doc, handle, node, options) {\n    var widget = new LineWidget(doc, node, options);\n    var cm = doc.cm;\n    if (cm && widget.noHScroll) cm.display.alignWidgets = true;\n    changeLine(doc, handle, \"widget\", function(line) {\n      var widgets = line.widgets || (line.widgets = []);\n      if (widget.insertAt == null) widgets.push(widget);\n      else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);\n      widget.line = line;\n      if (cm && !lineIsHidden(doc, line)) {\n        var aboveVisible = heightAtLine(line) < doc.scrollTop;\n        updateLineHeight(line, line.height + widgetHeight(widget));\n        if (aboveVisible) addToScrollPos(cm, null, widget.height);\n        cm.curOp.forceUpdate = true;\n      }\n      return true;\n    });\n    return widget;\n  }\n\n  // LINE DATA STRUCTURE\n\n  // Line objects. These hold state related to a line, including\n  // highlighting info (the styles array).\n  var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {\n    this.text = text;\n    attachMarkedSpans(this, markedSpans);\n    this.height = estimateHeight ? estimateHeight(this) : 1;\n  };\n  eventMixin(Line);\n  Line.prototype.lineNo = function() { return lineNo(this); };\n\n  // Change the content (text, markers) of a line. Automatically\n  // invalidates cached information and tries to re-estimate the\n  // line's height.\n  function updateLine(line, text, markedSpans, estimateHeight) {\n    line.text = text;\n    if (line.stateAfter) line.stateAfter = null;\n    if (line.styles) line.styles = null;\n    if (line.order != null) line.order = null;\n    detachMarkedSpans(line);\n    attachMarkedSpans(line, markedSpans);\n    var estHeight = estimateHeight ? estimateHeight(line) : 1;\n    if (estHeight != line.height) updateLineHeight(line, estHeight);\n  }\n\n  // Detach a line from the document tree and its markers.\n  function cleanUpLine(line) {\n    line.parent = null;\n    detachMarkedSpans(line);\n  }\n\n  function extractLineClasses(type, output) {\n    if (type) for (;;) {\n      var lineClass = type.match(/(?:^|\\s+)line-(background-)?(\\S+)/);\n      if (!lineClass) break;\n      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);\n      var prop = lineClass[1] ? \"bgClass\" : \"textClass\";\n      if (output[prop] == null)\n        output[prop] = lineClass[2];\n      else if (!(new RegExp(\"(?:^|\\s)\" + lineClass[2] + \"(?:$|\\s)\")).test(output[prop]))\n        output[prop] += \" \" + lineClass[2];\n    }\n    return type;\n  }\n\n  function callBlankLine(mode, state) {\n    if (mode.blankLine) return mode.blankLine(state);\n    if (!mode.innerMode) return;\n    var inner = CodeMirror.innerMode(mode, state);\n    if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);\n  }\n\n  function readToken(mode, stream, state, inner) {\n    for (var i = 0; i < 10; i++) {\n      if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;\n      var style = mode.token(stream, state);\n      if (stream.pos > stream.start) return style;\n    }\n    throw new Error(\"Mode \" + mode.name + \" failed to advance stream.\");\n  }\n\n  // Utility for getTokenAt and getLineTokens\n  function takeToken(cm, pos, precise, asArray) {\n    function getObj(copy) {\n      return {start: stream.start, end: stream.pos,\n              string: stream.current(),\n              type: style || null,\n              state: copy ? copyState(doc.mode, state) : state};\n    }\n\n    var doc = cm.doc, mode = doc.mode, style;\n    pos = clipPos(doc, pos);\n    var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);\n    var stream = new StringStream(line.text, cm.options.tabSize), tokens;\n    if (asArray) tokens = [];\n    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {\n      stream.start = stream.pos;\n      style = readToken(mode, stream, state);\n      if (asArray) tokens.push(getObj(true));\n    }\n    return asArray ? tokens : getObj();\n  }\n\n  // Run the given mode's parser over a line, calling f for each token.\n  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {\n    var flattenSpans = mode.flattenSpans;\n    if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;\n    var curStart = 0, curStyle = null;\n    var stream = new StringStream(text, cm.options.tabSize), style;\n    var inner = cm.options.addModeClass && [null];\n    if (text == \"\") extractLineClasses(callBlankLine(mode, state), lineClasses);\n    while (!stream.eol()) {\n      if (stream.pos > cm.options.maxHighlightLength) {\n        flattenSpans = false;\n        if (forceToEnd) processLine(cm, text, state, stream.pos);\n        stream.pos = text.length;\n        style = null;\n      } else {\n        style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);\n      }\n      if (inner) {\n        var mName = inner[0].name;\n        if (mName) style = \"m-\" + (style ? mName + \" \" + style : mName);\n      }\n      if (!flattenSpans || curStyle != style) {\n        while (curStart < stream.start) {\n          curStart = Math.min(stream.start, curStart + 50000);\n          f(curStart, curStyle);\n        }\n        curStyle = style;\n      }\n      stream.start = stream.pos;\n    }\n    while (curStart < stream.pos) {\n      // Webkit seems to refuse to render text nodes longer than 57444 characters\n      var pos = Math.min(stream.pos, curStart + 50000);\n      f(pos, curStyle);\n      curStart = pos;\n    }\n  }\n\n  // Compute a style array (an array starting with a mode generation\n  // -- for invalidation -- followed by pairs of end positions and\n  // style strings), which is used to highlight the tokens on the\n  // line.\n  function highlightLine(cm, line, state, forceToEnd) {\n    // A styles array always starts with a number identifying the\n    // mode/overlays that it is based on (for easy invalidation).\n    var st = [cm.state.modeGen], lineClasses = {};\n    // Compute the base array of styles\n    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {\n      st.push(end, style);\n    }, lineClasses, forceToEnd);\n\n    // Run overlays, adjust style array.\n    for (var o = 0; o < cm.state.overlays.length; ++o) {\n      var overlay = cm.state.overlays[o], i = 1, at = 0;\n      runMode(cm, line.text, overlay.mode, true, function(end, style) {\n        var start = i;\n        // Ensure there's a token end at the current position, and that i points at it\n        while (at < end) {\n          var i_end = st[i];\n          if (i_end > end)\n            st.splice(i, 1, end, st[i+1], i_end);\n          i += 2;\n          at = Math.min(end, i_end);\n        }\n        if (!style) return;\n        if (overlay.opaque) {\n          st.splice(start, i - start, end, \"cm-overlay \" + style);\n          i = start + 2;\n        } else {\n          for (; start < i; start += 2) {\n            var cur = st[start+1];\n            st[start+1] = (cur ? cur + \" \" : \"\") + \"cm-overlay \" + style;\n          }\n        }\n      }, lineClasses);\n    }\n\n    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};\n  }\n\n  function getLineStyles(cm, line, updateFrontier) {\n    if (!line.styles || line.styles[0] != cm.state.modeGen) {\n      var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));\n      line.styles = result.styles;\n      if (result.classes) line.styleClasses = result.classes;\n      else if (line.styleClasses) line.styleClasses = null;\n      if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;\n    }\n    return line.styles;\n  }\n\n  // Lightweight form of highlight -- proceed over this line and\n  // update state, but don't save a style array. Used for lines that\n  // aren't currently visible.\n  function processLine(cm, text, state, startAt) {\n    var mode = cm.doc.mode;\n    var stream = new StringStream(text, cm.options.tabSize);\n    stream.start = stream.pos = startAt || 0;\n    if (text == \"\") callBlankLine(mode, state);\n    while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {\n      readToken(mode, stream, state);\n      stream.start = stream.pos;\n    }\n  }\n\n  // Convert a style as returned by a mode (either null, or a string\n  // containing one or more styles) to a CSS style. This is cached,\n  // and also looks for line-wide styles.\n  var styleToClassCache = {}, styleToClassCacheWithMode = {};\n  function interpretTokenStyle(style, options) {\n    if (!style || /^\\s*$/.test(style)) return null;\n    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;\n    return cache[style] ||\n      (cache[style] = style.replace(/\\S+/g, \"cm-$&\"));\n  }\n\n  // Render the DOM representation of the text of a line. Also builds\n  // up a 'line map', which points at the DOM nodes that represent\n  // specific stretches of text, and is used by the measuring code.\n  // The returned object contains the DOM node, this map, and\n  // information about line-wide styles that were set by the mode.\n  function buildLineContent(cm, lineView) {\n    // The padding-right forces the element to have a 'border', which\n    // is needed on Webkit to be able to get line-level bounding\n    // rectangles for it (in measureChar).\n    var content = elt(\"span\", null, null, webkit ? \"padding-right: .1px\" : null);\n    var builder = {pre: elt(\"pre\", [content]), content: content,\n                   col: 0, pos: 0, cm: cm,\n                   splitSpaces: (ie || webkit) && cm.getOption(\"lineWrapping\")};\n    lineView.measure = {};\n\n    // Iterate over the logical lines that make up this visual line.\n    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {\n      var line = i ? lineView.rest[i - 1] : lineView.line, order;\n      builder.pos = 0;\n      builder.addToken = buildToken;\n      // Optionally wire in some hacks into the token-rendering\n      // algorithm, to deal with browser quirks.\n      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))\n        builder.addToken = buildTokenBadBidi(builder.addToken, order);\n      builder.map = [];\n      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);\n      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));\n      if (line.styleClasses) {\n        if (line.styleClasses.bgClass)\n          builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || \"\");\n        if (line.styleClasses.textClass)\n          builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || \"\");\n      }\n\n      // Ensure at least a single node is present, for measuring.\n      if (builder.map.length == 0)\n        builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));\n\n      // Store the map and a cache object for the current logical line\n      if (i == 0) {\n        lineView.measure.map = builder.map;\n        lineView.measure.cache = {};\n      } else {\n        (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);\n        (lineView.measure.caches || (lineView.measure.caches = [])).push({});\n      }\n    }\n\n    // See issue #2901\n    if (webkit && /\\bcm-tab\\b/.test(builder.content.lastChild.className))\n      builder.content.className = \"cm-tab-wrap-hack\";\n\n    signal(cm, \"renderLine\", cm, lineView.line, builder.pre);\n    if (builder.pre.className)\n      builder.textClass = joinClasses(builder.pre.className, builder.textClass || \"\");\n\n    return builder;\n  }\n\n  function defaultSpecialCharPlaceholder(ch) {\n    var token = elt(\"span\", \"\\u2022\", \"cm-invalidchar\");\n    token.title = \"\\\\u\" + ch.charCodeAt(0).toString(16);\n    token.setAttribute(\"aria-label\", token.title);\n    return token;\n  }\n\n  // Build up the DOM representation for a single token, and add it to\n  // the line map. Takes care to render special characters separately.\n  function buildToken(builder, text, style, startStyle, endStyle, title, css) {\n    if (!text) return;\n    var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;\n    var special = builder.cm.state.specialChars, mustWrap = false;\n    if (!special.test(text)) {\n      builder.col += text.length;\n      var content = document.createTextNode(displayText);\n      builder.map.push(builder.pos, builder.pos + text.length, content);\n      if (ie && ie_version < 9) mustWrap = true;\n      builder.pos += text.length;\n    } else {\n      var content = document.createDocumentFragment(), pos = 0;\n      while (true) {\n        special.lastIndex = pos;\n        var m = special.exec(text);\n        var skipped = m ? m.index - pos : text.length - pos;\n        if (skipped) {\n          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));\n          if (ie && ie_version < 9) content.appendChild(elt(\"span\", [txt]));\n          else content.appendChild(txt);\n          builder.map.push(builder.pos, builder.pos + skipped, txt);\n          builder.col += skipped;\n          builder.pos += skipped;\n        }\n        if (!m) break;\n        pos += skipped + 1;\n        if (m[0] == \"\\t\") {\n          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;\n          var txt = content.appendChild(elt(\"span\", spaceStr(tabWidth), \"cm-tab\"));\n          txt.setAttribute(\"role\", \"presentation\");\n          txt.setAttribute(\"cm-text\", \"\\t\");\n          builder.col += tabWidth;\n        } else {\n          var txt = builder.cm.options.specialCharPlaceholder(m[0]);\n          txt.setAttribute(\"cm-text\", m[0]);\n          if (ie && ie_version < 9) content.appendChild(elt(\"span\", [txt]));\n          else content.appendChild(txt);\n          builder.col += 1;\n        }\n        builder.map.push(builder.pos, builder.pos + 1, txt);\n        builder.pos++;\n      }\n    }\n    if (style || startStyle || endStyle || mustWrap || css) {\n      var fullStyle = style || \"\";\n      if (startStyle) fullStyle += startStyle;\n      if (endStyle) fullStyle += endStyle;\n      var token = elt(\"span\", [content], fullStyle, css);\n      if (title) token.title = title;\n      return builder.content.appendChild(token);\n    }\n    builder.content.appendChild(content);\n  }\n\n  function splitSpaces(old) {\n    var out = \" \";\n    for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? \" \" : \"\\u00a0\";\n    out += \" \";\n    return out;\n  }\n\n  // Work around nonsense dimensions being reported for stretches of\n  // right-to-left text.\n  function buildTokenBadBidi(inner, order) {\n    return function(builder, text, style, startStyle, endStyle, title, css) {\n      style = style ? style + \" cm-force-border\" : \"cm-force-border\";\n      var start = builder.pos, end = start + text.length;\n      for (;;) {\n        // Find the part that overlaps with the start of this text\n        for (var i = 0; i < order.length; i++) {\n          var part = order[i];\n          if (part.to > start && part.from <= start) break;\n        }\n        if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);\n        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);\n        startStyle = null;\n        text = text.slice(part.to - start);\n        start = part.to;\n      }\n    };\n  }\n\n  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {\n    var widget = !ignoreWidget && marker.widgetNode;\n    if (widget) builder.map.push(builder.pos, builder.pos + size, widget);\n    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {\n      if (!widget)\n        widget = builder.content.appendChild(document.createElement(\"span\"));\n      widget.setAttribute(\"cm-marker\", marker.id);\n    }\n    if (widget) {\n      builder.cm.display.input.setUneditable(widget);\n      builder.content.appendChild(widget);\n    }\n    builder.pos += size;\n  }\n\n  // Outputs a number of spans to make up a line, taking highlighting\n  // and marked text into account.\n  function insertLineContent(line, builder, styles) {\n    var spans = line.markedSpans, allText = line.text, at = 0;\n    if (!spans) {\n      for (var i = 1; i < styles.length; i+=2)\n        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));\n      return;\n    }\n\n    var len = allText.length, pos = 0, i = 1, text = \"\", style, css;\n    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;\n    for (;;) {\n      if (nextChange == pos) { // Update current marker set\n        spanStyle = spanEndStyle = spanStartStyle = title = css = \"\";\n        collapsed = null; nextChange = Infinity;\n        var foundBookmarks = [];\n        for (var j = 0; j < spans.length; ++j) {\n          var sp = spans[j], m = sp.marker;\n          if (m.type == \"bookmark\" && sp.from == pos && m.widgetNode) {\n            foundBookmarks.push(m);\n          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {\n            if (sp.to != null && sp.to != pos && nextChange > sp.to) {\n              nextChange = sp.to;\n              spanEndStyle = \"\";\n            }\n            if (m.className) spanStyle += \" \" + m.className;\n            if (m.css) css = m.css;\n            if (m.startStyle && sp.from == pos) spanStartStyle += \" \" + m.startStyle;\n            if (m.endStyle && sp.to == nextChange) spanEndStyle += \" \" + m.endStyle;\n            if (m.title && !title) title = m.title;\n            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))\n              collapsed = sp;\n          } else if (sp.from > pos && nextChange > sp.from) {\n            nextChange = sp.from;\n          }\n        }\n        if (collapsed && (collapsed.from || 0) == pos) {\n          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,\n                             collapsed.marker, collapsed.from == null);\n          if (collapsed.to == null) return;\n          if (collapsed.to == pos) collapsed = false;\n        }\n        if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)\n          buildCollapsedSpan(builder, 0, foundBookmarks[j]);\n      }\n      if (pos >= len) break;\n\n      var upto = Math.min(len, nextChange);\n      while (true) {\n        if (text) {\n          var end = pos + text.length;\n          if (!collapsed) {\n            var tokenText = end > upto ? text.slice(0, upto - pos) : text;\n            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,\n                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : \"\", title, css);\n          }\n          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}\n          pos = end;\n          spanStartStyle = \"\";\n        }\n        text = allText.slice(at, at = styles[i++]);\n        style = interpretTokenStyle(styles[i++], builder.cm.options);\n      }\n    }\n  }\n\n  // DOCUMENT DATA STRUCTURE\n\n  // By default, updates that start and end at the beginning of a line\n  // are treated specially, in order to make the association of line\n  // widgets and marker elements with the text behave more intuitive.\n  function isWholeLineUpdate(doc, change) {\n    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == \"\" &&\n      (!doc.cm || doc.cm.options.wholeLineUpdateBefore);\n  }\n\n  // Perform a change on the document data structure.\n  function updateDoc(doc, change, markedSpans, estimateHeight) {\n    function spansFor(n) {return markedSpans ? markedSpans[n] : null;}\n    function update(line, text, spans) {\n      updateLine(line, text, spans, estimateHeight);\n      signalLater(line, \"change\", line, change);\n    }\n    function linesFor(start, end) {\n      for (var i = start, result = []; i < end; ++i)\n        result.push(new Line(text[i], spansFor(i), estimateHeight));\n      return result;\n    }\n\n    var from = change.from, to = change.to, text = change.text;\n    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);\n    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;\n\n    // Adjust the line structure\n    if (change.full) {\n      doc.insert(0, linesFor(0, text.length));\n      doc.remove(text.length, doc.size - text.length);\n    } else if (isWholeLineUpdate(doc, change)) {\n      // This is a whole-line replace. Treated specially to make\n      // sure line objects move the way they are supposed to.\n      var added = linesFor(0, text.length - 1);\n      update(lastLine, lastLine.text, lastSpans);\n      if (nlines) doc.remove(from.line, nlines);\n      if (added.length) doc.insert(from.line, added);\n    } else if (firstLine == lastLine) {\n      if (text.length == 1) {\n        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);\n      } else {\n        var added = linesFor(1, text.length - 1);\n        added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));\n        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n        doc.insert(from.line + 1, added);\n      }\n    } else if (text.length == 1) {\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));\n      doc.remove(from.line + 1, nlines);\n    } else {\n      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);\n      var added = linesFor(1, text.length - 1);\n      if (nlines > 1) doc.remove(from.line + 1, nlines - 1);\n      doc.insert(from.line + 1, added);\n    }\n\n    signalLater(doc, \"change\", doc, change);\n  }\n\n  // The document is represented as a BTree consisting of leaves, with\n  // chunk of lines in them, and branches, with up to ten leaves or\n  // other branch nodes below them. The top node is always a branch\n  // node, and is the document object itself (meaning it has\n  // additional methods and properties).\n  //\n  // All nodes have parent links. The tree is used both to go from\n  // line numbers to line objects, and to go from objects to numbers.\n  // It also indexes by height, and is used to convert between height\n  // and line object, and to find the total height of the document.\n  //\n  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html\n\n  function LeafChunk(lines) {\n    this.lines = lines;\n    this.parent = null;\n    for (var i = 0, height = 0; i < lines.length; ++i) {\n      lines[i].parent = this;\n      height += lines[i].height;\n    }\n    this.height = height;\n  }\n\n  LeafChunk.prototype = {\n    chunkSize: function() { return this.lines.length; },\n    // Remove the n lines at offset 'at'.\n    removeInner: function(at, n) {\n      for (var i = at, e = at + n; i < e; ++i) {\n        var line = this.lines[i];\n        this.height -= line.height;\n        cleanUpLine(line);\n        signalLater(line, \"delete\");\n      }\n      this.lines.splice(at, n);\n    },\n    // Helper used to collapse a small branch into a single leaf.\n    collapse: function(lines) {\n      lines.push.apply(lines, this.lines);\n    },\n    // Insert the given array of lines at offset 'at', count them as\n    // having the given height.\n    insertInner: function(at, lines, height) {\n      this.height += height;\n      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));\n      for (var i = 0; i < lines.length; ++i) lines[i].parent = this;\n    },\n    // Used to iterate over a part of the tree.\n    iterN: function(at, n, op) {\n      for (var e = at + n; at < e; ++at)\n        if (op(this.lines[at])) return true;\n    }\n  };\n\n  function BranchChunk(children) {\n    this.children = children;\n    var size = 0, height = 0;\n    for (var i = 0; i < children.length; ++i) {\n      var ch = children[i];\n      size += ch.chunkSize(); height += ch.height;\n      ch.parent = this;\n    }\n    this.size = size;\n    this.height = height;\n    this.parent = null;\n  }\n\n  BranchChunk.prototype = {\n    chunkSize: function() { return this.size; },\n    removeInner: function(at, n) {\n      this.size -= n;\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this.children[i], sz = child.chunkSize();\n        if (at < sz) {\n          var rm = Math.min(n, sz - at), oldHeight = child.height;\n          child.removeInner(at, rm);\n          this.height -= oldHeight - child.height;\n          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }\n          if ((n -= rm) == 0) break;\n          at = 0;\n        } else at -= sz;\n      }\n      // If the result is smaller than 25 lines, ensure that it is a\n      // single leaf node.\n      if (this.size - n < 25 &&\n          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {\n        var lines = [];\n        this.collapse(lines);\n        this.children = [new LeafChunk(lines)];\n        this.children[0].parent = this;\n      }\n    },\n    collapse: function(lines) {\n      for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);\n    },\n    insertInner: function(at, lines, height) {\n      this.size += lines.length;\n      this.height += height;\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this.children[i], sz = child.chunkSize();\n        if (at <= sz) {\n          child.insertInner(at, lines, height);\n          if (child.lines && child.lines.length > 50) {\n            while (child.lines.length > 50) {\n              var spilled = child.lines.splice(child.lines.length - 25, 25);\n              var newleaf = new LeafChunk(spilled);\n              child.height -= newleaf.height;\n              this.children.splice(i + 1, 0, newleaf);\n              newleaf.parent = this;\n            }\n            this.maybeSpill();\n          }\n          break;\n        }\n        at -= sz;\n      }\n    },\n    // When a node has grown, check whether it should be split.\n    maybeSpill: function() {\n      if (this.children.length <= 10) return;\n      var me = this;\n      do {\n        var spilled = me.children.splice(me.children.length - 5, 5);\n        var sibling = new BranchChunk(spilled);\n        if (!me.parent) { // Become the parent node\n          var copy = new BranchChunk(me.children);\n          copy.parent = me;\n          me.children = [copy, sibling];\n          me = copy;\n        } else {\n          me.size -= sibling.size;\n          me.height -= sibling.height;\n          var myIndex = indexOf(me.parent.children, me);\n          me.parent.children.splice(myIndex + 1, 0, sibling);\n        }\n        sibling.parent = me.parent;\n      } while (me.children.length > 10);\n      me.parent.maybeSpill();\n    },\n    iterN: function(at, n, op) {\n      for (var i = 0; i < this.children.length; ++i) {\n        var child = this.children[i], sz = child.chunkSize();\n        if (at < sz) {\n          var used = Math.min(n, sz - at);\n          if (child.iterN(at, used, op)) return true;\n          if ((n -= used) == 0) break;\n          at = 0;\n        } else at -= sz;\n      }\n    }\n  };\n\n  var nextDocId = 0;\n  var Doc = CodeMirror.Doc = function(text, mode, firstLine) {\n    if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);\n    if (firstLine == null) firstLine = 0;\n\n    BranchChunk.call(this, [new LeafChunk([new Line(\"\", null)])]);\n    this.first = firstLine;\n    this.scrollTop = this.scrollLeft = 0;\n    this.cantEdit = false;\n    this.cleanGeneration = 1;\n    this.frontier = firstLine;\n    var start = Pos(firstLine, 0);\n    this.sel = simpleSelection(start);\n    this.history = new History(null);\n    this.id = ++nextDocId;\n    this.modeOption = mode;\n\n    if (typeof text == \"string\") text = splitLines(text);\n    updateDoc(this, {from: start, to: start, text: text});\n    setSelection(this, simpleSelection(start), sel_dontScroll);\n  };\n\n  Doc.prototype = createObj(BranchChunk.prototype, {\n    constructor: Doc,\n    // Iterate over the document. Supports two forms -- with only one\n    // argument, it calls that for each line in the document. With\n    // three, it iterates over the range given by the first two (with\n    // the second being non-inclusive).\n    iter: function(from, to, op) {\n      if (op) this.iterN(from - this.first, to - from, op);\n      else this.iterN(this.first, this.first + this.size, from);\n    },\n\n    // Non-public interface for adding and removing lines.\n    insert: function(at, lines) {\n      var height = 0;\n      for (var i = 0; i < lines.length; ++i) height += lines[i].height;\n      this.insertInner(at - this.first, lines, height);\n    },\n    remove: function(at, n) { this.removeInner(at - this.first, n); },\n\n    // From here, the methods are part of the public interface. Most\n    // are also available from CodeMirror (editor) instances.\n\n    getValue: function(lineSep) {\n      var lines = getLines(this, this.first, this.first + this.size);\n      if (lineSep === false) return lines;\n      return lines.join(lineSep || \"\\n\");\n    },\n    setValue: docMethodOp(function(code) {\n      var top = Pos(this.first, 0), last = this.first + this.size - 1;\n      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),\n                        text: splitLines(code), origin: \"setValue\", full: true}, true);\n      setSelection(this, simpleSelection(top));\n    }),\n    replaceRange: function(code, from, to, origin) {\n      from = clipPos(this, from);\n      to = to ? clipPos(this, to) : from;\n      replaceRange(this, code, from, to, origin);\n    },\n    getRange: function(from, to, lineSep) {\n      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));\n      if (lineSep === false) return lines;\n      return lines.join(lineSep || \"\\n\");\n    },\n\n    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},\n\n    getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},\n    getLineNumber: function(line) {return lineNo(line);},\n\n    getLineHandleVisualStart: function(line) {\n      if (typeof line == \"number\") line = getLine(this, line);\n      return visualLine(line);\n    },\n\n    lineCount: function() {return this.size;},\n    firstLine: function() {return this.first;},\n    lastLine: function() {return this.first + this.size - 1;},\n\n    clipPos: function(pos) {return clipPos(this, pos);},\n\n    getCursor: function(start) {\n      var range = this.sel.primary(), pos;\n      if (start == null || start == \"head\") pos = range.head;\n      else if (start == \"anchor\") pos = range.anchor;\n      else if (start == \"end\" || start == \"to\" || start === false) pos = range.to();\n      else pos = range.from();\n      return pos;\n    },\n    listSelections: function() { return this.sel.ranges; },\n    somethingSelected: function() {return this.sel.somethingSelected();},\n\n    setCursor: docMethodOp(function(line, ch, options) {\n      setSimpleSelection(this, clipPos(this, typeof line == \"number\" ? Pos(line, ch || 0) : line), null, options);\n    }),\n    setSelection: docMethodOp(function(anchor, head, options) {\n      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);\n    }),\n    extendSelection: docMethodOp(function(head, other, options) {\n      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);\n    }),\n    extendSelections: docMethodOp(function(heads, options) {\n      extendSelections(this, clipPosArray(this, heads, options));\n    }),\n    extendSelectionsBy: docMethodOp(function(f, options) {\n      extendSelections(this, map(this.sel.ranges, f), options);\n    }),\n    setSelections: docMethodOp(function(ranges, primary, options) {\n      if (!ranges.length) return;\n      for (var i = 0, out = []; i < ranges.length; i++)\n        out[i] = new Range(clipPos(this, ranges[i].anchor),\n                           clipPos(this, ranges[i].head));\n      if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);\n      setSelection(this, normalizeSelection(out, primary), options);\n    }),\n    addSelection: docMethodOp(function(anchor, head, options) {\n      var ranges = this.sel.ranges.slice(0);\n      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));\n      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);\n    }),\n\n    getSelection: function(lineSep) {\n      var ranges = this.sel.ranges, lines;\n      for (var i = 0; i < ranges.length; i++) {\n        var sel = getBetween(this, ranges[i].from(), ranges[i].to());\n        lines = lines ? lines.concat(sel) : sel;\n      }\n      if (lineSep === false) return lines;\n      else return lines.join(lineSep || \"\\n\");\n    },\n    getSelections: function(lineSep) {\n      var parts = [], ranges = this.sel.ranges;\n      for (var i = 0; i < ranges.length; i++) {\n        var sel = getBetween(this, ranges[i].from(), ranges[i].to());\n        if (lineSep !== false) sel = sel.join(lineSep || \"\\n\");\n        parts[i] = sel;\n      }\n      return parts;\n    },\n    replaceSelection: function(code, collapse, origin) {\n      var dup = [];\n      for (var i = 0; i < this.sel.ranges.length; i++)\n        dup[i] = code;\n      this.replaceSelections(dup, collapse, origin || \"+input\");\n    },\n    replaceSelections: docMethodOp(function(code, collapse, origin) {\n      var changes = [], sel = this.sel;\n      for (var i = 0; i < sel.ranges.length; i++) {\n        var range = sel.ranges[i];\n        changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};\n      }\n      var newSel = collapse && collapse != \"end\" && computeReplacedSel(this, changes, collapse);\n      for (var i = changes.length - 1; i >= 0; i--)\n        makeChange(this, changes[i]);\n      if (newSel) setSelectionReplaceHistory(this, newSel);\n      else if (this.cm) ensureCursorVisible(this.cm);\n    }),\n    undo: docMethodOp(function() {makeChangeFromHistory(this, \"undo\");}),\n    redo: docMethodOp(function() {makeChangeFromHistory(this, \"redo\");}),\n    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"undo\", true);}),\n    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"redo\", true);}),\n\n    setExtending: function(val) {this.extend = val;},\n    getExtending: function() {return this.extend;},\n\n    historySize: function() {\n      var hist = this.history, done = 0, undone = 0;\n      for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;\n      for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;\n      return {undo: done, redo: undone};\n    },\n    clearHistory: function() {this.history = new History(this.history.maxGeneration);},\n\n    markClean: function() {\n      this.cleanGeneration = this.changeGeneration(true);\n    },\n    changeGeneration: function(forceSplit) {\n      if (forceSplit)\n        this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;\n      return this.history.generation;\n    },\n    isClean: function (gen) {\n      return this.history.generation == (gen || this.cleanGeneration);\n    },\n\n    getHistory: function() {\n      return {done: copyHistoryArray(this.history.done),\n              undone: copyHistoryArray(this.history.undone)};\n    },\n    setHistory: function(histData) {\n      var hist = this.history = new History(this.history.maxGeneration);\n      hist.done = copyHistoryArray(histData.done.slice(0), null, true);\n      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);\n    },\n\n    addLineClass: docMethodOp(function(handle, where, cls) {\n      return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function(line) {\n        var prop = where == \"text\" ? \"textClass\"\n                 : where == \"background\" ? \"bgClass\"\n                 : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n        if (!line[prop]) line[prop] = cls;\n        else if (classTest(cls).test(line[prop])) return false;\n        else line[prop] += \" \" + cls;\n        return true;\n      });\n    }),\n    removeLineClass: docMethodOp(function(handle, where, cls) {\n      return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function(line) {\n        var prop = where == \"text\" ? \"textClass\"\n                 : where == \"background\" ? \"bgClass\"\n                 : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n        var cur = line[prop];\n        if (!cur) return false;\n        else if (cls == null) line[prop] = null;\n        else {\n          var found = cur.match(classTest(cls));\n          if (!found) return false;\n          var end = found.index + found[0].length;\n          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? \"\" : \" \") + cur.slice(end) || null;\n        }\n        return true;\n      });\n    }),\n\n    addLineWidget: docMethodOp(function(handle, node, options) {\n      return addLineWidget(this, handle, node, options);\n    }),\n    removeLineWidget: function(widget) { widget.clear(); },\n\n    markText: function(from, to, options) {\n      return markText(this, clipPos(this, from), clipPos(this, to), options, \"range\");\n    },\n    setBookmark: function(pos, options) {\n      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),\n                      insertLeft: options && options.insertLeft,\n                      clearWhenEmpty: false, shared: options && options.shared,\n                      handleMouseEvents: options && options.handleMouseEvents};\n      pos = clipPos(this, pos);\n      return markText(this, pos, pos, realOpts, \"bookmark\");\n    },\n    findMarksAt: function(pos) {\n      pos = clipPos(this, pos);\n      var markers = [], spans = getLine(this, pos.line).markedSpans;\n      if (spans) for (var i = 0; i < spans.length; ++i) {\n        var span = spans[i];\n        if ((span.from == null || span.from <= pos.ch) &&\n            (span.to == null || span.to >= pos.ch))\n          markers.push(span.marker.parent || span.marker);\n      }\n      return markers;\n    },\n    findMarks: function(from, to, filter) {\n      from = clipPos(this, from); to = clipPos(this, to);\n      var found = [], lineNo = from.line;\n      this.iter(from.line, to.line + 1, function(line) {\n        var spans = line.markedSpans;\n        if (spans) for (var i = 0; i < spans.length; i++) {\n          var span = spans[i];\n          if (!(lineNo == from.line && from.ch > span.to ||\n                span.from == null && lineNo != from.line||\n                lineNo == to.line && span.from > to.ch) &&\n              (!filter || filter(span.marker)))\n            found.push(span.marker.parent || span.marker);\n        }\n        ++lineNo;\n      });\n      return found;\n    },\n    getAllMarks: function() {\n      var markers = [];\n      this.iter(function(line) {\n        var sps = line.markedSpans;\n        if (sps) for (var i = 0; i < sps.length; ++i)\n          if (sps[i].from != null) markers.push(sps[i].marker);\n      });\n      return markers;\n    },\n\n    posFromIndex: function(off) {\n      var ch, lineNo = this.first;\n      this.iter(function(line) {\n        var sz = line.text.length + 1;\n        if (sz > off) { ch = off; return true; }\n        off -= sz;\n        ++lineNo;\n      });\n      return clipPos(this, Pos(lineNo, ch));\n    },\n    indexFromPos: function (coords) {\n      coords = clipPos(this, coords);\n      var index = coords.ch;\n      if (coords.line < this.first || coords.ch < 0) return 0;\n      this.iter(this.first, coords.line, function (line) {\n        index += line.text.length + 1;\n      });\n      return index;\n    },\n\n    copy: function(copyHistory) {\n      var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);\n      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;\n      doc.sel = this.sel;\n      doc.extend = false;\n      if (copyHistory) {\n        doc.history.undoDepth = this.history.undoDepth;\n        doc.setHistory(this.getHistory());\n      }\n      return doc;\n    },\n\n    linkedDoc: function(options) {\n      if (!options) options = {};\n      var from = this.first, to = this.first + this.size;\n      if (options.from != null && options.from > from) from = options.from;\n      if (options.to != null && options.to < to) to = options.to;\n      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);\n      if (options.sharedHist) copy.history = this.history;\n      (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});\n      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];\n      copySharedMarkers(copy, findSharedMarkers(this));\n      return copy;\n    },\n    unlinkDoc: function(other) {\n      if (other instanceof CodeMirror) other = other.doc;\n      if (this.linked) for (var i = 0; i < this.linked.length; ++i) {\n        var link = this.linked[i];\n        if (link.doc != other) continue;\n        this.linked.splice(i, 1);\n        other.unlinkDoc(this);\n        detachSharedMarkers(findSharedMarkers(this));\n        break;\n      }\n      // If the histories were shared, split them again\n      if (other.history == this.history) {\n        var splitIds = [other.id];\n        linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);\n        other.history = new History(null);\n        other.history.done = copyHistoryArray(this.history.done, splitIds);\n        other.history.undone = copyHistoryArray(this.history.undone, splitIds);\n      }\n    },\n    iterLinkedDocs: function(f) {linkedDocs(this, f);},\n\n    getMode: function() {return this.mode;},\n    getEditor: function() {return this.cm;}\n  });\n\n  // Public alias.\n  Doc.prototype.eachLine = Doc.prototype.iter;\n\n  // Set up methods on CodeMirror's prototype to redirect to the editor's document.\n  var dontDelegate = \"iter insert remove copy getEditor constructor\".split(\" \");\n  for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)\n    CodeMirror.prototype[prop] = (function(method) {\n      return function() {return method.apply(this.doc, arguments);};\n    })(Doc.prototype[prop]);\n\n  eventMixin(Doc);\n\n  // Call f for all linked documents.\n  function linkedDocs(doc, f, sharedHistOnly) {\n    function propagate(doc, skip, sharedHist) {\n      if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {\n        var rel = doc.linked[i];\n        if (rel.doc == skip) continue;\n        var shared = sharedHist && rel.sharedHist;\n        if (sharedHistOnly && !shared) continue;\n        f(rel.doc, shared);\n        propagate(rel.doc, doc, shared);\n      }\n    }\n    propagate(doc, null, true);\n  }\n\n  // Attach a document to an editor.\n  function attachDoc(cm, doc) {\n    if (doc.cm) throw new Error(\"This document is already in use.\");\n    cm.doc = doc;\n    doc.cm = cm;\n    estimateLineHeights(cm);\n    loadMode(cm);\n    if (!cm.options.lineWrapping) findMaxLine(cm);\n    cm.options.mode = doc.modeOption;\n    regChange(cm);\n  }\n\n  // LINE UTILITIES\n\n  // Find the line object corresponding to the given line number.\n  function getLine(doc, n) {\n    n -= doc.first;\n    if (n < 0 || n >= doc.size) throw new Error(\"There is no line \" + (n + doc.first) + \" in the document.\");\n    for (var chunk = doc; !chunk.lines;) {\n      for (var i = 0;; ++i) {\n        var child = chunk.children[i], sz = child.chunkSize();\n        if (n < sz) { chunk = child; break; }\n        n -= sz;\n      }\n    }\n    return chunk.lines[n];\n  }\n\n  // Get the part of a document between two positions, as an array of\n  // strings.\n  function getBetween(doc, start, end) {\n    var out = [], n = start.line;\n    doc.iter(start.line, end.line + 1, function(line) {\n      var text = line.text;\n      if (n == end.line) text = text.slice(0, end.ch);\n      if (n == start.line) text = text.slice(start.ch);\n      out.push(text);\n      ++n;\n    });\n    return out;\n  }\n  // Get the lines between from and to, as array of strings.\n  function getLines(doc, from, to) {\n    var out = [];\n    doc.iter(from, to, function(line) { out.push(line.text); });\n    return out;\n  }\n\n  // Update the height of a line, propagating the height change\n  // upwards to parent nodes.\n  function updateLineHeight(line, height) {\n    var diff = height - line.height;\n    if (diff) for (var n = line; n; n = n.parent) n.height += diff;\n  }\n\n  // Given a line object, find its line number by walking up through\n  // its parent links.\n  function lineNo(line) {\n    if (line.parent == null) return null;\n    var cur = line.parent, no = indexOf(cur.lines, line);\n    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {\n      for (var i = 0;; ++i) {\n        if (chunk.children[i] == cur) break;\n        no += chunk.children[i].chunkSize();\n      }\n    }\n    return no + cur.first;\n  }\n\n  // Find the line at the given vertical position, using the height\n  // information in the document tree.\n  function lineAtHeight(chunk, h) {\n    var n = chunk.first;\n    outer: do {\n      for (var i = 0; i < chunk.children.length; ++i) {\n        var child = chunk.children[i], ch = child.height;\n        if (h < ch) { chunk = child; continue outer; }\n        h -= ch;\n        n += child.chunkSize();\n      }\n      return n;\n    } while (!chunk.lines);\n    for (var i = 0; i < chunk.lines.length; ++i) {\n      var line = chunk.lines[i], lh = line.height;\n      if (h < lh) break;\n      h -= lh;\n    }\n    return n + i;\n  }\n\n\n  // Find the height above the given line.\n  function heightAtLine(lineObj) {\n    lineObj = visualLine(lineObj);\n\n    var h = 0, chunk = lineObj.parent;\n    for (var i = 0; i < chunk.lines.length; ++i) {\n      var line = chunk.lines[i];\n      if (line == lineObj) break;\n      else h += line.height;\n    }\n    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {\n      for (var i = 0; i < p.children.length; ++i) {\n        var cur = p.children[i];\n        if (cur == chunk) break;\n        else h += cur.height;\n      }\n    }\n    return h;\n  }\n\n  // Get the bidi ordering for the given line (and cache it). Returns\n  // false for lines that are fully left-to-right, and an array of\n  // BidiSpan objects otherwise.\n  function getOrder(line) {\n    var order = line.order;\n    if (order == null) order = line.order = bidiOrdering(line.text);\n    return order;\n  }\n\n  // HISTORY\n\n  function History(startGen) {\n    // Arrays of change events and selections. Doing something adds an\n    // event to done and clears undo. Undoing moves events from done\n    // to undone, redoing moves them in the other direction.\n    this.done = []; this.undone = [];\n    this.undoDepth = Infinity;\n    // Used to track when changes can be merged into a single undo\n    // event\n    this.lastModTime = this.lastSelTime = 0;\n    this.lastOp = this.lastSelOp = null;\n    this.lastOrigin = this.lastSelOrigin = null;\n    // Used by the isClean() method\n    this.generation = this.maxGeneration = startGen || 1;\n  }\n\n  // Create a history change event from an updateDoc-style change\n  // object.\n  function historyChangeFromChange(doc, change) {\n    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};\n    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);\n    linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);\n    return histChange;\n  }\n\n  // Pop all selection events off the end of a history array. Stop at\n  // a change event.\n  function clearSelectionEvents(array) {\n    while (array.length) {\n      var last = lst(array);\n      if (last.ranges) array.pop();\n      else break;\n    }\n  }\n\n  // Find the top change event in the history. Pop off selection\n  // events that are in the way.\n  function lastChangeEvent(hist, force) {\n    if (force) {\n      clearSelectionEvents(hist.done);\n      return lst(hist.done);\n    } else if (hist.done.length && !lst(hist.done).ranges) {\n      return lst(hist.done);\n    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {\n      hist.done.pop();\n      return lst(hist.done);\n    }\n  }\n\n  // Register a change in the history. Merges changes that are within\n  // a single operation, ore are close together with an origin that\n  // allows merging (starting with \"+\") into a single event.\n  function addChangeToHistory(doc, change, selAfter, opId) {\n    var hist = doc.history;\n    hist.undone.length = 0;\n    var time = +new Date, cur;\n\n    if ((hist.lastOp == opId ||\n         hist.lastOrigin == change.origin && change.origin &&\n         ((change.origin.charAt(0) == \"+\" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||\n          change.origin.charAt(0) == \"*\")) &&\n        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {\n      // Merge this change into the last event\n      var last = lst(cur.changes);\n      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {\n        // Optimized case for simple insertion -- don't want to add\n        // new changesets for every character typed\n        last.to = changeEnd(change);\n      } else {\n        // Add new sub-event\n        cur.changes.push(historyChangeFromChange(doc, change));\n      }\n    } else {\n      // Can not be merged, start a new event.\n      var before = lst(hist.done);\n      if (!before || !before.ranges)\n        pushSelectionToHistory(doc.sel, hist.done);\n      cur = {changes: [historyChangeFromChange(doc, change)],\n             generation: hist.generation};\n      hist.done.push(cur);\n      while (hist.done.length > hist.undoDepth) {\n        hist.done.shift();\n        if (!hist.done[0].ranges) hist.done.shift();\n      }\n    }\n    hist.done.push(selAfter);\n    hist.generation = ++hist.maxGeneration;\n    hist.lastModTime = hist.lastSelTime = time;\n    hist.lastOp = hist.lastSelOp = opId;\n    hist.lastOrigin = hist.lastSelOrigin = change.origin;\n\n    if (!last) signal(doc, \"historyAdded\");\n  }\n\n  function selectionEventCanBeMerged(doc, origin, prev, sel) {\n    var ch = origin.charAt(0);\n    return ch == \"*\" ||\n      ch == \"+\" &&\n      prev.ranges.length == sel.ranges.length &&\n      prev.somethingSelected() == sel.somethingSelected() &&\n      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);\n  }\n\n  // Called whenever the selection changes, sets the new selection as\n  // the pending selection in the history, and pushes the old pending\n  // selection into the 'done' array when it was significantly\n  // different (in number of selected ranges, emptiness, or time).\n  function addSelectionToHistory(doc, sel, opId, options) {\n    var hist = doc.history, origin = options && options.origin;\n\n    // A new event is started when the previous origin does not match\n    // the current, or the origins don't allow matching. Origins\n    // starting with * are always merged, those starting with + are\n    // merged when similar and close together in time.\n    if (opId == hist.lastSelOp ||\n        (origin && hist.lastSelOrigin == origin &&\n         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||\n          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))\n      hist.done[hist.done.length - 1] = sel;\n    else\n      pushSelectionToHistory(sel, hist.done);\n\n    hist.lastSelTime = +new Date;\n    hist.lastSelOrigin = origin;\n    hist.lastSelOp = opId;\n    if (options && options.clearRedo !== false)\n      clearSelectionEvents(hist.undone);\n  }\n\n  function pushSelectionToHistory(sel, dest) {\n    var top = lst(dest);\n    if (!(top && top.ranges && top.equals(sel)))\n      dest.push(sel);\n  }\n\n  // Used to store marked span information in the history.\n  function attachLocalSpans(doc, change, from, to) {\n    var existing = change[\"spans_\" + doc.id], n = 0;\n    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {\n      if (line.markedSpans)\n        (existing || (existing = change[\"spans_\" + doc.id] = {}))[n] = line.markedSpans;\n      ++n;\n    });\n  }\n\n  // When un/re-doing restores text containing marked spans, those\n  // that have been explicitly cleared should not be restored.\n  function removeClearedSpans(spans) {\n    if (!spans) return null;\n    for (var i = 0, out; i < spans.length; ++i) {\n      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }\n      else if (out) out.push(spans[i]);\n    }\n    return !out ? spans : out.length ? out : null;\n  }\n\n  // Retrieve and filter the old marked spans stored in a change event.\n  function getOldSpans(doc, change) {\n    var found = change[\"spans_\" + doc.id];\n    if (!found) return null;\n    for (var i = 0, nw = []; i < change.text.length; ++i)\n      nw.push(removeClearedSpans(found[i]));\n    return nw;\n  }\n\n  // Used both to provide a JSON-safe object in .getHistory, and, when\n  // detaching a document, to split the history in two\n  function copyHistoryArray(events, newGroup, instantiateSel) {\n    for (var i = 0, copy = []; i < events.length; ++i) {\n      var event = events[i];\n      if (event.ranges) {\n        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);\n        continue;\n      }\n      var changes = event.changes, newChanges = [];\n      copy.push({changes: newChanges});\n      for (var j = 0; j < changes.length; ++j) {\n        var change = changes[j], m;\n        newChanges.push({from: change.from, to: change.to, text: change.text});\n        if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\\d+)$/)) {\n          if (indexOf(newGroup, Number(m[1])) > -1) {\n            lst(newChanges)[prop] = change[prop];\n            delete change[prop];\n          }\n        }\n      }\n    }\n    return copy;\n  }\n\n  // Rebasing/resetting history to deal with externally-sourced changes\n\n  function rebaseHistSelSingle(pos, from, to, diff) {\n    if (to < pos.line) {\n      pos.line += diff;\n    } else if (from < pos.line) {\n      pos.line = from;\n      pos.ch = 0;\n    }\n  }\n\n  // Tries to rebase an array of history events given a change in the\n  // document. If the change touches the same lines as the event, the\n  // event, and everything 'behind' it, is discarded. If the change is\n  // before the event, the event's positions are updated. Uses a\n  // copy-on-write scheme for the positions, to avoid having to\n  // reallocate them all on every rebase, but also avoid problems with\n  // shared position objects being unsafely updated.\n  function rebaseHistArray(array, from, to, diff) {\n    for (var i = 0; i < array.length; ++i) {\n      var sub = array[i], ok = true;\n      if (sub.ranges) {\n        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }\n        for (var j = 0; j < sub.ranges.length; j++) {\n          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);\n          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);\n        }\n        continue;\n      }\n      for (var j = 0; j < sub.changes.length; ++j) {\n        var cur = sub.changes[j];\n        if (to < cur.from.line) {\n          cur.from = Pos(cur.from.line + diff, cur.from.ch);\n          cur.to = Pos(cur.to.line + diff, cur.to.ch);\n        } else if (from <= cur.to.line) {\n          ok = false;\n          break;\n        }\n      }\n      if (!ok) {\n        array.splice(0, i + 1);\n        i = 0;\n      }\n    }\n  }\n\n  function rebaseHist(hist, change) {\n    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;\n    rebaseHistArray(hist.done, from, to, diff);\n    rebaseHistArray(hist.undone, from, to, diff);\n  }\n\n  // EVENT UTILITIES\n\n  // Due to the fact that we still support jurassic IE versions, some\n  // compatibility wrappers are needed.\n\n  var e_preventDefault = CodeMirror.e_preventDefault = function(e) {\n    if (e.preventDefault) e.preventDefault();\n    else e.returnValue = false;\n  };\n  var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {\n    if (e.stopPropagation) e.stopPropagation();\n    else e.cancelBubble = true;\n  };\n  function e_defaultPrevented(e) {\n    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;\n  }\n  var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};\n\n  function e_target(e) {return e.target || e.srcElement;}\n  function e_button(e) {\n    var b = e.which;\n    if (b == null) {\n      if (e.button & 1) b = 1;\n      else if (e.button & 2) b = 3;\n      else if (e.button & 4) b = 2;\n    }\n    if (mac && e.ctrlKey && b == 1) b = 3;\n    return b;\n  }\n\n  // EVENT HANDLING\n\n  // Lightweight event framework. on/off also work on DOM nodes,\n  // registering native DOM handlers.\n\n  var on = CodeMirror.on = function(emitter, type, f) {\n    if (emitter.addEventListener)\n      emitter.addEventListener(type, f, false);\n    else if (emitter.attachEvent)\n      emitter.attachEvent(\"on\" + type, f);\n    else {\n      var map = emitter._handlers || (emitter._handlers = {});\n      var arr = map[type] || (map[type] = []);\n      arr.push(f);\n    }\n  };\n\n  var off = CodeMirror.off = function(emitter, type, f) {\n    if (emitter.removeEventListener)\n      emitter.removeEventListener(type, f, false);\n    else if (emitter.detachEvent)\n      emitter.detachEvent(\"on\" + type, f);\n    else {\n      var arr = emitter._handlers && emitter._handlers[type];\n      if (!arr) return;\n      for (var i = 0; i < arr.length; ++i)\n        if (arr[i] == f) { arr.splice(i, 1); break; }\n    }\n  };\n\n  var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {\n    var arr = emitter._handlers && emitter._handlers[type];\n    if (!arr) return;\n    var args = Array.prototype.slice.call(arguments, 2);\n    for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);\n  };\n\n  var orphanDelayedCallbacks = null;\n\n  // Often, we want to signal events at a point where we are in the\n  // middle of some work, but don't want the handler to start calling\n  // other methods on the editor, which might be in an inconsistent\n  // state or simply not expect any other events to happen.\n  // signalLater looks whether there are any handlers, and schedules\n  // them to be executed when the last operation ends, or, if no\n  // operation is active, when a timeout fires.\n  function signalLater(emitter, type /*, values...*/) {\n    var arr = emitter._handlers && emitter._handlers[type];\n    if (!arr) return;\n    var args = Array.prototype.slice.call(arguments, 2), list;\n    if (operationGroup) {\n      list = operationGroup.delayedCallbacks;\n    } else if (orphanDelayedCallbacks) {\n      list = orphanDelayedCallbacks;\n    } else {\n      list = orphanDelayedCallbacks = [];\n      setTimeout(fireOrphanDelayed, 0);\n    }\n    function bnd(f) {return function(){f.apply(null, args);};};\n    for (var i = 0; i < arr.length; ++i)\n      list.push(bnd(arr[i]));\n  }\n\n  function fireOrphanDelayed() {\n    var delayed = orphanDelayedCallbacks;\n    orphanDelayedCallbacks = null;\n    for (var i = 0; i < delayed.length; ++i) delayed[i]();\n  }\n\n  // The DOM events that CodeMirror handles can be overridden by\n  // registering a (non-DOM) handler on the editor for the event name,\n  // and preventDefault-ing the event in that handler.\n  function signalDOMEvent(cm, e, override) {\n    if (typeof e == \"string\")\n      e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};\n    signal(cm, override || e.type, cm, e);\n    return e_defaultPrevented(e) || e.codemirrorIgnore;\n  }\n\n  function signalCursorActivity(cm) {\n    var arr = cm._handlers && cm._handlers.cursorActivity;\n    if (!arr) return;\n    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);\n    for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)\n      set.push(arr[i]);\n  }\n\n  function hasHandler(emitter, type) {\n    var arr = emitter._handlers && emitter._handlers[type];\n    return arr && arr.length > 0;\n  }\n\n  // Add on and off methods to a constructor's prototype, to make\n  // registering events on such objects more convenient.\n  function eventMixin(ctor) {\n    ctor.prototype.on = function(type, f) {on(this, type, f);};\n    ctor.prototype.off = function(type, f) {off(this, type, f);};\n  }\n\n  // MISC UTILITIES\n\n  // Number of pixels added to scroller and sizer to hide scrollbar\n  var scrollerGap = 30;\n\n  // Returned or thrown by various protocols to signal 'I'm not\n  // handling this'.\n  var Pass = CodeMirror.Pass = {toString: function(){return \"CodeMirror.Pass\";}};\n\n  // Reused option objects for setSelection & friends\n  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: \"*mouse\"}, sel_move = {origin: \"+move\"};\n\n  function Delayed() {this.id = null;}\n  Delayed.prototype.set = function(ms, f) {\n    clearTimeout(this.id);\n    this.id = setTimeout(f, ms);\n  };\n\n  // Counts the column offset in a string, taking tabs into account.\n  // Used mostly to find indentation.\n  var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {\n    if (end == null) {\n      end = string.search(/[^\\s\\u00a0]/);\n      if (end == -1) end = string.length;\n    }\n    for (var i = startIndex || 0, n = startValue || 0;;) {\n      var nextTab = string.indexOf(\"\\t\", i);\n      if (nextTab < 0 || nextTab >= end)\n        return n + (end - i);\n      n += nextTab - i;\n      n += tabSize - (n % tabSize);\n      i = nextTab + 1;\n    }\n  };\n\n  // The inverse of countColumn -- find the offset that corresponds to\n  // a particular column.\n  function findColumn(string, goal, tabSize) {\n    for (var pos = 0, col = 0;;) {\n      var nextTab = string.indexOf(\"\\t\", pos);\n      if (nextTab == -1) nextTab = string.length;\n      var skipped = nextTab - pos;\n      if (nextTab == string.length || col + skipped >= goal)\n        return pos + Math.min(skipped, goal - col);\n      col += nextTab - pos;\n      col += tabSize - (col % tabSize);\n      pos = nextTab + 1;\n      if (col >= goal) return pos;\n    }\n  }\n\n  var spaceStrs = [\"\"];\n  function spaceStr(n) {\n    while (spaceStrs.length <= n)\n      spaceStrs.push(lst(spaceStrs) + \" \");\n    return spaceStrs[n];\n  }\n\n  function lst(arr) { return arr[arr.length-1]; }\n\n  var selectInput = function(node) { node.select(); };\n  if (ios) // Mobile Safari apparently has a bug where select() is broken.\n    selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };\n  else if (ie) // Suppress mysterious IE10 errors\n    selectInput = function(node) { try { node.select(); } catch(_e) {} };\n\n  function indexOf(array, elt) {\n    for (var i = 0; i < array.length; ++i)\n      if (array[i] == elt) return i;\n    return -1;\n  }\n  function map(array, f) {\n    var out = [];\n    for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);\n    return out;\n  }\n\n  function nothing() {}\n\n  function createObj(base, props) {\n    var inst;\n    if (Object.create) {\n      inst = Object.create(base);\n    } else {\n      nothing.prototype = base;\n      inst = new nothing();\n    }\n    if (props) copyObj(props, inst);\n    return inst;\n  };\n\n  function copyObj(obj, target, overwrite) {\n    if (!target) target = {};\n    for (var prop in obj)\n      if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n        target[prop] = obj[prop];\n    return target;\n  }\n\n  function bind(f) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return function(){return f.apply(null, args);};\n  }\n\n  var nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/;\n  var isWordCharBasic = CodeMirror.isWordChar = function(ch) {\n    return /\\w/.test(ch) || ch > \"\\x80\" &&\n      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));\n  };\n  function isWordChar(ch, helper) {\n    if (!helper) return isWordCharBasic(ch);\n    if (helper.source.indexOf(\"\\\\w\") > -1 && isWordCharBasic(ch)) return true;\n    return helper.test(ch);\n  }\n\n  function isEmpty(obj) {\n    for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;\n    return true;\n  }\n\n  // Extending unicode characters. A series of a non-extending char +\n  // any number of extending chars is treated as a single unit as far\n  // as editing and measuring is concerned. This is not fully correct,\n  // since some scripts/fonts/browsers also treat other configurations\n  // of code points as a group.\n  var extendingChars = /[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/;\n  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }\n\n  // DOM UTILITIES\n\n  function elt(tag, content, className, style) {\n    var e = document.createElement(tag);\n    if (className) e.className = className;\n    if (style) e.style.cssText = style;\n    if (typeof content == \"string\") e.appendChild(document.createTextNode(content));\n    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);\n    return e;\n  }\n\n  var range;\n  if (document.createRange) range = function(node, start, end, endNode) {\n    var r = document.createRange();\n    r.setEnd(endNode || node, end);\n    r.setStart(node, start);\n    return r;\n  };\n  else range = function(node, start, end) {\n    var r = document.body.createTextRange();\n    try { r.moveToElementText(node.parentNode); }\n    catch(e) { return r; }\n    r.collapse(true);\n    r.moveEnd(\"character\", end);\n    r.moveStart(\"character\", start);\n    return r;\n  };\n\n  function removeChildren(e) {\n    for (var count = e.childNodes.length; count > 0; --count)\n      e.removeChild(e.firstChild);\n    return e;\n  }\n\n  function removeChildrenAndAdd(parent, e) {\n    return removeChildren(parent).appendChild(e);\n  }\n\n  var contains = CodeMirror.contains = function(parent, child) {\n    if (child.nodeType == 3) // Android browser always returns false when child is a textnode\n      child = child.parentNode;\n    if (parent.contains)\n      return parent.contains(child);\n    do {\n      if (child.nodeType == 11) child = child.host;\n      if (child == parent) return true;\n    } while (child = child.parentNode);\n  };\n\n  function activeElt() { return document.activeElement; }\n  // Older versions of IE throws unspecified error when touching\n  // document.activeElement in some cases (during loading, in iframe)\n  if (ie && ie_version < 11) activeElt = function() {\n    try { return document.activeElement; }\n    catch(e) { return document.body; }\n  };\n\n  function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\"); }\n  var rmClass = CodeMirror.rmClass = function(node, cls) {\n    var current = node.className;\n    var match = classTest(cls).exec(current);\n    if (match) {\n      var after = current.slice(match.index + match[0].length);\n      node.className = current.slice(0, match.index) + (after ? match[1] + after : \"\");\n    }\n  };\n  var addClass = CodeMirror.addClass = function(node, cls) {\n    var current = node.className;\n    if (!classTest(cls).test(current)) node.className += (current ? \" \" : \"\") + cls;\n  };\n  function joinClasses(a, b) {\n    var as = a.split(\" \");\n    for (var i = 0; i < as.length; i++)\n      if (as[i] && !classTest(as[i]).test(b)) b += \" \" + as[i];\n    return b;\n  }\n\n  // WINDOW-WIDE EVENTS\n\n  // These must be handled carefully, because naively registering a\n  // handler for each editor will cause the editors to never be\n  // garbage collected.\n\n  function forEachCodeMirror(f) {\n    if (!document.body.getElementsByClassName) return;\n    var byClass = document.body.getElementsByClassName(\"CodeMirror\");\n    for (var i = 0; i < byClass.length; i++) {\n      var cm = byClass[i].CodeMirror;\n      if (cm) f(cm);\n    }\n  }\n\n  var globalsRegistered = false;\n  function ensureGlobalHandlers() {\n    if (globalsRegistered) return;\n    registerGlobalHandlers();\n    globalsRegistered = true;\n  }\n  function registerGlobalHandlers() {\n    // When the window resizes, we need to refresh active editors.\n    var resizeTimer;\n    on(window, \"resize\", function() {\n      if (resizeTimer == null) resizeTimer = setTimeout(function() {\n        resizeTimer = null;\n        forEachCodeMirror(onResize);\n      }, 100);\n    });\n    // When the window loses focus, we want to show the editor as blurred\n    on(window, \"blur\", function() {\n      forEachCodeMirror(onBlur);\n    });\n  }\n\n  // FEATURE DETECTION\n\n  // Detect drag-and-drop\n  var dragAndDrop = function() {\n    // There is *some* kind of drag-and-drop support in IE6-8, but I\n    // couldn't get it to work yet.\n    if (ie && ie_version < 9) return false;\n    var div = elt('div');\n    return \"draggable\" in div || \"dragDrop\" in div;\n  }();\n\n  var zwspSupported;\n  function zeroWidthElement(measure) {\n    if (zwspSupported == null) {\n      var test = elt(\"span\", \"\\u200b\");\n      removeChildrenAndAdd(measure, elt(\"span\", [test, document.createTextNode(\"x\")]));\n      if (measure.firstChild.offsetHeight != 0)\n        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);\n    }\n    var node = zwspSupported ? elt(\"span\", \"\\u200b\") :\n      elt(\"span\", \"\\u00a0\", null, \"display: inline-block; width: 1px; margin-right: -1px\");\n    node.setAttribute(\"cm-text\", \"\");\n    return node;\n  }\n\n  // Feature-detect IE's crummy client rect reporting for bidi text\n  var badBidiRects;\n  function hasBadBidiRects(measure) {\n    if (badBidiRects != null) return badBidiRects;\n    var txt = removeChildrenAndAdd(measure, document.createTextNode(\"A\\u062eA\"));\n    var r0 = range(txt, 0, 1).getBoundingClientRect();\n    if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)\n    var r1 = range(txt, 1, 2).getBoundingClientRect();\n    return badBidiRects = (r1.right - r0.right < 3);\n  }\n\n  // See if \"\".split is the broken IE version, if so, provide an\n  // alternative way to split lines.\n  var splitLines = CodeMirror.splitLines = \"\\n\\nb\".split(/\\n/).length != 3 ? function(string) {\n    var pos = 0, result = [], l = string.length;\n    while (pos <= l) {\n      var nl = string.indexOf(\"\\n\", pos);\n      if (nl == -1) nl = string.length;\n      var line = string.slice(pos, string.charAt(nl - 1) == \"\\r\" ? nl - 1 : nl);\n      var rt = line.indexOf(\"\\r\");\n      if (rt != -1) {\n        result.push(line.slice(0, rt));\n        pos += rt + 1;\n      } else {\n        result.push(line);\n        pos = nl + 1;\n      }\n    }\n    return result;\n  } : function(string){return string.split(/\\r\\n?|\\n/);};\n\n  var hasSelection = window.getSelection ? function(te) {\n    try { return te.selectionStart != te.selectionEnd; }\n    catch(e) { return false; }\n  } : function(te) {\n    try {var range = te.ownerDocument.selection.createRange();}\n    catch(e) {}\n    if (!range || range.parentElement() != te) return false;\n    return range.compareEndPoints(\"StartToEnd\", range) != 0;\n  };\n\n  var hasCopyEvent = (function() {\n    var e = elt(\"div\");\n    if (\"oncopy\" in e) return true;\n    e.setAttribute(\"oncopy\", \"return;\");\n    return typeof e.oncopy == \"function\";\n  })();\n\n  var badZoomedRects = null;\n  function hasBadZoomedRects(measure) {\n    if (badZoomedRects != null) return badZoomedRects;\n    var node = removeChildrenAndAdd(measure, elt(\"span\", \"x\"));\n    var normal = node.getBoundingClientRect();\n    var fromRange = range(node, 0, 1).getBoundingClientRect();\n    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;\n  }\n\n  // KEY NAMES\n\n  var keyNames = {3: \"Enter\", 8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 16: \"Shift\", 17: \"Ctrl\", 18: \"Alt\",\n                  19: \"Pause\", 20: \"CapsLock\", 27: \"Esc\", 32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\",\n                  36: \"Home\", 37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"PrintScrn\", 45: \"Insert\",\n                  46: \"Delete\", 59: \";\", 61: \"=\", 91: \"Mod\", 92: \"Mod\", 93: \"Mod\", 107: \"=\", 109: \"-\", 127: \"Delete\",\n                  173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\", 219: \"[\", 220: \"\\\\\",\n                  221: \"]\", 222: \"'\", 63232: \"Up\", 63233: \"Down\", 63234: \"Left\", 63235: \"Right\", 63272: \"Delete\",\n                  63273: \"Home\", 63275: \"End\", 63276: \"PageUp\", 63277: \"PageDown\", 63302: \"Insert\"};\n  CodeMirror.keyNames = keyNames;\n  (function() {\n    // Number keys\n    for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);\n    // Alphabetic keys\n    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);\n    // Function keys\n    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = \"F\" + i;\n  })();\n\n  // BIDI HELPERS\n\n  function iterateBidiSections(order, from, to, f) {\n    if (!order) return f(from, to, \"ltr\");\n    var found = false;\n    for (var i = 0; i < order.length; ++i) {\n      var part = order[i];\n      if (part.from < to && part.to > from || from == to && part.to == from) {\n        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? \"rtl\" : \"ltr\");\n        found = true;\n      }\n    }\n    if (!found) f(from, to, \"ltr\");\n  }\n\n  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }\n  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }\n\n  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }\n  function lineRight(line) {\n    var order = getOrder(line);\n    if (!order) return line.text.length;\n    return bidiRight(lst(order));\n  }\n\n  function lineStart(cm, lineN) {\n    var line = getLine(cm.doc, lineN);\n    var visual = visualLine(line);\n    if (visual != line) lineN = lineNo(visual);\n    var order = getOrder(visual);\n    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);\n    return Pos(lineN, ch);\n  }\n  function lineEnd(cm, lineN) {\n    var merged, line = getLine(cm.doc, lineN);\n    while (merged = collapsedSpanAtEnd(line)) {\n      line = merged.find(1, true).line;\n      lineN = null;\n    }\n    var order = getOrder(line);\n    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);\n    return Pos(lineN == null ? lineNo(line) : lineN, ch);\n  }\n  function lineStartSmart(cm, pos) {\n    var start = lineStart(cm, pos.line);\n    var line = getLine(cm.doc, start.line);\n    var order = getOrder(line);\n    if (!order || order[0].level == 0) {\n      var firstNonWS = Math.max(0, line.text.search(/\\S/));\n      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;\n      return Pos(start.line, inWS ? 0 : firstNonWS);\n    }\n    return start;\n  }\n\n  function compareBidiLevel(order, a, b) {\n    var linedir = order[0].level;\n    if (a == linedir) return true;\n    if (b == linedir) return false;\n    return a < b;\n  }\n  var bidiOther;\n  function getBidiPartAt(order, pos) {\n    bidiOther = null;\n    for (var i = 0, found; i < order.length; ++i) {\n      var cur = order[i];\n      if (cur.from < pos && cur.to > pos) return i;\n      if ((cur.from == pos || cur.to == pos)) {\n        if (found == null) {\n          found = i;\n        } else if (compareBidiLevel(order, cur.level, order[found].level)) {\n          if (cur.from != cur.to) bidiOther = found;\n          return i;\n        } else {\n          if (cur.from != cur.to) bidiOther = i;\n          return found;\n        }\n      }\n    }\n    return found;\n  }\n\n  function moveInLine(line, pos, dir, byUnit) {\n    if (!byUnit) return pos + dir;\n    do pos += dir;\n    while (pos > 0 && isExtendingChar(line.text.charAt(pos)));\n    return pos;\n  }\n\n  // This is needed in order to move 'visually' through bi-directional\n  // text -- i.e., pressing left should make the cursor go left, even\n  // when in RTL text. The tricky part is the 'jumps', where RTL and\n  // LTR text touch each other. This often requires the cursor offset\n  // to move more than one unit, in order to visually move one unit.\n  function moveVisually(line, start, dir, byUnit) {\n    var bidi = getOrder(line);\n    if (!bidi) return moveLogically(line, start, dir, byUnit);\n    var pos = getBidiPartAt(bidi, start), part = bidi[pos];\n    var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);\n\n    for (;;) {\n      if (target > part.from && target < part.to) return target;\n      if (target == part.from || target == part.to) {\n        if (getBidiPartAt(bidi, target) == pos) return target;\n        part = bidi[pos += dir];\n        return (dir > 0) == part.level % 2 ? part.to : part.from;\n      } else {\n        part = bidi[pos += dir];\n        if (!part) return null;\n        if ((dir > 0) == part.level % 2)\n          target = moveInLine(line, part.to, -1, byUnit);\n        else\n          target = moveInLine(line, part.from, 1, byUnit);\n      }\n    }\n  }\n\n  function moveLogically(line, start, dir, byUnit) {\n    var target = start + dir;\n    if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;\n    return target < 0 || target > line.text.length ? null : target;\n  }\n\n  // Bidirectional ordering algorithm\n  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm\n  // that this (partially) implements.\n\n  // One-char codes used for character types:\n  // L (L):   Left-to-Right\n  // R (R):   Right-to-Left\n  // r (AL):  Right-to-Left Arabic\n  // 1 (EN):  European Number\n  // + (ES):  European Number Separator\n  // % (ET):  European Number Terminator\n  // n (AN):  Arabic Number\n  // , (CS):  Common Number Separator\n  // m (NSM): Non-Spacing Mark\n  // b (BN):  Boundary Neutral\n  // s (B):   Paragraph Separator\n  // t (S):   Segment Separator\n  // w (WS):  Whitespace\n  // N (ON):  Other Neutrals\n\n  // Returns null if characters are ordered as they appear\n  // (left-to-right), or an array of sections ({from, to, level}\n  // objects) in the order in which they occur visually.\n  var bidiOrdering = (function() {\n    // Character types for codepoints 0 to 0xff\n    var lowTypes = \"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\";\n    // Character types for codepoints 0x600 to 0x6ff\n    var arabicTypes = \"rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm\";\n    function charType(code) {\n      if (code <= 0xf7) return lowTypes.charAt(code);\n      else if (0x590 <= code && code <= 0x5f4) return \"R\";\n      else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);\n      else if (0x6ee <= code && code <= 0x8ac) return \"r\";\n      else if (0x2000 <= code && code <= 0x200b) return \"w\";\n      else if (code == 0x200c) return \"b\";\n      else return \"L\";\n    }\n\n    var bidiRE = /[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/;\n    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;\n    // Browsers seem to always treat the boundaries of block elements as being L.\n    var outerType = \"L\";\n\n    function BidiSpan(level, from, to) {\n      this.level = level;\n      this.from = from; this.to = to;\n    }\n\n    return function(str) {\n      if (!bidiRE.test(str)) return false;\n      var len = str.length, types = [];\n      for (var i = 0, type; i < len; ++i)\n        types.push(type = charType(str.charCodeAt(i)));\n\n      // W1. Examine each non-spacing mark (NSM) in the level run, and\n      // change the type of the NSM to the type of the previous\n      // character. If the NSM is at the start of the level run, it will\n      // get the type of sor.\n      for (var i = 0, prev = outerType; i < len; ++i) {\n        var type = types[i];\n        if (type == \"m\") types[i] = prev;\n        else prev = type;\n      }\n\n      // W2. Search backwards from each instance of a European number\n      // until the first strong type (R, L, AL, or sor) is found. If an\n      // AL is found, change the type of the European number to Arabic\n      // number.\n      // W3. Change all ALs to R.\n      for (var i = 0, cur = outerType; i < len; ++i) {\n        var type = types[i];\n        if (type == \"1\" && cur == \"r\") types[i] = \"n\";\n        else if (isStrong.test(type)) { cur = type; if (type == \"r\") types[i] = \"R\"; }\n      }\n\n      // W4. A single European separator between two European numbers\n      // changes to a European number. A single common separator between\n      // two numbers of the same type changes to that type.\n      for (var i = 1, prev = types[0]; i < len - 1; ++i) {\n        var type = types[i];\n        if (type == \"+\" && prev == \"1\" && types[i+1] == \"1\") types[i] = \"1\";\n        else if (type == \",\" && prev == types[i+1] &&\n                 (prev == \"1\" || prev == \"n\")) types[i] = prev;\n        prev = type;\n      }\n\n      // W5. A sequence of European terminators adjacent to European\n      // numbers changes to all European numbers.\n      // W6. Otherwise, separators and terminators change to Other\n      // Neutral.\n      for (var i = 0; i < len; ++i) {\n        var type = types[i];\n        if (type == \",\") types[i] = \"N\";\n        else if (type == \"%\") {\n          for (var end = i + 1; end < len && types[end] == \"%\"; ++end) {}\n          var replace = (i && types[i-1] == \"!\") || (end < len && types[end] == \"1\") ? \"1\" : \"N\";\n          for (var j = i; j < end; ++j) types[j] = replace;\n          i = end - 1;\n        }\n      }\n\n      // W7. Search backwards from each instance of a European number\n      // until the first strong type (R, L, or sor) is found. If an L is\n      // found, then change the type of the European number to L.\n      for (var i = 0, cur = outerType; i < len; ++i) {\n        var type = types[i];\n        if (cur == \"L\" && type == \"1\") types[i] = \"L\";\n        else if (isStrong.test(type)) cur = type;\n      }\n\n      // N1. A sequence of neutrals takes the direction of the\n      // surrounding strong text if the text on both sides has the same\n      // direction. European and Arabic numbers act as if they were R in\n      // terms of their influence on neutrals. Start-of-level-run (sor)\n      // and end-of-level-run (eor) are used at level run boundaries.\n      // N2. Any remaining neutrals take the embedding direction.\n      for (var i = 0; i < len; ++i) {\n        if (isNeutral.test(types[i])) {\n          for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}\n          var before = (i ? types[i-1] : outerType) == \"L\";\n          var after = (end < len ? types[end] : outerType) == \"L\";\n          var replace = before || after ? \"L\" : \"R\";\n          for (var j = i; j < end; ++j) types[j] = replace;\n          i = end - 1;\n        }\n      }\n\n      // Here we depart from the documented algorithm, in order to avoid\n      // building up an actual levels array. Since there are only three\n      // levels (0, 1, 2) in an implementation that doesn't take\n      // explicit embedding into account, we can build up the order on\n      // the fly, without following the level-based algorithm.\n      var order = [], m;\n      for (var i = 0; i < len;) {\n        if (countsAsLeft.test(types[i])) {\n          var start = i;\n          for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}\n          order.push(new BidiSpan(0, start, i));\n        } else {\n          var pos = i, at = order.length;\n          for (++i; i < len && types[i] != \"L\"; ++i) {}\n          for (var j = pos; j < i;) {\n            if (countsAsNum.test(types[j])) {\n              if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));\n              var nstart = j;\n              for (++j; j < i && countsAsNum.test(types[j]); ++j) {}\n              order.splice(at, 0, new BidiSpan(2, nstart, j));\n              pos = j;\n            } else ++j;\n          }\n          if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));\n        }\n      }\n      if (order[0].level == 1 && (m = str.match(/^\\s+/))) {\n        order[0].from = m[0].length;\n        order.unshift(new BidiSpan(0, 0, m[0].length));\n      }\n      if (lst(order).level == 1 && (m = str.match(/\\s+$/))) {\n        lst(order).to -= m[0].length;\n        order.push(new BidiSpan(0, len - m[0].length, len));\n      }\n      if (order[0].level == 2)\n        order.unshift(new BidiSpan(1, order[0].to, order[0].to));\n      if (order[0].level != lst(order).level)\n        order.push(new BidiSpan(order[0].level, len, len));\n\n      return order;\n    };\n  })();\n\n  // THE END\n\n  CodeMirror.version = \"5.4.0\";\n\n  return CodeMirror;\n});\n"
  },
  {
    "path": "docs/libs/codemirror/mode/javascript/index.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: JavaScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../addon/comment/continuecomment.js\"></script>\n<script src=\"../../addon/comment/comment.js\"></script>\n<script src=\"javascript.js\"></script>\n<style type=\"text/css\">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"http://codemirror.net\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">JavaScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>JavaScript mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n// Demo code (the actual new parser character stream implementation)\n\nfunction StringStream(string) {\n  this.pos = 0;\n  this.string = string;\n}\n\nStringStream.prototype = {\n  done: function() {return this.pos >= this.string.length;},\n  peek: function() {return this.string.charAt(this.pos);},\n  next: function() {\n    if (this.pos &lt; this.string.length)\n      return this.string.charAt(this.pos++);\n  },\n  eat: function(match) {\n    var ch = this.string.charAt(this.pos);\n    if (typeof match == \"string\") var ok = ch == match;\n    else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);\n    if (ok) {this.pos++; return ch;}\n  },\n  eatWhile: function(match) {\n    var start = this.pos;\n    while (this.eat(match));\n    if (this.pos > start) return this.string.slice(start, this.pos);\n  },\n  backUp: function(n) {this.pos -= n;},\n  column: function() {return this.pos;},\n  eatSpace: function() {\n    var start = this.pos;\n    while (/\\s/.test(this.string.charAt(this.pos))) this.pos++;\n    return this.pos - start;\n  },\n  match: function(pattern, consume, caseInsensitive) {\n    if (typeof pattern == \"string\") {\n      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}\n      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {\n        if (consume !== false) this.pos += str.length;\n        return true;\n      }\n    }\n    else {\n      var match = this.string.slice(this.pos).match(pattern);\n      if (match &amp;&amp; consume !== false) this.pos += match[0].length;\n      return match;\n    }\n  }\n};\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        continueComments: \"Enter\",\n        extraKeys: {\"Ctrl-Q\": \"toggleComment\"}\n      });\n    </script>\n\n    <p>\n      JavaScript mode supports several configuration options:\n      <ul>\n        <li><code>json</code> which will set the mode to expect JSON\n        data rather than a JavaScript program.</li>\n        <li><code>jsonld</code> which will set the mode to expect\n        <a href=\"http://json-ld.org\">JSON-LD</a> linked data rather\n        than a JavaScript program (<a href=\"json-ld.html\">demo</a>).</li>\n        <li><code>typescript</code> which will activate additional\n        syntax highlighting and some other things for TypeScript code\n        (<a href=\"typescript.html\">demo</a>).</li>\n        <li><code>statementIndent</code> which (given a number) will\n        determine the amount of indentation to use for statements\n        continued on a new line.</li>\n        <li><code>wordCharacters</code>, a regexp that indicates which\n        characters should be considered part of an identifier.\n        Defaults to <code>/[\\w$]/</code>, which does not handle\n        non-ASCII identifiers. Can be set to something more elaborate\n        to improve Unicode support.</li>\n      </ul>\n    </p>\n\n    <p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>, <code>application/ld+json</code>, <code>text/typescript</code>, <code>application/typescript</code>.</p>\n  </article>\n"
  },
  {
    "path": "docs/libs/codemirror/mode/javascript/javascript.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/LICENSE\n\n// TODO actually recognize syntax of TypeScript constructs\n\n(function(mod) {\n  if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n    mod(require(\"../../lib/codemirror\"));\n  else if (typeof define == \"function\" && define.amd) // AMD\n    define([\"../../lib/codemirror\"], mod);\n  else // Plain browser env\n    mod(CodeMirror);\n})(function(CodeMirror) {\n\"use strict\";\n\nCodeMirror.defineMode(\"javascript\", function(config, parserConfig) {\n  var indentUnit = config.indentUnit;\n  var statementIndent = parserConfig.statementIndent;\n  var jsonldMode = parserConfig.jsonld;\n  var jsonMode = parserConfig.json || jsonldMode;\n  var isTS = parserConfig.typescript;\n  var wordRE = parserConfig.wordCharacters || /[\\w$\\xa1-\\uffff]/;\n\n  // Tokenizer\n\n  var keywords = function(){\n    function kw(type) {return {type: type, style: \"keyword\"};}\n    var A = kw(\"keyword a\"), B = kw(\"keyword b\"), C = kw(\"keyword c\");\n    var operator = kw(\"operator\"), atom = {type: \"atom\", style: \"atom\"};\n\n    var jsKeywords = {\n      \"if\": kw(\"if\"), \"while\": A, \"with\": A, \"else\": B, \"do\": B, \"try\": B, \"finally\": B,\n      \"return\": C, \"break\": C, \"continue\": C, \"new\": C, \"delete\": C, \"throw\": C, \"debugger\": C,\n      \"var\": kw(\"var\"), \"const\": kw(\"var\"), \"let\": kw(\"var\"),\n      \"function\": kw(\"function\"), \"catch\": kw(\"catch\"),\n      \"for\": kw(\"for\"), \"switch\": kw(\"switch\"), \"case\": kw(\"case\"), \"default\": kw(\"default\"),\n      \"in\": operator, \"typeof\": operator, \"instanceof\": operator,\n      \"true\": atom, \"false\": atom, \"null\": atom, \"undefined\": atom, \"NaN\": atom, \"Infinity\": atom,\n      \"this\": kw(\"this\"), \"module\": kw(\"module\"), \"class\": kw(\"class\"), \"super\": kw(\"atom\"),\n      \"yield\": C, \"export\": kw(\"export\"), \"import\": kw(\"import\"), \"extends\": C\n    };\n\n    // Extend the 'normal' keywords with the TypeScript language extensions\n    if (isTS) {\n      var type = {type: \"variable\", style: \"variable-3\"};\n      var tsKeywords = {\n        // object-like things\n        \"interface\": kw(\"interface\"),\n        \"extends\": kw(\"extends\"),\n        \"constructor\": kw(\"constructor\"),\n\n        // scope modifiers\n        \"public\": kw(\"public\"),\n        \"private\": kw(\"private\"),\n        \"protected\": kw(\"protected\"),\n        \"static\": kw(\"static\"),\n\n        // types\n        \"string\": type, \"number\": type, \"bool\": type, \"any\": type\n      };\n\n      for (var attr in tsKeywords) {\n        jsKeywords[attr] = tsKeywords[attr];\n      }\n    }\n\n    return jsKeywords;\n  }();\n\n  var isOperatorChar = /[+\\-*&%=<>!?|~^]/;\n  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)\"/;\n\n  function readRegexp(stream) {\n    var escaped = false, next, inSet = false;\n    while ((next = stream.next()) != null) {\n      if (!escaped) {\n        if (next == \"/\" && !inSet) return;\n        if (next == \"[\") inSet = true;\n        else if (inSet && next == \"]\") inSet = false;\n      }\n      escaped = !escaped && next == \"\\\\\";\n    }\n  }\n\n  // Used as scratch variables to communicate multiple values without\n  // consing up tons of objects.\n  var type, content;\n  function ret(tp, style, cont) {\n    type = tp; content = cont;\n    return style;\n  }\n  function tokenBase(stream, state) {\n    var ch = stream.next();\n    if (ch == '\"' || ch == \"'\") {\n      state.tokenize = tokenString(ch);\n      return state.tokenize(stream, state);\n    } else if (ch == \".\" && stream.match(/^\\d+(?:[eE][+\\-]?\\d+)?/)) {\n      return ret(\"number\", \"number\");\n    } else if (ch == \".\" && stream.match(\"..\")) {\n      return ret(\"spread\", \"meta\");\n    } else if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch)) {\n      return ret(ch);\n    } else if (ch == \"=\" && stream.eat(\">\")) {\n      return ret(\"=>\", \"operator\");\n    } else if (ch == \"0\" && stream.eat(/x/i)) {\n      stream.eatWhile(/[\\da-f]/i);\n      return ret(\"number\", \"number\");\n    } else if (/\\d/.test(ch)) {\n      stream.match(/^\\d*(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/);\n      return ret(\"number\", \"number\");\n    } else if (ch == \"/\") {\n      if (stream.eat(\"*\")) {\n        state.tokenize = tokenComment;\n        return tokenComment(stream, state);\n      } else if (stream.eat(\"/\")) {\n        stream.skipToEnd();\n        return ret(\"comment\", \"comment\");\n      } else if (state.lastType == \"operator\" || state.lastType == \"keyword c\" ||\n               state.lastType == \"sof\" || /^[\\[{}\\(,;:]$/.test(state.lastType)) {\n        readRegexp(stream);\n        stream.match(/^\\b(([gimyu])(?![gimyu]*\\2))+\\b/);\n        return ret(\"regexp\", \"string-2\");\n      } else {\n        stream.eatWhile(isOperatorChar);\n        return ret(\"operator\", \"operator\", stream.current());\n      }\n    } else if (ch == \"`\") {\n      state.tokenize = tokenQuasi;\n      return tokenQuasi(stream, state);\n    } else if (ch == \"#\") {\n      stream.skipToEnd();\n      return ret(\"error\", \"error\");\n    } else if (isOperatorChar.test(ch)) {\n      stream.eatWhile(isOperatorChar);\n      return ret(\"operator\", \"operator\", stream.current());\n    } else if (wordRE.test(ch)) {\n      stream.eatWhile(wordRE);\n      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];\n      return (known && state.lastType != \".\") ? ret(known.type, known.style, word) :\n                     ret(\"variable\", \"variable\", word);\n    }\n  }\n\n  function tokenString(quote) {\n    return function(stream, state) {\n      var escaped = false, next;\n      if (jsonldMode && stream.peek() == \"@\" && stream.match(isJsonldKeyword)){\n        state.tokenize = tokenBase;\n        return ret(\"jsonld-keyword\", \"meta\");\n      }\n      while ((next = stream.next()) != null) {\n        if (next == quote && !escaped) break;\n        escaped = !escaped && next == \"\\\\\";\n      }\n      if (!escaped) state.tokenize = tokenBase;\n      return ret(\"string\", \"string\");\n    };\n  }\n\n  function tokenComment(stream, state) {\n    var maybeEnd = false, ch;\n    while (ch = stream.next()) {\n      if (ch == \"/\" && maybeEnd) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      maybeEnd = (ch == \"*\");\n    }\n    return ret(\"comment\", \"comment\");\n  }\n\n  function tokenQuasi(stream, state) {\n    var escaped = false, next;\n    while ((next = stream.next()) != null) {\n      if (!escaped && (next == \"`\" || next == \"$\" && stream.eat(\"{\"))) {\n        state.tokenize = tokenBase;\n        break;\n      }\n      escaped = !escaped && next == \"\\\\\";\n    }\n    return ret(\"quasi\", \"string-2\", stream.current());\n  }\n\n  var brackets = \"([{}])\";\n  // This is a crude lookahead trick to try and notice that we're\n  // parsing the argument patterns for a fat-arrow function before we\n  // actually hit the arrow token. It only works if the arrow is on\n  // the same line as the arguments and there's no strange noise\n  // (comments) in between. Fallback is to only notice when we hit the\n  // arrow, and not declare the arguments as locals for the arrow\n  // body.\n  function findFatArrow(stream, state) {\n    if (state.fatArrowAt) state.fatArrowAt = null;\n    var arrow = stream.string.indexOf(\"=>\", stream.start);\n    if (arrow < 0) return;\n\n    var depth = 0, sawSomething = false;\n    for (var pos = arrow - 1; pos >= 0; --pos) {\n      var ch = stream.string.charAt(pos);\n      var bracket = brackets.indexOf(ch);\n      if (bracket >= 0 && bracket < 3) {\n        if (!depth) { ++pos; break; }\n        if (--depth == 0) break;\n      } else if (bracket >= 3 && bracket < 6) {\n        ++depth;\n      } else if (wordRE.test(ch)) {\n        sawSomething = true;\n      } else if (/[\"'\\/]/.test(ch)) {\n        return;\n      } else if (sawSomething && !depth) {\n        ++pos;\n        break;\n      }\n    }\n    if (sawSomething && !depth) state.fatArrowAt = pos;\n  }\n\n  // Parser\n\n  var atomicTypes = {\"atom\": true, \"number\": true, \"variable\": true, \"string\": true, \"regexp\": true, \"this\": true, \"jsonld-keyword\": true};\n\n  function JSLexical(indented, column, type, align, prev, info) {\n    this.indented = indented;\n    this.column = column;\n    this.type = type;\n    this.prev = prev;\n    this.info = info;\n    if (align != null) this.align = align;\n  }\n\n  function inScope(state, varname) {\n    for (var v = state.localVars; v; v = v.next)\n      if (v.name == varname) return true;\n    for (var cx = state.context; cx; cx = cx.prev) {\n      for (var v = cx.vars; v; v = v.next)\n        if (v.name == varname) return true;\n    }\n  }\n\n  function parseJS(state, style, type, content, stream) {\n    var cc = state.cc;\n    // Communicate our context to the combinators.\n    // (Less wasteful than consing up a hundred closures on every call.)\n    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;\n\n    if (!state.lexical.hasOwnProperty(\"align\"))\n      state.lexical.align = true;\n\n    while(true) {\n      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;\n      if (combinator(type, content)) {\n        while(cc.length && cc[cc.length - 1].lex)\n          cc.pop()();\n        if (cx.marked) return cx.marked;\n        if (type == \"variable\" && inScope(state, content)) return \"variable-2\";\n        return style;\n      }\n    }\n  }\n\n  // Combinator utils\n\n  var cx = {state: null, column: null, marked: null, cc: null};\n  function pass() {\n    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);\n  }\n  function cont() {\n    pass.apply(null, arguments);\n    return true;\n  }\n  function register(varname) {\n    function inList(list) {\n      for (var v = list; v; v = v.next)\n        if (v.name == varname) return true;\n      return false;\n    }\n    var state = cx.state;\n    if (state.context) {\n      cx.marked = \"def\";\n      if (inList(state.localVars)) return;\n      state.localVars = {name: varname, next: state.localVars};\n    } else {\n      if (inList(state.globalVars)) return;\n      if (parserConfig.globalVars)\n        state.globalVars = {name: varname, next: state.globalVars};\n    }\n  }\n\n  // Combinators\n\n  var defaultVars = {name: \"this\", next: {name: \"arguments\"}};\n  function pushcontext() {\n    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};\n    cx.state.localVars = defaultVars;\n  }\n  function popcontext() {\n    cx.state.localVars = cx.state.context.vars;\n    cx.state.context = cx.state.context.prev;\n  }\n  function pushlex(type, info) {\n    var result = function() {\n      var state = cx.state, indent = state.indented;\n      if (state.lexical.type == \"stat\") indent = state.lexical.indented;\n      else for (var outer = state.lexical; outer && outer.type == \")\" && outer.align; outer = outer.prev)\n        indent = outer.indented;\n      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);\n    };\n    result.lex = true;\n    return result;\n  }\n  function poplex() {\n    var state = cx.state;\n    if (state.lexical.prev) {\n      if (state.lexical.type == \")\")\n        state.indented = state.lexical.indented;\n      state.lexical = state.lexical.prev;\n    }\n  }\n  poplex.lex = true;\n\n  function expect(wanted) {\n    function exp(type) {\n      if (type == wanted) return cont();\n      else if (wanted == \";\") return pass();\n      else return cont(exp);\n    };\n    return exp;\n  }\n\n  function statement(type, value) {\n    if (type == \"var\") return cont(pushlex(\"vardef\", value.length), vardef, expect(\";\"), poplex);\n    if (type == \"keyword a\") return cont(pushlex(\"form\"), expression, statement, poplex);\n    if (type == \"keyword b\") return cont(pushlex(\"form\"), statement, poplex);\n    if (type == \"{\") return cont(pushlex(\"}\"), block, poplex);\n    if (type == \";\") return cont();\n    if (type == \"if\") {\n      if (cx.state.lexical.info == \"else\" && cx.state.cc[cx.state.cc.length - 1] == poplex)\n        cx.state.cc.pop()();\n      return cont(pushlex(\"form\"), expression, statement, poplex, maybeelse);\n    }\n    if (type == \"function\") return cont(functiondef);\n    if (type == \"for\") return cont(pushlex(\"form\"), forspec, statement, poplex);\n    if (type == \"variable\") return cont(pushlex(\"stat\"), maybelabel);\n    if (type == \"switch\") return cont(pushlex(\"form\"), expression, pushlex(\"}\", \"switch\"), expect(\"{\"),\n                                      block, poplex, poplex);\n    if (type == \"case\") return cont(expression, expect(\":\"));\n    if (type == \"default\") return cont(expect(\":\"));\n    if (type == \"catch\") return cont(pushlex(\"form\"), pushcontext, expect(\"(\"), funarg, expect(\")\"),\n                                     statement, poplex, popcontext);\n    if (type == \"module\") return cont(pushlex(\"form\"), pushcontext, afterModule, popcontext, poplex);\n    if (type == \"class\") return cont(pushlex(\"form\"), className, poplex);\n    if (type == \"export\") return cont(pushlex(\"form\"), afterExport, poplex);\n    if (type == \"import\") return cont(pushlex(\"form\"), afterImport, poplex);\n    return pass(pushlex(\"stat\"), expression, expect(\";\"), poplex);\n  }\n  function expression(type) {\n    return expressionInner(type, false);\n  }\n  function expressionNoComma(type) {\n    return expressionInner(type, true);\n  }\n  function expressionInner(type, noComma) {\n    if (cx.state.fatArrowAt == cx.stream.start) {\n      var body = noComma ? arrowBodyNoComma : arrowBody;\n      if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(pattern, \")\"), poplex, expect(\"=>\"), body, popcontext);\n      else if (type == \"variable\") return pass(pushcontext, pattern, expect(\"=>\"), body, popcontext);\n    }\n\n    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;\n    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);\n    if (type == \"function\") return cont(functiondef, maybeop);\n    if (type == \"keyword c\") return cont(noComma ? maybeexpressionNoComma : maybeexpression);\n    if (type == \"(\") return cont(pushlex(\")\"), maybeexpression, comprehension, expect(\")\"), poplex, maybeop);\n    if (type == \"operator\" || type == \"spread\") return cont(noComma ? expressionNoComma : expression);\n    if (type == \"[\") return cont(pushlex(\"]\"), arrayLiteral, poplex, maybeop);\n    if (type == \"{\") return contCommasep(objprop, \"}\", null, maybeop);\n    if (type == \"quasi\") { return pass(quasi, maybeop); }\n    return cont();\n  }\n  function maybeexpression(type) {\n    if (type.match(/[;\\}\\)\\],]/)) return pass();\n    return pass(expression);\n  }\n  function maybeexpressionNoComma(type) {\n    if (type.match(/[;\\}\\)\\],]/)) return pass();\n    return pass(expressionNoComma);\n  }\n\n  function maybeoperatorComma(type, value) {\n    if (type == \",\") return cont(expression);\n    return maybeoperatorNoComma(type, value, false);\n  }\n  function maybeoperatorNoComma(type, value, noComma) {\n    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;\n    var expr = noComma == false ? expression : expressionNoComma;\n    if (type == \"=>\") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);\n    if (type == \"operator\") {\n      if (/\\+\\+|--/.test(value)) return cont(me);\n      if (value == \"?\") return cont(expression, expect(\":\"), expr);\n      return cont(expr);\n    }\n    if (type == \"quasi\") { return pass(quasi, me); }\n    if (type == \";\") return;\n    if (type == \"(\") return contCommasep(expressionNoComma, \")\", \"call\", me);\n    if (type == \".\") return cont(property, me);\n    if (type == \"[\") return cont(pushlex(\"]\"), maybeexpression, expect(\"]\"), poplex, me);\n  }\n  function quasi(type, value) {\n    if (type != \"quasi\") return pass();\n    if (value.slice(value.length - 2) != \"${\") return cont(quasi);\n    return cont(expression, continueQuasi);\n  }\n  function continueQuasi(type) {\n    if (type == \"}\") {\n      cx.marked = \"string-2\";\n      cx.state.tokenize = tokenQuasi;\n      return cont(quasi);\n    }\n  }\n  function arrowBody(type) {\n    findFatArrow(cx.stream, cx.state);\n    return pass(type == \"{\" ? statement : expression);\n  }\n  function arrowBodyNoComma(type) {\n    findFatArrow(cx.stream, cx.state);\n    return pass(type == \"{\" ? statement : expressionNoComma);\n  }\n  function maybelabel(type) {\n    if (type == \":\") return cont(poplex, statement);\n    return pass(maybeoperatorComma, expect(\";\"), poplex);\n  }\n  function property(type) {\n    if (type == \"variable\") {cx.marked = \"property\"; return cont();}\n  }\n  function objprop(type, value) {\n    if (type == \"variable\" || cx.style == \"keyword\") {\n      cx.marked = \"property\";\n      if (value == \"get\" || value == \"set\") return cont(getterSetter);\n      return cont(afterprop);\n    } else if (type == \"number\" || type == \"string\") {\n      cx.marked = jsonldMode ? \"property\" : (cx.style + \" property\");\n      return cont(afterprop);\n    } else if (type == \"jsonld-keyword\") {\n      return cont(afterprop);\n    } else if (type == \"[\") {\n      return cont(expression, expect(\"]\"), afterprop);\n    }\n  }\n  function getterSetter(type) {\n    if (type != \"variable\") return pass(afterprop);\n    cx.marked = \"property\";\n    return cont(functiondef);\n  }\n  function afterprop(type) {\n    if (type == \":\") return cont(expressionNoComma);\n    if (type == \"(\") return pass(functiondef);\n  }\n  function commasep(what, end) {\n    function proceed(type) {\n      if (type == \",\") {\n        var lex = cx.state.lexical;\n        if (lex.info == \"call\") lex.pos = (lex.pos || 0) + 1;\n        return cont(what, proceed);\n      }\n      if (type == end) return cont();\n      return cont(expect(end));\n    }\n    return function(type) {\n      if (type == end) return cont();\n      return pass(what, proceed);\n    };\n  }\n  function contCommasep(what, end, info) {\n    for (var i = 3; i < arguments.length; i++)\n      cx.cc.push(arguments[i]);\n    return cont(pushlex(end, info), commasep(what, end), poplex);\n  }\n  function block(type) {\n    if (type == \"}\") return cont();\n    return pass(statement, block);\n  }\n  function maybetype(type) {\n    if (isTS && type == \":\") return cont(typedef);\n  }\n  function maybedefault(_, value) {\n    if (value == \"=\") return cont(expressionNoComma);\n  }\n  function typedef(type) {\n    if (type == \"variable\") {cx.marked = \"variable-3\"; return cont();}\n  }\n  function vardef() {\n    return pass(pattern, maybetype, maybeAssign, vardefCont);\n  }\n  function pattern(type, value) {\n    if (type == \"variable\") { register(value); return cont(); }\n    if (type == \"[\") return contCommasep(pattern, \"]\");\n    if (type == \"{\") return contCommasep(proppattern, \"}\");\n  }\n  function proppattern(type, value) {\n    if (type == \"variable\" && !cx.stream.match(/^\\s*:/, false)) {\n      register(value);\n      return cont(maybeAssign);\n    }\n    if (type == \"variable\") cx.marked = \"property\";\n    return cont(expect(\":\"), pattern, maybeAssign);\n  }\n  function maybeAssign(_type, value) {\n    if (value == \"=\") return cont(expressionNoComma);\n  }\n  function vardefCont(type) {\n    if (type == \",\") return cont(vardef);\n  }\n  function maybeelse(type, value) {\n    if (type == \"keyword b\" && value == \"else\") return cont(pushlex(\"form\", \"else\"), statement, poplex);\n  }\n  function forspec(type) {\n    if (type == \"(\") return cont(pushlex(\")\"), forspec1, expect(\")\"), poplex);\n  }\n  function forspec1(type) {\n    if (type == \"var\") return cont(vardef, expect(\";\"), forspec2);\n    if (type == \";\") return cont(forspec2);\n    if (type == \"variable\") return cont(formaybeinof);\n    return pass(expression, expect(\";\"), forspec2);\n  }\n  function formaybeinof(_type, value) {\n    if (value == \"in\" || value == \"of\") { cx.marked = \"keyword\"; return cont(expression); }\n    return cont(maybeoperatorComma, forspec2);\n  }\n  function forspec2(type, value) {\n    if (type == \";\") return cont(forspec3);\n    if (value == \"in\" || value == \"of\") { cx.marked = \"keyword\"; return cont(expression); }\n    return pass(expression, expect(\";\"), forspec3);\n  }\n  function forspec3(type) {\n    if (type != \")\") cont(expression);\n  }\n  function functiondef(type, value) {\n    if (value == \"*\") {cx.marked = \"keyword\"; return cont(functiondef);}\n    if (type == \"variable\") {register(value); return cont(functiondef);}\n    if (type == \"(\") return cont(pushcontext, pushlex(\")\"), commasep(funarg, \")\"), poplex, statement, popcontext);\n  }\n  function funarg(type) {\n    if (type == \"spread\") return cont(funarg);\n    return pass(pattern, maybetype, maybedefault);\n  }\n  function className(type, value) {\n    if (type == \"variable\") {register(value); return cont(classNameAfter);}\n  }\n  function classNameAfter(type, value) {\n    if (value == \"extends\") return cont(expression, classNameAfter);\n    if (type == \"{\") return cont(pushlex(\"}\"), classBody, poplex);\n  }\n  function classBody(type, value) {\n    if (type == \"variable\" || cx.style == \"keyword\") {\n      if (value == \"static\") {\n        cx.marked = \"keyword\";\n        return cont(classBody);\n      }\n      cx.marked = \"property\";\n      if (value == \"get\" || value == \"set\") return cont(classGetterSetter, functiondef, classBody);\n      return cont(functiondef, classBody);\n    }\n    if (value == \"*\") {\n      cx.marked = \"keyword\";\n      return cont(classBody);\n    }\n    if (type == \";\") return cont(classBody);\n    if (type == \"}\") return cont();\n  }\n  function classGetterSetter(type) {\n    if (type != \"variable\") return pass();\n    cx.marked = \"property\";\n    return cont();\n  }\n  function afterModule(type, value) {\n    if (type == \"string\") return cont(statement);\n    if (type == \"variable\") { register(value); return cont(maybeFrom); }\n  }\n  function afterExport(_type, value) {\n    if (value == \"*\") { cx.marked = \"keyword\"; return cont(maybeFrom, expect(\";\")); }\n    if (value == \"default\") { cx.marked = \"keyword\"; return cont(expression, expect(\";\")); }\n    return pass(statement);\n  }\n  function afterImport(type) {\n    if (type == \"string\") return cont();\n    return pass(importSpec, maybeFrom);\n  }\n  function importSpec(type, value) {\n    if (type == \"{\") return contCommasep(importSpec, \"}\");\n    if (type == \"variable\") register(value);\n    if (value == \"*\") cx.marked = \"keyword\";\n    return cont(maybeAs);\n  }\n  function maybeAs(_type, value) {\n    if (value == \"as\") { cx.marked = \"keyword\"; return cont(importSpec); }\n  }\n  function maybeFrom(_type, value) {\n    if (value == \"from\") { cx.marked = \"keyword\"; return cont(expression); }\n  }\n  function arrayLiteral(type) {\n    if (type == \"]\") return cont();\n    return pass(expressionNoComma, maybeArrayComprehension);\n  }\n  function maybeArrayComprehension(type) {\n    if (type == \"for\") return pass(comprehension, expect(\"]\"));\n    if (type == \",\") return cont(commasep(maybeexpressionNoComma, \"]\"));\n    return pass(commasep(expressionNoComma, \"]\"));\n  }\n  function comprehension(type) {\n    if (type == \"for\") return cont(forspec, comprehension);\n    if (type == \"if\") return cont(expression, comprehension);\n  }\n\n  function isContinuedStatement(state, textAfter) {\n    return state.lastType == \"operator\" || state.lastType == \",\" ||\n      isOperatorChar.test(textAfter.charAt(0)) ||\n      /[,.]/.test(textAfter.charAt(0));\n  }\n\n  // Interface\n\n  return {\n    startState: function(basecolumn) {\n      var state = {\n        tokenize: tokenBase,\n        lastType: \"sof\",\n        cc: [],\n        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, \"block\", false),\n        localVars: parserConfig.localVars,\n        context: parserConfig.localVars && {vars: parserConfig.localVars},\n        indented: 0\n      };\n      if (parserConfig.globalVars && typeof parserConfig.globalVars == \"object\")\n        state.globalVars = parserConfig.globalVars;\n      return state;\n    },\n\n    token: function(stream, state) {\n      if (stream.sol()) {\n        if (!state.lexical.hasOwnProperty(\"align\"))\n          state.lexical.align = false;\n        state.indented = stream.indentation();\n        findFatArrow(stream, state);\n      }\n      if (state.tokenize != tokenComment && stream.eatSpace()) return null;\n      var style = state.tokenize(stream, state);\n      if (type == \"comment\") return style;\n      state.lastType = type == \"operator\" && (content == \"++\" || content == \"--\") ? \"incdec\" : type;\n      return parseJS(state, style, type, content, stream);\n    },\n\n    indent: function(state, textAfter) {\n      if (state.tokenize == tokenComment) return CodeMirror.Pass;\n      if (state.tokenize != tokenBase) return 0;\n      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;\n      // Kludge to prevent 'maybelse' from blocking lexical scope pops\n      if (!/^\\s*else\\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {\n        var c = state.cc[i];\n        if (c == poplex) lexical = lexical.prev;\n        else if (c != maybeelse) break;\n      }\n      if (lexical.type == \"stat\" && firstChar == \"}\") lexical = lexical.prev;\n      if (statementIndent && lexical.type == \")\" && lexical.prev.type == \"stat\")\n        lexical = lexical.prev;\n      var type = lexical.type, closing = firstChar == type;\n\n      if (type == \"vardef\") return lexical.indented + (state.lastType == \"operator\" || state.lastType == \",\" ? lexical.info + 1 : 0);\n      else if (type == \"form\" && firstChar == \"{\") return lexical.indented;\n      else if (type == \"form\") return lexical.indented + indentUnit;\n      else if (type == \"stat\")\n        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);\n      else if (lexical.info == \"switch\" && !closing && parserConfig.doubleIndentSwitch != false)\n        return lexical.indented + (/^(?:case|default)\\b/.test(textAfter) ? indentUnit : 2 * indentUnit);\n      else if (lexical.align) return lexical.column + (closing ? 0 : 1);\n      else return lexical.indented + (closing ? 0 : indentUnit);\n    },\n\n    electricInput: /^\\s*(?:case .*?:|default:|\\{|\\})$/,\n    blockCommentStart: jsonMode ? null : \"/*\",\n    blockCommentEnd: jsonMode ? null : \"*/\",\n    lineComment: jsonMode ? null : \"//\",\n    fold: \"brace\",\n    closeBrackets: \"()[]{}''\\\"\\\"``\",\n\n    helperType: jsonMode ? \"json\" : \"javascript\",\n    jsonldMode: jsonldMode,\n    jsonMode: jsonMode\n  };\n});\n\nCodeMirror.registerHelper(\"wordChars\", \"javascript\", /[\\w$]/);\n\nCodeMirror.defineMIME(\"text/javascript\", \"javascript\");\nCodeMirror.defineMIME(\"text/ecmascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/javascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/x-javascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/ecmascript\", \"javascript\");\nCodeMirror.defineMIME(\"application/json\", {name: \"javascript\", json: true});\nCodeMirror.defineMIME(\"application/x-json\", {name: \"javascript\", json: true});\nCodeMirror.defineMIME(\"application/ld+json\", {name: \"javascript\", jsonld: true});\nCodeMirror.defineMIME(\"text/typescript\", { name: \"javascript\", typescript: true });\nCodeMirror.defineMIME(\"application/typescript\", { name: \"javascript\", typescript: true });\n\n});\n"
  },
  {
    "path": "docs/libs/codemirror/mode/javascript/json-ld.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: JSON-LD mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"../../addon/edit/matchbrackets.js\"></script>\n<script src=\"../../addon/comment/continuecomment.js\"></script>\n<script src=\"../../addon/comment/comment.js\"></script>\n<script src=\"javascript.js\"></script>\n<style type=\"text/css\">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=\"nav\">\n  <a href=\"http://codemirror.net\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\"/></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">JSON-LD</a>\n  </ul>\n</div>\n\n<article>\n<h2>JSON-LD mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\n{\n  \"@context\": {\n    \"name\": \"http://schema.org/name\",\n    \"description\": \"http://schema.org/description\",\n    \"image\": {\n      \"@id\": \"http://schema.org/image\",\n      \"@type\": \"@id\"\n    },\n    \"geo\": \"http://schema.org/geo\",\n    \"latitude\": {\n      \"@id\": \"http://schema.org/latitude\",\n      \"@type\": \"xsd:float\"\n    },\n    \"longitude\": {\n      \"@id\": \"http://schema.org/longitude\",\n      \"@type\": \"xsd:float\"\n    },\n    \"xsd\": \"http://www.w3.org/2001/XMLSchema#\"\n  },\n  \"name\": \"The Empire State Building\",\n  \"description\": \"The Empire State Building is a 102-story landmark in New York City.\",\n  \"image\": \"http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg\",\n  \"geo\": {\n    \"latitude\": \"40.75\",\n    \"longitude\": \"73.98\"\n  }\n}\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        matchBrackets: true,\n        autoCloseBrackets: true,\n        mode: \"application/ld+json\",\n        lineWrapping: true\n      });\n    </script>\n    \n    <p>This is a specialization of the <a href=\"index.html\">JavaScript mode</a>.</p>\n  </article>\n"
  },
  {
    "path": "docs/libs/codemirror/mode/javascript/test.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/LICENSE\n\n(function() {\n  var mode = CodeMirror.getMode({indentUnit: 2}, \"javascript\");\n  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }\n\n  MT(\"locals\",\n     \"[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }\");\n\n  MT(\"comma-and-binop\",\n     \"[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }\");\n\n  MT(\"destructuring\",\n     \"([keyword function]([def a], [[[def b], [def c] ]]) {\",\n     \"  [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);\",\n     \"  [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];\",\n     \"})();\");\n\n  MT(\"class_body\",\n     \"[keyword class] [variable Foo] {\",\n     \"  [property constructor]() {}\",\n     \"  [property sayName]() {\",\n     \"    [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"class\",\n     \"[keyword class] [variable Point] [keyword extends] [variable SuperThing] {\",\n     \"  [property get] [property prop]() { [keyword return] [number 24]; }\",\n     \"  [property constructor]([def x], [def y]) {\",\n     \"    [keyword super]([string 'something']);\",\n     \"    [keyword this].[property x] [operator =] [variable-2 x];\",\n     \"  }\",\n     \"}\");\n\n  MT(\"module\",\n     \"[keyword module] [string 'foo'] {\",\n     \"  [keyword export] [keyword let] [def x] [operator =] [number 42];\",\n     \"  [keyword export] [keyword *] [keyword from] [string 'somewhere'];\",\n     \"}\");\n\n  MT(\"import\",\n     \"[keyword function] [variable foo]() {\",\n     \"  [keyword import] [def $] [keyword from] [string 'jquery'];\",\n     \"  [keyword module] [def crypto] [keyword from] [string 'crypto'];\",\n     \"  [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];\",\n     \"}\");\n\n  MT(\"const\",\n     \"[keyword function] [variable f]() {\",\n     \"  [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];\",\n     \"}\");\n\n  MT(\"for/of\",\n     \"[keyword for]([keyword let] [variable of] [keyword of] [variable something]) {}\");\n\n  MT(\"generator\",\n     \"[keyword function*] [variable repeat]([def n]) {\",\n     \"  [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])\",\n     \"    [keyword yield] [variable-2 i];\",\n     \"}\");\n\n  MT(\"quotedStringAddition\",\n     \"[keyword let] [variable f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];\");\n\n  MT(\"quotedFatArrow\",\n     \"[keyword let] [variable f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];\");\n\n  MT(\"fatArrow\",\n     \"[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);\",\n     \"[variable a];\", // No longer in scope\n     \"[keyword let] [variable f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];\",\n     \"[variable c];\");\n\n  MT(\"spread\",\n     \"[keyword function] [variable f]([def a], [meta ...][def b]) {\",\n     \"  [variable something]([variable-2 a], [meta ...][variable-2 b]);\",\n     \"}\");\n\n  MT(\"comprehension\",\n     \"[keyword function] [variable f]() {\",\n     \"  [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];\",\n     \"  ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));\",\n     \"}\");\n\n  MT(\"quasi\",\n     \"[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]\");\n\n  MT(\"quasi_no_function\",\n     \"[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]\");\n\n  MT(\"indent_statement\",\n     \"[keyword var] [variable x] [operator =] [number 10]\",\n     \"[variable x] [operator +=] [variable y] [operator +]\",\n     \"  [atom Infinity]\",\n     \"[keyword debugger];\");\n\n  MT(\"indent_if\",\n     \"[keyword if] ([number 1])\",\n     \"  [keyword break];\",\n     \"[keyword else] [keyword if] ([number 2])\",\n     \"  [keyword continue];\",\n     \"[keyword else]\",\n     \"  [number 10];\",\n     \"[keyword if] ([number 1]) {\",\n     \"  [keyword break];\",\n     \"} [keyword else] [keyword if] ([number 2]) {\",\n     \"  [keyword continue];\",\n     \"} [keyword else] {\",\n     \"  [number 10];\",\n     \"}\");\n\n  MT(\"indent_for\",\n     \"[keyword for] ([keyword var] [variable i] [operator =] [number 0];\",\n     \"     [variable i] [operator <] [number 100];\",\n     \"     [variable i][operator ++])\",\n     \"  [variable doSomething]([variable i]);\",\n     \"[keyword debugger];\");\n\n  MT(\"indent_c_style\",\n     \"[keyword function] [variable foo]()\",\n     \"{\",\n     \"  [keyword debugger];\",\n     \"}\");\n\n  MT(\"indent_else\",\n     \"[keyword for] (;;)\",\n     \"  [keyword if] ([variable foo])\",\n     \"    [keyword if] ([variable bar])\",\n     \"      [number 1];\",\n     \"    [keyword else]\",\n     \"      [number 2];\",\n     \"  [keyword else]\",\n     \"    [number 3];\");\n\n  MT(\"indent_funarg\",\n     \"[variable foo]([number 10000],\",\n     \"    [keyword function]([def a]) {\",\n     \"  [keyword debugger];\",\n     \"};\");\n\n  MT(\"indent_below_if\",\n     \"[keyword for] (;;)\",\n     \"  [keyword if] ([variable foo])\",\n     \"    [number 1];\",\n     \"[number 2];\");\n\n  MT(\"multilinestring\",\n     \"[keyword var] [variable x] [operator =] [string 'foo\\\\]\",\n     \"[string bar'];\");\n\n  MT(\"scary_regexp\",\n     \"[string-2 /foo[[/]]bar/];\");\n\n  MT(\"indent_strange_array\",\n     \"[keyword var] [variable x] [operator =] [[\",\n     \"  [number 1],,\",\n     \"  [number 2],\",\n     \"]];\",\n     \"[number 10];\");\n\n  MT(\"param_default\",\n     \"[keyword function] [variable foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {\",\n     \"  [keyword return] [variable-2 x];\",\n     \"}\");\n\n  var jsonld_mode = CodeMirror.getMode(\n    {indentUnit: 2},\n    {name: \"javascript\", jsonld: true}\n  );\n  function LD(name) {\n    test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));\n  }\n\n  LD(\"json_ld_keywords\",\n    '{',\n    '  [meta \"@context\"]: {',\n    '    [meta \"@base\"]: [string \"http://example.com\"],',\n    '    [meta \"@vocab\"]: [string \"http://xmlns.com/foaf/0.1/\"],',\n    '    [property \"likesFlavor\"]: {',\n    '      [meta \"@container\"]: [meta \"@list\"]',\n    '      [meta \"@reverse\"]: [string \"@beFavoriteOf\"]',\n    '    },',\n    '    [property \"nick\"]: { [meta \"@container\"]: [meta \"@set\"] },',\n    '    [property \"nick\"]: { [meta \"@container\"]: [meta \"@index\"] }',\n    '  },',\n    '  [meta \"@graph\"]: [[ {',\n    '    [meta \"@id\"]: [string \"http://dbpedia.org/resource/John_Lennon\"],',\n    '    [property \"name\"]: [string \"John Lennon\"],',\n    '    [property \"modified\"]: {',\n    '      [meta \"@value\"]: [string \"2010-05-29T14:17:39+02:00\"],',\n    '      [meta \"@type\"]: [string \"http://www.w3.org/2001/XMLSchema#dateTime\"]',\n    '    }',\n    '  } ]]',\n    '}');\n\n  LD(\"json_ld_fake\",\n    '{',\n    '  [property \"@fake\"]: [string \"@fake\"],',\n    '  [property \"@contextual\"]: [string \"@identifier\"],',\n    '  [property \"user@domain.com\"]: [string \"@graphical\"],',\n    '  [property \"@ID\"]: [string \"@@ID\"]',\n    '}');\n})();\n"
  },
  {
    "path": "docs/libs/codemirror/mode/javascript/typescript.html",
    "content": "<!doctype html>\n\n<title>CodeMirror: TypeScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.css\">\n\n<link rel=\"stylesheet\" href=\"../../lib/codemirror.css\">\n<script src=\"../../lib/codemirror.js\"></script>\n<script src=\"javascript.js\"></script>\n<style type=\"text/css\">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>\n<div id=nav>\n  <a href=\"http://codemirror.net\"><h1>CodeMirror</h1><img id=logo src=\"../../doc/logo.png\"></a>\n\n  <ul>\n    <li><a href=\"../../index.html\">Home</a>\n    <li><a href=\"../../doc/manual.html\">Manual</a>\n    <li><a href=\"https://github.com/codemirror/codemirror\">Code</a>\n  </ul>\n  <ul>\n    <li><a href=\"../index.html\">Language modes</a>\n    <li><a class=active href=\"#\">TypeScript</a>\n  </ul>\n</div>\n\n<article>\n<h2>TypeScript mode</h2>\n\n\n<div><textarea id=\"code\" name=\"code\">\nclass Greeter {\n\tgreeting: string;\n\tconstructor (message: string) {\n\t\tthis.greeting = message;\n\t}\n\tgreet() {\n\t\treturn \"Hello, \" + this.greeting;\n\t}\n}   \n\nvar greeter = new Greeter(\"world\");\n\nvar button = document.createElement('button')\nbutton.innerText = \"Say Hello\"\nbutton.onclick = function() {\n\talert(greeter.greet())\n}\n\ndocument.body.appendChild(button)\n\n</textarea></div>\n\n    <script>\n      var editor = CodeMirror.fromTextArea(document.getElementById(\"code\"), {\n        lineNumbers: true,\n        matchBrackets: true,\n        mode: \"text/typescript\"\n      });\n    </script>\n\n    <p>This is a specialization of the <a href=\"index.html\">JavaScript mode</a>.</p>\n  </article>\n"
  },
  {
    "path": "docs/libs/codemirror/mode/shell/shell.js",
    "content": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/5/LICENSE\n\n(function(mod) {\n    if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n      mod(require(\"../../lib/codemirror\"));\n    else if (typeof define == \"function\" && define.amd) // AMD\n      define([\"../../lib/codemirror\"], mod);\n    else // Plain browser env\n      mod(CodeMirror);\n  })(function(CodeMirror) {\n  \"use strict\";\n  \n  CodeMirror.defineMode('shell', function() {\n  \n    var words = {};\n    function define(style, dict) {\n      for(var i = 0; i < dict.length; i++) {\n        words[dict[i]] = style;\n      }\n    };\n  \n    var commonAtoms = [\"true\", \"false\"];\n    var commonKeywords = [\"if\", \"then\", \"do\", \"else\", \"elif\", \"while\", \"until\", \"for\", \"in\", \"esac\", \"fi\",\n      \"fin\", \"fil\", \"done\", \"exit\", \"set\", \"unset\", \"export\", \"function\"];\n    var commonCommands = [\"ab\", \"awk\", \"bash\", \"beep\", \"cat\", \"cc\", \"cd\", \"chown\", \"chmod\", \"chroot\", \"clear\",\n      \"cp\", \"curl\", \"cut\", \"diff\", \"echo\", \"find\", \"gawk\", \"gcc\", \"get\", \"git\", \"grep\", \"hg\", \"kill\", \"killall\",\n      \"ln\", \"ls\", \"make\", \"mkdir\", \"openssl\", \"mv\", \"nc\", \"nl\", \"node\", \"npm\", \"ping\", \"ps\", \"restart\", \"rm\",\n      \"rmdir\", \"sed\", \"service\", \"sh\", \"shopt\", \"shred\", \"source\", \"sort\", \"sleep\", \"ssh\", \"start\", \"stop\",\n      \"su\", \"sudo\", \"svn\", \"tee\", \"telnet\", \"top\", \"touch\", \"vi\", \"vim\", \"wall\", \"wc\", \"wget\", \"who\", \"write\",\n      \"yes\", \"zsh\"];\n  \n    CodeMirror.registerHelper(\"hintWords\", \"shell\", commonAtoms.concat(commonKeywords, commonCommands));\n  \n    define('atom', commonAtoms);\n    define('keyword', commonKeywords);\n    define('builtin', commonCommands);\n  \n    function tokenBase(stream, state) {\n      if (stream.eatSpace()) return null;\n  \n      var sol = stream.sol();\n      var ch = stream.next();\n  \n      if (ch === '\\\\') {\n        stream.next();\n        return null;\n      }\n      if (ch === '\\'' || ch === '\"' || ch === '`') {\n        state.tokens.unshift(tokenString(ch, ch === \"`\" ? \"quote\" : \"string\"));\n        return tokenize(stream, state);\n      }\n      if (ch === '#') {\n        if (sol && stream.eat('!')) {\n          stream.skipToEnd();\n          return 'meta'; // 'comment'?\n        }\n        stream.skipToEnd();\n        return 'comment';\n      }\n      if (ch === '$') {\n        state.tokens.unshift(tokenDollar);\n        return tokenize(stream, state);\n      }\n      if (ch === '+' || ch === '=') {\n        return 'operator';\n      }\n      if (ch === '-') {\n        stream.eat('-');\n        stream.eatWhile(/\\w/);\n        return 'attribute';\n      }\n      if (ch == \"<\") {\n        if (stream.match(\"<<\")) return \"operator\"\n        var heredoc = stream.match(/^<-?\\s*['\"]?([^'\"]*)['\"]?/)\n        if (heredoc) {\n          state.tokens.unshift(tokenHeredoc(heredoc[1]))\n          return 'string-2'\n        }\n      }\n      if (/\\d/.test(ch)) {\n        stream.eatWhile(/\\d/);\n        if(stream.eol() || !/\\w/.test(stream.peek())) {\n          return 'number';\n        }\n      }\n      stream.eatWhile(/[\\w-]/);\n      var cur = stream.current();\n      if (stream.peek() === '=' && /\\w+/.test(cur)) return 'def';\n      return words.hasOwnProperty(cur) ? words[cur] : null;\n    }\n  \n    function tokenString(quote, style) {\n      var close = quote == \"(\" ? \")\" : quote == \"{\" ? \"}\" : quote\n      return function(stream, state) {\n        var next, escaped = false;\n        while ((next = stream.next()) != null) {\n          if (next === close && !escaped) {\n            state.tokens.shift();\n            break;\n          } else if (next === '$' && !escaped && quote !== \"'\" && stream.peek() != close) {\n            escaped = true;\n            stream.backUp(1);\n            state.tokens.unshift(tokenDollar);\n            break;\n          } else if (!escaped && quote !== close && next === quote) {\n            state.tokens.unshift(tokenString(quote, style))\n            return tokenize(stream, state)\n          } else if (!escaped && /['\"]/.test(next) && !/['\"]/.test(quote)) {\n            state.tokens.unshift(tokenStringStart(next, \"string\"));\n            stream.backUp(1);\n            break;\n          }\n          escaped = !escaped && next === '\\\\';\n        }\n        return style;\n      };\n    };\n  \n    function tokenStringStart(quote, style) {\n      return function(stream, state) {\n        state.tokens[0] = tokenString(quote, style)\n        stream.next()\n        return tokenize(stream, state)\n      }\n    }\n  \n    var tokenDollar = function(stream, state) {\n      if (state.tokens.length > 1) stream.eat('$');\n      var ch = stream.next()\n      if (/['\"({]/.test(ch)) {\n        state.tokens[0] = tokenString(ch, ch == \"(\" ? \"quote\" : ch == \"{\" ? \"def\" : \"string\");\n        return tokenize(stream, state);\n      }\n      if (!/\\d/.test(ch)) stream.eatWhile(/\\w/);\n      state.tokens.shift();\n      return 'def';\n    };\n  \n    function tokenHeredoc(delim) {\n      return function(stream, state) {\n        if (stream.sol() && stream.string == delim) state.tokens.shift()\n        stream.skipToEnd()\n        return \"string-2\"\n      }\n    }\n  \n    function tokenize(stream, state) {\n      return (state.tokens[0] || tokenBase) (stream, state);\n    };\n  \n    return {\n      startState: function() {return {tokens:[]};},\n      token: function(stream, state) {\n        return tokenize(stream, state);\n      },\n      closeBrackets: \"()[]{}''\\\"\\\"``\",\n      lineComment: '#',\n      fold: \"brace\"\n    };\n  });\n  \n  CodeMirror.defineMIME('text/x-sh', 'shell');\n  // Apache uses a slightly different Media Type for Shell scripts\n  // http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\n  CodeMirror.defineMIME('application/x-sh', 'shell');\n  \n  });\n  "
  },
  {
    "path": "docs/src/footer.inc.html",
    "content": "﻿<link rel=\"stylesheet\" type=\"text/css\" href=\"libs/codemirror/lib/codemirror.css\" />\n<script type=\"text/javascript\" src=\"libs/jquery/jquery-1.11.1.min.js\"></script>\n<script type=\"text/javascript\" src=\"libs/chroma.min.cjs\"></script>\n<script type=\"text/javascript\" src=\"libs/codemirror/lib/codemirror.js\"></script>\n<script type=\"text/javascript\" src=\"libs/codemirror/mode/javascript/javascript.js\"></script>\n<script type=\"text/javascript\" src=\"libs/codemirror/mode/shell/shell.js\"></script>\n\n<script type=\"text/javascript\">\n    function toggleMenu() {\n        const menu = document.querySelector('.toc-container');\n        menu.classList.toggle('open');\n        menu.addEventListener('click', function (e) {\n            if (e.target.tagName === 'A') {\n                menu.classList.remove('open');\n            }\n        });\n    }\n\n    (function ($) {\n        $('code.lang-shell').each(function () {\n            var code = this;\n\n            var cm = CodeMirror(\n                function (elt) {\n                    code.parentNode.replaceChild(elt, code);\n                },\n                {\n                    value: code.innerHTML.trim(),\n                    indentUnit: 4,\n                    mode: 'shell',\n                    readOnly: true,\n                    lineWrapping: true,\n                    scrollbarStyle: 'null',\n                }\n            );\n        });\n        $('code.lang-js').each(function () {\n            var code = this;\n\n            var cm = CodeMirror(\n                function (elt) {\n                    code.parentNode.replaceChild(elt, code);\n                },\n                {\n                    value: code.innerHTML.trim(),\n                    indentUnit: 4,\n                    mode: 'javascript',\n                    lineWrapping: true,\n                    scrollbarStyle: 'null'\n                }\n            );\n\n            cm.on('update', function (_cm, change) {\n                showColors(_cm);\n            });\n\n            var resDisplay = $('<div class=\"result-display\" />').appendTo(\n                cm.display.wrapper.parentNode\n            );\n\n            if (!cm.getDoc().getValue().includes('import')) showColors(cm);\n\n            function showColors(cm) {\n                $('.cm-string', cm.display.wrapper).each(styleSpan);\n                $('.cm-number', cm.display.wrapper).each(enableSlider);\n\n                // evaluate script\n                var src = cm.getDoc().getValue();\n                //resDisplay.html('');\n                chroma.setLabWhitePoint('D65');\n                try {\n                    var s = src.split(';').filter(d => d).map(eval);\n                    resDisplay.html(\n                        '<ol><li>' +\n                            s\n                                .map(resRec)\n                                .map(d => d || '&nbsp;')\n                                // .filter(function (d) {\n                                //     return d !== undefined;\n                                // })\n                                .join('</li><li>') +\n                            '</li></ol>'\n                    );\n\n                    $('.cm-string', resDisplay).each(styleSpan);\n                } catch (e) {\n                    // console.warn(e);\n                }\n\n                function resRec(d) {\n                    if ($.isArray(d)) {\n                        return '[' + d.map(d.length > 2 ? resShort : resLong).join(',') + ']';\n                    }\n                    return resLong(d);\n\n                    function resLong(d) {\n                        if (typeof d == 'boolean') {\n                            return '<span class=\"cm-number\">' + (d ? 'true' : 'false') + '</span>';\n                        } else if (typeof d == 'string') {\n                            // string color, e.g. hex value\n                            return '<span class=\"cm-string\">\"' + d + '\"</span>';\n                        } else if (typeof d == 'object' && d._rgb) {\n                            // chroma.js object\n                            return (\n                                '<span class=\"cm-string cm-color\" data-color=\"' +\n                                d.css() +\n                                '\">' +\n                                d.hex() +\n                                '</span>'\n                            );\n                        } else if ($.isNumeric(d)) {\n                            return '<span class=\"cm-number\">' + round(d, 3) + '</span>';\n                        } else if ($.isFunction(d)) {\n                            var s = '';\n                            var dom = d.domain ? d.domain() : [0, 1],\n                                dmin = Math.min(dom[0], dom[dom.length - 1]),\n                                dmax = Math.max(dom[dom.length - 1], dom[0]);\n                            for (var i = 0; i <= 100; i++) {\n                                s +=\n                                    '<span class=\"grad-step\" style=\"background-color:' +\n                                    d(dmin + (i / 100) * (dmax - dmin)) +\n                                    '\"></span>';\n                            }\n                            s += '<span class=\"domain-min\">' + dmin + '</span>';\n                            s += '<span class=\"domain-med\">' + (dmin + dmax) * 0.5 + '</span>';\n                            s += '<span class=\"domain-max\">' + dmax + '</span>';\n                            return '<div class=\"gradient\">' + s + '</div>';\n                        }\n                    }\n\n                    function resShort(d) {\n                        if (typeof d == 'string') {\n                            // string color, e.g. hex value\n                            return (\n                                '<span class=\"cm-string cm-color cm-small\" data-color=\"' +\n                                d +\n                                '\"><span class=\"cm-hidden-text\">\\'' +\n                                chroma(d).hex() +\n                                \"'</span></span>\"\n                            );\n                        } else if (typeof d == 'object' && d._rgb) {\n                            // chroma.js object\n                            return (\n                                '<span class=\"cm-string cm-color cm-small\" data-color=\"' +\n                                d.css() +\n                                '\"><span class=\"cm-hidden-text\">\\'' +\n                                d.hex() +\n                                \"'</span></span>\"\n                            );\n                        } else if ($.isNumeric(d)) {\n                            return '<span class=\"cm-number\">' + round(d, 2) + '</span>';\n                        } else if (isNaN(d)) {\n                            return '<span class=\"cm-number cm-nan\">NaN</span>';\n                        }\n                    }\n\n                    function round(d, p) {\n                        var n = Math.pow(10, p);\n                        return Math.round(d * n) / n;\n                    }\n                }\n            }\n\n            function styleSpan() {\n                var span = $(this);\n                //setTimeout(function() {\n                val = span.data('color') || span.html().replace(/['\"]/g, '').trim();\n                if (chroma[val]) {\n                    span.attr('style', '');\n                    return;\n                }\n\n                try {\n                    if (chroma.valid(val)) {\n                        const col = chroma(val);\n                        const l = col.oklch()[0];\n                        const isCSS = /[a-z]+\\([^\\)]+\\)/.test(val)\n                        span.attr(\n                            'style',\n                            [\n                                'background-color:' + (isCSS ? val : col.hex()),\n                                'color:' + (l < 0.7 ? 'white' : 'black'),\n                                ...(isCSS ? [] : ['opacity:' + col.alpha()])\n                            ].join(';')\n                        );\n                        \n                    }\n                } catch (e) {\n                    //console.log(e);\n                    span.attr('style', '');\n                    // not a color, so ignore\n                }\n                //}, 50);\n            }\n\n            function enableSlider() {\n                return;\n                var span = $(this),\n                    slider = $('<div></div>').addClass('slider'),\n                    input = $('<input type=\"range\" />').appendTo(slider);\n\n                span.off('mouseenter').on('mouseenter', function () {\n                    var v = +span.text(),\n                        d = Math.pow(10, Math.max(1, Math.log10(v))),\n                        min = v - d,\n                        max = v + d;\n                    input.attr({ min: min, max: max }).prop('value', v);\n                    console.log('span', v);\n\n                    span.append(slider);\n                });\n                span.off('mouseleave').on('mouseleave', function () {\n                    //slider.remove();\n                });\n            }\n        });\n\n        var toc = $('<ul />')\n            .addClass('toc')\n            .appendTo($('<div>').addClass('toc-container').appendTo('.wrap'));\n\n        var hue = Math.random() * 360;\n        $('h2,h3').each(function () {\n            var h = $(this),\n                l = h.attr('id'),\n                t = h.is('h2');\n            toc.append(\n                '<li class=\"' +\n                    ('level-' + (t ? '1' : '2')) +\n                    '\"><a style=\"color:' +\n                    chroma.lch(50, 80, hue) +\n                    '\" href=\"#' +\n                    l +\n                    '\">' +\n                    h.text() +\n                    '</a></li>'\n            );\n            hue = (hue + 20) % 360;\n            var a = $('<a />')\n                .attr('href', '#' + l)\n                .html(h.html());\n            h.html('').append(a);\n        });\n\n        $('h3+h4').each(function (i, el) {\n            el.previousElementSibling.appendChild(el);\n        });\n    })(jQuery);\n</script>\n<a href=\"https://github.com/gka/chroma.js\" class=\"github-corner\"\n    ><svg\n        width=\"80\"\n        height=\"80\"\n        viewBox=\"0 0 250 250\"\n        style=\"fill: #64ceaa; color: #fff; position: absolute; top: 0; border: 0; right: 0\"\n    >\n        <path d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"></path>\n        <path\n            d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\"\n            fill=\"currentColor\"\n            style=\"transform-origin: 130px 106px\"\n            class=\"octo-arm\"\n        ></path>\n        <path\n            d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\"\n            fill=\"currentColor\"\n            class=\"octo-body\"\n        ></path></svg\n></a>\n<style>\n    .github-corner:hover .octo-arm {\n        animation: octocat-wave 560ms ease-in-out;\n    }\n    @keyframes octocat-wave {\n        0%,\n        100% {\n            transform: rotate(0);\n        }\n        20%,\n        60% {\n            transform: rotate(-25deg);\n        }\n        40%,\n        80% {\n            transform: rotate(10deg);\n        }\n    }\n    @media (max-width: 500px) {\n        .github-corner:hover .octo-arm {\n            animation: none;\n        }\n        .github-corner .octo-arm {\n            animation: octocat-wave 560ms ease-in-out;\n        }\n    }\n</style>\n"
  },
  {
    "path": "docs/src/index.css",
    "content": "﻿@import 'https://fonts.googleapis.com/css?family=Roboto:100,300,400,700';\n\nbody {\n    font-family: -apple-system, BlinkMacSystemFont, 'Roboto', \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n.wrap {\n    max-width: 1000px;\n    margin: 0 auto;\n}\n\n.wrap>h1,\n.wrap>h2,\n.wrap>h3,\n.wrap>h4,\n.wrap>p,\n.wrap>pre,\n.wrap>ul,\n.wrap>table {\n    position: relative;\n    /*left: -80px;*/\n    padding-left: 300px;\n}\n\n.wrap>h2,\n.wrap>h3 {\n    padding-top: 10px;\n}\n\n.wrap>h2 a,\n.wrap>h3 a {\n    color: #000;\n    text-decoration: none;\n}\n\n.wrap>h2 a:hover:before,\n.wrap>h3 a:hover:before {\n    content: '#';\n    color: #ddd;\n    position: absolute;\n    left: 280px;\n}\n\n.wrap>ul {\n    margin-left: 30px;\n}\n\n.wrap>p {\n    line-height: 1.5;\n}\n\n.wrap>p,\n.wrap>table {\n    margin-right: 200px;\n}\n\ntable th {\n    font-weight: bold;\n    text-align: left;\n    font-size: 90%;\n}\n\ntable td {\n    vertical-align: top;\n    font-size: 90%;\n}\n\ntable td+td {\n    padding-left: 1em;\n}\n\n\nul.toc {\n    position: fixed;\n    top: 0px;\n    width: 220px;\n    height: 100%;\n    overflow-y: auto;\n    list-style: none;\n    margin: 0;\n}\n\n\n.toc li a {\n    text-decoration: none;\n    margin-bottom: 5px;\n    font-size: 13px;\n    border-bottom: 1px dotted white;\n    padding-bottom: 5px;\n    display: block;\n    position: relative;\n}\n\n.toc li.level-1 a {\n    margin-top: 15px;\n    font-weight: bold;\n    font-size: 17px;\n}\n\n.toc li.level-2 a {\n    padding-left: 20px;\n    font-size: 15px;\n}\n\n\n.toc li.level-2 a:before {\n    position: absolute;\n    left: 5px;\n    top: -2px;\n    font-size: 18px;\n    opacity: 0.3;\n    content: '•';\n}\n\n\n.wrap>h1 {\n    font-weight: 100;\n    font-size: 48px;\n    letter-spacing: -2px;\n}\n\n.wrap>h2 {\n    padding-top: 30px;\n    margin-top: 20px;\n}\n\n.wrap>h3 h4 {\n    font-weight: 400;\n    display: inline-block;\n    color: #999;\n    margin: 0 0 0 3px;\n}\n\n.wrap p code,\n.wrap li code {\n    font-size: 15px;\n    margin-left: 2px;\n    margin-right: 2px;\n    color: #069;\n}\n\nhtml .cm-s-default .cm-comment {\n    color: #B3A9A9;\n    font-style: italic;\n}\n\npre {\n    position: relative;\n    margin-top: 30px;\n    margin-bottom: 30px;\n}\n\npre .CodeMirror {\n    height: auto;\n    font-size: 16px;\n    line-height: 22px;\n    background: #f9f9f9;\n    box-shadow: inset 2px 2px 6px rgba(0, 0, 0, .1);\n    border-radius: 10px;\n    margin: -5px;\n    padding: 5px;\n    margin-right: 200px;\n    margin-bottom: 20px;\n}\n\n\npre .CodeMirror:hover,\npre .CodeMirror:focus {\n    background: #f6f6f0;\n}\n\n.result-display {\n    position: absolute;\n    right: 0px;\n    top: 4px;\n    width: 180px;\n    white-space: normal;\n    line-height: 22px;\n}\n\n.result-display .cm-string {\n    padding: 0px 4px;\n    display: inline-block;\n    min-width: 13px;\n    min-height: 13px;\n    border-radius: 3px;\n    box-sizing: border-box;\n    position: relative;\n    vertical-align: middle;\n    margin-left: 2px;\n    margin-bottom: 2px;\n    position: relative;\n    top: 1px;\n}\n\n.result-display .cm-string:before {\n    content: \" \";\n    position: absolute;\n    left: 0;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    background-image: url(../assets/bg.png);\n    opacity: 0.99;\n    z-index: -10;\n}\n\n.result-display li {\n    line-height: 24px;\n}\n\n.result-display .cm-number {\n    color: #164;\n}\n\n.CodeMirror,\n.CodeMirror .CodeMirror-scroll {\n    overflow: visible !important;\n}\n\n.CodeMirror .cm-number {\n    position: relative;\n}\n\n.CodeMirror .cm-number .slider {\n    position: absolute;\n    left: 50%;\n    top: -15px;\n}\n\n.CodeMirror .cm-number .slider input[type=\"range\"] {\n    position: absolute;\n    left: -50px;\n    top: 0px;\n    width: 100px;\n}\n\n\n.result-display .cm-nan {\n    color: #614;\n}\n\n.result-display ol {\n    padding: 0;\n    margin: 0;\n    counter-reset: commands;\n}\n\n\n.result-display ol li {\n    list-style-position: inside;\n    list-style: none;\n    position: relative;\n}\n\n.result-display ol li:before {\n    content: '';\n    counter-increment: commands;\n    padding-right: 0px;\n    position: absolute;\n    left: -10px;\n    top: 2px;\n    color: #bbb;\n}\n\n.result-display:before {\n    content: \"RESULT\";\n    position: absolute;\n    left: 0;\n    top: -25px;\n    font-size: 11px;\n    color: #ccc;\n}\n\n.gradient {\n    width: 85%;\n    white-space: nowrap;\n    position: relative;\n    display: inline-block;\n    top: 4px;\n    padding-bottom: 15px;\n}\n\n\n.gradient .domain-min {\n    position: absolute;\n    left: 0;\n    font-size: 11px;\n    bottom: 3px;\n}\n\n.gradient .domain-med {\n    position: absolute;\n    right: 25%;\n    left: 25%;\n    text-align: center;\n    font-size: 11px;\n    bottom: 3px;\n}\n\n.gradient .domain-max {\n    position: absolute;\n    right: 0;\n    font-size: 11px;\n    bottom: 3px;\n}\n\n\n.grad-step {\n    display: inline-block;\n    height: 20px;\n    width: 1%;\n}\n\n.cm-small {\n    height: 15px;\n    width: 15px;\n}\n\n.cm-hidden-text {\n    display: inline-block;\n    width: 1px;\n    /* height:0px; */\n    opacity: 0;\n    overflow: hidden;\n}\n\n.menu-button {\n    display: none;\n}\n\n/* Responsive design */\n@media (max-width: 768px) {\n\n    .wrap>h1,\n    .wrap>h2,\n    .wrap>h3,\n    .wrap>h4,\n    .wrap>p,\n    .wrap>pre,\n    .wrap>ul,\n    .wrap>table {\n        padding-left: 0px;\n    }\n\n    .wrap>p,\n    .wrap>table,\n    pre .CodeMirror {\n        margin-right: 0px;\n    }\n\n    pre .CodeMirror {\n        font-size: 14px;\n        line-height: 20px;\n    }\n\n    .CodeMirror,\n    .CodeMirror .CodeMirror-scroll {\n        overflow: visible !important;\n    }\n\n    div.CodeMirror-scroll {\n        height: auto !important;\n        overflow: visible;\n    }\n\n    .result-display {\n        display: block;\n        position: static;\n    }\n\n    /* Base styles for the toc-container */\n    .toc-container {\n        position: relative;\n        max-width: 300px;\n        background-color: #fff;\n        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n        z-index: 1000;\n        display: none;\n        /* Hidden by default */\n        flex-direction: column;\n        align-items: flex-start;\n        padding: 0px;\n    }\n\n    /* Menu button */\n    .menu-button {\n        border: 0;\n        background: white;\n        padding: 10px;\n        display: block;\n        position: fixed;\n        top: 10px;\n        right: 10px;\n        z-index: 10000;\n        -webkit-user-select: none;\n        user-select: none;\n    }\n\n    .github-corner {\n        display: none;\n    }\n\n    .menu-button span {\n        display: block;\n        width: 30px;\n        height: 3px;\n        margin-bottom: 5px;\n        position: relative;\n\n        background: #222222;\n        border-radius: 3px;\n\n        z-index: 1;\n\n        transform-origin: 4px 0px;\n\n        transition: transform 0.5s cubic-bezier(0.77, 0.2, 0.05, 1.0),\n            background 0.5s cubic-bezier(0.77, 0.2, 0.05, 1.0),\n            opacity 0.55s ease;\n    }\n\n    .menu-button span:first-child {\n        transform-origin: 0% 0%;\n    }\n\n    .menu-button span:nth-last-child(2) {\n        transform-origin: 0% 100%;\n    }\n\n\n    /* Show the menu when toggled */\n    .toc-container.open {\n        display: flex;\n    }\n\n    /* Styling for the toc list */\n    .toc {\n        list-style-type: none;\n        margin: 0;\n        padding: 0;\n        width: 100%;\n    }\n\n    .toc li {\n        padding: 8px 0;\n        border-bottom: 1px solid #e0e0e0;\n    }\n\n    .toc li a {\n        text-decoration: none;\n        font-size: 16px;\n        color: inherit;\n        /* Use the inline color style */\n    }\n\n    /* Nested menu items */\n    .level-2 {\n        padding-left: 20px;\n        /* Indent for sub-menu items */\n    }\n\n    ul.toc {\n        position: static;\n        width: calc(100% - 20px);\n        margin-left: 20px;\n    }\n\n    .toc-container {\n        width: 70%;\n        height: 100%;\n        position: fixed;\n        top: 0;\n        left: 0;\n        overflow-y: auto;\n    }\n}\n"
  },
  {
    "path": "docs/src/index.md",
    "content": "\n# chroma.js\n\n**chroma.js** is a [small-ish](https://bundlephobia.com/result?p=chroma-js) zero-dependency JavaScript library ([13.5kB](https://bundlephobia.com/result?p=chroma-js)) for all kinds of color conversions and color scales.\n\n[![Build Status](https://api.travis-ci.com/gka/chroma.js.svg?branch=master)](https://travis-ci.com/gka/chroma.js)\n\n\n## Quick-start\n\nHere are a couple of things chroma.js can do for you:\n\n* read colors from a wide range of formats\n* analyze and manipulate colors\n* convert colors into wide range of formats\n* linear and bezier interpolation in different color spaces\n\nHere's an example for a simple read / manipulate / output chain:\n\n```js\nchroma('pink').darken().saturate(2).hex()\n```\n\nAside from that, chroma.js can also help you **generate nice colors** using various methods, for instance to be [used](https://www.vis4.net/blog/posts/avoid-equidistant-hsv-colors/) in color palette for maps or data visualization.\n\n```js\nchroma.scale(['#fafa6e', '#2A4858'])\n    .mode('lch').colors(6)\n```\n\nchroma.js has a lot more to offer, but that's the gist of it.\n\n## Installation\n\nFor Node.js: Install the `chroma-js` npm module using your favorite package manager:\n\n```shell\nnpm install chroma-js\n# pnpm add chroma-js\n# yarn add chroma-js\n```\n\nThen import the module into your JavaScript:\n\n```js\nimport chroma from 'chroma-js';\n```\n\nIf you just want to use parts of chroma.js and not bundle the entire package, you can import directly from `chroma-js/src/*` to benefit from treeshaking. For instance, the following import would only result in a [1.24kB bundle increase](https://bundlejs.com/?q=chroma-js%2Fsrc%2Futils%2Fdelta-e.js&treeshake=%5B*+as+default%5D&config=%7B%22analysis%22%3A%22treemap%22%7D):\n\n```js\nimport deltaE from 'chroma-js/src/utils/deltaE.js\n```\n\nAnd for browsers, download [`chroma.min.js`](https://unpkg.com/chroma-js/dist/chroma.min.cjs) or use the [hosted version on unpkg.com](https://unpkg.com/chroma-js/dist/chroma.min.cjs).\n\nYou can also just import chroma.js as ES module, as demonstrated in this [StackBlitz](https://stackblitz.com/edit/stackblitz-starters-axiqsz?description=HTML/CSS/JS%20Starter&file=script.js,styles.css&terminalHeight=10&title=Static%20Starter). \n\n\nTo use chroma.js in [Observable notebooks](https://observablehq.com/), you can import it like this:\n\n```js\nimport { chroma } from \"@gka/chroma-js\"\n```\n\n\nThe [interactive documentation](http://gka.github.io/chroma.js/) continues below (and there's a [static version](https://github.com/gka/chroma.js/blob/master/docs/src/index.md), too) for usage examples. Or use it from SASS using [chromatic-sass](https://github.com/bugsnag/chromatic-sass)!\n\n\n\n## API\n\n### chroma\n#### (*color*)\n\nThe first step is to get your color into chroma.js. That's what the generic constructor ``chroma()`` does. This function attempts to guess the format of the input color for you. For instance, it will recognize any named color from the W3CX11 specification:\n\n```js\nchroma('hotpink')\n```\n\nIf there's no matching named color, chroma.js checks for a **hexadecimal string**. It ignores case, the `#` sign is optional, and it can  recognize the shorter three letter format as well. So, any of these are valid hexadecimal representations: `#ff3399`, `FF3399`, `#f39`, etc.\n\n```js\nchroma('#ff3399');\nchroma('F39');\n```\n\nIn addition to hex strings, **hexadecimal numbers** (in fact, just any number between `0` and `16777215`) will be recognized, too.\n\n```js\nchroma(0xff3399)\n```\n\nYou also can pass RGB values individually. Each parameter must be within `0..255`. You can pass the numbers as individual arguments or as an array.\n\n```js\nchroma(0xff, 0x33, 0x99);\nchroma(255, 51, 153);\nchroma([255, 51, 153]);\n```\n\nYou can construct colors from different color spaces by passing the name of color space as the last argument. Here we define the same color in HSL by passing the h*ue angle (0-360) and percentages for *s*aturation and *l*ightness:\n\n```js\nchroma(330, 1, 0.6, 'hsl')\n```\n\n**New (since 2.0):** you can also construct colors by passing an plain JS object with attributes corresponding to a color space supported by chroma.js:\n\n```js\nchroma({ h:120, s:1, l:0.75});\nchroma({ l:80, c:25, h:200 });\nchroma({ c:1, m:0.5, y:0, k:0.2});\n```\n\n### chroma.valid\n\nAlso new: you can use `chroma.valid` to try if a color argument can be correctly parsed as color by chroma.js:\n\n```js\nchroma.valid('red');\nchroma.valid('bread');\nchroma.valid('#F0000D');\nchroma.valid('#FOOOOD');\n```\n\n\n### chroma.hsl\n#### (hue, saturation, lightness)\n\nAlternatively, every color space has its own constructor function under the `chroma` namespace. For a list of all supported color spaces, check the [appendix](#supported-color-spaces-and-output-formats).\n\n```js\nchroma.hsl(330, 1, 0.6)\n```\n\n### chroma.hsv\n#### (hue, saturation, value)\n\n### chroma.lab\n#### (Lightness, a, b)\n\nCIE Lab color space. To calculate the lightness value of a color, the CIE Lab color space uses a reference white point. This reference white point defines what is considered to be \"white\" in the color space. By default chroma.js is using the D65 reference point.\n\n```js\nchroma.lab(40, -20, 50);\nchroma.lab(50, -20, 50);\nchroma.lab(80, -20, 50);\n```\n\n### chroma.setLabWhitePoint\n#### (whitePoint)\n\nSets the current CIE Lab white reference point. \n\nPossible values:\n\n|  |                                                                                            |\n|-------------|-------------------------------------------------------------------------------------------------------|\n| `D50`         | Represents the color temperature of daylight at 5000K.  |\n| `D55`         | Represents mid-morning or mid-afternoon daylight at 5500K.             |\n| `D65`         | Represents average daylight at 6500K.   |\n| `A`           | Represents the color temperature of a typical incandescent light bulb at approximately 2856K.           |\n| `B`           | Represents noon daylight with a color temperature of approximately 4874K.                              |\n| `C`           | Represents average or north sky daylight; it's a theoretical construct, not often used in practical applications. |\n| `F2`          | Represents cool white fluorescent light.                                                              |\n| `F7`          | This is a broad-band fluorescent light source with a color temperature of approximately 6500K.         |\n| `F11`         | This is a narrow tri-band fluorescent light source with a color temperature of approximately 4000K.    |\n| `E`           | Represents an equal energy white point, where all wavelengths in the visible spectrum are equally represented. |\n\n```js\nchroma('hotpink').lab();\nchroma.setLabWhitePoint('F2');\nchroma('hotpink').lab();\n```\n\n### chroma.getLabWhitePoint\n\nReturns the name of the currently set CIE Lab white reference point. \n\n```js\nchroma.getLabWhitePoint();\n```\n\n### chroma.lch\n#### (Lightness, chroma, hue)\n\nThe range for `lightness` and `chroma` depend on the hue, but go roughly from 0..100-150. The range for `hue` is 0..360.\n\n```js\nchroma.lch(80, 40, 130);\nchroma(80, 40, 130, 'lch');\n```\n\n### chroma.hcl\n#### (hue, chroma, lightness)\n\nYou can use **hcl** instead of Lch. Lightness and hue channels are switched to be more consistent with HSL.\n\n```js\nchroma.hcl(130, 40, 80);\nchroma(130, 40, 80, 'hcl');\n```\n\n### chroma.oklab\n#### (Lightness, a, b)\n\n[Oklab color space](https://bottosson.github.io/posts/oklab/)\n\n```js\nchroma.oklab(0.4,-0.2,0.5);\nchroma.oklab(0.5,-0.2,0.5);\nchroma.oklab(0.8,-0.2,0.5);\n```\n\n### chroma.oklch\n#### (Lightness, chromacity, hue)\n\n```js\nchroma.oklch(0.5, 0.2, 240);\nchroma(0.8, 0.12, 60, 'oklch');\n```\n\n\n### chroma.cmyk\n#### (cyan, magenta, yellow, black)\n\nEach between 0 and 1.\n\n```js\nchroma.cmyk(0.2, 0.8, 0, 0);\nchroma(0.2, 0.8, 0, 0, 'cmyk');\n```\n\n### chroma.gl\n#### (red, green, blue, [alpha])\n\n**GL** is a variant of RGB(A), with the only difference that the components are normalized to the range of `0..1`.\n\n```js\nchroma.gl(0.6, 0, 0.8);\nchroma.gl(0.6, 0, 0.8, 0.5);\nchroma(0.6, 0, 0.8, 'gl');\n```\n\n### chroma.temperature\n#### (K)\n\nReturns a color from the [color temperature](http://www.zombieprototypes.com/?p=210) scale. Based on [Neil Bartlett's implementation](https://github.com/neilbartlett/color-temperature).\n\n```js\nchroma.temperature(2000); // candle light\nchroma.temperature(3500); // sunset\nchroma.temperature(6500); // daylight\n```\n\nThe effective temperature range goes from `0` to about `30000` Kelvin,\n\n```js\nf = function(i) {\n    return chroma.temperature(i * 30000)\n}\n```\n\n### chroma.mix\n#### (color1, color2, ratio=0.5, mode='lrgb')\n\nMixes two colors. The mix *ratio* is a value between 0 and 1.\n\n```js\nchroma.mix('red', 'blue');\nchroma.mix('red', 'blue', 0.25);\nchroma.mix('red', 'blue', 0.75);\n```\n\nThe color mixing produces different results based the color space used for interpolation.\n\n```js\nchroma.mix('red', 'blue', 0.5, 'rgb');\nchroma.mix('red', 'blue', 0.5, 'hsl');\nchroma.mix('red', 'blue', 0.5, 'lab');\nchroma.mix('red', 'blue', 0.5, 'lch');\nchroma.mix('red', 'blue', 0.5, 'lrgb');\n```\n\n\n### chroma.average\n#### (colors, mode='lrgb', weights=[])\n\nSimilar to `chroma.mix`, but accepts more than two colors. Simple averaging of R,G,B components and the alpha channel.\n\n```js\ncolors = ['#ddd', 'yellow', 'red', 'teal'];\nchroma.average(colors); // lrgb\nchroma.average(colors, 'rgb');\nchroma.average(colors, 'lab');\nchroma.average(colors, 'lch');\n```\n\nAlso works with alpha channels.\n\n```js\nchroma.average(['red', 'rgba(0,0,0,0.5)']).css();\n```\n\nAs of version 2.1 you can also provide an array of `weights` to\ncompute a **weighted average** of colors.\n\n```js\ncolors = ['#ddd', 'yellow', 'red', 'teal'];\nchroma.average(colors, 'lch'); // unweighted\nchroma.average(colors, 'lch', [1,1,2,1]);\nchroma.average(colors, 'lch', [1.5,0.5,1,2.3]);\n```\n\n\n### chroma.blend\n#### (color1, color2, mode)\n\nBlends two colors using RGB channel-wise blend functions. Valid blend modes are `multiply`, `darken`, `lighten`, `screen`, `overlay`, `burn`, and `dodge`.\n\n```js\nchroma.blend('4CBBFC', 'EEEE22', 'multiply');\nchroma.blend('4CBBFC', 'EEEE22', 'darken');\nchroma.blend('4CBBFC', 'EEEE22', 'lighten');\n```\n\n### chroma.random\n#### (rng = Math.random)\n\nCreates a random color by generating a [random hexadecimal string](https://github.com/gka/chroma.js/blob/main/src/generator/random.js#L7-L11). You can also pass a custom random number generator function as first argument.\n\n```js\nchroma.random();\nchroma.random();\nchroma.random();\n```\n\n### chroma.contrast\n#### (color1, color2)\n\nComputes the WCAG contrast ratio between two colors. A minimum contrast of 4.5:1 [is recommended](http://www.w3.org/TR/WCAG20-TECHS/G18.html) to ensure that text is still readable against a background color.\n\n```js\n// contrast smaller than 4.5 = too low\nchroma.contrast('pink', 'hotpink');\n// contrast greater than 4.5 = high enough\nchroma.contrast('pink', 'purple');\n```\n\n### chroma.contrastAPCA\n#### (text, background)\n\n**New (3.1):** Computes the [APCA contrast](https://www.myndex.com/APCA/) ratio of a text color against its background color. The basic idea is that you check the contrast between the text and background color and then use [this lookup table](https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAlookupByContrast.jpeg) to find the minimum font size you're allowed to use (given the font weight and purpose of the text). \n\n```js\nchroma.contrastAPCA('hotpink', 'pink');\nchroma.contrastAPCA('purple', 'pink');\n```\n\nRead more about how to interpret and use this metric at [APCA Readability Criterion](https://readtech.org/ARC). Please note that the APCA algorithm is still in beta and may change be subject to changes in the future.\n\n\n### chroma.distance\n#### (color1, color2, mode='lab')\n\nComputes the [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance#Three_dimensions) between two colors in a given color space (default is `Lab`).\n\n```js\nchroma.distance('#fff', '#ff0', 'rgb');\nchroma.distance('#fff', '#f0f', 'rgb');\nchroma.distance('#fff', '#ff0');\nchroma.distance('#fff', '#f0f');\n```\n\n### chroma.deltaE\n#### (color1, color2, Kl=1, Kc=1, Kh=1)\n\nComputes [color difference](https://en.wikipedia.org/wiki/Color_difference#CIEDE2000) as developed by the International Commission on Illumination (CIE) in 2000. The implementation is based on the formula from [Bruce Lindbloom](http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html). Resulting values range from 0 (no difference) to 100 (maximum difference), and are a metric for how the human eye percieves color difference. The optional parameters Kl, Kc, and Kh may be used to adjust weightings of lightness, chroma, and hue.\n\n```js\nchroma.deltaE('#ededee', '#ededee');\nchroma.deltaE('#ededee', '#edeeed');\nchroma.deltaE('#ececee', '#eceeec');\nchroma.deltaE('#e9e9ee', '#e9eee9');\nchroma.deltaE('#e4e4ee', '#e4eee4');\nchroma.deltaE('#e0e0ee', '#e0eee0');\nchroma.deltaE('#000000', '#ffffff');\n\n```\n\n### chroma.brewer\n\nchroma.brewer is an map of [ColorBrewer palettes](http://colorbrewer2.org/) that are included in chroma.js for convenience. chroma.scale uses the colors to construct. \n\n```js\nchroma.brewer.OrRd\n```\n\nNote that chroma.js only includes the 9-step versions of the palettes (11 steps for the diverging palettes). So, for instance, if you use chroma.js to construct a 5-color palette, they will be different from the \"official\" 5-color palettes in ColorBrewer (which have lower contrast).\n\n```js\nchroma.scale('RdBu').colors(5);\n// offical 5-color RdBu:\n['#ca0020', '#f4a582', '#f7f7f7', '#92c5de', '#0571b0']\n```\n\nOne way to compensate for this would be to \"slice off\" the extreme colors:\n\n```js\nchroma\n    .scale(chroma.brewer.RdBu.slice(1,-1))\n    .colors(5);\n```\n\nOf course you can also just construct the scale from the official 5-step colors that you can copy and paste from [colorbrewer2.org](https://colorbrewer2.org/#type=diverging&scheme=RdBu&n=5):\n\n```js\nchroma.scale(['#ca0020', '#f4a582', '#f7f7f7', '#92c5de', '#0571b0'])\n```\n\nYou can access a list of all available palettes via `Object.keys(chroma.brewer)`:\n\n```js\nObject.keys(chroma.brewer)\n// ['OrRd', 'PuBu', 'BuPu', 'Oranges', 'BuGn', 'YlOrBr', 'YlGn', 'Reds', 'RdPu', 'Greens', 'YlGnBu', 'Purples', 'GnBu', 'Greys', 'YlOrRd', 'PuRd', 'Blues', 'PuBuGn', 'Viridis', 'Spectral', 'RdYlGn', 'RdBu', 'PiYG', 'PRGn', 'RdYlBu', 'BrBG', 'RdGy', 'PuOr', 'Set2', 'Accent', 'Set1', 'Set3', 'Dark2', 'Paired', 'Pastel2', 'Pastel1']\n```\n\n### chroma.limits\n#### (data, mode, n)\n\nA helper function that computes class breaks for you, based on data. It supports the modes _equidistant_ (e), _quantile_ (q), _logarithmic_ (l), and _k-means_ (k). Let's take a few numbers as sample data.\n\n```js\nvar data = [2.0,3.5,3.6,3.8,3.8,4.1,4.3,4.4,\n            4.6,4.9,5.2,5.3,5.4,5.7,5.8,5.9,\n            6.2,6.5,6.8,7.2,8];\n```\n\n**equidistant** breaks are computed by dividing the total range of the data into _n_ groups of equal size.\n\n```js\nchroma.limits(data, 'e', 4);\n```\n\nIn the **quantile** mode, the input domain is divided by quantile ranges.\n\n```js\nchroma.limits(data, 'q', 4);\n```\n\n**logarithmic** breaks are equidistant breaks but on a logarithmic scale.\n\n```js\nchroma.limits(data, 'l', 4);\n```\n\n**k-means** break is using the 1-dimensional [k-means clustering](https://en.wikipedia.org/wiki/K-means_clustering) algorithm to find (roughly) _n_ groups of \"similar\" values. Note that this k-means implementation does not guarantee to find exactly _n_ groups.\n\n```js\nchroma.limits(data, 'k', 4);\n```\n\n## color\n\n### color.alpha\n#### (a)\n\nGet and set the color opacity using ``color.alpha``.\n\n```js\nchroma('red').alpha(0.5);\nchroma('rgba(255,0,0,0.35)').alpha();\n```\n\n### color.darken\n#### (value=1)\n\nOnce loaded, chroma.js can change colors. One way we already saw above, you can change the lightness.\n\n```js\nchroma('hotpink').darken();\nchroma('hotpink').darken(2);\nchroma('hotpink').darken(2.6);\n```\n\n### color.brighten\n#### (value=1)\n\nSimilar to `darken`, but the opposite direction\n\n```js\nchroma('hotpink').brighten();\nchroma('hotpink').brighten(2);\nchroma('hotpink').brighten(3);\n```\n\n### color.saturate\n#### (value=1)\n\nChanges the saturation of a color by manipulating the Lch chromaticity.\n\n```js\nchroma('slategray').saturate();\nchroma('slategray').saturate(2);\nchroma('slategray').saturate(3);\n```\n\n### color.desaturate\n#### (value=1)\n\nSimilar to `saturate`, but the opposite direction.\n\n```js\nchroma('hotpink').desaturate();\nchroma('hotpink').desaturate(2);\nchroma('hotpink').desaturate(3);\n```\n\n### color.mix\n#### (targetcolor, ratio=0.5, mode='lrgb')\n\nMix this color with a target color. The mix *ratio* is a value between 0 and 1. This is the same as `chroma.mix` but with the first parameter already set. As such, the color space used can be adjusted.\n\n```js\nchroma('hotpink').mix('blue');\nchroma('hotpink').mix('blue', 0.25);\nchroma('hotpink').mix('blue', 0.75, 'lab');\n```\n\n### color.shade\n#### (ratio=0.5, mode='lrgb')\n\nProduce a shade of the color. This is syntactic sugar for `color.mix` with a target color of black.\n\n```js\nchroma('hotpink').shade(0.25);\nchroma('hotpink').shade(0.5);\nchroma('hotpink').shade(0.75);\n```\n\n### color.tint\n#### (ratio=0.5, mode='lrgb')\n\nProduce a tint of the color. This is syntactic sugar for `color.mix` with a target color of white.\n\n```js\nchroma('hotpink').tint(0.25);\nchroma('hotpink').tint(0.5);\nchroma('hotpink').tint(0.75);\n```\n\n### color.set\n#### (channel, value)\n\nChanges a single channel and returns the result a new `chroma` object.\n\n```js\n// change hue to 0 deg (=red)\nchroma('skyblue').set('hsl.h', 0);\n// set chromaticity to 30\nchroma('hotpink').set('lch.c', 30);\n```\n\nRelative changes work, too:\n\n```js\n// half Lab lightness\nchroma('orangered').set('lab.l', '*0.5');\n// double Lch saturation\nchroma('darkseagreen').set('lch.c', '*2');\n```\n\n### color.get\n#### (channel)\n\nReturns a single channel value.\n\n```js\nchroma('orangered').get('lab.l');\nchroma('orangered').get('hsl.l');\nchroma('orangered').get('rgb.g');\n```\n\n### color.luminance\n#### ([lum, mode='rgb'])\n\nIf called without arguments color.luminance returns the relative brightness, according to the [WCAG definition](http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef). Normalized to `0` for darkest black and `1` for lightest white.\n\n```js\nchroma('white').luminance();\nchroma('aquamarine').luminance();\nchroma('hotpink').luminance();\nchroma('darkslateblue').luminance();\nchroma('black').luminance();\n```\n\nchroma.js also allows you to **adjust the luminance** of a color. The source color will be interpolated with black or white until the correct luminance is found.\n\n```js\n// set lumincance to 50% for all colors\nchroma('white').luminance(0.5);\nchroma('aquamarine').luminance(0.5);\nchroma('hotpink').luminance(0.5);\nchroma('darkslateblue').luminance(0.5);\n```\n\nBy default, this interpolation is done in RGB, but you can interpolate in different color spaces by passing them as second argument:\n\n```js\nchroma('aquamarine').luminance(0.5); // rgb\nchroma('aquamarine').luminance(0.5, 'lab');\nchroma('aquamarine').luminance(0.5, 'hsl');\n```\n\n### color.hex\n#### (mode='auto|rgb|rgba|argb')\n\nFinally, chroma.js allows you to output colors in various color spaces and formats. Most often you will want to output the color as hexadecimal string.\n\n```js\nchroma('orange').hex()\n```\n\n**Note** that as of version 1.4.0 the default mode is \"auto\" which means that the hex string will include the alpha channel if it's less than 1. If you don't want the alpha channel to be included you must explicitly set the mode to \"rgb\" now:\n\n```js\nchroma('orange').hex();\nchroma('orange').alpha(0.5).hex();\nchroma('orange').alpha(0.5).hex('rgb');\n```\n\nYou can use `.hex('argb')` in [case](https://developer.android.com/reference/android/graphics/Color) you need to encode the color with the alpha channel as first byte rather than the last:\n\n```js\nchroma('orange').hex('argb');; // '#ffffa500'\n```\n\n### color.name\n\nReturns the named color. Falls back to hexadecimal RGB string, if the color isn't present.\n\n```js\nchroma('#ffa500').name();\nchroma('#ffa505').name();\n```\n\n### color.css\n\nReturns a CSS string representation that can be used as CSS-color definition.\n\n```js\nchroma('teal').css();\nchroma('teal').alpha(0.5).css();\n```\n\nBy default chroma is using the rgb() color space, but you can pass a color space name as first argument. Accepted color spaces are `rgb`, `hsl`, `lab`, `lch`, `oklab`, and `oklch`.\n\n```js\nchroma('teal').css('hsl');\nchroma('teal').css('lab');\nchroma('teal').css('oklch');\n```\n\n### color.rgb\n#### (round=true)\n\nReturns an array with the `red`, `green`, and `blue` component, each as number within the range `0..255`. Chroma internally stores RGB channels as floats but rounds the numbers before returning them. You can pass `false` to prevent the rounding.\n\n```js\nchroma('orange').rgb();\nchroma('orange').darken().rgb();\nchroma('orange').darken().rgb(false);\n```\n\n### color.rgba\n#### (round=true)\n\nJust like `color.rgb` but adds the alpha channel to the returned array.\n\n```js\nchroma('orange').rgba();\nchroma('hsla(20, 100%, 40%, 0.5)').rgba();\n```\n\n### color.hsl\n\nReturns an array with the `hue`, `saturation`, and `lightness` component. Hue is the color angle in degree (`0..360`), saturation and lightness are within `0..1`. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.\n\n```js\nchroma('orange').hsl();\nchroma('white').hsl();\n```\n\n### color.hsv\n\nReturns an array with the `hue`, `saturation`, and `value` components. Hue is the color angle in degree (`0..360`), saturation and value are within `0..1`. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.\n\n```js\nchroma('orange').hsv();\nchroma('white').hsv();\n```\n\n### color.hsi\n\nReturns an array with the `hue`, `saturation`, and `intensity` components, each as number between 0 and 255. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.\n\n```js\nchroma('orange').hsi();\nchroma('white').hsi();\n```\n\n### color.lab\n\nReturns an array with the **L**, **a**, and **b** components.\n\n```js\nchroma('orange').lab()\n```\n\n### color.lch\n\nReturns an array with the **Lightness**, **chroma**, and **hue** components.\n\n```js\nchroma('skyblue').lch()\n```\n\n### color.hcl\n\nAlias of [lch](#color-lch), but with the components in reverse order.\n\n```js\nchroma('skyblue').hcl()\n```\n\n### color.oklab\n\nReturns an array with the **L**, **a**, and **b** components in the [OKLab](https://bottosson.github.io/posts/oklab/) color space.\n\n```js\nchroma('orange').oklab()\n```\n\n### color.oklch\n\nReturns an array with the **Lightness**, **chroma**, and **hue** components in the [OKLch](https://bottosson.github.io/posts/oklab/) color space.\n\n```js\nchroma('skyblue').oklch()\n```\n\n### color.num\n\nReturns the numeric representation of the hexadecimal RGB color.\n\n```js\nchroma('#000000').num();\nchroma('#0000ff').num();\nchroma('#00ff00').num();\nchroma('#ff0000').num();\n```\n\n### color.temperature\n\nEstimate the temperature in Kelvin of any given color, though this makes the only sense for colors from the [temperature gradient](#chroma-temperature) above.\n\n```js\nchroma('#ff3300').temperature();\nchroma('#ff8a13').temperature();\nchroma('#ffe3cd').temperature();\nchroma('#cbdbff').temperature();\nchroma('#b3ccff').temperature();\n```\n\n\n### color.gl\n\nLike RGB, but in the channel range of `[0..1]` instead of `[0..255]`\n\n```js\nchroma('33cc00').gl();\n```\n\n### color.clipped\n\nWhen converting colors from CIELab color spaces to RGB the color channels get clipped to the range of `[0..255]`. Colors outside that range may exist in nature but are not displayable on RGB monitors (such as ultraviolet). you can use color.clipped to test if a color has been clipped or not.\n\n```js\n[c = chroma.hcl(50, 40, 20), c.clipped()];\n[c = chroma.hcl(50, 40, 40), c.clipped()];\n[c = chroma.hcl(50, 40, 60), c.clipped()];\n[c = chroma.hcl(50, 40, 80), c.clipped()];\n[c = chroma.hcl(50, 40, 100), c.clipped()];\n```\n\nAs a bonus feature you can access the unclipped RGB components using `color._rgb._unclipped`.\n\n```js\nchroma.hcl(50, 40, 100).rgb();\nchroma.hcl(50, 40, 100)._rgb._unclipped;\n```\n\n## color scales\n\n### chroma.scale\n#### (colors=['white', 'black'])\n\nA color scale, created with `chroma.scale`, is a function that maps numeric values to a color palette. The default scale has the domain `0..1` and goes from white to black.\n\n```js\nf = chroma.scale();\nf(0.25);\nf(0.5);\nf(0.75);\n```\n\nYou can pass an array of colors to `chroma.scale`. Any color that can be read by `chroma()` will work here, too. If you pass more than two colors, they will be evenly distributed along the gradient.\n\n\n```js\nchroma.scale(['yellow', '008ae5']);\nchroma.scale(['yellow', 'red', 'black']);\n```\n\n### scale.domain\n#### (domain)\n\nYou can change the input domain to match your specific use case. If called with no arguments, `scale.domain` returns the original array of positions along the scale where the color ramp was sampled.\n\n```js\n// default domain is [0,1]\nchroma.scale(['yellow', '008ae5']);\n// set domain to [0,100]\nchroma.scale(['yellow', '008ae5']).domain([0,100]);\n```\n\nYou can use the domain to set the exact positions of each color.\n\n```js\n// default domain is [0,1]\nchroma.scale(['yellow', 'lightgreen', '008ae5'])\n    .domain([0,0.25,1]);\n```\n\n\n### scale.mode\n#### (mode)\n\nAs with `chroma.mix`, the result of the color interpolation will depend on the color mode in which the channels are interpolated. The default mode is `RGB`:\n\n```js\nchroma.scale(['yellow', '008ae5']);\n```\n\nThis is often fine, but sometimes, two-color `RGB` gradients goes through kind of grayish colors, and `Lab` interpolation produces better results:\n\n```js\nchroma.scale(['yellow', 'navy']);\nchroma.scale(['yellow', 'navy']).mode('lab');\n```\n\nAlso note how the RGB interpolation can get very dark around the center. You can achieve better results using [linear RGB interpolation](https://www.youtube.com/watch?v=LKnqECcg6Gw):\n\n```js\nchroma.scale(['#f00', '#0f0']);\nchroma.scale(['#f00', '#0f0']).mode('lrgb');\n```\n\nOther useful interpolation modes could be `HSL` or `Lch`, though both tend to produce too saturated / glowing gradients.\n\n```js\nchroma.scale(['yellow', 'navy']).mode('lab');\nchroma.scale(['yellow', 'navy']).mode('hsl');\nchroma.scale(['yellow', 'navy']).mode('lch');\n```\n\n### scale.gamma\n\nGamma-correction can be used to \"shift\" a scale's center more the the beginning (gamma < 1) or end (gamma > 1), typically used to \"even\" the lightness gradient. Default is 1.\n\n```js\nchroma.scale('YlGn').gamma(0.5);\nchroma.scale('YlGn').gamma(1);\nchroma.scale('YlGn').gamma(2);\n```\n\n### scale.correctLightness\n\nThis makes sure the lightness range is spread evenly across a color scale. Especially useful when working with [multi-hue color scales](https://www.vis4.net/blog/2013/09/mastering-multi-hued-color-scales/), where simple gamma correction can't help you very much.\n\n```js\nchroma.scale(['black', 'red', 'yellow', 'white']);\n\nchroma.scale(['black', 'red', 'yellow', 'white'])\n    .correctLightness();\n```\n\n### scale.cache\n#### (true|false)\n\nBy default `chroma.scale` instances will cache each computed value => color pair. You can turn off the cache by setting\n\n```js\nchroma.scale(['yellow', '008ae5']).cache(false);\n```\n\n### scale.padding\n#### (pad)\n\nReduces the color range by cutting of a fraction of the gradient on both sides. If you pass a single number, the same padding will be applied to both ends.\n\n```js\nchroma.scale('RdYlBu');\nchroma.scale('RdYlBu').padding(0.15);\nchroma.scale('RdYlBu').padding(0.3);\nchroma.scale('RdYlBu').padding(-0.15);\n```\n\nAlternatively you can specify the padding for each sides individually by passing an array of two numbers.\n\n```js\nchroma.scale('OrRd');\nchroma.scale('OrRd').padding([0.2, 0]);\n```\n\n\n### scale.colors\n#### (num, format='hex')\n\nYou can call `scale.colors(n)` to quickly grab `n` equi-distant colors from a color scale. If called with no arguments, `scale.colors` returns the original array of colors used to create the scale.\n\n```js\nchroma.scale('OrRd').colors(5);\nchroma.scale(['white', 'black']).colors(12);\n```\n\nIf you want to return `chroma` instances just pass *null* as `format`.\n\n### scale.classes\n#### (numOrArray)\n\nIf you want the scale function to return a distinct set of colors instead of a continuous gradient, you can use `scale.classes`. If you pass a number the scale will broken into equi-distant classes:\n\n```js\n// continuous\nchroma.scale('OrRd');\n// class breaks\nchroma.scale('OrRd').classes(5);\nchroma.scale('OrRd').classes(8);\n```\n\nYou can also define custom class breaks by passing them as array:\n\n```js\nchroma.scale('OrRd').classes([0,0.3,0.55,0.85,1]);\n```\n\n### scale.nodata\n#### (color)\n\nWhen you pass a non-numeric value like `null` or `undefined` to a chroma.scale, \"#cccccc\" is returned as fallback or \"no data\" color. You can change the no-data color:\n\n```js\nchroma.scale('OrRd')(null);\nchroma.scale('OrRd')(undefined);\nchroma.scale('OrRd').nodata('#eee')(null);\n```\n\n### chroma.brewer\n\nchroma.js includes the definitions from [ColorBrewer2.org](http://colorbrewer2.org/). Read more about these colors [in the corresponding paper](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.361.6082&rep=rep1&type=pdf) by Mark Harrower and Cynthia A. Brewer.\n\n```js\nchroma.scale('YlGnBu');\nchroma.scale('Spectral');\n```\n\nTo reverse the colors you could simply reverse the domain:\n\n```js\nchroma.scale('Spectral').domain([1,0]);\n```\n\nYou can access the colors directly using `chroma.brewer`.\n\n```js\nchroma.brewer.OrRd\n```\n\n### chroma.bezier\n#### (colors)\n\n`chroma.bezier` returns a function that [bezier-interpolates between colors](https://www.vis4.net/blog/mastering-multi-hued-color-scales/) in `Lab` space. The input range of the function is `[0..1]`.\n\n```js\n// linear interpolation\nchroma.scale(['yellow', 'red', 'black']);\n// bezier interpolation\nchroma.bezier(['yellow', 'red', 'black']);\n```\n\nYou can convert an bezier interpolator into a chroma.scale instance\n\n```js\nchroma.bezier(['yellow', 'red', 'black'])\n    .scale()\n    .colors(5);\n```\n\n## cubehelix\n### chroma.cubehelix\n#### (start=300, rotations=-1.5, hue=1, gamma=1, lightness=[0,1])\n\nDave Green's [cubehelix color scheme](http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/)!!\n\n\n```js\n// use the default helix...\nchroma.cubehelix();\n// or customize it\nchroma.cubehelix()\n    .start(200)\n    .rotations(-0.5)\n    .gamma(0.8)\n    .lightness([0.3, 0.8]);\n```\n\n### cubehelix.start\n#### (hue)\n\n**start** color for [hue rotation](http://en.wikipedia.org/wiki/Hue#/media/File:HueScale.svg), default=`300`\n\n```js\nchroma.cubehelix().start(300);\nchroma.cubehelix().start(200);\n```\n\n### cubehelix.rotations\n#### (num)\n\nnumber (and direction) of hue rotations (e.g. 1=`360°`, 1.5=`540°``), default=-1.5\n\n```js\nchroma.cubehelix().rotations(-1.5);\nchroma.cubehelix().rotations(0.5);\nchroma.cubehelix().rotations(3);\n```\n\n### cubehelix.hue\n#### (numOrRange)\n\nhue controls how saturated the colour of all hues are. either single value or range, default=1\n\n```js\nchroma.cubehelix();\nchroma.cubehelix().hue(0.5);\nchroma.cubehelix().hue([1,0]);\n```\n\n### cubehelix.gamma\n#### (factor)\n\ngamma factor can be used to emphasise low or high intensity values, default=1\n\n```js\nchroma.cubehelix().gamma(1);\nchroma.cubehelix().gamma(0.5);\n```\n\n### cubehelix.lightness\n#### (range)\n\nlightness range: default: [0,1]  (black -> white)\n\n```js\nchroma.cubehelix().lightness([0,1]);\nchroma.cubehelix().lightness([1,0]);\nchroma.cubehelix().lightness([0.3,0.7]);\n```\n\n\n### cubehelix.scale\n\nYou can call `cubehelix.scale()` to use the cube-helix through the `chroma.scale` interface.\n\n```js\nchroma.cubehelix()\n    .start(200)\n    .rotations(-0.35)\n    .gamma(0.7)\n    .lightness([0.3, 0.8])\n  .scale() // convert to chroma.scale\n    .correctLightness()\n    .colors(5);\n```\n\n\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import globals from \"globals\";\nimport babelParser from \"@babel/eslint-parser\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport js from \"@eslint/js\";\nimport { FlatCompat } from \"@eslint/eslintrc\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst compat = new FlatCompat({\n    baseDirectory: __dirname,\n    recommendedConfig: js.configs.recommended,\n    allConfig: js.configs.all\n});\n\nexport default [...compat.extends(\"eslint:recommended\"), {\n    languageOptions: {\n        globals: {\n            ...globals.browser,\n            ...globals.commonjs,\n            '__dirname': 'readonly',\n        },\n\n        parser: babelParser,\n        ecmaVersion: 6,\n        sourceType: \"module\",\n\n        parserOptions: {\n            requireConfigFile: false,\n        },\n    },\n\n    rules: {\n        \"no-sequences\": \"error\",\n    },\n}];\n"
  },
  {
    "path": "index-light.js",
    "content": "import chroma from './src/chroma.js';\n\n// feel free to comment out anything to rollup\n// a smaller chroma.js built\n\n// io --> convert colors\nimport './src/io/css/index.js';\nimport './src/io/hex/index.js';\nimport './src/io/hsl/index.js';\nimport './src/io/lab/index.js';\nimport './src/io/oklab/index.js';\nimport './src/io/rgb/index.js';\n\n// operators --> modify existing Colors\nimport './src/ops/alpha.js';\nimport './src/ops/darken.js';\nimport './src/ops/get.js';\nimport './src/ops/mix.js';\nimport './src/ops/set.js';\nimport './src/ops/shade.js';\n\n// interpolators\nimport './src/interpolator/lrgb.js';\nimport './src/interpolator/oklab.js';\n\n// generators -- > create new colors\nimport mix from './src/generator/mix.js';\n\n// other utility methods\nimport valid from './src/utils/valid.js';\n\nimport Color from './src/Color.js';\n\nObject.assign(chroma, {\n    Color,\n    valid,\n    mix,\n    interpolate: mix\n});\n\nexport default chroma;\n\nexport { Color, valid, mix, mix as interpolate };\n"
  },
  {
    "path": "index.js",
    "content": "// feel free to comment out anything to rollup\n// a smaller chroma.js bundle\nimport chroma from './src/chroma.js';\n\n// io --> convert colors\nimport './src/io/named/index.js';\n\n// operators --> modify existing Colors\nimport './src/ops/alpha.js';\nimport './src/ops/clipped.js';\nimport './src/ops/darken.js';\nimport './src/ops/get.js';\nimport './src/ops/luminance.js';\nimport './src/ops/mix.js';\nimport './src/ops/premultiply.js';\nimport './src/ops/saturate.js';\nimport './src/ops/set.js';\nimport './src/ops/shade.js';\n\n// interpolators\nimport './src/interpolator/rgb.js';\nimport './src/interpolator/lrgb.js';\nimport './src/interpolator/lab.js';\nimport './src/interpolator/lch.js';\nimport './src/interpolator/num.js';\nimport './src/interpolator/hcg.js';\nimport './src/interpolator/hsi.js';\nimport './src/interpolator/hsl.js';\nimport './src/interpolator/hsv.js';\nimport './src/interpolator/oklab.js';\nimport './src/interpolator/oklch.js';\n\n// generators -- > create new colors\nimport average from './src/generator/average.js';\nimport bezier from './src/generator/bezier.js';\nimport blend from './src/generator/blend.js';\nimport cubehelix from './src/generator/cubehelix.js';\nimport mix from './src/generator/mix.js';\nimport random from './src/generator/random.js';\nimport scale from './src/generator/scale.js';\n\n// other utility methods\nimport { analyze } from './src/utils/analyze.js';\nimport contrast from './src/utils/contrast.js';\nimport contrastAPCA from './src/utils/contrastAPCA.js';\nimport deltaE from './src/utils/delta-e.js';\nimport distance from './src/utils/distance.js';\nimport { limits } from './src/utils/analyze.js';\nimport valid from './src/utils/valid.js';\nimport input from './src/io/input.js';\n\n// scale\nimport scales from './src/utils/scales.js';\n\n// colors\nimport colors from './src/colors/w3cx11.js';\nimport brewer from './src/colors/colorbrewer.js';\nimport Color from './src/Color.js';\n\nObject.assign(chroma, {\n    analyze,\n    average,\n    bezier,\n    blend,\n    brewer,\n    Color,\n    colors,\n    contrast,\n    contrastAPCA,\n    cubehelix,\n    deltaE,\n    distance,\n    input,\n    interpolate: mix,\n    limits,\n    mix,\n    random,\n    scale,\n    scales,\n    valid\n});\n\nexport default chroma;\n\nexport {\n    analyze,\n    average,\n    bezier,\n    blend,\n    brewer,\n    Color,\n    colors,\n    contrast,\n    contrastAPCA,\n    cubehelix,\n    deltaE,\n    distance,\n    input,\n    limits,\n    mix,\n    mix as interpolate,\n    random,\n    scale,\n    scales,\n    valid\n};\n\nexport * from './src/io/cmyk/index.js';\nexport * from './src/io/css/index.js';\nexport * from './src/io/gl/index.js';\nexport * from './src/io/hcg/index.js';\nexport * from './src/io/hex/index.js';\nexport * from './src/io/hsi/index.js';\nexport * from './src/io/hsl/index.js';\nexport * from './src/io/hsv/index.js';\nexport * from './src/io/lab/index.js';\nexport * from './src/io/lch/index.js';\nexport * from './src/io/num/index.js';\nexport * from './src/io/rgb/index.js';\nexport * from './src/io/temp/index.js';\nexport * from './src/io/oklab/index.js';\nexport * from './src/io/oklch/index.js';\n"
  },
  {
    "path": "index.umd.js",
    "content": "// feel free to comment out anything to rollup\n// a smaller chroma.js bundle\nimport chroma from './src/chroma.js';\n\n// io --> convert colors\nimport { cmyk } from './src/io/cmyk/index.js';\nimport { css } from './src/io/css/index.js';\nimport { gl } from './src/io/gl/index.js';\nimport { hcg } from './src/io/hcg/index.js';\nimport { hex } from './src/io/hex/index.js';\nimport { hsi } from './src/io/hsi/index.js';\nimport { hsl } from './src/io/hsl/index.js';\nimport { hsv } from './src/io/hsv/index.js';\nimport { lab, getLabWhitePoint, setLabWhitePoint } from './src/io/lab/index.js';\nimport { lch, hcl } from './src/io/lch/index.js';\nimport { num } from './src/io/num/index.js';\nimport { rgb } from './src/io/rgb/index.js';\nimport { temp, kelvin, temperature } from './src/io/temp/index.js';\nimport { oklab } from './src/io/oklab/index.js';\nimport { oklch } from './src/io/oklch/index.js';\n\nimport './src/io/named/index.js';\n\n// operators --> modify existing Colors\nimport './src/ops/alpha.js';\nimport './src/ops/clipped.js';\nimport './src/ops/darken.js';\nimport './src/ops/get.js';\nimport './src/ops/luminance.js';\nimport './src/ops/mix.js';\nimport './src/ops/premultiply.js';\nimport './src/ops/saturate.js';\nimport './src/ops/set.js';\nimport './src/ops/shade.js';\n\n// interpolators\nimport './src/interpolator/rgb.js';\nimport './src/interpolator/lrgb.js';\nimport './src/interpolator/lab.js';\nimport './src/interpolator/lch.js';\nimport './src/interpolator/num.js';\nimport './src/interpolator/hcg.js';\nimport './src/interpolator/hsi.js';\nimport './src/interpolator/hsl.js';\nimport './src/interpolator/hsv.js';\nimport './src/interpolator/oklab.js';\nimport './src/interpolator/oklch.js';\n\n// generators -- > create new colors\nimport average from './src/generator/average.js';\nimport bezier from './src/generator/bezier.js';\nimport blend from './src/generator/blend.js';\nimport cubehelix from './src/generator/cubehelix.js';\nimport mix from './src/generator/mix.js';\nimport random from './src/generator/random.js';\nimport scale from './src/generator/scale.js';\n\n// other utility methods\nimport { analyze } from './src/utils/analyze.js';\nimport contrast from './src/utils/contrast.js';\nimport contrastAPCA from './src/utils/contrastAPCA.js';\nimport deltaE from './src/utils/delta-e.js';\nimport distance from './src/utils/distance.js';\nimport { limits } from './src/utils/analyze.js';\nimport valid from './src/utils/valid.js';\nimport input from './src/io/input.js';\n\n// scale\nimport scales from './src/utils/scales.js';\n\n// colors\nimport colors from './src/colors/w3cx11.js';\nimport brewer from './src/colors/colorbrewer.js';\nimport Color from './src/Color.js';\n\nObject.assign(chroma, {\n    analyze,\n    average,\n    bezier,\n    blend,\n    brewer,\n    Color,\n    colors,\n    contrast,\n    contrastAPCA,\n    cubehelix,\n    deltaE,\n    distance,\n    input,\n    interpolate: mix,\n    limits,\n    mix,\n    random,\n    scale,\n    scales,\n    valid,\n    cmyk,\n    css,\n    gl,\n    hcg,\n    hex,\n    hsi,\n    hsl,\n    hsv,\n    lab,\n    lch,\n    hcl,\n    num,\n    rgb,\n    temp,\n    kelvin,\n    temperature,\n    oklab,\n    oklch,\n    getLabWhitePoint,\n    setLabWhitePoint\n});\n\nexport default chroma;\n"
  },
  {
    "path": "index.umd.light.js",
    "content": "import chroma from './src/chroma.js';\n\n// feel free to comment out anything to rollup\n// a smaller chroma.js bundle\n\n// io --> convert colors\nimport { css } from './src/io/css/index.js';\nimport { hex } from './src/io/hex/index.js';\nimport { hsl } from './src/io/hsl/index.js';\nimport { lab } from './src/io/lab/index.js';\nimport { oklab } from './src/io/oklab/index.js';\nimport { rgb } from './src/io/rgb/index.js';\n\n// operators --> modify existing Colors\nimport './src/ops/alpha.js';\nimport './src/ops/darken.js';\nimport './src/ops/get.js';\nimport './src/ops/mix.js';\nimport './src/ops/set.js';\nimport './src/ops/shade.js';\n\n// interpolators\nimport './src/interpolator/lrgb.js';\nimport './src/interpolator/oklab.js';\n\n// generators -- > create new colors\nimport mix from './src/generator/mix.js';\n\n// other utility methods\nimport valid from './src/utils/valid.js';\nimport Color from './src/Color.js';\n\nObject.assign(chroma, {\n    Color,\n    valid,\n    css,\n    hex,\n    hsl,\n    lab,\n    oklab,\n    rgb,\n    mix,\n    interpolate: mix\n});\n\nexport default chroma;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"chroma-js\",\n  \"description\": \"JavaScript library for color conversions\",\n  \"version\": \"3.2.0\",\n  \"packageManager\": \"pnpm@10.24.0\",\n  \"author\": \"Gregor Aisch\",\n  \"type\": \"module\",\n  \"homepage\": \"https://github.com/gka/chroma.js\",\n  \"keywords\": [\n    \"color\"\n  ],\n  \"maintainers\": [\n    {\n      \"name\": \"Gregor Aisch\",\n      \"email\": \"mail@driven-by-data.net\",\n      \"web\": \"https://vis4.net\"\n    }\n  ],\n  \"bugs\": \"https://github.com/gka/chroma.js/issues\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/gka/chroma.js.git\"\n  },\n  \"exports\": {\n    \".\": {\n      \"import\": \"./index.js\",\n      \"require\": \"./dist/chroma.cjs\"\n    },\n    \"./light\": {\n      \"import\": \"./index-light.js\",\n      \"require\": \"./dist/chroma-light.cjs\"\n    },\n    \"./src/colors/*.js\": {\n      \"import\": \"./src/colors/*.js\"\n    },\n    \"./src/generator/*.js\": {\n      \"import\": \"./src/generator/*.js\"\n    },\n    \"./src/interpolator/*.js\": {\n      \"import\": \"./src/interpolator/*.js\"\n    },\n    \"./src/io/*.js\": {\n      \"import\": \"./src/io/*.js\"\n    },\n    \"./src/ops/*.js\": {\n      \"import\": \"./src/ops/*.js\"\n    },\n    \"./src/utils/*.js\": {\n      \"import\": \"./src/utils/*.js\"\n    },\n    \"./src/*.js\": {\n      \"import\": \"./src/*.js\"\n    }\n  },\n  \"main\": \"./index.js\",\n  \"scripts\": {\n    \"prepublishOnly\": \"npm test -- --run && node .bin/update-version.cjs && npm run build && npm run docs\",\n    \"build\": \"rollup -c && cross-env DEV=1 rollup -c \",\n    \"docs\": \"cd docs && make\",\n    \"docs-preview\": \"cd docs && make && make preview\",\n    \"test\": \"vitest\",\n    \"test:update\": \"vitest -u\",\n    \"lint\": \"prettier --check index.js index.umd.js index.umd.light.js src *.config.js test && eslint index.js index.umd.js index.umd.light.js src\",\n    \"format\": \"prettier --write index.js index.umd.js index.umd.light.js src *.config.js test\",\n    \"prepare\": \"husky\"\n  },\n  \"devDependencies\": {\n    \"@babel/eslint-parser\": \"^7.28.5\",\n    \"@eslint/eslintrc\": \"^3.3.1\",\n    \"@eslint/js\": \"^9.39.1\",\n    \"@rollup/plugin-buble\": \"^1.0.3\",\n    \"@rollup/plugin-terser\": \"^0.4.4\",\n    \"cross-env\": \"^10.1.0\",\n    \"eslint\": \"^9.39.1\",\n    \"globals\": \"^16.5.0\",\n    \"http-server\": \"^14.1.1\",\n    \"husky\": \"^9.1.7\",\n    \"markdown-to-html\": \"0.0.13\",\n    \"minimatch\": \"^10.1.1\",\n    \"prettier\": \"^3.7.1\",\n    \"rollup\": \"^4.53.3\",\n    \"rollup-plugin-license\": \"^3.6.0\",\n    \"vitest\": \"^4.0.14\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"license\": \"(BSD-3-Clause AND Apache-2.0)\",\n  \"spm\": {\n    \"main\": \"chroma.js\",\n    \"ignore\": [\n      \"src\",\n      \"doc\",\n      \"test\"\n    ]\n  }\n}\n"
  },
  {
    "path": "prettier.config.js",
    "content": "// prettier.config.js, .prettierrc.js, prettier.config.mjs, or .prettierrc.mjs\n\n/**\n * @see https://prettier.io/docs/en/configuration.html\n * @type {import(\"prettier\").Config}\n */\nconst config = {\n    trailingComma: 'none',\n    tabWidth: 4,\n    semi: true,\n    singleQuote: true,\n    printWidth: 80,\n    overrides: [\n        {\n            files: 'src/colors/colorbrewer.js',\n            options: {\n                printWidth: 160\n            }\n        },\n        {\n            files: 'test/*.js',\n            options: {\n                printWidth: 160\n            }\n        }\n    ]\n};\n\nexport default config;\n"
  },
  {
    "path": "readme.md",
    "content": "# Chroma.js\n\n[Chroma.js](https://vis4.net/chromajs/) is a ~~tiny~~ [small-ish](https://bundlejs.com/?q=chroma-js) zero-dependency JavaScript library for all kinds of color conversions and color scales.\n\n[![Build Status](https://api.travis-ci.com/gka/chroma.js.svg?branch=master)](https://travis-ci.com/gka/chroma.js)\n[![Build size](https://deno.bundlejs.com/?q=chroma-js&badge)](https://bundlejs.com/?q=chroma-js)\n\n\n\n### Usage\n\nInstall from npm\n\n```\nnpm install chroma-js\n```\n\nImport package into project\n\n```javascript\nimport chroma from \"chroma-js\";\n```\n\nInitiate and manipulate colors:\n\n```javascript\nchroma('#D4F880').darken().hex();  // #a1c550\n```\n\nWorking with color scales is easy, too:\n\n```javascript\nscale = chroma.scale(['white', 'red']);\nscale(0.5).hex(); // #FF7F7F\n```\n\nLab/Lch interpolation looks better than RGB\n\n```javascript\nchroma.scale(['white', 'red']).mode('lab');\n```\n\nCustom domains! Quantiles! Color Brewer!!\n\n```javascript\nchroma.scale('RdYlBu').domain(myValues, 7, 'quantiles');\n```\n\nAnd why not use logarithmic color scales once in your life?\n\n```javascript\nchroma.scale(['lightyellow', 'navy']).domain([1, 100000], 7, 'log');\n```\n\n### Like it?\n\nWhy not dive into the [interactive documentation](http://gka.github.io/chroma.js/) (there's a [static version](https://github.com/gka/chroma.js/blob/master/docs/src/index.md), too). You can download [chroma.min.js](https://raw.github.com/gka/chroma.js/master/chroma.min.js) or use the [hosted version on unpkg.com](https://app.unpkg.com/chroma-js@latest/files/dist).\n\nYou can use it in node.js, too!\n\n    npm install chroma-js\n\nOr you can use it in SASS using [chromatic-sass](https://github.com/bugsnag/chromatic-sass)!\n\n### Want to contribute?\n\nCome over and say hi in our [Discord channel](https://discord.gg/7fgurEqTRe)!\n\n### Build instructions\n\nFirst clone the repository and install the dev dependencies:\n\n    git clone git@github.com:gka/chroma.js.git\n    cd chroma.js\n    npm install\n\nThen compile the coffee-script source files to the build files:\n\n    npm run build\n\nDon't forget to tests your changes! You will probably also want to add new test to the `/test` folder in case you added a feature.\n\n    npm test\n\nAnd to update the documentation just run\n\n    npm run docs\n\nTo preview the docs locally you can use\n\n    npm run docs-preview\n\n### Similar Libraries / Prior Art\n\n* [Chromatist](https://github.com/jrus/chromatist)\n* [GrapeFruit](https://github.com/xav/Grapefruit) (Python)\n* [colors.py](https://github.com/mattrobenolt/colors.py) (Python)\n* [d3.js](https://github.com/mbostock/d3)\n* [Color Art](https://github.com/JiatLn/color-art) (Rust)\n\n\n\n### Author\n\nChroma.js is written by [Gregor Aisch](http://driven-by-data.net).\n\n### License\n\nReleased under [BSD license](http://opensource.org/licenses/BSD-3-Clause).\nVersions prior to 0.4 were released under [GPL](http://www.gnu.org/licenses/gpl-3.0).\n\n### Further reading\n\n* [How To Avoid Equidistant HSV Colors](https://www.vis4.net/blog/avoid-equidistant-hsv-colors/)\n* [Mastering Multi-hued Color Scales with Chroma.js](https://www.vis4.net/blog/mastering-multi-hued-color-scales/)\n\n### FAQ\n\n**There have been no commits in X weeks. Is chroma.js dead?**\n\nNo! It's just that the author of this library has other things to do than devoting every week of his life to making cosmetic changes to a piece of software that is working just fine as it is, just so that people like you don't feel like it's abandoned and left alone in this world to die. Bugs will be fixed. Some new things will come at some point. Patience.\n\n**I want to help maintaining chroma.js!**\n\nYay, that's awesome! Please say hi at our [Discord chat](https://discord.gg/m2M7k5Nf) to get in touch\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import buble from '@rollup/plugin-buble';\nimport license from 'rollup-plugin-license';\nimport path from 'path';\nimport terser from '@rollup/plugin-terser';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\n\nconst minify = !process.env.ROLLUP_WATCH && !process.env.DEV;\n/** globals process, __dirname **/\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nexport default [\n    bundle('index.umd.js', 'chroma'),\n    bundle('index.umd.light.js', 'chroma-light')\n];\n\nfunction bundle(input, target) {\n    return {\n        input,\n        output: {\n            file: `dist/${target}${minify ? '.min' : ''}.cjs`,\n            format: 'umd',\n            globals: {}, // If you have any external dependencies, list them here\n            exports: 'default',\n            name: 'chroma'\n        },\n        plugins: [\n            // If we're building for production (npm run build\n            // instead of npm run dev), transpile and minify\n            buble({\n                transforms: { dangerousForOf: true }\n            }),\n            minify &&\n                terser({\n                    mangle: true\n                }),\n            license({\n                sourcemap: true,\n                //cwd: '.', // Default is process.cwd()\n\n                banner: {\n                    content: {\n                        file: path.join(__dirname, 'LICENSE')\n                    }\n                }\n            })\n        ]\n    };\n}\n"
  },
  {
    "path": "src/Color.js",
    "content": "import { last, clip_rgb, type } from './utils/index.js';\nimport _input from './io/input.js';\n\nclass Color {\n    constructor(...args) {\n        const me = this;\n        if (\n            type(args[0]) === 'object' &&\n            args[0].constructor &&\n            args[0].constructor === this.constructor\n        ) {\n            // the argument is already a Color instance\n            return args[0];\n        }\n        // last argument could be the mode\n        let mode = last(args);\n        let autodetect = false;\n        if (!mode) {\n            autodetect = true;\n\n            if (!_input.sorted) {\n                _input.autodetect = _input.autodetect.sort((a, b) => b.p - a.p);\n                _input.sorted = true;\n            }\n\n            // auto-detect format\n            for (let chk of _input.autodetect) {\n                mode = chk.test(...args);\n                if (mode) break;\n            }\n        }\n        if (_input.format[mode]) {\n            const rgb = _input.format[mode].apply(\n                null,\n                autodetect ? args : args.slice(0, -1)\n            );\n            me._rgb = clip_rgb(rgb);\n        } else {\n            throw new Error('unknown format: ' + args);\n        }\n        // add alpha channel\n        if (me._rgb.length === 3) me._rgb.push(1);\n    }\n    toString() {\n        if (type(this.hex) == 'function') return this.hex();\n        return `[${this._rgb.join(',')}]`;\n    }\n}\nexport default Color;\n"
  },
  {
    "path": "src/chroma.js",
    "content": "import Color from './Color.js';\nimport { version } from './version.js';\n\nconst chroma = (...args) => {\n    return new Color(...args);\n};\n\nchroma.version = version;\n\nexport default chroma;\n"
  },
  {
    "path": "src/colors/colorbrewer.js",
    "content": "/**\n    ColorBrewer colors for chroma.js\n\n    Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The\n    Pennsylvania State University.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software distributed\n    under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n    CONDITIONS OF ANY KIND, either express or implied. See the License for the\n    specific language governing permissions and limitations under the License.\n*/\n\nconst colorbrewer = {\n    // sequential\n    OrRd: ['#fff7ec', '#fee8c8', '#fdd49e', '#fdbb84', '#fc8d59', '#ef6548', '#d7301f', '#b30000', '#7f0000'],\n    PuBu: ['#fff7fb', '#ece7f2', '#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0', '#0570b0', '#045a8d', '#023858'],\n    BuPu: ['#f7fcfd', '#e0ecf4', '#bfd3e6', '#9ebcda', '#8c96c6', '#8c6bb1', '#88419d', '#810f7c', '#4d004b'],\n    Oranges: ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704'],\n    BuGn: ['#f7fcfd', '#e5f5f9', '#ccece6', '#99d8c9', '#66c2a4', '#41ae76', '#238b45', '#006d2c', '#00441b'],\n    YlOrBr: ['#ffffe5', '#fff7bc', '#fee391', '#fec44f', '#fe9929', '#ec7014', '#cc4c02', '#993404', '#662506'],\n    YlGn: ['#ffffe5', '#f7fcb9', '#d9f0a3', '#addd8e', '#78c679', '#41ab5d', '#238443', '#006837', '#004529'],\n    Reds: ['#fff5f0', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d'],\n    RdPu: ['#fff7f3', '#fde0dd', '#fcc5c0', '#fa9fb5', '#f768a1', '#dd3497', '#ae017e', '#7a0177', '#49006a'],\n    Greens: ['#f7fcf5', '#e5f5e0', '#c7e9c0', '#a1d99b', '#74c476', '#41ab5d', '#238b45', '#006d2c', '#00441b'],\n    YlGnBu: ['#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58'],\n    Purples: ['#fcfbfd', '#efedf5', '#dadaeb', '#bcbddc', '#9e9ac8', '#807dba', '#6a51a3', '#54278f', '#3f007d'],\n    GnBu: ['#f7fcf0', '#e0f3db', '#ccebc5', '#a8ddb5', '#7bccc4', '#4eb3d3', '#2b8cbe', '#0868ac', '#084081'],\n    Greys: ['#ffffff', '#f0f0f0', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525', '#000000'],\n    YlOrRd: ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'],\n    PuRd: ['#f7f4f9', '#e7e1ef', '#d4b9da', '#c994c7', '#df65b0', '#e7298a', '#ce1256', '#980043', '#67001f'],\n    Blues: ['#f7fbff', '#deebf7', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5', '#08519c', '#08306b'],\n    PuBuGn: ['#fff7fb', '#ece2f0', '#d0d1e6', '#a6bddb', '#67a9cf', '#3690c0', '#02818a', '#016c59', '#014636'],\n    Viridis: ['#440154', '#482777', '#3f4a8a', '#31678e', '#26838f', '#1f9d8a', '#6cce5a', '#b6de2b', '#fee825'],\n\n    // diverging\n    Spectral: ['#9e0142', '#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#e6f598', '#abdda4', '#66c2a5', '#3288bd', '#5e4fa2'],\n    RdYlGn: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850', '#006837'],\n    RdBu: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#f7f7f7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],\n    PiYG: ['#8e0152', '#c51b7d', '#de77ae', '#f1b6da', '#fde0ef', '#f7f7f7', '#e6f5d0', '#b8e186', '#7fbc41', '#4d9221', '#276419'],\n    PRGn: ['#40004b', '#762a83', '#9970ab', '#c2a5cf', '#e7d4e8', '#f7f7f7', '#d9f0d3', '#a6dba0', '#5aae61', '#1b7837', '#00441b'],\n    RdYlBu: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'],\n    BrBG: ['#543005', '#8c510a', '#bf812d', '#dfc27d', '#f6e8c3', '#f5f5f5', '#c7eae5', '#80cdc1', '#35978f', '#01665e', '#003c30'],\n    RdGy: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#ffffff', '#e0e0e0', '#bababa', '#878787', '#4d4d4d', '#1a1a1a'],\n    PuOr: ['#7f3b08', '#b35806', '#e08214', '#fdb863', '#fee0b6', '#f7f7f7', '#d8daeb', '#b2abd2', '#8073ac', '#542788', '#2d004b'],\n\n    // qualitative\n    Set2: ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3'],\n    Accent: ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666'],\n    Set1: ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999'],\n    Set3: ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', '#ccebc5', '#ffed6f'],\n    Dark2: ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d', '#666666'],\n    Paired: ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a', '#ffff99', '#b15928'],\n    Pastel2: ['#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc'],\n    Pastel1: ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2']\n};\n\nconst colorbrewerTypes = Object.keys(colorbrewer);\nconst typeMap = new Map(colorbrewerTypes.map((key) => [key.toLowerCase(), key]));\n\n// use Proxy to allow case-insensitive access to palettes\nconst colorbrewerProxy =\n    typeof Proxy === 'function'\n        ? new Proxy(colorbrewer, {\n              get(target, prop) {\n                  const lower = prop.toLowerCase();\n                  if (typeMap.has(lower)) {\n                      return target[typeMap.get(lower)];\n                  }\n              },\n              getOwnPropertyNames() {\n                  return Object.getOwnPropertyNames(colorbrewerTypes);\n              }\n          })\n        : colorbrewer;\n\nexport default colorbrewerProxy;\n"
  },
  {
    "path": "src/colors/w3cx11.js",
    "content": "/**\n\tX11 color names\n\n\thttp://www.w3.org/TR/css3-color/#svg-color\n*/\n\nconst w3cx11 = {\n    aliceblue: '#f0f8ff',\n    antiquewhite: '#faebd7',\n    aqua: '#00ffff',\n    aquamarine: '#7fffd4',\n    azure: '#f0ffff',\n    beige: '#f5f5dc',\n    bisque: '#ffe4c4',\n    black: '#000000',\n    blanchedalmond: '#ffebcd',\n    blue: '#0000ff',\n    blueviolet: '#8a2be2',\n    brown: '#a52a2a',\n    burlywood: '#deb887',\n    cadetblue: '#5f9ea0',\n    chartreuse: '#7fff00',\n    chocolate: '#d2691e',\n    coral: '#ff7f50',\n    cornflowerblue: '#6495ed',\n    cornsilk: '#fff8dc',\n    crimson: '#dc143c',\n    cyan: '#00ffff',\n    darkblue: '#00008b',\n    darkcyan: '#008b8b',\n    darkgoldenrod: '#b8860b',\n    darkgray: '#a9a9a9',\n    darkgreen: '#006400',\n    darkgrey: '#a9a9a9',\n    darkkhaki: '#bdb76b',\n    darkmagenta: '#8b008b',\n    darkolivegreen: '#556b2f',\n    darkorange: '#ff8c00',\n    darkorchid: '#9932cc',\n    darkred: '#8b0000',\n    darksalmon: '#e9967a',\n    darkseagreen: '#8fbc8f',\n    darkslateblue: '#483d8b',\n    darkslategray: '#2f4f4f',\n    darkslategrey: '#2f4f4f',\n    darkturquoise: '#00ced1',\n    darkviolet: '#9400d3',\n    deeppink: '#ff1493',\n    deepskyblue: '#00bfff',\n    dimgray: '#696969',\n    dimgrey: '#696969',\n    dodgerblue: '#1e90ff',\n    firebrick: '#b22222',\n    floralwhite: '#fffaf0',\n    forestgreen: '#228b22',\n    fuchsia: '#ff00ff',\n    gainsboro: '#dcdcdc',\n    ghostwhite: '#f8f8ff',\n    gold: '#ffd700',\n    goldenrod: '#daa520',\n    gray: '#808080',\n    green: '#008000',\n    greenyellow: '#adff2f',\n    grey: '#808080',\n    honeydew: '#f0fff0',\n    hotpink: '#ff69b4',\n    indianred: '#cd5c5c',\n    indigo: '#4b0082',\n    ivory: '#fffff0',\n    khaki: '#f0e68c',\n    laserlemon: '#ffff54',\n    lavender: '#e6e6fa',\n    lavenderblush: '#fff0f5',\n    lawngreen: '#7cfc00',\n    lemonchiffon: '#fffacd',\n    lightblue: '#add8e6',\n    lightcoral: '#f08080',\n    lightcyan: '#e0ffff',\n    lightgoldenrod: '#fafad2',\n    lightgoldenrodyellow: '#fafad2',\n    lightgray: '#d3d3d3',\n    lightgreen: '#90ee90',\n    lightgrey: '#d3d3d3',\n    lightpink: '#ffb6c1',\n    lightsalmon: '#ffa07a',\n    lightseagreen: '#20b2aa',\n    lightskyblue: '#87cefa',\n    lightslategray: '#778899',\n    lightslategrey: '#778899',\n    lightsteelblue: '#b0c4de',\n    lightyellow: '#ffffe0',\n    lime: '#00ff00',\n    limegreen: '#32cd32',\n    linen: '#faf0e6',\n    magenta: '#ff00ff',\n    maroon: '#800000',\n    maroon2: '#7f0000',\n    maroon3: '#b03060',\n    mediumaquamarine: '#66cdaa',\n    mediumblue: '#0000cd',\n    mediumorchid: '#ba55d3',\n    mediumpurple: '#9370db',\n    mediumseagreen: '#3cb371',\n    mediumslateblue: '#7b68ee',\n    mediumspringgreen: '#00fa9a',\n    mediumturquoise: '#48d1cc',\n    mediumvioletred: '#c71585',\n    midnightblue: '#191970',\n    mintcream: '#f5fffa',\n    mistyrose: '#ffe4e1',\n    moccasin: '#ffe4b5',\n    navajowhite: '#ffdead',\n    navy: '#000080',\n    oldlace: '#fdf5e6',\n    olive: '#808000',\n    olivedrab: '#6b8e23',\n    orange: '#ffa500',\n    orangered: '#ff4500',\n    orchid: '#da70d6',\n    palegoldenrod: '#eee8aa',\n    palegreen: '#98fb98',\n    paleturquoise: '#afeeee',\n    palevioletred: '#db7093',\n    papayawhip: '#ffefd5',\n    peachpuff: '#ffdab9',\n    peru: '#cd853f',\n    pink: '#ffc0cb',\n    plum: '#dda0dd',\n    powderblue: '#b0e0e6',\n    purple: '#800080',\n    purple2: '#7f007f',\n    purple3: '#a020f0',\n    rebeccapurple: '#663399',\n    red: '#ff0000',\n    rosybrown: '#bc8f8f',\n    royalblue: '#4169e1',\n    saddlebrown: '#8b4513',\n    salmon: '#fa8072',\n    sandybrown: '#f4a460',\n    seagreen: '#2e8b57',\n    seashell: '#fff5ee',\n    sienna: '#a0522d',\n    silver: '#c0c0c0',\n    skyblue: '#87ceeb',\n    slateblue: '#6a5acd',\n    slategray: '#708090',\n    slategrey: '#708090',\n    snow: '#fffafa',\n    springgreen: '#00ff7f',\n    steelblue: '#4682b4',\n    tan: '#d2b48c',\n    teal: '#008080',\n    thistle: '#d8bfd8',\n    tomato: '#ff6347',\n    turquoise: '#40e0d0',\n    violet: '#ee82ee',\n    wheat: '#f5deb3',\n    white: '#ffffff',\n    whitesmoke: '#f5f5f5',\n    yellow: '#ffff00',\n    yellowgreen: '#9acd32'\n};\n\nexport default w3cx11;\n"
  },
  {
    "path": "src/generator/average.js",
    "content": "import Color from '../Color.js';\nimport { clip_rgb } from '../utils/index.js';\n\nconst { pow, sqrt, PI, cos, sin, atan2 } = Math;\n\nexport default (colors, mode = 'lrgb', weights = null) => {\n    const l = colors.length;\n    if (!weights) weights = Array.from(new Array(l)).map(() => 1);\n    // normalize weights\n    const k =\n        l /\n        weights.reduce(function (a, b) {\n            return a + b;\n        });\n    weights.forEach((w, i) => {\n        weights[i] *= k;\n    });\n    // convert colors to Color objects\n    colors = colors.map((c) => new Color(c));\n    if (mode === 'lrgb') {\n        return _average_lrgb(colors, weights);\n    }\n    const first = colors.shift();\n    const xyz = first.get(mode);\n    const cnt = [];\n    let dx = 0;\n    let dy = 0;\n    // initial color\n    for (let i = 0; i < xyz.length; i++) {\n        xyz[i] = (xyz[i] || 0) * weights[0];\n        cnt.push(isNaN(xyz[i]) ? 0 : weights[0]);\n        if (mode.charAt(i) === 'h' && !isNaN(xyz[i])) {\n            const A = (xyz[i] / 180) * PI;\n            dx += cos(A) * weights[0];\n            dy += sin(A) * weights[0];\n        }\n    }\n\n    let alpha = first.alpha() * weights[0];\n    colors.forEach((c, ci) => {\n        const xyz2 = c.get(mode);\n        alpha += c.alpha() * weights[ci + 1];\n        for (let i = 0; i < xyz.length; i++) {\n            if (!isNaN(xyz2[i])) {\n                cnt[i] += weights[ci + 1];\n                if (mode.charAt(i) === 'h') {\n                    const A = (xyz2[i] / 180) * PI;\n                    dx += cos(A) * weights[ci + 1];\n                    dy += sin(A) * weights[ci + 1];\n                } else {\n                    xyz[i] += xyz2[i] * weights[ci + 1];\n                }\n            }\n        }\n    });\n\n    for (let i = 0; i < xyz.length; i++) {\n        if (mode.charAt(i) === 'h') {\n            let A = (atan2(dy / cnt[i], dx / cnt[i]) / PI) * 180;\n            while (A < 0) A += 360;\n            while (A >= 360) A -= 360;\n            xyz[i] = A;\n        } else {\n            xyz[i] = xyz[i] / cnt[i];\n        }\n    }\n    alpha /= l;\n    return new Color(xyz, mode).alpha(alpha > 0.99999 ? 1 : alpha, true);\n};\n\nconst _average_lrgb = (colors, weights) => {\n    const l = colors.length;\n    const xyz = [0, 0, 0, 0];\n    for (let i = 0; i < colors.length; i++) {\n        const col = colors[i];\n        const f = weights[i] / l;\n        const rgb = col._rgb;\n        xyz[0] += pow(rgb[0], 2) * f;\n        xyz[1] += pow(rgb[1], 2) * f;\n        xyz[2] += pow(rgb[2], 2) * f;\n        xyz[3] += rgb[3] * f;\n    }\n    xyz[0] = sqrt(xyz[0]);\n    xyz[1] = sqrt(xyz[1]);\n    xyz[2] = sqrt(xyz[2]);\n    if (xyz[3] > 0.9999999) xyz[3] = 1;\n    return new Color(clip_rgb(xyz));\n};\n"
  },
  {
    "path": "src/generator/bezier.js",
    "content": "//\n// interpolates between a set of colors uzing a bezier spline\n//\n\n// @requires utils lab\nimport Color from '../Color.js';\nimport '../io/lab/index.js';\nimport scale from './scale.js';\n\n// nth row of the pascal triangle\nconst binom_row = function (n) {\n    let row = [1, 1];\n    for (let i = 1; i < n; i++) {\n        let newrow = [1];\n        for (let j = 1; j <= row.length; j++) {\n            newrow[j] = (row[j] || 0) + row[j - 1];\n        }\n        row = newrow;\n    }\n    return row;\n};\n\nconst bezier = function (colors) {\n    let I, lab0, lab1, lab2;\n    colors = colors.map((c) => new Color(c));\n    if (colors.length === 2) {\n        // linear interpolation\n        [lab0, lab1] = colors.map((c) => c.lab());\n        I = function (t) {\n            const lab = [0, 1, 2].map((i) => lab0[i] + t * (lab1[i] - lab0[i]));\n            return new Color(lab, 'lab');\n        };\n    } else if (colors.length === 3) {\n        // quadratic bezier interpolation\n        [lab0, lab1, lab2] = colors.map((c) => c.lab());\n        I = function (t) {\n            const lab = [0, 1, 2].map(\n                (i) =>\n                    (1 - t) * (1 - t) * lab0[i] +\n                    2 * (1 - t) * t * lab1[i] +\n                    t * t * lab2[i]\n            );\n            return new Color(lab, 'lab');\n        };\n    } else if (colors.length === 4) {\n        // cubic bezier interpolation\n        let lab3;\n        [lab0, lab1, lab2, lab3] = colors.map((c) => c.lab());\n        I = function (t) {\n            const lab = [0, 1, 2].map(\n                (i) =>\n                    (1 - t) * (1 - t) * (1 - t) * lab0[i] +\n                    3 * (1 - t) * (1 - t) * t * lab1[i] +\n                    3 * (1 - t) * t * t * lab2[i] +\n                    t * t * t * lab3[i]\n            );\n            return new Color(lab, 'lab');\n        };\n    } else if (colors.length >= 5) {\n        // general case (degree n bezier)\n        let labs, row, n;\n        labs = colors.map((c) => c.lab());\n        n = colors.length - 1;\n        row = binom_row(n);\n        I = function (t) {\n            const u = 1 - t;\n            const lab = [0, 1, 2].map((i) =>\n                labs.reduce(\n                    (sum, el, j) =>\n                        sum + row[j] * u ** (n - j) * t ** j * el[i],\n                    0\n                )\n            );\n            return new Color(lab, 'lab');\n        };\n    } else {\n        throw new RangeError('No point in running bezier with only one color.');\n    }\n    return I;\n};\n\nexport default (colors) => {\n    const f = bezier(colors);\n    f.scale = () => scale(f);\n    return f;\n};\n"
  },
  {
    "path": "src/generator/blend.js",
    "content": "/*\n * interpolates between a set of colors uzing a bezier spline\n * blend mode formulas taken from https://web.archive.org/web/20180110014946/http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/\n */\n\nimport '../io/rgb/index.js';\nimport chroma from '../chroma.js';\n\nconst blend = (bottom, top, mode) => {\n    if (!blend[mode]) {\n        throw new Error('unknown blend mode ' + mode);\n    }\n    return blend[mode](bottom, top);\n};\n\nconst blend_f = (f) => (bottom, top) => {\n    const c0 = chroma(top).rgb();\n    const c1 = chroma(bottom).rgb();\n    return chroma.rgb(f(c0, c1));\n};\n\nconst each = (f) => (c0, c1) => {\n    const out = [];\n    out[0] = f(c0[0], c1[0]);\n    out[1] = f(c0[1], c1[1]);\n    out[2] = f(c0[2], c1[2]);\n    return out;\n};\n\nconst normal = (a) => a;\nconst multiply = (a, b) => (a * b) / 255;\nconst darken = (a, b) => (a > b ? b : a);\nconst lighten = (a, b) => (a > b ? a : b);\nconst screen = (a, b) => 255 * (1 - (1 - a / 255) * (1 - b / 255));\nconst overlay = (a, b) =>\n    b < 128 ? (2 * a * b) / 255 : 255 * (1 - 2 * (1 - a / 255) * (1 - b / 255));\nconst burn = (a, b) => 255 * (1 - (1 - b / 255) / (a / 255));\nconst dodge = (a, b) => {\n    if (a === 255) return 255;\n    a = (255 * (b / 255)) / (1 - a / 255);\n    return a > 255 ? 255 : a;\n};\n\n// # add = (a,b) ->\n// #     if (a + b > 255) then 255 else a + b\n\nblend.normal = blend_f(each(normal));\nblend.multiply = blend_f(each(multiply));\nblend.screen = blend_f(each(screen));\nblend.overlay = blend_f(each(overlay));\nblend.darken = blend_f(each(darken));\nblend.lighten = blend_f(each(lighten));\nblend.dodge = blend_f(each(dodge));\nblend.burn = blend_f(each(burn));\n// blend.add = blend_f(each(add));\n\nexport default blend;\n"
  },
  {
    "path": "src/generator/cubehelix.js",
    "content": "// cubehelix interpolation\n// based on D.A. Green \"A colour scheme for the display of astronomical intensity images\"\n// http://astron-soc.in/bulletin/11June/289392011.pdf\nimport { type, clip_rgb, TWOPI } from '../utils/index.js';\nimport chroma from '../chroma.js';\nconst { pow, sin, cos } = Math;\n\nexport default function (\n    start = 300,\n    rotations = -1.5,\n    hue = 1,\n    gamma = 1,\n    lightness = [0, 1]\n) {\n    let dh = 0,\n        dl;\n    if (type(lightness) === 'array') {\n        dl = lightness[1] - lightness[0];\n    } else {\n        dl = 0;\n        lightness = [lightness, lightness];\n    }\n    const f = function (fract) {\n        const a = TWOPI * ((start + 120) / 360 + rotations * fract);\n        const l = pow(lightness[0] + dl * fract, gamma);\n        const h = dh !== 0 ? hue[0] + fract * dh : hue;\n        const amp = (h * l * (1 - l)) / 2;\n        const cos_a = cos(a);\n        const sin_a = sin(a);\n        const r = l + amp * (-0.14861 * cos_a + 1.78277 * sin_a);\n        const g = l + amp * (-0.29227 * cos_a - 0.90649 * sin_a);\n        const b = l + amp * (+1.97294 * cos_a);\n        return chroma(clip_rgb([r * 255, g * 255, b * 255, 1]));\n    };\n    f.start = function (s) {\n        if (s == null) {\n            return start;\n        }\n        start = s;\n        return f;\n    };\n    f.rotations = function (r) {\n        if (r == null) {\n            return rotations;\n        }\n        rotations = r;\n        return f;\n    };\n    f.gamma = function (g) {\n        if (g == null) {\n            return gamma;\n        }\n        gamma = g;\n        return f;\n    };\n    f.hue = function (h) {\n        if (h == null) {\n            return hue;\n        }\n        hue = h;\n        if (type(hue) === 'array') {\n            dh = hue[1] - hue[0];\n            if (dh === 0) {\n                hue = hue[1];\n            }\n        } else {\n            dh = 0;\n        }\n        return f;\n    };\n    f.lightness = function (h) {\n        if (h == null) {\n            return lightness;\n        }\n        if (type(h) === 'array') {\n            lightness = h;\n            dl = h[1] - h[0];\n        } else {\n            lightness = [h, h];\n            dl = 0;\n        }\n        return f;\n    };\n    f.scale = () => chroma.scale(f);\n    f.hue(hue);\n    return f;\n}\n"
  },
  {
    "path": "src/generator/mix.js",
    "content": "import Color from '../Color.js';\nimport { type } from '../utils/index.js';\nimport interpolator from '../interpolator/index.js';\n\nexport default (col1, col2, f = 0.5, ...rest) => {\n    let mode = rest[0] || 'lrgb';\n    if (!interpolator[mode] && !rest.length) {\n        // fall back to the first supported mode\n        mode = Object.keys(interpolator)[0];\n    }\n    if (!interpolator[mode]) {\n        throw new Error(`interpolation mode ${mode} is not defined`);\n    }\n    if (type(col1) !== 'object') col1 = new Color(col1);\n    if (type(col2) !== 'object') col2 = new Color(col2);\n    return interpolator[mode](col1, col2, f).alpha(\n        col1.alpha() + f * (col2.alpha() - col1.alpha())\n    );\n};\n"
  },
  {
    "path": "src/generator/random.js",
    "content": "import Color from '../Color.js';\nconst digits = '0123456789abcdef';\n\nconst { floor, random } = Math;\n\n/**\n * Generates a random color.\n * @param {() => number} rng - A random number generator function.\n */\nexport default (rng = random) => {\n    let code = '#';\n    for (let i = 0; i < 6; i++) {\n        code += digits.charAt(floor(rng() * 16));\n    }\n    return new Color(code, 'hex');\n};\n"
  },
  {
    "path": "src/generator/scale.js",
    "content": "// minimal multi-purpose interface\n\n// @requires utils color analyze\nimport chroma from '../chroma.js';\nimport { limit, type } from '../utils/index.js';\n\nconst { pow } = Math;\n\nexport default function (colors) {\n    // constructor\n    let _mode = 'rgb';\n    let _nacol = chroma('#ccc');\n    let _spread = 0;\n    // const _fixed = false;\n    let _positions = [0, 1];\n    let _domain = [0, 1];\n    let _pos = [];\n    let _padding = [0, 0];\n    let _classes = false;\n    let _colors = [];\n    let _out = false;\n    let _min = 0;\n    let _max = 1;\n    let _correctLightness = false;\n    let _colorCache = {};\n    let _useCache = true;\n    let _gamma = 1;\n\n    // private methods\n\n    const setColors = function (colors) {\n        colors = colors || ['#fff', '#000'];\n        if (\n            colors &&\n            type(colors) === 'string' &&\n            chroma.brewer &&\n            chroma.brewer[colors.toLowerCase()]\n        ) {\n            colors = chroma.brewer[colors.toLowerCase()];\n        }\n        if (type(colors) === 'array') {\n            // handle single color\n            if (colors.length === 1) {\n                colors = [colors[0], colors[0]];\n            }\n            // make a copy of the colors\n            colors = colors.slice(0);\n            // convert to chroma classes\n            for (let c = 0; c < colors.length; c++) {\n                colors[c] = chroma(colors[c]);\n            }\n            // auto-fill color position\n            _pos.length = 0;\n            for (let c = 0; c < colors.length; c++) {\n                _pos.push(c / (colors.length - 1));\n            }\n        }\n        resetCache();\n        return (_colors = colors);\n    };\n\n    const getClass = function (value) {\n        if (_classes != null) {\n            const n = _classes.length - 1;\n            let i = 0;\n            while (i < n && value >= _classes[i]) {\n                i++;\n            }\n            return i - 1;\n        }\n        return 0;\n    };\n\n    let tMapLightness = (t) => t;\n    let tMapDomain = (t) => t;\n\n    // const classifyValue = function(value) {\n    //     let val = value;\n    //     if (_classes.length > 2) {\n    //         const n = _classes.length-1;\n    //         const i = getClass(value);\n    //         const minc = _classes[0] + ((_classes[1]-_classes[0]) * (0 + (_spread * 0.5)));  // center of 1st class\n    //         const maxc = _classes[n-1] + ((_classes[n]-_classes[n-1]) * (1 - (_spread * 0.5)));  // center of last class\n    //         val = _min + ((((_classes[i] + ((_classes[i+1] - _classes[i]) * 0.5)) - minc) / (maxc-minc)) * (_max - _min));\n    //     }\n    //     return val;\n    // };\n\n    const getColor = function (val, bypassMap) {\n        let col, t;\n        if (bypassMap == null) {\n            bypassMap = false;\n        }\n        if (isNaN(val) || val === null) {\n            return _nacol;\n        }\n        if (!bypassMap) {\n            if (_classes && _classes.length > 2) {\n                // find the class\n                const c = getClass(val);\n                t = c / (_classes.length - 2);\n            } else if (_max !== _min) {\n                // just interpolate between min/max\n                t = (val - _min) / (_max - _min);\n            } else {\n                t = 1;\n            }\n        } else {\n            t = val;\n        }\n\n        // domain map\n        t = tMapDomain(t);\n\n        if (!bypassMap) {\n            t = tMapLightness(t); // lightness correction\n        }\n\n        if (_gamma !== 1) {\n            t = pow(t, _gamma);\n        }\n\n        t = _padding[0] + t * (1 - _padding[0] - _padding[1]);\n\n        t = limit(t, 0, 1);\n\n        const k = Math.floor(t * 10000);\n\n        if (_useCache && _colorCache[k]) {\n            col = _colorCache[k];\n        } else {\n            if (type(_colors) === 'array') {\n                //for i in [0.._pos.length-1]\n                for (let i = 0; i < _pos.length; i++) {\n                    const p = _pos[i];\n                    if (t <= p) {\n                        col = _colors[i];\n                        break;\n                    }\n                    if (t >= p && i === _pos.length - 1) {\n                        col = _colors[i];\n                        break;\n                    }\n                    if (t > p && t < _pos[i + 1]) {\n                        t = (t - p) / (_pos[i + 1] - p);\n                        col = chroma.interpolate(\n                            _colors[i],\n                            _colors[i + 1],\n                            t,\n                            _mode\n                        );\n                        break;\n                    }\n                }\n            } else if (type(_colors) === 'function') {\n                col = _colors(t);\n            }\n            if (_useCache) {\n                _colorCache[k] = col;\n            }\n        }\n        return col;\n    };\n\n    var resetCache = () => (_colorCache = {});\n\n    setColors(colors);\n\n    // public interface\n\n    const f = function (v) {\n        const c = chroma(getColor(v));\n        if (_out && c[_out]) {\n            return c[_out]();\n        } else {\n            return c;\n        }\n    };\n\n    f.classes = function (classes) {\n        if (classes != null) {\n            if (type(classes) === 'array') {\n                _classes = classes;\n                _positions = [classes[0], classes[classes.length - 1]];\n            } else {\n                const d = chroma.analyze(_positions);\n                if (classes === 0) {\n                    _classes = [d.min, d.max];\n                } else {\n                    _classes = chroma.limits(d, 'e', classes);\n                }\n            }\n            return f;\n        }\n        return _classes;\n    };\n\n    f.domain = function (domain) {\n        if (!arguments.length) {\n            // return original domain\n            return _domain;\n        }\n        // store original domain so we can return it later\n        _domain = domain.slice(0);\n        _min = domain[0];\n        _max = domain[domain.length - 1];\n        _pos = [];\n        const k = _colors.length;\n        if (domain.length === k && _min !== _max) {\n            // update positions\n            for (let d of Array.from(domain)) {\n                _pos.push((d - _min) / (_max - _min));\n            }\n        } else {\n            for (let c = 0; c < k; c++) {\n                _pos.push(c / (k - 1));\n            }\n            if (domain.length > 2) {\n                // set domain map\n                const tOut = domain.map((d, i) => i / (domain.length - 1));\n                const tBreaks = domain.map((d) => (d - _min) / (_max - _min));\n                if (!tBreaks.every((val, i) => tOut[i] === val)) {\n                    tMapDomain = (t) => {\n                        if (t <= 0 || t >= 1) return t;\n                        let i = 0;\n                        while (t >= tBreaks[i + 1]) i++;\n                        const f =\n                            (t - tBreaks[i]) / (tBreaks[i + 1] - tBreaks[i]);\n                        const out = tOut[i] + f * (tOut[i + 1] - tOut[i]);\n                        return out;\n                    };\n                }\n            }\n        }\n        _positions = [_min, _max];\n        return f;\n    };\n\n    f.mode = function (_m) {\n        if (!arguments.length) {\n            return _mode;\n        }\n        _mode = _m;\n        resetCache();\n        return f;\n    };\n\n    f.range = function (colors, _pos) {\n        setColors(colors, _pos);\n        return f;\n    };\n\n    f.out = function (_o) {\n        _out = _o;\n        return f;\n    };\n\n    f.spread = function (val) {\n        if (!arguments.length) {\n            return _spread;\n        }\n        _spread = val;\n        return f;\n    };\n\n    f.correctLightness = function (v) {\n        if (v == null) {\n            v = true;\n        }\n        _correctLightness = v;\n        resetCache();\n        if (_correctLightness) {\n            tMapLightness = function (t) {\n                const L0 = getColor(0, true).lab()[0];\n                const L1 = getColor(1, true).lab()[0];\n                const pol = L0 > L1;\n                let L_actual = getColor(t, true).lab()[0];\n                const L_ideal = L0 + (L1 - L0) * t;\n                let L_diff = L_actual - L_ideal;\n                let t0 = 0;\n                let t1 = 1;\n                let max_iter = 20;\n                while (Math.abs(L_diff) > 1e-2 && max_iter-- > 0) {\n                    (function () {\n                        if (pol) {\n                            L_diff *= -1;\n                        }\n                        if (L_diff < 0) {\n                            t0 = t;\n                            t += (t1 - t) * 0.5;\n                        } else {\n                            t1 = t;\n                            t += (t0 - t) * 0.5;\n                        }\n                        L_actual = getColor(t, true).lab()[0];\n                        return (L_diff = L_actual - L_ideal);\n                    })();\n                }\n                return t;\n            };\n        } else {\n            tMapLightness = (t) => t;\n        }\n        return f;\n    };\n\n    f.padding = function (p) {\n        if (p != null) {\n            if (type(p) === 'number') {\n                p = [p, p];\n            }\n            _padding = p;\n            return f;\n        } else {\n            return _padding;\n        }\n    };\n\n    f.colors = function (numColors, out) {\n        // If no arguments are given, return the original colors that were provided\n        if (arguments.length < 2) {\n            out = 'hex';\n        }\n        let result = [];\n\n        if (arguments.length === 0) {\n            result = _colors.slice(0);\n        } else if (numColors === 1) {\n            result = [f(0.5)];\n        } else if (numColors > 1) {\n            const dm = _positions[0];\n            const dd = _positions[1] - dm;\n            result = __range__(0, numColors, false).map((i) =>\n                f(dm + (i / (numColors - 1)) * dd)\n            );\n        } else {\n            // returns all colors based on the defined classes\n            colors = [];\n            let samples = [];\n            if (_classes && _classes.length > 2) {\n                for (\n                    let i = 1, end = _classes.length, asc = 1 <= end;\n                    asc ? i < end : i > end;\n                    asc ? i++ : i--\n                ) {\n                    samples.push((_classes[i - 1] + _classes[i]) * 0.5);\n                }\n            } else {\n                samples = _positions;\n            }\n            result = samples.map((v) => f(v));\n        }\n\n        if (chroma[out]) {\n            result = result.map((c) => c[out]());\n        }\n        return result;\n    };\n\n    f.cache = function (c) {\n        if (c != null) {\n            _useCache = c;\n            return f;\n        } else {\n            return _useCache;\n        }\n    };\n\n    f.gamma = function (g) {\n        if (g != null) {\n            _gamma = g;\n            return f;\n        } else {\n            return _gamma;\n        }\n    };\n\n    f.nodata = function (d) {\n        if (d != null) {\n            _nacol = chroma(d);\n            return f;\n        } else {\n            return _nacol;\n        }\n    };\n\n    return f;\n}\n\nfunction __range__(left, right, inclusive) {\n    let range = [];\n    let ascending = left < right;\n    let end = !inclusive ? right : ascending ? right + 1 : right - 1;\n    for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {\n        range.push(i);\n    }\n    return range;\n}\n"
  },
  {
    "path": "src/interpolator/_hsx.js",
    "content": "import Color from '../Color.js';\n\nexport default (col1, col2, f, m) => {\n    let xyz0, xyz1;\n    if (m === 'hsl') {\n        xyz0 = col1.hsl();\n        xyz1 = col2.hsl();\n    } else if (m === 'hsv') {\n        xyz0 = col1.hsv();\n        xyz1 = col2.hsv();\n    } else if (m === 'hcg') {\n        xyz0 = col1.hcg();\n        xyz1 = col2.hcg();\n    } else if (m === 'hsi') {\n        xyz0 = col1.hsi();\n        xyz1 = col2.hsi();\n    } else if (m === 'lch' || m === 'hcl') {\n        m = 'hcl';\n        xyz0 = col1.hcl();\n        xyz1 = col2.hcl();\n    } else if (m === 'oklch') {\n        xyz0 = col1.oklch().reverse();\n        xyz1 = col2.oklch().reverse();\n    }\n\n    let hue0, hue1, sat0, sat1, lbv0, lbv1;\n    if (m.substr(0, 1) === 'h' || m === 'oklch') {\n        [hue0, sat0, lbv0] = xyz0;\n        [hue1, sat1, lbv1] = xyz1;\n    }\n\n    let sat, hue, lbv, dh;\n\n    if (!isNaN(hue0) && !isNaN(hue1)) {\n        // both colors have hue\n        if (hue1 > hue0 && hue1 - hue0 > 180) {\n            dh = hue1 - (hue0 + 360);\n        } else if (hue1 < hue0 && hue0 - hue1 > 180) {\n            dh = hue1 + 360 - hue0;\n        } else {\n            dh = hue1 - hue0;\n        }\n        hue = hue0 + f * dh;\n    } else if (!isNaN(hue0)) {\n        hue = hue0;\n        if ((lbv1 == 1 || lbv1 == 0) && m != 'hsv') sat = sat0;\n    } else if (!isNaN(hue1)) {\n        hue = hue1;\n        if ((lbv0 == 1 || lbv0 == 0) && m != 'hsv') sat = sat1;\n    } else {\n        hue = Number.NaN;\n    }\n\n    if (sat === undefined) sat = sat0 + f * (sat1 - sat0);\n    lbv = lbv0 + f * (lbv1 - lbv0);\n    return m === 'oklch'\n        ? new Color([lbv, sat, hue], m)\n        : new Color([hue, sat, lbv], m);\n};\n"
  },
  {
    "path": "src/interpolator/hcg.js",
    "content": "import '../io/hcg/index.js';\nimport interpolate_hsx from './_hsx.js';\nimport index from './index.js';\n\nconst hcg = (col1, col2, f) => {\n    return interpolate_hsx(col1, col2, f, 'hcg');\n};\n\n// register interpolator\nindex.hcg = hcg;\n\nexport default hcg;\n"
  },
  {
    "path": "src/interpolator/hsi.js",
    "content": "import '../io/hsi/index.js';\nimport interpolate_hsx from './_hsx.js';\nimport index from './index.js';\n\nconst hsi = (col1, col2, f) => {\n    return interpolate_hsx(col1, col2, f, 'hsi');\n};\n\n// register interpolator\nindex.hsi = hsi;\n\nexport default hsi;\n"
  },
  {
    "path": "src/interpolator/hsl.js",
    "content": "import '../io/hsl/index.js';\nimport interpolate_hsx from './_hsx.js';\nimport index from './index.js';\n\nconst hsl = (col1, col2, f) => {\n    return interpolate_hsx(col1, col2, f, 'hsl');\n};\n\n// register interpolator\nindex.hsl = hsl;\n\nexport default hsl;\n"
  },
  {
    "path": "src/interpolator/hsv.js",
    "content": "import '../io/hsv/index.js';\nimport interpolate_hsx from './_hsx.js';\nimport index from './index.js';\n\nconst hsv = (col1, col2, f) => {\n    return interpolate_hsx(col1, col2, f, 'hsv');\n};\n\n// register interpolator\nindex.hsv = hsv;\n\nexport default hsv;\n"
  },
  {
    "path": "src/interpolator/index.js",
    "content": "export default {};\n"
  },
  {
    "path": "src/interpolator/lab.js",
    "content": "import '../io/lab/index.js';\nimport index from './index.js';\nimport Color from '../Color.js';\n\nconst lab = (col1, col2, f) => {\n    const xyz0 = col1.lab();\n    const xyz1 = col2.lab();\n    return new Color(\n        xyz0[0] + f * (xyz1[0] - xyz0[0]),\n        xyz0[1] + f * (xyz1[1] - xyz0[1]),\n        xyz0[2] + f * (xyz1[2] - xyz0[2]),\n        'lab'\n    );\n};\n\n// register interpolator\nindex.lab = lab;\n\nexport default lab;\n"
  },
  {
    "path": "src/interpolator/lch.js",
    "content": "import '../io/lch/index.js';\nimport interpolate_hsx from './_hsx.js';\nimport index from './index.js';\n\nconst lch = (col1, col2, f) => {\n    return interpolate_hsx(col1, col2, f, 'lch');\n};\n\n// register interpolator\nindex.lch = lch;\nindex.hcl = lch;\n\nexport default lch;\n"
  },
  {
    "path": "src/interpolator/lrgb.js",
    "content": "import Color from '../Color.js';\nconst { sqrt, pow } = Math;\nimport index from './index.js';\n\nconst lrgb = (col1, col2, f) => {\n    const [x1, y1, z1] = col1._rgb;\n    const [x2, y2, z2] = col2._rgb;\n    return new Color(\n        sqrt(pow(x1, 2) * (1 - f) + pow(x2, 2) * f),\n        sqrt(pow(y1, 2) * (1 - f) + pow(y2, 2) * f),\n        sqrt(pow(z1, 2) * (1 - f) + pow(z2, 2) * f),\n        'rgb'\n    );\n};\n\n// register interpolator\nindex.lrgb = lrgb;\n\nexport default lrgb;\n"
  },
  {
    "path": "src/interpolator/num.js",
    "content": "import '../io/num/index.js';\nimport index from './index.js';\n\nimport Color from '../Color.js';\n\nconst num = (col1, col2, f) => {\n    const c1 = col1.num();\n    const c2 = col2.num();\n    return new Color(c1 + f * (c2 - c1), 'num');\n};\n\n// register interpolator\nindex.num = num;\n\nexport default num;\n"
  },
  {
    "path": "src/interpolator/oklab.js",
    "content": "import '../io/oklab/index.js';\nimport index from './index.js';\nimport Color from '../Color.js';\n\nconst oklab = (col1, col2, f) => {\n    const xyz0 = col1.oklab();\n    const xyz1 = col2.oklab();\n    return new Color(\n        xyz0[0] + f * (xyz1[0] - xyz0[0]),\n        xyz0[1] + f * (xyz1[1] - xyz0[1]),\n        xyz0[2] + f * (xyz1[2] - xyz0[2]),\n        'oklab'\n    );\n};\n\n// register interpolator\nindex.oklab = oklab;\n\nexport default oklab;\n"
  },
  {
    "path": "src/interpolator/oklch.js",
    "content": "import '../io/lch/index.js';\nimport interpolate_hsx from './_hsx.js';\nimport index from './index.js';\n\nconst oklch = (col1, col2, f) => {\n    return interpolate_hsx(col1, col2, f, 'oklch');\n};\n\n// register interpolator\nindex.oklch = oklch;\n\nexport default oklch;\n"
  },
  {
    "path": "src/interpolator/rgb.js",
    "content": "import Color from '../Color.js';\nimport index from './index.js';\n\nconst rgb = (col1, col2, f) => {\n    const xyz0 = col1._rgb;\n    const xyz1 = col2._rgb;\n    return new Color(\n        xyz0[0] + f * (xyz1[0] - xyz0[0]),\n        xyz0[1] + f * (xyz1[1] - xyz0[1]),\n        xyz0[2] + f * (xyz1[2] - xyz0[2]),\n        'rgb'\n    );\n};\n\n// register interpolator\nindex.rgb = rgb;\n\nexport default rgb;\n"
  },
  {
    "path": "src/io/cmyk/cmyk2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\n\nconst cmyk2rgb = (...args) => {\n    args = unpack(args, 'cmyk');\n    const [c, m, y, k] = args;\n    const alpha = args.length > 4 ? args[4] : 1;\n    if (k === 1) return [0, 0, 0, alpha];\n    return [\n        c >= 1 ? 0 : 255 * (1 - c) * (1 - k), // r\n        m >= 1 ? 0 : 255 * (1 - m) * (1 - k), // g\n        y >= 1 ? 0 : 255 * (1 - y) * (1 - k), // b\n        alpha\n    ];\n};\n\nexport default cmyk2rgb;\n"
  },
  {
    "path": "src/io/cmyk/index.js",
    "content": "import chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport { unpack, type } from '../../utils/index.js';\nimport cmyk2rgb from './cmyk2rgb.js';\nimport rgb2cmyk from './rgb2cmyk.js';\n\nColor.prototype.cmyk = function () {\n    return rgb2cmyk(this._rgb);\n};\n\nconst cmyk = (...args) => new Color(...args, 'cmyk');\nObject.assign(chroma, { cmyk });\n\ninput.format.cmyk = cmyk2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'cmyk');\n        if (type(args) === 'array' && args.length === 4) {\n            return 'cmyk';\n        }\n    }\n});\n\nexport { cmyk };\n"
  },
  {
    "path": "src/io/cmyk/rgb2cmyk.js",
    "content": "import { unpack } from '../../utils/index.js';\nconst { max } = Math;\n\nconst rgb2cmyk = (...args) => {\n    let [r, g, b] = unpack(args, 'rgb');\n    r = r / 255;\n    g = g / 255;\n    b = b / 255;\n    const k = 1 - max(r, max(g, b));\n    const f = k < 1 ? 1 / (1 - k) : 0;\n    const c = (1 - r - k) * f;\n    const m = (1 - g - k) * f;\n    const y = (1 - b - k) * f;\n    return [c, m, y, k];\n};\n\nexport default rgb2cmyk;\n"
  },
  {
    "path": "src/io/css/css2rgb.js",
    "content": "import hsl2rgb from '../hsl/hsl2rgb.js';\nimport lab2rgb from '../lab/lab2rgb.js';\nimport lch2rgb from '../lch/lch2rgb.js';\nimport oklab2rgb from '../oklab/oklab2rgb.js';\nimport oklch2rgb from '../oklch/oklch2rgb.js';\nimport input from '../input.js';\nimport limit from '../../utils/limit.js';\nimport { getLabWhitePoint, setLabWhitePoint } from '../lab/lab-constants.js';\n\nconst INT_OR_PCT = /((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)/.source;\nconst FLOAT_OR_PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)/.source;\nconst PCT = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)/.source;\nconst RE_S = /\\s*/.source;\nconst SEP = /\\s+/.source;\nconst COMMA = /\\s*,\\s*/.source;\nconst ANLGE = /((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)/.source;\nconst ALPHA = /\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?/.source;\n\n// e.g. rgb(250 20 0), rgb(100% 50% 20%), rgb(100% 50% 20% / 0.5)\nconst RE_RGB = new RegExp(\n    '^rgba?\\\\(' +\n        RE_S +\n        [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(SEP) +\n        ALPHA +\n        '\\\\)$'\n);\nconst RE_RGB_LEGACY = new RegExp(\n    '^rgb\\\\(' +\n        RE_S +\n        [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT].join(COMMA) +\n        RE_S +\n        '\\\\)$'\n);\nconst RE_RGBA_LEGACY = new RegExp(\n    '^rgba\\\\(' +\n        RE_S +\n        [INT_OR_PCT, INT_OR_PCT, INT_OR_PCT, FLOAT_OR_PCT].join(COMMA) +\n        RE_S +\n        '\\\\)$'\n);\n\nconst RE_HSL = new RegExp(\n    '^hsla?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(SEP) + ALPHA + '\\\\)$'\n);\nconst RE_HSL_LEGACY = new RegExp(\n    '^hsl?\\\\(' + RE_S + [ANLGE, PCT, PCT].join(COMMA) + RE_S + '\\\\)$'\n);\nconst RE_HSLA_LEGACY =\n    /^hsla\\(\\s*(-?\\d+(?:\\.\\d+)?),\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)$/;\n\nconst RE_LAB = new RegExp(\n    '^lab\\\\(' +\n        RE_S +\n        [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n        ALPHA +\n        '\\\\)$'\n);\nconst RE_LCH = new RegExp(\n    '^lch\\\\(' +\n        RE_S +\n        [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n        ALPHA +\n        '\\\\)$'\n);\nconst RE_OKLAB = new RegExp(\n    '^oklab\\\\(' +\n        RE_S +\n        [FLOAT_OR_PCT, FLOAT_OR_PCT, FLOAT_OR_PCT].join(SEP) +\n        ALPHA +\n        '\\\\)$'\n);\nconst RE_OKLCH = new RegExp(\n    '^oklch\\\\(' +\n        RE_S +\n        [FLOAT_OR_PCT, FLOAT_OR_PCT, ANLGE].join(SEP) +\n        ALPHA +\n        '\\\\)$'\n);\n\nconst { round } = Math;\n\nconst roundRGB = (rgb) => {\n    return rgb.map((v, i) => (i <= 2 ? limit(round(v), 0, 255) : v));\n};\n\nconst percentToAbsolute = (pct, min = 0, max = 100, signed = false) => {\n    if (typeof pct === 'string' && pct.endsWith('%')) {\n        pct = parseFloat(pct.substring(0, pct.length - 1)) / 100;\n        if (signed) {\n            // signed percentages are in the range -100% to 100%\n            pct = min + (pct + 1) * 0.5 * (max - min);\n        } else {\n            pct = min + pct * (max - min);\n        }\n    }\n    return +pct;\n};\n\nconst noneToValue = (v, noneValue) => {\n    return v === 'none' ? noneValue : v;\n};\n\nconst css2rgb = (css) => {\n    css = css.toLowerCase().trim();\n\n    if (css === 'transparent') {\n        return [0, 0, 0, 0];\n    }\n\n    let m;\n\n    if (input.format.named) {\n        try {\n            return input.format.named(css);\n            // eslint-disable-next-line\n        } catch (e) {}\n    }\n\n    // rgb(250 20 0) or rgb(250,20,0)\n    if ((m = css.match(RE_RGB)) || (m = css.match(RE_RGB_LEGACY))) {\n        let rgb = m.slice(1, 4);\n        for (let i = 0; i < 3; i++) {\n            rgb[i] = +percentToAbsolute(noneToValue(rgb[i], 0), 0, 255);\n        }\n        rgb = roundRGB(rgb);\n        const alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n        rgb[3] = alpha; // default alpha\n        return rgb;\n    }\n\n    // rgba(250,20,0,0.4)\n    if ((m = css.match(RE_RGBA_LEGACY))) {\n        const rgb = m.slice(1, 5);\n        for (let i = 0; i < 4; i++) {\n            rgb[i] = +percentToAbsolute(rgb[i], 0, 255);\n        }\n        return rgb;\n    }\n\n    // hsl(0,100%,50%)\n    if ((m = css.match(RE_HSL)) || (m = css.match(RE_HSL_LEGACY))) {\n        const hsl = m.slice(1, 4);\n        hsl[0] = +noneToValue(hsl[0].replace('deg', ''), 0);\n        hsl[1] = +percentToAbsolute(noneToValue(hsl[1], 0), 0, 100) * 0.01;\n        hsl[2] = +percentToAbsolute(noneToValue(hsl[2], 0), 0, 100) * 0.01;\n        const rgb = roundRGB(hsl2rgb(hsl));\n        const alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n        rgb[3] = alpha;\n        return rgb;\n    }\n\n    // hsla(0,100%,50%,0.5)\n    if ((m = css.match(RE_HSLA_LEGACY))) {\n        const hsl = m.slice(1, 4);\n        hsl[1] *= 0.01;\n        hsl[2] *= 0.01;\n        const rgb = hsl2rgb(hsl);\n        for (let i = 0; i < 3; i++) {\n            rgb[i] = round(rgb[i]);\n        }\n        rgb[3] = +m[4]; // default alpha = 1\n        return rgb;\n    }\n\n    if ((m = css.match(RE_LAB))) {\n        const lab = m.slice(1, 4);\n        lab[0] = percentToAbsolute(noneToValue(lab[0], 0), 0, 100);\n        lab[1] = percentToAbsolute(noneToValue(lab[1], 0), -125, 125, true);\n        lab[2] = percentToAbsolute(noneToValue(lab[2], 0), -125, 125, true);\n        // convert to D50 Lab whitepoint\n        const wp = getLabWhitePoint();\n        setLabWhitePoint('d50');\n        const rgb = roundRGB(lab2rgb(lab));\n        // convert back to original Lab whitepoint\n        setLabWhitePoint(wp);\n        const alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n        rgb[3] = alpha;\n        return rgb;\n    }\n\n    if ((m = css.match(RE_LCH))) {\n        const lch = m.slice(1, 4);\n        lch[0] = percentToAbsolute(lch[0], 0, 100);\n        lch[1] = percentToAbsolute(noneToValue(lch[1], 0), 0, 150, false);\n        lch[2] = +noneToValue(lch[2].replace('deg', ''), 0);\n        // convert to D50 Lab whitepoint\n        const wp = getLabWhitePoint();\n        setLabWhitePoint('d50');\n        const rgb = roundRGB(lch2rgb(lch));\n        // convert back to original Lab whitepoint\n        setLabWhitePoint(wp);\n        const alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n        rgb[3] = alpha;\n        return rgb;\n    }\n\n    if ((m = css.match(RE_OKLAB))) {\n        const oklab = m.slice(1, 4);\n        oklab[0] = percentToAbsolute(noneToValue(oklab[0], 0), 0, 1);\n        oklab[1] = percentToAbsolute(noneToValue(oklab[1], 0), -0.4, 0.4, true);\n        oklab[2] = percentToAbsolute(noneToValue(oklab[2], 0), -0.4, 0.4, true);\n        const rgb = roundRGB(oklab2rgb(oklab));\n        const alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n        rgb[3] = alpha;\n        return rgb;\n    }\n\n    if ((m = css.match(RE_OKLCH))) {\n        const oklch = m.slice(1, 4);\n        oklch[0] = percentToAbsolute(noneToValue(oklch[0], 0), 0, 1);\n        oklch[1] = percentToAbsolute(noneToValue(oklch[1], 0), 0, 0.4, false);\n        oklch[2] = +noneToValue(oklch[2].replace('deg', ''), 0);\n        const rgb = roundRGB(oklch2rgb(oklch));\n        const alpha = m[4] !== undefined ? +percentToAbsolute(m[4], 0, 1) : 1;\n        rgb[3] = alpha;\n        return rgb;\n    }\n};\n\ncss2rgb.test = (s) => {\n    return (\n        // modern\n        RE_RGB.test(s) ||\n        RE_HSL.test(s) ||\n        RE_LAB.test(s) ||\n        RE_LCH.test(s) ||\n        RE_OKLAB.test(s) ||\n        RE_OKLCH.test(s) ||\n        // legacy\n        RE_RGB_LEGACY.test(s) ||\n        RE_RGBA_LEGACY.test(s) ||\n        RE_HSL_LEGACY.test(s) ||\n        RE_HSLA_LEGACY.test(s) ||\n        s === 'transparent'\n    );\n};\n\nexport default css2rgb;\n"
  },
  {
    "path": "src/io/css/hsl2css.js",
    "content": "import { unpack, last, rnd2 } from '../../utils/index.js';\n\n/*\n * supported arguments:\n * - hsl2css(h,s,l)\n * - hsl2css(h,s,l,a)\n * - hsl2css([h,s,l], mode)\n * - hsl2css([h,s,l,a], mode)\n * - hsl2css({h,s,l,a}, mode)\n */\nconst hsl2css = (...args) => {\n    const hsla = unpack(args, 'hsla');\n    let mode = last(args) || 'lsa';\n    hsla[0] = rnd2(hsla[0] || 0) + 'deg';\n    hsla[1] = rnd2(hsla[1] * 100) + '%';\n    hsla[2] = rnd2(hsla[2] * 100) + '%';\n    if (mode === 'hsla' || (hsla.length > 3 && hsla[3] < 1)) {\n        hsla[3] = '/ ' + (hsla.length > 3 ? hsla[3] : 1);\n        mode = 'hsla';\n    } else {\n        hsla.length = 3;\n    }\n    return `${mode.substr(0, 3)}(${hsla.join(' ')})`;\n};\n\nexport default hsl2css;\n"
  },
  {
    "path": "src/io/css/index.js",
    "content": "import chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport { type } from '../../utils/index.js';\n\nimport rgb2css from './rgb2css.js';\nimport css2rgb from './css2rgb.js';\n\nColor.prototype.css = function (mode) {\n    return rgb2css(this._rgb, mode);\n};\n\nconst css = (...args) => new Color(...args, 'css');\nchroma.css = css;\n\ninput.format.css = css2rgb;\n\ninput.autodetect.push({\n    p: 5,\n    test: (h, ...rest) => {\n        if (!rest.length && type(h) === 'string' && css2rgb.test(h)) {\n            return 'css';\n        }\n    }\n});\n\nexport { css };\n"
  },
  {
    "path": "src/io/css/lab2css.js",
    "content": "import { unpack, last, rnd2 } from '../../utils/index.js';\n\n/*\n * supported arguments:\n * - lab2css(l,a,b)\n * - lab2css(l,a,b,alpha)\n * - lab2css([l,a,b], mode)\n * - lab2css([l,a,b,alpha], mode)\n */\nconst lab2css = (...args) => {\n    const laba = unpack(args, 'lab');\n    let mode = last(args) || 'lab';\n    laba[0] = rnd2(laba[0]) + '%';\n    laba[1] = rnd2(laba[1]);\n    laba[2] = rnd2(laba[2]);\n    if (mode === 'laba' || (laba.length > 3 && laba[3] < 1)) {\n        laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n    } else {\n        laba.length = 3;\n    }\n    return `lab(${laba.join(' ')})`;\n};\n\nexport default lab2css;\n"
  },
  {
    "path": "src/io/css/lch2css.js",
    "content": "import { unpack, last, rnd2 } from '../../utils/index.js';\n\n/*\n * supported arguments:\n * - lab2css(l,a,b)\n * - lab2css(l,a,b,alpha)\n * - lab2css([l,a,b], mode)\n * - lab2css([l,a,b,alpha], mode)\n */\nconst lch2css = (...args) => {\n    const lcha = unpack(args, 'lch');\n    let mode = last(args) || 'lab';\n    lcha[0] = rnd2(lcha[0]) + '%';\n    lcha[1] = rnd2(lcha[1]);\n    lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n    if (mode === 'lcha' || (lcha.length > 3 && lcha[3] < 1)) {\n        lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n    } else {\n        lcha.length = 3;\n    }\n    return `lch(${lcha.join(' ')})`;\n};\n\nexport default lch2css;\n"
  },
  {
    "path": "src/io/css/oklab2css.js",
    "content": "import { unpack, rnd2, rnd3 } from '../../utils/index.js';\n\nconst oklab2css = (...args) => {\n    const laba = unpack(args, 'lab');\n    laba[0] = rnd2(laba[0] * 100) + '%';\n    laba[1] = rnd3(laba[1]);\n    laba[2] = rnd3(laba[2]);\n    if (laba.length > 3 && laba[3] < 1) {\n        laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1);\n    } else {\n        laba.length = 3;\n    }\n    return `oklab(${laba.join(' ')})`;\n};\n\nexport default oklab2css;\n"
  },
  {
    "path": "src/io/css/oklch2css.js",
    "content": "import { unpack, rnd2, rnd3 } from '../../utils/index.js';\n\nconst oklch2css = (...args) => {\n    const lcha = unpack(args, 'lch');\n    lcha[0] = rnd2(lcha[0] * 100) + '%';\n    lcha[1] = rnd3(lcha[1]);\n    lcha[2] = isNaN(lcha[2]) ? 'none' : rnd2(lcha[2]) + 'deg'; // add deg unit to hue\n    if (lcha.length > 3 && lcha[3] < 1) {\n        lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1);\n    } else {\n        lcha.length = 3;\n    }\n    return `oklch(${lcha.join(' ')})`;\n};\n\nexport default oklch2css;\n"
  },
  {
    "path": "src/io/css/rgb2css.js",
    "content": "import { unpack, last } from '../../utils/index.js';\nimport hsl2css from './hsl2css.js';\nimport rgb2hsl from '../hsl/rgb2hsl.js';\nimport lab2css from './lab2css.js';\nimport rgb2lab from '../lab/rgb2lab.js';\nimport lch2css from './lch2css.js';\nimport rgb2lch from '../lch/rgb2lch.js';\nimport rgb2oklab from '../oklab/rgb2oklab.js';\nimport oklab2css from './oklab2css.js';\nimport rgb2oklch from '../oklch/rgb2oklch.js';\nimport oklch2css from './oklch2css.js';\nimport { getLabWhitePoint, setLabWhitePoint } from '../lab/lab-constants.js';\nconst { round } = Math;\n\n/*\n * supported arguments:\n * - rgb2css(r,g,b)\n * - rgb2css(r,g,b,a)\n * - rgb2css([r,g,b], mode)\n * - rgb2css([r,g,b,a], mode)\n * - rgb2css({r,g,b,a}, mode)\n */\nconst rgb2css = (...args) => {\n    const rgba = unpack(args, 'rgba');\n    let mode = last(args) || 'rgb';\n    if (mode.substr(0, 3) === 'hsl') {\n        return hsl2css(rgb2hsl(rgba), mode);\n    }\n    if (mode.substr(0, 3) === 'lab') {\n        // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n        const prevWhitePoint = getLabWhitePoint();\n        setLabWhitePoint('d50');\n        const cssColor = lab2css(rgb2lab(rgba), mode);\n        setLabWhitePoint(prevWhitePoint);\n        return cssColor;\n    }\n    if (mode.substr(0, 3) === 'lch') {\n        // change to D50 lab whitepoint since this is what W3C is using for CSS Lab colors\n        const prevWhitePoint = getLabWhitePoint();\n        setLabWhitePoint('d50');\n        const cssColor = lch2css(rgb2lch(rgba), mode);\n        setLabWhitePoint(prevWhitePoint);\n        return cssColor;\n    }\n    if (mode.substr(0, 5) === 'oklab') {\n        return oklab2css(rgb2oklab(rgba));\n    }\n    if (mode.substr(0, 5) === 'oklch') {\n        return oklch2css(rgb2oklch(rgba));\n    }\n    rgba[0] = round(rgba[0]);\n    rgba[1] = round(rgba[1]);\n    rgba[2] = round(rgba[2]);\n    if (mode === 'rgba' || (rgba.length > 3 && rgba[3] < 1)) {\n        rgba[3] = '/ ' + (rgba.length > 3 ? rgba[3] : 1);\n        mode = 'rgba';\n    }\n    return `${mode.substr(0, 3)}(${rgba.slice(0, mode === 'rgb' ? 3 : 4).join(' ')})`;\n};\n\nexport default rgb2css;\n"
  },
  {
    "path": "src/io/delta-e.coffee",
    "content": "chroma.deltaE = (a, b, L=1, C=1) ->\n    # Delta E (CMC)\n    # see http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CMC.html\n    a = new Color a if type(a) in ['string', 'number']\n    b = new Color b if type(b) in ['string', 'number']\n    [L1,a1,b1] = a.lab()\n    [L2,a2,b2] = b.lab()\n    c1 = sqrt(a1 * a1 + b1 * b1)\n    c2 = sqrt(a2 * a2 + b2 * b2)\n    sl = if L1 < 16.0 then 0.511 else (0.040975 * L1) / (1.0 + 0.01765 * L1)\n    sc = (0.0638 * c1) / (1.0 + 0.0131 * c1) + 0.638\n    h1 = if c1 < 0.000001 then 0.0 else (atan2(b1, a1) * 180.0) / PI\n    h1 += 360 while h1 < 0\n    h1 -= 360 while h1 >= 360\n    t = if (h1 >= 164.0) && (h1 <= 345.0) then (0.56 + abs(0.2 * cos((PI * (h1 + 168.0)) / 180.0))) else (0.36 + abs(0.4 * cos((PI * (h1 + 35.0)) / 180.0)))\n    c4 = c1 * c1 * c1 * c1\n    f = sqrt(c4 / (c4 + 1900.0))\n    sh = sc * (f * t + 1.0 - f)\n    delL = L1 - L2\n    delC = c1 - c2\n    delA = a1 - a2\n    delB = b1 - b2\n    dH2 = delA * delA + delB * delB - delC * delC\n    v1 = delL / (L * sl)\n    v2 = delC / (C * sc)\n    v3 = sh\n    sqrt(v1 * v1 + v2 * v2 + (dH2 / (v3 * v3)))\n    \n\n"
  },
  {
    "path": "src/io/distance.coffee",
    "content": "# simple Euclidean distance\nchroma.distance = (a, b, mode='lab') ->\n    # Delta E (CIE 1976)\n    # see http://www.brucelindbloom.com/index.html?Equations.html\n    a = new Color a if type(a) in ['string', 'number']\n    b = new Color b if type(b) in ['string', 'number']\n    l1 = a.get mode\n    l2 = b.get mode\n    sum_sq = 0\n    for i of l1\n        d = (l1[i] || 0) - (l2[i] || 0)\n        sum_sq += d*d\n    Math.sqrt sum_sq\n"
  },
  {
    "path": "src/io/gl/index.js",
    "content": "import Color from '../../Color.js';\nimport chroma from '../../chroma.js';\nimport input from '../input.js';\nimport { unpack } from '../../utils/index.js';\n\ninput.format.gl = (...args) => {\n    const rgb = unpack(args, 'rgba');\n    rgb[0] *= 255;\n    rgb[1] *= 255;\n    rgb[2] *= 255;\n    return rgb;\n};\n\nconst gl = (...args) => new Color(...args, 'gl');\nchroma.gl = gl;\n\nColor.prototype.gl = function () {\n    const rgb = this._rgb;\n    return [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, rgb[3]];\n};\n\nexport { gl };\n"
  },
  {
    "path": "src/io/hcg/hcg2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\nconst { floor } = Math;\n\n/*\n * this is basically just HSV with some minor tweaks\n *\n * hue.. [0..360]\n * chroma .. [0..1]\n * grayness .. [0..1]\n */\n\nconst hcg2rgb = (...args) => {\n    args = unpack(args, 'hcg');\n    let [h, c, _g] = args;\n    let r, g, b;\n    _g = _g * 255;\n    const _c = c * 255;\n    if (c === 0) {\n        r = g = b = _g;\n    } else {\n        if (h === 360) h = 0;\n        if (h > 360) h -= 360;\n        if (h < 0) h += 360;\n        h /= 60;\n        const i = floor(h);\n        const f = h - i;\n        const p = _g * (1 - c);\n        const q = p + _c * (1 - f);\n        const t = p + _c * f;\n        const v = p + _c;\n        switch (i) {\n            case 0:\n                [r, g, b] = [v, t, p];\n                break;\n            case 1:\n                [r, g, b] = [q, v, p];\n                break;\n            case 2:\n                [r, g, b] = [p, v, t];\n                break;\n            case 3:\n                [r, g, b] = [p, q, v];\n                break;\n            case 4:\n                [r, g, b] = [t, p, v];\n                break;\n            case 5:\n                [r, g, b] = [v, p, q];\n                break;\n        }\n    }\n    return [r, g, b, args.length > 3 ? args[3] : 1];\n};\n\nexport default hcg2rgb;\n"
  },
  {
    "path": "src/io/hcg/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport hcg2rgb from './hcg2rgb.js';\nimport rgb2hcg from './rgb2hcg.js';\n\nColor.prototype.hcg = function () {\n    return rgb2hcg(this._rgb);\n};\n\nconst hcg = (...args) => new Color(...args, 'hcg');\nchroma.hcg = hcg;\n\ninput.format.hcg = hcg2rgb;\n\ninput.autodetect.push({\n    p: 1,\n    test: (...args) => {\n        args = unpack(args, 'hcg');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'hcg';\n        }\n    }\n});\n\nexport { hcg };\n"
  },
  {
    "path": "src/io/hcg/rgb2hcg.js",
    "content": "import { unpack, max, min } from '../../utils/index.js';\n\nconst rgb2hcg = (...args) => {\n    const [r, g, b] = unpack(args, 'rgb');\n    const minRgb = min(r, g, b);\n    const maxRgb = max(r, g, b);\n    const delta = maxRgb - minRgb;\n    const c = (delta * 100) / 255;\n    const _g = (minRgb / (255 - delta)) * 100;\n    let h;\n    if (delta === 0) {\n        h = Number.NaN;\n    } else {\n        if (r === maxRgb) h = (g - b) / delta;\n        if (g === maxRgb) h = 2 + (b - r) / delta;\n        if (b === maxRgb) h = 4 + (r - g) / delta;\n        h *= 60;\n        if (h < 0) h += 360;\n    }\n    return [h, c, _g];\n};\n\nexport default rgb2hcg;\n"
  },
  {
    "path": "src/io/hex/hex2rgb.js",
    "content": "const RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\nconst RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/;\n\nconst hex2rgb = (hex) => {\n    if (hex.match(RE_HEX)) {\n        // remove optional leading #\n        if (hex.length === 4 || hex.length === 7) {\n            hex = hex.substr(1);\n        }\n        // expand short-notation to full six-digit\n        if (hex.length === 3) {\n            hex = hex.split('');\n            hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n        }\n        const u = parseInt(hex, 16);\n        const r = u >> 16;\n        const g = (u >> 8) & 0xff;\n        const b = u & 0xff;\n        return [r, g, b, 1];\n    }\n\n    // match rgba hex format, eg #FF000077\n    if (hex.match(RE_HEXA)) {\n        if (hex.length === 5 || hex.length === 9) {\n            // remove optional leading #\n            hex = hex.substr(1);\n        }\n        // expand short-notation to full eight-digit\n        if (hex.length === 4) {\n            hex = hex.split('');\n            hex =\n                hex[0] +\n                hex[0] +\n                hex[1] +\n                hex[1] +\n                hex[2] +\n                hex[2] +\n                hex[3] +\n                hex[3];\n        }\n        const u = parseInt(hex, 16);\n        const r = (u >> 24) & 0xff;\n        const g = (u >> 16) & 0xff;\n        const b = (u >> 8) & 0xff;\n        const a = Math.round(((u & 0xff) / 0xff) * 100) / 100;\n        return [r, g, b, a];\n    }\n\n    // we used to check for css colors here\n    // if _input.css? and rgb = _input.css hex\n    //     return rgb\n\n    throw new Error(`unknown hex color: ${hex}`);\n};\n\nexport default hex2rgb;\n"
  },
  {
    "path": "src/io/hex/index.js",
    "content": "import Color from '../../Color.js';\nimport chroma from '../../chroma.js';\nimport { type } from '../../utils/index.js';\nimport input from '../input.js';\nimport hex2rgb from './hex2rgb.js';\nimport rgb2hex from './rgb2hex.js';\n\nColor.prototype.hex = function (mode) {\n    return rgb2hex(this._rgb, mode);\n};\n\nconst hex = (...args) => new Color(...args, 'hex');\nchroma.hex = hex;\n\ninput.format.hex = hex2rgb;\ninput.autodetect.push({\n    p: 4,\n    test: (h, ...rest) => {\n        if (\n            !rest.length &&\n            type(h) === 'string' &&\n            [3, 4, 5, 6, 7, 8, 9].indexOf(h.length) >= 0\n        ) {\n            return 'hex';\n        }\n    }\n});\n\nexport { hex };\n"
  },
  {
    "path": "src/io/hex/rgb2hex.js",
    "content": "import { unpack, last } from '../../utils/index.js';\nconst { round } = Math;\n\nconst rgb2hex = (...args) => {\n    let [r, g, b, a] = unpack(args, 'rgba');\n    let mode = last(args) || 'auto';\n    if (a === undefined) a = 1;\n    if (mode === 'auto') {\n        mode = a < 1 ? 'rgba' : 'rgb';\n    }\n    r = round(r);\n    g = round(g);\n    b = round(b);\n    const u = (r << 16) | (g << 8) | b;\n    let str = '000000' + u.toString(16); //#.toUpperCase();\n    str = str.substr(str.length - 6);\n    let hxa = '0' + round(a * 255).toString(16);\n    hxa = hxa.substr(hxa.length - 2);\n    switch (mode.toLowerCase()) {\n        case 'rgba':\n            return `#${str}${hxa}`;\n        case 'argb':\n            return `#${hxa}${str}`;\n        default:\n            return `#${str}`;\n    }\n};\n\nexport default rgb2hex;\n"
  },
  {
    "path": "src/io/hsi/hsi2rgb.js",
    "content": "import { unpack, limit, TWOPI, PITHIRD } from '../../utils/index.js';\nconst { cos } = Math;\n\n/*\n * hue [0..360]\n * saturation [0..1]\n * intensity [0..1]\n */\nconst hsi2rgb = (...args) => {\n    /*\n    borrowed from here:\n    http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/hsi2rgb.cpp\n    */\n    args = unpack(args, 'hsi');\n    let [h, s, i] = args;\n    let r, g, b;\n\n    if (isNaN(h)) h = 0;\n    if (isNaN(s)) s = 0;\n    // normalize hue\n    if (h > 360) h -= 360;\n    if (h < 0) h += 360;\n    h /= 360;\n    if (h < 1 / 3) {\n        b = (1 - s) / 3;\n        r = (1 + (s * cos(TWOPI * h)) / cos(PITHIRD - TWOPI * h)) / 3;\n        g = 1 - (b + r);\n    } else if (h < 2 / 3) {\n        h -= 1 / 3;\n        r = (1 - s) / 3;\n        g = (1 + (s * cos(TWOPI * h)) / cos(PITHIRD - TWOPI * h)) / 3;\n        b = 1 - (r + g);\n    } else {\n        h -= 2 / 3;\n        g = (1 - s) / 3;\n        b = (1 + (s * cos(TWOPI * h)) / cos(PITHIRD - TWOPI * h)) / 3;\n        r = 1 - (g + b);\n    }\n    r = limit(i * r * 3);\n    g = limit(i * g * 3);\n    b = limit(i * b * 3);\n    return [r * 255, g * 255, b * 255, args.length > 3 ? args[3] : 1];\n};\n\nexport default hsi2rgb;\n"
  },
  {
    "path": "src/io/hsi/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport hsi2rgb from './hsi2rgb.js';\nimport rgb2hsi from './rgb2hsi.js';\n\nColor.prototype.hsi = function () {\n    return rgb2hsi(this._rgb);\n};\n\nconst hsi = (...args) => new Color(...args, 'hsi');\nchroma.hsi = hsi;\n\ninput.format.hsi = hsi2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'hsi');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'hsi';\n        }\n    }\n});\n\nexport { hsi };\n"
  },
  {
    "path": "src/io/hsi/rgb2hsi.js",
    "content": "import { unpack, TWOPI } from '../../utils/index.js';\nconst { min, sqrt, acos } = Math;\n\nconst rgb2hsi = (...args) => {\n    /*\n    borrowed from here:\n    http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/rgb2hsi.cpp\n    */\n    let [r, g, b] = unpack(args, 'rgb');\n    r /= 255;\n    g /= 255;\n    b /= 255;\n    let h;\n    const min_ = min(r, g, b);\n    const i = (r + g + b) / 3;\n    const s = i > 0 ? 1 - min_ / i : 0;\n    if (s === 0) {\n        h = NaN;\n    } else {\n        h = (r - g + (r - b)) / 2;\n        h /= sqrt((r - g) * (r - g) + (r - b) * (g - b));\n        h = acos(h);\n        if (b > g) {\n            h = TWOPI - h;\n        }\n        h /= TWOPI;\n    }\n    return [h * 360, s, i];\n};\n\nexport default rgb2hsi;\n"
  },
  {
    "path": "src/io/hsl/hsl2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\n\nconst hsl2rgb = (...args) => {\n    args = unpack(args, 'hsl');\n    const [h, s, l] = args;\n    let r, g, b;\n    if (s === 0) {\n        r = g = b = l * 255;\n    } else {\n        const t3 = [0, 0, 0];\n        const c = [0, 0, 0];\n        const t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;\n        const t1 = 2 * l - t2;\n        const h_ = h / 360;\n        t3[0] = h_ + 1 / 3;\n        t3[1] = h_;\n        t3[2] = h_ - 1 / 3;\n        for (let i = 0; i < 3; i++) {\n            if (t3[i] < 0) t3[i] += 1;\n            if (t3[i] > 1) t3[i] -= 1;\n            if (6 * t3[i] < 1) c[i] = t1 + (t2 - t1) * 6 * t3[i];\n            else if (2 * t3[i] < 1) c[i] = t2;\n            else if (3 * t3[i] < 2) c[i] = t1 + (t2 - t1) * (2 / 3 - t3[i]) * 6;\n            else c[i] = t1;\n        }\n        [r, g, b] = [c[0] * 255, c[1] * 255, c[2] * 255];\n    }\n    if (args.length > 3) {\n        // keep alpha channel\n        return [r, g, b, args[3]];\n    }\n    return [r, g, b, 1];\n};\n\nexport default hsl2rgb;\n"
  },
  {
    "path": "src/io/hsl/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport hsl2rgb from './hsl2rgb.js';\nimport rgb2hsl from './rgb2hsl.js';\n\nColor.prototype.hsl = function () {\n    return rgb2hsl(this._rgb);\n};\n\nconst hsl = (...args) => new Color(...args, 'hsl');\nchroma.hsl = hsl;\n\ninput.format.hsl = hsl2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'hsl');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'hsl';\n        }\n    }\n});\n\nexport { hsl };\n"
  },
  {
    "path": "src/io/hsl/rgb2hsl.js",
    "content": "import { unpack, min, max } from '../../utils/index.js';\n\n/*\n * supported arguments:\n * - rgb2hsl(r,g,b)\n * - rgb2hsl(r,g,b,a)\n * - rgb2hsl([r,g,b])\n * - rgb2hsl([r,g,b,a])\n * - rgb2hsl({r,g,b,a})\n */\nconst rgb2hsl = (...args) => {\n    args = unpack(args, 'rgba');\n    let [r, g, b] = args;\n\n    r /= 255;\n    g /= 255;\n    b /= 255;\n\n    const minRgb = min(r, g, b);\n    const maxRgb = max(r, g, b);\n\n    const l = (maxRgb + minRgb) / 2;\n    let s, h;\n\n    if (maxRgb === minRgb) {\n        s = 0;\n        h = Number.NaN;\n    } else {\n        s =\n            l < 0.5\n                ? (maxRgb - minRgb) / (maxRgb + minRgb)\n                : (maxRgb - minRgb) / (2 - maxRgb - minRgb);\n    }\n\n    if (r == maxRgb) h = (g - b) / (maxRgb - minRgb);\n    else if (g == maxRgb) h = 2 + (b - r) / (maxRgb - minRgb);\n    else if (b == maxRgb) h = 4 + (r - g) / (maxRgb - minRgb);\n\n    h *= 60;\n    if (h < 0) h += 360;\n    if (args.length > 3 && args[3] !== undefined) return [h, s, l, args[3]];\n    return [h, s, l];\n};\n\nexport default rgb2hsl;\n"
  },
  {
    "path": "src/io/hsv/hsv2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\nconst { floor } = Math;\n\nconst hsv2rgb = (...args) => {\n    args = unpack(args, 'hsv');\n    let [h, s, v] = args;\n    let r, g, b;\n    v *= 255;\n    if (s === 0) {\n        r = g = b = v;\n    } else {\n        if (h === 360) h = 0;\n        if (h > 360) h -= 360;\n        if (h < 0) h += 360;\n        h /= 60;\n\n        const i = floor(h);\n        const f = h - i;\n        const p = v * (1 - s);\n        const q = v * (1 - s * f);\n        const t = v * (1 - s * (1 - f));\n\n        switch (i) {\n            case 0:\n                [r, g, b] = [v, t, p];\n                break;\n            case 1:\n                [r, g, b] = [q, v, p];\n                break;\n            case 2:\n                [r, g, b] = [p, v, t];\n                break;\n            case 3:\n                [r, g, b] = [p, q, v];\n                break;\n            case 4:\n                [r, g, b] = [t, p, v];\n                break;\n            case 5:\n                [r, g, b] = [v, p, q];\n                break;\n        }\n    }\n    return [r, g, b, args.length > 3 ? args[3] : 1];\n};\n\nexport default hsv2rgb;\n"
  },
  {
    "path": "src/io/hsv/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport hsv2rgb from './hsv2rgb.js';\nimport rgb2hsv from './rgb2hsv.js';\n\nColor.prototype.hsv = function () {\n    return rgb2hsv(this._rgb);\n};\n\nconst hsv = (...args) => new Color(...args, 'hsv');\nchroma.hsv = hsv;\n\ninput.format.hsv = hsv2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'hsv');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'hsv';\n        }\n    }\n});\n\nexport { hsv };\n"
  },
  {
    "path": "src/io/hsv/rgb2hsv.js",
    "content": "import { unpack } from '../../utils/index.js';\nconst { min, max } = Math;\n\n/*\n * supported arguments:\n * - rgb2hsv(r,g,b)\n * - rgb2hsv([r,g,b])\n * - rgb2hsv({r,g,b})\n */\nconst rgb2hsl = (...args) => {\n    args = unpack(args, 'rgb');\n    let [r, g, b] = args;\n    const min_ = min(r, g, b);\n    const max_ = max(r, g, b);\n    const delta = max_ - min_;\n    let h, s, v;\n    v = max_ / 255.0;\n    if (max_ === 0) {\n        h = Number.NaN;\n        s = 0;\n    } else {\n        s = delta / max_;\n        if (r === max_) h = (g - b) / delta;\n        if (g === max_) h = 2 + (b - r) / delta;\n        if (b === max_) h = 4 + (r - g) / delta;\n        h *= 60;\n        if (h < 0) h += 360;\n    }\n    return [h, s, v];\n};\n\nexport default rgb2hsl;\n"
  },
  {
    "path": "src/io/input.js",
    "content": "export default {\n    format: {},\n    autodetect: []\n};\n"
  },
  {
    "path": "src/io/lab/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport lab2rgb from './lab2rgb.js';\nimport rgb2lab from './rgb2lab.js';\nimport { getLabWhitePoint, setLabWhitePoint } from './lab-constants.js';\n\nColor.prototype.lab = function () {\n    return rgb2lab(this._rgb);\n};\n\nconst lab = (...args) => new Color(...args, 'lab');\nObject.assign(chroma, { lab, getLabWhitePoint, setLabWhitePoint });\n\ninput.format.lab = lab2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'lab');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'lab';\n        }\n    }\n});\n\nexport { lab, getLabWhitePoint, setLabWhitePoint };\n"
  },
  {
    "path": "src/io/lab/lab-constants.js",
    "content": "const labConstants = {\n    // Corresponds roughly to RGB brighter/darker\n    Kn: 18,\n\n    // D65 standard referent\n    labWhitePoint: 'd65',\n    Xn: 0.95047,\n    Yn: 1,\n    Zn: 1.08883,\n\n    t0: 0.137931034, // 4 / 29\n    t1: 0.206896552, // 6 / 29\n    t2: 0.12841855, // 3 * t1 * t1\n    t3: 0.008856452, // t1 * t1 * t1,\n\n    kE: 216.0 / 24389.0,\n    kKE: 8.0,\n    kK: 24389.0 / 27.0,\n\n    RefWhiteRGB: {\n        // sRGB\n        X: 0.95047,\n        Y: 1,\n        Z: 1.08883\n    },\n\n    MtxRGB2XYZ: {\n        m00: 0.4124564390896922,\n        m01: 0.21267285140562253,\n        m02: 0.0193338955823293,\n        m10: 0.357576077643909,\n        m11: 0.715152155287818,\n        m12: 0.11919202588130297,\n        m20: 0.18043748326639894,\n        m21: 0.07217499330655958,\n        m22: 0.9503040785363679\n    },\n\n    MtxXYZ2RGB: {\n        m00: 3.2404541621141045,\n        m01: -0.9692660305051868,\n        m02: 0.055643430959114726,\n        m10: -1.5371385127977166,\n        m11: 1.8760108454466942,\n        m12: -0.2040259135167538,\n        m20: -0.498531409556016,\n        m21: 0.041556017530349834,\n        m22: 1.0572251882231791\n    },\n\n    // used in rgb2xyz\n    As: 0.9414285350000001,\n    Bs: 1.040417467,\n    Cs: 1.089532651,\n\n    MtxAdaptMa: {\n        m00: 0.8951,\n        m01: -0.7502,\n        m02: 0.0389,\n        m10: 0.2664,\n        m11: 1.7135,\n        m12: -0.0685,\n        m20: -0.1614,\n        m21: 0.0367,\n        m22: 1.0296\n    },\n\n    MtxAdaptMaI: {\n        m00: 0.9869929054667123,\n        m01: 0.43230526972339456,\n        m02: -0.008528664575177328,\n        m10: -0.14705425642099013,\n        m11: 0.5183602715367776,\n        m12: 0.04004282165408487,\n        m20: 0.15996265166373125,\n        m21: 0.0492912282128556,\n        m22: 0.9684866957875502\n    }\n};\n\nexport default labConstants;\n\n// taken from https://de.mathworks.com/help/images/ref/whitepoint.html\nconst ILLUMINANTS = new Map([\n    // ASTM E308-01\n    ['a', [1.0985, 0.35585]],\n    // Wyszecki & Stiles, p. 769\n    ['b', [1.0985, 0.35585]],\n    // C ASTM E308-01\n    ['c', [0.98074, 1.18232]],\n    // D50 (ASTM E308-01)\n    ['d50', [0.96422, 0.82521]],\n    // D55 (ASTM E308-01)\n    ['d55', [0.95682, 0.92149]],\n    // D65 (ASTM E308-01)\n    ['d65', [0.95047, 1.08883]],\n    // E (ASTM E308-01)\n    ['e', [1, 1, 1]],\n    // F2 (ASTM E308-01)\n    ['f2', [0.99186, 0.67393]],\n    // F7 (ASTM E308-01)\n    ['f7', [0.95041, 1.08747]],\n    // F11 (ASTM E308-01)\n    ['f11', [1.00962, 0.6435]],\n    ['icc', [0.96422, 0.82521]]\n]);\n\nexport function setLabWhitePoint(name) {\n    const ill = ILLUMINANTS.get(String(name).toLowerCase());\n    if (!ill) {\n        throw new Error('unknown Lab illuminant ' + name);\n    }\n    labConstants.labWhitePoint = name;\n    labConstants.Xn = ill[0];\n    labConstants.Zn = ill[1];\n}\n\nexport function getLabWhitePoint() {\n    return labConstants.labWhitePoint;\n}\n"
  },
  {
    "path": "src/io/lab/lab2rgb.js",
    "content": "import LAB_CONSTANTS from './lab-constants.js';\nimport { unpack } from '../../utils/index.js';\n\n/*\n * L* [0..100]\n * a [-100..100]\n * b [-100..100]\n */\nconst lab2rgb = (...args) => {\n    args = unpack(args, 'lab');\n    const [L, a, b] = args;\n    const [x, y, z] = lab2xyz(L, a, b);\n    const [r, g, b_] = xyz2rgb(x, y, z);\n    return [r, g, b_, args.length > 3 ? args[3] : 1];\n};\n\nconst lab2xyz = (L, a, b) => {\n    const { kE, kK, kKE, Xn, Yn, Zn } = LAB_CONSTANTS;\n\n    const fy = (L + 16.0) / 116.0;\n    const fx = 0.002 * a + fy;\n    const fz = fy - 0.005 * b;\n\n    const fx3 = fx * fx * fx;\n    const fz3 = fz * fz * fz;\n\n    const xr = fx3 > kE ? fx3 : (116.0 * fx - 16.0) / kK;\n    const yr = L > kKE ? Math.pow((L + 16.0) / 116.0, 3.0) : L / kK;\n    const zr = fz3 > kE ? fz3 : (116.0 * fz - 16.0) / kK;\n\n    const x = xr * Xn;\n    const y = yr * Yn;\n    const z = zr * Zn;\n\n    return [x, y, z];\n};\n\nconst compand = (linear) => {\n    /* sRGB */\n    const sign = Math.sign(linear);\n    linear = Math.abs(linear);\n    return (\n        (linear <= 0.0031308\n            ? linear * 12.92\n            : 1.055 * Math.pow(linear, 1.0 / 2.4) - 0.055) * sign\n    );\n};\n\nconst xyz2rgb = (x, y, z) => {\n    const { MtxAdaptMa, MtxAdaptMaI, MtxXYZ2RGB, RefWhiteRGB, Xn, Yn, Zn } =\n        LAB_CONSTANTS;\n\n    const As = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n    const Bs = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n    const Cs = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n    const Ad =\n        RefWhiteRGB.X * MtxAdaptMa.m00 +\n        RefWhiteRGB.Y * MtxAdaptMa.m10 +\n        RefWhiteRGB.Z * MtxAdaptMa.m20;\n    const Bd =\n        RefWhiteRGB.X * MtxAdaptMa.m01 +\n        RefWhiteRGB.Y * MtxAdaptMa.m11 +\n        RefWhiteRGB.Z * MtxAdaptMa.m21;\n    const Cd =\n        RefWhiteRGB.X * MtxAdaptMa.m02 +\n        RefWhiteRGB.Y * MtxAdaptMa.m12 +\n        RefWhiteRGB.Z * MtxAdaptMa.m22;\n\n    const X1 =\n        (x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20) *\n        (Ad / As);\n    const Y1 =\n        (x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21) *\n        (Bd / Bs);\n    const Z1 =\n        (x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22) *\n        (Cd / Cs);\n\n    const X2 =\n        X1 * MtxAdaptMaI.m00 + Y1 * MtxAdaptMaI.m10 + Z1 * MtxAdaptMaI.m20;\n    const Y2 =\n        X1 * MtxAdaptMaI.m01 + Y1 * MtxAdaptMaI.m11 + Z1 * MtxAdaptMaI.m21;\n    const Z2 =\n        X1 * MtxAdaptMaI.m02 + Y1 * MtxAdaptMaI.m12 + Z1 * MtxAdaptMaI.m22;\n\n    const r = compand(\n        X2 * MtxXYZ2RGB.m00 + Y2 * MtxXYZ2RGB.m10 + Z2 * MtxXYZ2RGB.m20\n    );\n    const g = compand(\n        X2 * MtxXYZ2RGB.m01 + Y2 * MtxXYZ2RGB.m11 + Z2 * MtxXYZ2RGB.m21\n    );\n    const b = compand(\n        X2 * MtxXYZ2RGB.m02 + Y2 * MtxXYZ2RGB.m12 + Z2 * MtxXYZ2RGB.m22\n    );\n\n    return [r * 255, g * 255, b * 255];\n};\n\nexport default lab2rgb;\nexport { xyz2rgb };\n"
  },
  {
    "path": "src/io/lab/rgb2lab.js",
    "content": "import LAB_CONSTANTS from './lab-constants.js';\nimport { unpack } from '../../utils/index.js';\n\nconst rgb2lab = (...args) => {\n    const [r, g, b, ...rest] = unpack(args, 'rgb');\n    const [x, y, z] = rgb2xyz(r, g, b);\n    const [L, a, b_] = xyz2lab(x, y, z);\n    return [L, a, b_, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])];\n};\n\nfunction xyz2lab(x, y, z) {\n    const { Xn, Yn, Zn, kE, kK } = LAB_CONSTANTS;\n    const xr = x / Xn;\n    const yr = y / Yn;\n    const zr = z / Zn;\n\n    const fx = xr > kE ? Math.pow(xr, 1.0 / 3.0) : (kK * xr + 16.0) / 116.0;\n    const fy = yr > kE ? Math.pow(yr, 1.0 / 3.0) : (kK * yr + 16.0) / 116.0;\n    const fz = zr > kE ? Math.pow(zr, 1.0 / 3.0) : (kK * zr + 16.0) / 116.0;\n\n    return [116.0 * fy - 16.0, 500.0 * (fx - fy), 200.0 * (fy - fz)];\n}\n\nfunction gammaAdjustSRGB(companded) {\n    const sign = Math.sign(companded);\n    companded = Math.abs(companded);\n    const linear =\n        companded <= 0.04045\n            ? companded / 12.92\n            : Math.pow((companded + 0.055) / 1.055, 2.4);\n    return linear * sign;\n}\n\nconst rgb2xyz = (r, g, b) => {\n    // normalize and gamma adjust\n    r = gammaAdjustSRGB(r / 255);\n    g = gammaAdjustSRGB(g / 255);\n    b = gammaAdjustSRGB(b / 255);\n\n    const { MtxRGB2XYZ, MtxAdaptMa, MtxAdaptMaI, Xn, Yn, Zn, As, Bs, Cs } =\n        LAB_CONSTANTS;\n\n    let x = r * MtxRGB2XYZ.m00 + g * MtxRGB2XYZ.m10 + b * MtxRGB2XYZ.m20;\n    let y = r * MtxRGB2XYZ.m01 + g * MtxRGB2XYZ.m11 + b * MtxRGB2XYZ.m21;\n    let z = r * MtxRGB2XYZ.m02 + g * MtxRGB2XYZ.m12 + b * MtxRGB2XYZ.m22;\n\n    const Ad = Xn * MtxAdaptMa.m00 + Yn * MtxAdaptMa.m10 + Zn * MtxAdaptMa.m20;\n    const Bd = Xn * MtxAdaptMa.m01 + Yn * MtxAdaptMa.m11 + Zn * MtxAdaptMa.m21;\n    const Cd = Xn * MtxAdaptMa.m02 + Yn * MtxAdaptMa.m12 + Zn * MtxAdaptMa.m22;\n\n    let X = x * MtxAdaptMa.m00 + y * MtxAdaptMa.m10 + z * MtxAdaptMa.m20;\n    let Y = x * MtxAdaptMa.m01 + y * MtxAdaptMa.m11 + z * MtxAdaptMa.m21;\n    let Z = x * MtxAdaptMa.m02 + y * MtxAdaptMa.m12 + z * MtxAdaptMa.m22;\n\n    X *= Ad / As;\n    Y *= Bd / Bs;\n    Z *= Cd / Cs;\n\n    x = X * MtxAdaptMaI.m00 + Y * MtxAdaptMaI.m10 + Z * MtxAdaptMaI.m20;\n    y = X * MtxAdaptMaI.m01 + Y * MtxAdaptMaI.m11 + Z * MtxAdaptMaI.m21;\n    z = X * MtxAdaptMaI.m02 + Y * MtxAdaptMaI.m12 + Z * MtxAdaptMaI.m22;\n\n    return [x, y, z];\n};\n\nexport default rgb2lab;\nexport { rgb2xyz };\n"
  },
  {
    "path": "src/io/lch/hcl2rgb.js",
    "content": "import { unpack, reverse3 } from '../../utils/index.js';\nimport lch2rgb from './lch2rgb.js';\n\nconst hcl2rgb = (...args) => {\n    const hcl = reverse3(unpack(args, 'hcl'));\n    return lch2rgb(...hcl);\n};\n\nexport default hcl2rgb;\n"
  },
  {
    "path": "src/io/lch/index.js",
    "content": "import { unpack, type, reverse3 } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport lch2rgb from './lch2rgb.js';\nimport hcl2rgb from './hcl2rgb.js';\nimport rgb2lch from './rgb2lch.js';\n\nColor.prototype.lch = function () {\n    return rgb2lch(this._rgb);\n};\nColor.prototype.hcl = function () {\n    return reverse3(rgb2lch(this._rgb));\n};\n\nconst lch = (...args) => new Color(...args, 'lch');\nconst hcl = (...args) => new Color(...args, 'hcl');\n\nObject.assign(chroma, { lch, hcl });\n\ninput.format.lch = lch2rgb;\ninput.format.hcl = hcl2rgb;\n['lch', 'hcl'].forEach((m) =>\n    input.autodetect.push({\n        p: 2,\n        test: (...args) => {\n            args = unpack(args, m);\n            if (type(args) === 'array' && args.length === 3) {\n                return m;\n            }\n        }\n    })\n);\n\nexport { lch, hcl };\n"
  },
  {
    "path": "src/io/lch/lab2lch.js",
    "content": "import { unpack, RAD2DEG } from '../../utils/index.js';\nconst { sqrt, atan2, round } = Math;\n\nconst lab2lch = (...args) => {\n    const [l, a, b] = unpack(args, 'lab');\n    const c = sqrt(a * a + b * b);\n    let h = (atan2(b, a) * RAD2DEG + 360) % 360;\n    if (round(c * 10000) === 0) h = Number.NaN;\n    return [l, c, h];\n};\n\nexport default lab2lch;\n"
  },
  {
    "path": "src/io/lch/lch2lab.js",
    "content": "import { unpack, DEG2RAD } from '../../utils/index.js';\nconst { sin, cos } = Math;\n\nconst lch2lab = (...args) => {\n    /*\n    Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n    These formulas were invented by David Dalrymple to obtain maximum contrast without going\n    out of gamut if the parameters are in the range 0-1.\n\n    A saturation multiplier was added by Gregor Aisch\n    */\n    let [l, c, h] = unpack(args, 'lch');\n    if (isNaN(h)) h = 0;\n    h = h * DEG2RAD;\n    return [l, cos(h) * c, sin(h) * c];\n};\n\nexport default lch2lab;\n"
  },
  {
    "path": "src/io/lch/lch2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\nimport lch2lab from './lch2lab.js';\nimport lab2rgb from '../lab/lab2rgb.js';\n\nconst lch2rgb = (...args) => {\n    args = unpack(args, 'lch');\n    const [l, c, h] = args;\n    const [L, a, b_] = lch2lab(l, c, h);\n    const [r, g, b] = lab2rgb(L, a, b_);\n    return [r, g, b, args.length > 3 ? args[3] : 1];\n};\n\nexport default lch2rgb;\n"
  },
  {
    "path": "src/io/lch/rgb2lch.js",
    "content": "import { unpack } from '../../utils/index.js';\nimport rgb2lab from '../lab/rgb2lab.js';\nimport lab2lch from './lab2lch.js';\n\nconst rgb2lch = (...args) => {\n    const [r, g, b, ...rest] = unpack(args, 'rgb');\n    const [l, a, b_] = rgb2lab(r, g, b);\n    const [L, c, h] = lab2lch(l, a, b_);\n    return [L, c, h, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])];\n};\n\nexport default rgb2lch;\n"
  },
  {
    "path": "src/io/named/index.js",
    "content": "import Color from '../../Color.js';\nimport input from '../input.js';\nimport { type } from '../../utils/index.js';\n\nimport w3cx11 from '../../colors/w3cx11.js';\nimport hex2rgb from '../hex/hex2rgb.js';\nimport rgb2hex from '../hex/rgb2hex.js';\n\nColor.prototype.name = function () {\n    const hex = rgb2hex(this._rgb, 'rgb');\n    for (let n of Object.keys(w3cx11)) {\n        if (w3cx11[n] === hex) return n.toLowerCase();\n    }\n    return hex;\n};\n\ninput.format.named = (name) => {\n    name = name.toLowerCase();\n    if (w3cx11[name]) return hex2rgb(w3cx11[name]);\n    throw new Error('unknown color name: ' + name);\n};\n\ninput.autodetect.push({\n    p: 5,\n    test: (h, ...rest) => {\n        if (!rest.length && type(h) === 'string' && w3cx11[h.toLowerCase()]) {\n            return 'named';\n        }\n    }\n});\n"
  },
  {
    "path": "src/io/num/index.js",
    "content": "import chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport { type } from '../../utils/index.js';\nimport num2rgb from './num2rgb.js';\nimport rgb2num from './rgb2num.js';\n\nColor.prototype.num = function () {\n    return rgb2num(this._rgb);\n};\n\nconst num = (...args) => new Color(...args, 'num');\n\nObject.assign(chroma, { num });\n\ninput.format.num = num2rgb;\n\ninput.autodetect.push({\n    p: 5,\n    test: (...args) => {\n        if (\n            args.length === 1 &&\n            type(args[0]) === 'number' &&\n            args[0] >= 0 &&\n            args[0] <= 0xffffff\n        ) {\n            return 'num';\n        }\n    }\n});\n\nexport { num };\n"
  },
  {
    "path": "src/io/num/num2rgb.js",
    "content": "import { type } from '../../utils/index.js';\n\nconst num2rgb = (num) => {\n    if (type(num) == 'number' && num >= 0 && num <= 0xffffff) {\n        const r = num >> 16;\n        const g = (num >> 8) & 0xff;\n        const b = num & 0xff;\n        return [r, g, b, 1];\n    }\n    throw new Error('unknown num color: ' + num);\n};\n\nexport default num2rgb;\n"
  },
  {
    "path": "src/io/num/rgb2num.js",
    "content": "import { unpack } from '../../utils/index.js';\n\nconst rgb2num = (...args) => {\n    const [r, g, b] = unpack(args, 'rgb');\n    return (r << 16) + (g << 8) + b;\n};\n\nexport default rgb2num;\n"
  },
  {
    "path": "src/io/oklab/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport oklab2rgb from './oklab2rgb.js';\nimport rgb2oklab from './rgb2oklab.js';\n\nColor.prototype.oklab = function () {\n    return rgb2oklab(this._rgb);\n};\n\nconst oklab = (...args) => new Color(...args, 'oklab');\nObject.assign(chroma, { oklab });\n\ninput.format.oklab = oklab2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'oklab');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'oklab';\n        }\n    }\n});\n\nexport { oklab };\n"
  },
  {
    "path": "src/io/oklab/oklab2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\nimport multiplyMatrices from '../../utils/multiply-matrices.js';\nimport { xyz2rgb } from '../lab/lab2rgb.js';\n\nconst oklab2rgb = (...args) => {\n    args = unpack(args, 'lab');\n    const [L, a, b, ...rest] = args;\n    const [X, Y, Z] = OKLab_to_XYZ([L, a, b]);\n    const [r, g, b_] = xyz2rgb(X, Y, Z);\n    return [r, g, b_, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])];\n};\n\n// from https://www.w3.org/TR/css-color-4/#color-conversion-code\nfunction OKLab_to_XYZ(OKLab) {\n    // Given OKLab, convert to XYZ relative to D65\n    var LMStoXYZ = [\n        [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],\n        [-0.0405757452148008, 1.112286803280317, -0.0717110580655164],\n        [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]\n    ];\n    var OKLabtoLMS = [\n        [1.0, 0.3963377773761749, 0.2158037573099136],\n        [1.0, -0.1055613458156586, -0.0638541728258133],\n        [1.0, -0.0894841775298119, -1.2914855480194092]\n    ];\n\n    var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab);\n    return multiplyMatrices(\n        LMStoXYZ,\n        LMSnl.map((c) => c ** 3)\n    );\n}\n\nexport default oklab2rgb;\n"
  },
  {
    "path": "src/io/oklab/rgb2oklab.js",
    "content": "import { unpack } from '../../utils/index.js';\nimport multiplyMatrices from '../../utils/multiply-matrices.js';\nimport { rgb2xyz } from '../lab/rgb2lab.js';\n\nconst rgb2oklab = (...args) => {\n    const [r, g, b, ...rest] = unpack(args, 'rgb');\n    const xyz = rgb2xyz(r, g, b);\n    const oklab = XYZ_to_OKLab(xyz);\n    return [...oklab, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])];\n};\n\n// from https://www.w3.org/TR/css-color-4/#color-conversion-code\nfunction XYZ_to_OKLab(XYZ) {\n    // Given XYZ relative to D65, convert to OKLab\n    const XYZtoLMS = [\n        [0.819022437996703, 0.3619062600528904, -0.1288737815209879],\n        [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],\n        [0.0481771893596242, 0.2642395317527308, 0.6335478284694309]\n    ];\n    const LMStoOKLab = [\n        [0.210454268309314, 0.7936177747023054, -0.0040720430116193],\n        [1.9779985324311684, -2.4285922420485799, 0.450593709617411],\n        [0.0259040424655478, 0.7827717124575296, -0.8086757549230774]\n    ];\n\n    const LMS = multiplyMatrices(XYZtoLMS, XYZ);\n    // JavaScript Math.cbrt returns a sign-matched cube root\n    // beware if porting to other languages\n    // especially if tempted to use a general power function\n    return multiplyMatrices(\n        LMStoOKLab,\n        LMS.map((c) => Math.cbrt(c))\n    );\n    // L in range [0,1]. For use in CSS, multiply by 100 and add a percent\n}\n\nexport default rgb2oklab;\n"
  },
  {
    "path": "src/io/oklch/index.js",
    "content": "import { unpack, type } from '../../utils/index.js';\nimport chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport oklch2rgb from './oklch2rgb.js';\nimport rgb2oklch from './rgb2oklch.js';\n\nColor.prototype.oklch = function () {\n    return rgb2oklch(this._rgb);\n};\n\nconst oklch = (...args) => new Color(...args, 'oklch');\nObject.assign(chroma, { oklch });\n\ninput.format.oklch = oklch2rgb;\n\ninput.autodetect.push({\n    p: 2,\n    test: (...args) => {\n        args = unpack(args, 'oklch');\n        if (type(args) === 'array' && args.length === 3) {\n            return 'oklch';\n        }\n    }\n});\n\nexport { oklch };\n"
  },
  {
    "path": "src/io/oklch/oklch2rgb.js",
    "content": "import { unpack } from '../../utils/index.js';\nimport lch2lab from '../lch/lch2lab.js';\nimport oklab2rgb from '../oklab/oklab2rgb.js';\n\nconst oklch2rgb = (...args) => {\n    args = unpack(args, 'lch');\n    const [l, c, h, ...rest] = args;\n    const [L, a, b_] = lch2lab(l, c, h);\n    const [r, g, b] = oklab2rgb(L, a, b_);\n    return [r, g, b, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])];\n};\n\nexport default oklch2rgb;\n"
  },
  {
    "path": "src/io/oklch/rgb2oklch.js",
    "content": "import { unpack } from '../../utils/index.js';\nimport rgb2oklab from '../oklab/rgb2oklab.js';\nimport lab2lch from '../lch/lab2lch.js';\n\nconst rgb2oklch = (...args) => {\n    const [r, g, b, ...rest] = unpack(args, 'rgb');\n    const [l, a, b_] = rgb2oklab(r, g, b);\n    const [L, c, h] = lab2lch(l, a, b_);\n    return [L, c, h, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])];\n};\n\nexport default rgb2oklch;\n"
  },
  {
    "path": "src/io/rgb/index.js",
    "content": "import chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport { unpack, type } from '../../utils/index.js';\nconst { round } = Math;\n\nColor.prototype.rgb = function (rnd = true) {\n    if (rnd === false) return this._rgb.slice(0, 3);\n    return this._rgb.slice(0, 3).map(round);\n};\n\nColor.prototype.rgba = function (rnd = true) {\n    return this._rgb.slice(0, 4).map((v, i) => {\n        return i < 3 ? (rnd === false ? v : round(v)) : v;\n    });\n};\n\nconst rgb = (...args) => new Color(...args, 'rgb');\nObject.assign(chroma, { rgb });\n\ninput.format.rgb = (...args) => {\n    const rgba = unpack(args, 'rgba');\n    if (rgba[3] === undefined) rgba[3] = 1;\n    return rgba;\n};\n\ninput.autodetect.push({\n    p: 3,\n    test: (...args) => {\n        args = unpack(args, 'rgba');\n        if (\n            type(args) === 'array' &&\n            (args.length === 3 ||\n                (args.length === 4 &&\n                    type(args[3]) == 'number' &&\n                    args[3] >= 0 &&\n                    args[3] <= 1))\n        ) {\n            return 'rgb';\n        }\n    }\n});\n\nexport { rgb };\n"
  },
  {
    "path": "src/io/temp/index.js",
    "content": "import chroma from '../../chroma.js';\nimport Color from '../../Color.js';\nimport input from '../input.js';\nimport temperature2rgb from './temperature2rgb.js';\nimport rgb2temperature from './rgb2temperature.js';\n\nColor.prototype.temp =\n    Color.prototype.kelvin =\n    Color.prototype.temperature =\n        function () {\n            return rgb2temperature(this._rgb);\n        };\n\nconst temp = (...args) => new Color(...args, 'temp');\nObject.assign(chroma, { temp, kelvin: temp, temperature: temp });\n\ninput.format.temp =\n    input.format.kelvin =\n    input.format.temperature =\n        temperature2rgb;\n\nexport { temp, temp as kelvin, temp as temperature };\n"
  },
  {
    "path": "src/io/temp/rgb2temperature.js",
    "content": "/*\n * Based on implementation by Neil Bartlett\n * https://github.com/neilbartlett/color-temperature\n **/\n\nimport temperature2rgb from './temperature2rgb.js';\nimport { unpack } from '../../utils/index.js';\nconst { round } = Math;\n\nconst rgb2temperature = (...args) => {\n    const rgb = unpack(args, 'rgb');\n    const r = rgb[0],\n        b = rgb[2];\n    let minTemp = 1000;\n    let maxTemp = 40000;\n    const eps = 0.4;\n    let temp;\n    while (maxTemp - minTemp > eps) {\n        temp = (maxTemp + minTemp) * 0.5;\n        const rgb = temperature2rgb(temp);\n        if (rgb[2] / rgb[0] >= b / r) {\n            maxTemp = temp;\n        } else {\n            minTemp = temp;\n        }\n    }\n    return round(temp);\n};\n\nexport default rgb2temperature;\n"
  },
  {
    "path": "src/io/temp/temperature2rgb.js",
    "content": "/*\n * Based on implementation by Neil Bartlett\n * https://github.com/neilbartlett/color-temperature\n */\n\nconst { log } = Math;\n\nconst temperature2rgb = (kelvin) => {\n    const temp = kelvin / 100;\n    let r, g, b;\n    if (temp < 66) {\n        r = 255;\n        g =\n            temp < 6\n                ? 0\n                : -155.25485562709179 -\n                  0.44596950469579133 * (g = temp - 2) +\n                  104.49216199393888 * log(g);\n        b =\n            temp < 20\n                ? 0\n                : -254.76935184120902 +\n                  0.8274096064007395 * (b = temp - 10) +\n                  115.67994401066147 * log(b);\n    } else {\n        r =\n            351.97690566805693 +\n            0.114206453784165 * (r = temp - 55) -\n            40.25366309332127 * log(r);\n        g =\n            325.4494125711974 +\n            0.07943456536662342 * (g = temp - 50) -\n            28.0852963507957 * log(g);\n        b = 255;\n    }\n    return [r, g, b, 1];\n};\n\nexport default temperature2rgb;\n"
  },
  {
    "path": "src/ops/alpha.js",
    "content": "import Color from '../Color.js';\nimport { type } from '../utils/index.js';\n\nColor.prototype.alpha = function (a, mutate = false) {\n    if (a !== undefined && type(a) === 'number') {\n        if (mutate) {\n            this._rgb[3] = a;\n            return this;\n        }\n        return new Color([this._rgb[0], this._rgb[1], this._rgb[2], a], 'rgb');\n    }\n    return this._rgb[3];\n};\n"
  },
  {
    "path": "src/ops/clipped.js",
    "content": "import Color from '../Color.js';\n\nColor.prototype.clipped = function () {\n    return this._rgb._clipped || false;\n};\n"
  },
  {
    "path": "src/ops/darken.js",
    "content": "import '../io/lab/index.js';\nimport Color from '../Color.js';\nimport LAB_CONSTANTS from '../io/lab/lab-constants.js';\n\nColor.prototype.darken = function (amount = 1) {\n    const me = this;\n    const lab = me.lab();\n    lab[0] -= LAB_CONSTANTS.Kn * amount;\n    return new Color(lab, 'lab').alpha(me.alpha(), true);\n};\n\nColor.prototype.brighten = function (amount = 1) {\n    return this.darken(-amount);\n};\n\nColor.prototype.darker = Color.prototype.darken;\nColor.prototype.brighter = Color.prototype.brighten;\n"
  },
  {
    "path": "src/ops/get.js",
    "content": "import Color from '../Color.js';\n\nColor.prototype.get = function (mc) {\n    const [mode, channel] = mc.split('.');\n    const src = this[mode]();\n    if (channel) {\n        const i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n        if (i > -1) return src[i];\n        throw new Error(`unknown channel ${channel} in mode ${mode}`);\n    } else {\n        return src;\n    }\n};\n"
  },
  {
    "path": "src/ops/luminance.js",
    "content": "import Color from '../Color.js';\nimport { type } from '../utils/index.js';\nconst { pow } = Math;\n\nconst EPS = 1e-7;\nconst MAX_ITER = 20;\n\nColor.prototype.luminance = function (lum, mode = 'rgb') {\n    if (lum !== undefined && type(lum) === 'number') {\n        if (lum === 0) {\n            // return pure black\n            return new Color([0, 0, 0, this._rgb[3]], 'rgb');\n        }\n        if (lum === 1) {\n            // return pure white\n            return new Color([255, 255, 255, this._rgb[3]], 'rgb');\n        }\n        // compute new color using...\n        let cur_lum = this.luminance();\n        let max_iter = MAX_ITER;\n\n        const test = (low, high) => {\n            const mid = low.interpolate(high, 0.5, mode);\n            const lm = mid.luminance();\n            if (Math.abs(lum - lm) < EPS || !max_iter--) {\n                // close enough\n                return mid;\n            }\n            return lm > lum ? test(low, mid) : test(mid, high);\n        };\n\n        const rgb = (\n            cur_lum > lum\n                ? test(new Color([0, 0, 0]), this)\n                : test(this, new Color([255, 255, 255]))\n        ).rgb();\n        return new Color([...rgb, this._rgb[3]]);\n    }\n    return rgb2luminance(...this._rgb.slice(0, 3));\n};\n\nconst rgb2luminance = (r, g, b) => {\n    // relative luminance\n    // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\n    r = luminance_x(r);\n    g = luminance_x(g);\n    b = luminance_x(b);\n    return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n};\n\nconst luminance_x = (x) => {\n    x /= 255;\n    return x <= 0.03928 ? x / 12.92 : pow((x + 0.055) / 1.055, 2.4);\n};\n"
  },
  {
    "path": "src/ops/mix.js",
    "content": "import Color from '../Color.js';\nimport mix from '../generator/mix.js';\n\nColor.prototype.mix = Color.prototype.interpolate = function (\n    col2,\n    f = 0.5,\n    ...rest\n) {\n    return mix(this, col2, f, ...rest);\n};\n"
  },
  {
    "path": "src/ops/premultiply.js",
    "content": "import Color from '../Color.js';\n\nColor.prototype.premultiply = function (mutate = false) {\n    const rgb = this._rgb;\n    const a = rgb[3];\n    if (mutate) {\n        this._rgb = [rgb[0] * a, rgb[1] * a, rgb[2] * a, a];\n        return this;\n    } else {\n        return new Color([rgb[0] * a, rgb[1] * a, rgb[2] * a, a], 'rgb');\n    }\n};\n"
  },
  {
    "path": "src/ops/saturate.js",
    "content": "import '../io/lch/index.js';\nimport Color from '../Color.js';\nimport LAB_CONSTANTS from '../io/lab/lab-constants.js';\n\nColor.prototype.saturate = function (amount = 1) {\n    const me = this;\n    const lch = me.lch();\n    lch[1] += LAB_CONSTANTS.Kn * amount;\n    if (lch[1] < 0) lch[1] = 0;\n    return new Color(lch, 'lch').alpha(me.alpha(), true);\n};\n\nColor.prototype.desaturate = function (amount = 1) {\n    return this.saturate(-amount);\n};\n"
  },
  {
    "path": "src/ops/set.js",
    "content": "import Color from '../Color.js';\nimport { type } from '../utils/index.js';\n\nColor.prototype.set = function (mc, value, mutate = false) {\n    const [mode, channel] = mc.split('.');\n    const src = this[mode]();\n    if (channel) {\n        const i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);\n        if (i > -1) {\n            if (type(value) == 'string') {\n                switch (value.charAt(0)) {\n                    case '+':\n                        src[i] += +value;\n                        break;\n                    case '-':\n                        src[i] += +value;\n                        break;\n                    case '*':\n                        src[i] *= +value.substr(1);\n                        break;\n                    case '/':\n                        src[i] /= +value.substr(1);\n                        break;\n                    default:\n                        src[i] = +value;\n                }\n            } else if (type(value) === 'number') {\n                src[i] = value;\n            } else {\n                throw new Error(`unsupported value for Color.set`);\n            }\n            const out = new Color(src, mode);\n            if (mutate) {\n                this._rgb = out._rgb;\n                return this;\n            }\n            return out;\n        }\n        throw new Error(`unknown channel ${channel} in mode ${mode}`);\n    } else {\n        return src;\n    }\n};\n"
  },
  {
    "path": "src/ops/shade.js",
    "content": "import '../io/lab/index.js';\nimport Color from '../Color.js';\nimport mix from '../generator/mix.js';\n\nColor.prototype.tint = function (f = 0.5, ...rest) {\n    return mix(this, 'white', f, ...rest);\n};\n\nColor.prototype.shade = function (f = 0.5, ...rest) {\n    return mix(this, 'black', f, ...rest);\n};\n"
  },
  {
    "path": "src/utils/analyze.js",
    "content": "import type from './type.js';\n\nconst { log, pow, floor, abs } = Math;\n\nexport function analyze(data, key = null) {\n    const r = {\n        min: Number.MAX_VALUE,\n        max: Number.MAX_VALUE * -1,\n        sum: 0,\n        values: [],\n        count: 0\n    };\n    if (type(data) === 'object') {\n        data = Object.values(data);\n    }\n    data.forEach((val) => {\n        if (key && type(val) === 'object') val = val[key];\n        if (val !== undefined && val !== null && !isNaN(val)) {\n            r.values.push(val);\n            r.sum += val;\n            if (val < r.min) r.min = val;\n            if (val > r.max) r.max = val;\n            r.count += 1;\n        }\n    });\n\n    r.domain = [r.min, r.max];\n\n    r.limits = (mode, num) => limits(r, mode, num);\n\n    return r;\n}\n\nexport function limits(data, mode = 'equal', num = 7) {\n    if (type(data) == 'array') {\n        data = analyze(data);\n    }\n    const { min, max } = data;\n    const values = data.values.sort((a, b) => a - b);\n\n    if (num === 1) {\n        return [min, max];\n    }\n\n    const limits = [];\n\n    if (mode.substr(0, 1) === 'c') {\n        // continuous\n        limits.push(min);\n        limits.push(max);\n    }\n\n    if (mode.substr(0, 1) === 'e') {\n        // equal interval\n        limits.push(min);\n        for (let i = 1; i < num; i++) {\n            limits.push(min + (i / num) * (max - min));\n        }\n        limits.push(max);\n    } else if (mode.substr(0, 1) === 'l') {\n        // log scale\n        if (min <= 0) {\n            throw new Error(\n                'Logarithmic scales are only possible for values > 0'\n            );\n        }\n        const min_log = Math.LOG10E * log(min);\n        const max_log = Math.LOG10E * log(max);\n        limits.push(min);\n        for (let i = 1; i < num; i++) {\n            limits.push(pow(10, min_log + (i / num) * (max_log - min_log)));\n        }\n        limits.push(max);\n    } else if (mode.substr(0, 1) === 'q') {\n        // quantile scale\n        limits.push(min);\n        for (let i = 1; i < num; i++) {\n            const p = ((values.length - 1) * i) / num;\n            const pb = floor(p);\n            if (pb === p) {\n                limits.push(values[pb]);\n            } else {\n                // p > pb\n                const pr = p - pb;\n                limits.push(values[pb] * (1 - pr) + values[pb + 1] * pr);\n            }\n        }\n        limits.push(max);\n    } else if (mode.substr(0, 1) === 'k') {\n        // k-means clustering\n        /*\n        implementation based on\n        http://code.google.com/p/figue/source/browse/trunk/figue.js#336\n        simplified for 1-d input values\n        */\n        let cluster;\n        const n = values.length;\n        const assignments = new Array(n);\n        const clusterSizes = new Array(num);\n        let repeat = true;\n        let nb_iters = 0;\n        let centroids = null;\n\n        // get seed values\n        centroids = [];\n        centroids.push(min);\n        for (let i = 1; i < num; i++) {\n            centroids.push(min + (i / num) * (max - min));\n        }\n        centroids.push(max);\n\n        while (repeat) {\n            // assignment step\n            for (let j = 0; j < num; j++) {\n                clusterSizes[j] = 0;\n            }\n            for (let i = 0; i < n; i++) {\n                const value = values[i];\n                let mindist = Number.MAX_VALUE;\n                let best;\n                for (let j = 0; j < num; j++) {\n                    const dist = abs(centroids[j] - value);\n                    if (dist < mindist) {\n                        mindist = dist;\n                        best = j;\n                    }\n                    clusterSizes[best]++;\n                    assignments[i] = best;\n                }\n            }\n\n            // update centroids step\n            const newCentroids = new Array(num);\n            for (let j = 0; j < num; j++) {\n                newCentroids[j] = null;\n            }\n            for (let i = 0; i < n; i++) {\n                cluster = assignments[i];\n                if (newCentroids[cluster] === null) {\n                    newCentroids[cluster] = values[i];\n                } else {\n                    newCentroids[cluster] += values[i];\n                }\n            }\n            for (let j = 0; j < num; j++) {\n                newCentroids[j] *= 1 / clusterSizes[j];\n            }\n\n            // check convergence\n            repeat = false;\n            for (let j = 0; j < num; j++) {\n                if (newCentroids[j] !== centroids[j]) {\n                    repeat = true;\n                    break;\n                }\n            }\n\n            centroids = newCentroids;\n            nb_iters++;\n\n            if (nb_iters > 200) {\n                repeat = false;\n            }\n        }\n\n        // finished k-means clustering\n        // the next part is borrowed from gabrielflor.it\n        const kClusters = {};\n        for (let j = 0; j < num; j++) {\n            kClusters[j] = [];\n        }\n        for (let i = 0; i < n; i++) {\n            cluster = assignments[i];\n            kClusters[cluster].push(values[i]);\n        }\n        let tmpKMeansBreaks = [];\n        for (let j = 0; j < num; j++) {\n            tmpKMeansBreaks.push(kClusters[j][0]);\n            tmpKMeansBreaks.push(kClusters[j][kClusters[j].length - 1]);\n        }\n        tmpKMeansBreaks = tmpKMeansBreaks.sort((a, b) => a - b);\n        limits.push(tmpKMeansBreaks[0]);\n        for (let i = 1; i < tmpKMeansBreaks.length; i += 2) {\n            const v = tmpKMeansBreaks[i];\n            if (!isNaN(v) && limits.indexOf(v) === -1) {\n                limits.push(v);\n            }\n        }\n    }\n    return limits;\n}\n"
  },
  {
    "path": "src/utils/clip_rgb.js",
    "content": "import limit from './limit.js';\n\nexport default (rgb) => {\n    rgb._clipped = false;\n    rgb._unclipped = rgb.slice(0);\n    for (let i = 0; i <= 3; i++) {\n        if (i < 3) {\n            if (rgb[i] < 0 || rgb[i] > 255) rgb._clipped = true;\n            rgb[i] = limit(rgb[i], 0, 255);\n        } else if (i === 3) {\n            rgb[i] = limit(rgb[i], 0, 1);\n        }\n    }\n    return rgb;\n};\n"
  },
  {
    "path": "src/utils/contrast.js",
    "content": "import Color from '../Color.js';\nimport '../ops/luminance.js';\n\nexport default (a, b) => {\n    // WCAG contrast ratio\n    // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef\n    a = new Color(a);\n    b = new Color(b);\n    const l1 = a.luminance();\n    const l2 = b.luminance();\n    return l1 > l2 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05);\n};\n"
  },
  {
    "path": "src/utils/contrastAPCA.js",
    "content": "import Color from '../Color.js';\nimport mix from '../generator/mix.js';\n\n/**\n * @license\n *\n * The APCA contrast prediction algorithm is based of the formulas published\n * in the APCA-1.0.98G specification by Myndex. The specification is available at:\n * https://raw.githubusercontent.com/Myndex/apca-w3/master/images/APCAw3_0.1.17_APCA0.0.98G.svg\n *\n * Note that the APCA implementation is still beta, so please update to\n * future versions of chroma.js when they become available.\n *\n * You can read more about the APCA Readability Criterion at\n * https://readtech.org/ARC/\n */\n\n// constants\nconst W_offset = 0.027;\nconst P_in = 0.0005;\nconst P_out = 0.1;\nconst R_scale = 1.14;\nconst B_threshold = 0.022;\nconst B_exp = 1.414;\n\nexport default (text, bg) => {\n    // parse input colors\n    text = new Color(text);\n    bg = new Color(bg);\n    // if text color has alpha, blend against background\n    if (text.alpha() < 1) {\n        text = mix(bg, text, text.alpha(), 'rgb');\n    }\n    const l_text = lum(...text.rgb());\n    const l_bg = lum(...bg.rgb());\n\n    // soft clamp black levels\n    const Y_text =\n        l_text >= B_threshold\n            ? l_text\n            : l_text + Math.pow(B_threshold - l_text, B_exp);\n    const Y_bg =\n        l_bg >= B_threshold ? l_bg : l_bg + Math.pow(B_threshold - l_bg, B_exp);\n\n    // normal polarity (dark text on light background)\n    const S_norm = Math.pow(Y_bg, 0.56) - Math.pow(Y_text, 0.57);\n    // reverse polarity (light text on dark background)\n    const S_rev = Math.pow(Y_bg, 0.65) - Math.pow(Y_text, 0.62);\n    // clamp noise then scale\n    const C =\n        Math.abs(Y_bg - Y_text) < P_in\n            ? 0\n            : Y_text < Y_bg\n              ? S_norm * R_scale\n              : S_rev * R_scale;\n    // clamp minimum contrast then offset\n    const S_apc = Math.abs(C) < P_out ? 0 : C > 0 ? C - W_offset : C + W_offset;\n    // scale to 100\n    return S_apc * 100;\n};\n\nfunction lum(r, g, b) {\n    return (\n        0.2126729 * Math.pow(r / 255, 2.4) +\n        0.7151522 * Math.pow(g / 255, 2.4) +\n        0.072175 * Math.pow(b / 255, 2.4)\n    );\n}\n"
  },
  {
    "path": "src/utils/delta-e.js",
    "content": "import Color from '../Color.js';\nconst { sqrt, pow, min, max, atan2, abs, cos, sin, exp, PI } = Math;\n\nexport default function (a, b, Kl = 1, Kc = 1, Kh = 1) {\n    // Delta E (CIE 2000)\n    // see http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html\n    var rad2deg = function (rad) {\n        return (360 * rad) / (2 * PI);\n    };\n    var deg2rad = function (deg) {\n        return (2 * PI * deg) / 360;\n    };\n    a = new Color(a);\n    b = new Color(b);\n    const [L1, a1, b1] = Array.from(a.lab());\n    const [L2, a2, b2] = Array.from(b.lab());\n    const avgL = (L1 + L2) / 2;\n    const C1 = sqrt(pow(a1, 2) + pow(b1, 2));\n    const C2 = sqrt(pow(a2, 2) + pow(b2, 2));\n    const avgC = (C1 + C2) / 2;\n    const G = 0.5 * (1 - sqrt(pow(avgC, 7) / (pow(avgC, 7) + pow(25, 7))));\n    const a1p = a1 * (1 + G);\n    const a2p = a2 * (1 + G);\n    const C1p = sqrt(pow(a1p, 2) + pow(b1, 2));\n    const C2p = sqrt(pow(a2p, 2) + pow(b2, 2));\n    const avgCp = (C1p + C2p) / 2;\n    const arctan1 = rad2deg(atan2(b1, a1p));\n    const arctan2 = rad2deg(atan2(b2, a2p));\n    const h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360;\n    const h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360;\n    const avgHp =\n        abs(h1p - h2p) > 180 ? (h1p + h2p + 360) / 2 : (h1p + h2p) / 2;\n    const T =\n        1 -\n        0.17 * cos(deg2rad(avgHp - 30)) +\n        0.24 * cos(deg2rad(2 * avgHp)) +\n        0.32 * cos(deg2rad(3 * avgHp + 6)) -\n        0.2 * cos(deg2rad(4 * avgHp - 63));\n    let deltaHp = h2p - h1p;\n    deltaHp =\n        abs(deltaHp) <= 180\n            ? deltaHp\n            : h2p <= h1p\n              ? deltaHp + 360\n              : deltaHp - 360;\n    deltaHp = 2 * sqrt(C1p * C2p) * sin(deg2rad(deltaHp) / 2);\n    const deltaL = L2 - L1;\n    const deltaCp = C2p - C1p;\n    const sl = 1 + (0.015 * pow(avgL - 50, 2)) / sqrt(20 + pow(avgL - 50, 2));\n    const sc = 1 + 0.045 * avgCp;\n    const sh = 1 + 0.015 * avgCp * T;\n    const deltaTheta = 30 * exp(-pow((avgHp - 275) / 25, 2));\n    const Rc = 2 * sqrt(pow(avgCp, 7) / (pow(avgCp, 7) + pow(25, 7)));\n    const Rt = -Rc * sin(2 * deg2rad(deltaTheta));\n    const result = sqrt(\n        pow(deltaL / (Kl * sl), 2) +\n            pow(deltaCp / (Kc * sc), 2) +\n            pow(deltaHp / (Kh * sh), 2) +\n            Rt * (deltaCp / (Kc * sc)) * (deltaHp / (Kh * sh))\n    );\n    return max(0, min(100, result));\n}\n"
  },
  {
    "path": "src/utils/distance.js",
    "content": "import Color from '../Color.js';\n\n// simple Euclidean distance\nexport default function (a, b, mode = 'lab') {\n    // Delta E (CIE 1976)\n    // see http://www.brucelindbloom.com/index.html?Equations.html\n    a = new Color(a);\n    b = new Color(b);\n    const l1 = a.get(mode);\n    const l2 = b.get(mode);\n    let sum_sq = 0;\n    for (let i in l1) {\n        const d = (l1[i] || 0) - (l2[i] || 0);\n        sum_sq += d * d;\n    }\n    return Math.sqrt(sum_sq);\n}\n"
  },
  {
    "path": "src/utils/index.js",
    "content": "const { PI, min, max } = Math;\n\nconst rnd2 = (a) => Math.round(a * 100) / 100;\nconst rnd3 = (a) => Math.round(a * 100) / 100;\n\nexport { default as clip_rgb } from './clip_rgb.js';\nexport { default as limit } from './limit.js';\nexport { default as type } from './type.js';\nexport { default as unpack } from './unpack.js';\nexport { default as last } from './last.js';\n\nconst TWOPI = PI * 2;\nconst PITHIRD = PI / 3;\nconst DEG2RAD = PI / 180;\nconst RAD2DEG = 180 / PI;\n\n/**\n * Reverse the first three elements of an array\n *\n * @param {any[]} arr\n * @returns {any[]}\n */\nfunction reverse3(arr) {\n    return [...arr.slice(0, 3).reverse(), ...arr.slice(3)];\n}\n\nexport { PI, TWOPI, PITHIRD, DEG2RAD, RAD2DEG, min, max, rnd2, rnd3, reverse3 };\n"
  },
  {
    "path": "src/utils/last.js",
    "content": "import type from './type.js';\n\nexport default (args) => {\n    if (args.length < 2) return null;\n    const l = args.length - 1;\n    if (type(args[l]) == 'string') return args[l].toLowerCase();\n    return null;\n};\n"
  },
  {
    "path": "src/utils/limit.js",
    "content": "const { min, max } = Math;\n\nexport default (x, low = 0, high = 1) => {\n    return min(max(low, x), high);\n};\n"
  },
  {
    "path": "src/utils/multiply-matrices.js",
    "content": "// from https://www.w3.org/TR/css-color-4/multiply-matrices.js\nexport default function multiplyMatrices(A, B) {\n    let m = A.length;\n\n    if (!Array.isArray(A[0])) {\n        // A is vector, convert to [[a, b, c, ...]]\n        A = [A];\n    }\n\n    if (!Array.isArray(B[0])) {\n        // B is vector, convert to [[a], [b], [c], ...]]\n        B = B.map((x) => [x]);\n    }\n\n    let p = B[0].length;\n    let B_cols = B[0].map((_, i) => B.map((x) => x[i])); // transpose B\n    let product = A.map((row) =>\n        B_cols.map((col) => {\n            if (!Array.isArray(row)) {\n                return col.reduce((a, c) => a + c * row, 0);\n            }\n\n            return row.reduce((a, c, i) => a + c * (col[i] || 0), 0);\n        })\n    );\n\n    if (m === 1) {\n        product = product[0]; // Avoid [[a, b, c, ...]]\n    }\n\n    if (p === 1) {\n        return product.map((x) => x[0]); // Avoid [[a], [b], [c], ...]]\n    }\n\n    return product;\n}\n"
  },
  {
    "path": "src/utils/scales.js",
    "content": "// some pre-defined color scales:\nimport chroma from '../chroma.js';\nimport '../io/hsl/index.js';\nimport scale from '../generator/scale.js';\n\nexport default {\n    cool() {\n        return scale([chroma.hsl(180, 1, 0.9), chroma.hsl(250, 0.7, 0.4)]);\n    },\n    hot() {\n        return scale(['#000', '#f00', '#ff0', '#fff'], [0, 0.25, 0.75, 1]).mode(\n            'rgb'\n        );\n    }\n};\n"
  },
  {
    "path": "src/utils/type.js",
    "content": "// ported from jQuery's $.type\nconst classToType = {};\nfor (let name of [\n    'Boolean',\n    'Number',\n    'String',\n    'Function',\n    'Array',\n    'Date',\n    'RegExp',\n    'Undefined',\n    'Null'\n]) {\n    classToType[`[object ${name}]`] = name.toLowerCase();\n}\nexport default function (obj) {\n    return classToType[Object.prototype.toString.call(obj)] || 'object';\n}\n"
  },
  {
    "path": "src/utils/unpack.js",
    "content": "import type from './type.js';\n\nexport default (args, keyOrder = null) => {\n    // if called with more than 3 arguments, we return the arguments\n    if (args.length >= 3) return Array.prototype.slice.call(args);\n    // with less than 3 args we check if first arg is object\n    // and use the keyOrder string to extract and sort properties\n    if (type(args[0]) == 'object' && keyOrder) {\n        return keyOrder\n            .split('')\n            .filter((k) => args[0][k] !== undefined)\n            .map((k) => args[0][k]);\n    }\n    // otherwise we just return the first argument\n    // (which we suppose is an array of args)\n    return args[0].slice(0);\n};\n"
  },
  {
    "path": "src/utils/valid.js",
    "content": "import Color from '../Color.js';\n\nexport default (...args) => {\n    try {\n        new Color(...args);\n        return true;\n        // eslint-disable-next-line\n    } catch (e) {\n        return false;\n    }\n};\n"
  },
  {
    "path": "src/version.js",
    "content": "// this gets updated automatically\nexport const version = '3.2.0';\n"
  },
  {
    "path": "test/alpha.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Tests for the alpha channel', () => {\n    it('setting & getting alpha channel', () => {\n        const color = chroma('red');\n        expect(color.alpha()).toBe(1);\n        expect(color.alpha(0.5).alpha()).toBe(0.5);\n        expect(color.alpha()).toBe(1);\n    });\n\n    it('interpolating alpha channel', () => {\n        const color = chroma.mix(chroma('white').alpha(0), chroma('black').alpha(1), 0.3, 'rgb');\n        expect(color.hex('rgb')).toBe('#b3b3b3');\n        expect(color.hex()).toBe('#b3b3b34d');\n        expect(color.alpha()).toBe(0.3);\n    });\n\n    it('constructing rgba color', () => {\n        const color = new chroma.Color(255, 0, 0, 0.5, 'rgb');\n        expect(color.alpha()).toBe(0.5);\n    });\n\n    it('constructing rgba color, rgb shorthand', () => {\n        const color = chroma.rgb(255, 0, 0, 0.5);\n        expect(color.alpha()).toBe(0.5);\n    });\n\n    it('constructing rgba color, hsl shorthand', () => {\n        const color = chroma.hsl(0, 1, 0.5).alpha(0.5);\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.5);\n    });\n\n    it('parsing hex rgba colors', () => {\n        const color = chroma('#ff00004d');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.3);\n        expect(color.rgba()).toEqual([255, 0, 0, 0.3]);\n    });\n\n    it('parsing rgba colors', () => {\n        const color = chroma.css('rgb(255 0 0 / 0.3)');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.3);\n        expect(color.rgba()).toEqual([255, 0, 0, 0.3]);\n    });\n\n    it('parsing rgba colors (percentage)', () => {\n        const color = chroma.css('rgb(100% 0% 0% / 0.2)');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.2);\n        expect(color.rgb()).toEqual([255, 0, 0]);\n        expect(color.rgba()).toEqual([255, 0, 0, 0.2]);\n    });\n\n    it('parsing hsla colors', () => {\n        const color = chroma.css('hsla(0,100%,50%,0.25)');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n        expect(color.rgb()).toEqual([255, 0, 0]);\n        expect(color.rgba()).toEqual([255, 0, 0, 0.25]);\n    });\n\n    it('constructing hsla color', () => {\n        const color = chroma(0, 1, 0.5, 0.25, 'hsl');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n    });\n\n    it('constructing hsva color', () => {\n        const color = chroma(0, 1, 1, 0.25, 'hsv');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n    });\n\n    it('constructing hsia color', () => {\n        const color = chroma(0, 1, 0.3333334, 0.25, 'hsi');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n    });\n\n    it('constructing laba color', () => {\n        const color = chroma(53.24079414130722, 80.09245959641109, 67.20319651585301, 0.25, 'lab');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n    });\n\n    it('constructing lcha color', () => {\n        const color = chroma(53.24079414130722, 104.55176567686985, 39.99901061253297, 0.25, 'lch');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n    });\n\n    it('constructing cmyka color', () => {\n        const color = chroma(0, 1, 1, 0, 0.25, 'cmyk');\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n    });\n\n    it('gl output', () => {\n        const color = chroma.gl(1, 0, 0, 0.25);\n        expect(color.name()).toBe('red');\n        expect(color.alpha()).toBe(0.25);\n        expect(color.gl()).toEqual([1, 0, 0, 0.25]);\n    });\n\n    it('rgba css output', () => {\n        const color = chroma.css('hsla(0,100%,50%,0.25)');\n        expect(color.css()).toBe('rgb(255 0 0 / 0.25)');\n    });\n\n    it('hex output', () => {\n        const color = chroma.gl(1, 0, 0, 0.25);\n        expect(color.hex()).toBe('#ff000040');\n        expect(color.hex('rgb')).toBe('#ff0000');\n        expect(color.hex('rgba')).toBe('#ff000040');\n        expect(color.hex('argb')).toBe('#40ff0000');\n    });\n\n    it('num output', () => {\n        const color = chroma.gl(1, 0, 0, 0.25);\n        expect(color.num()).toBe(0xff0000);\n    });\n\n    it('setting alpha returns new instance', () => {\n        const color = chroma('red');\n        color.alpha(0.5);\n        expect(color.alpha()).toBe(1);\n    });\n});\n"
  },
  {
    "path": "test/analyze.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst analyze = chroma.analyze;\n\ndescribe('Some tests for analyze()', () => {\n    it('analyze an array of numbers', () => {\n        const result = analyze([1, 2, 2, 3, 4, 5]);\n        expect(result.sum).toBe(17);\n        expect(result.count).toBe(6);\n        expect(result.max).toBe(5);\n        expect(result.min).toBe(1);\n        expect(result.domain).toEqual([1, 5]);\n    });\n\n    it('analyze an object of numbers', () => {\n        const result = analyze({ a: 1, b: 2, c: 2, d: 3, e: 4, f: 5 });\n        expect(result.sum).toBe(17);\n        expect(result.count).toBe(6);\n        expect(result.max).toBe(5);\n        expect(result.min).toBe(1);\n        expect(result.domain).toEqual([1, 5]);\n    });\n\n    it('analyze an array of objects', () => {\n        const result = analyze([{ k: 1 }, { k: 2 }, { k: 2 }, { k: 3 }, { k: 4 }, { k: 5 }], 'k');\n        expect(result.sum).toBe(17);\n        expect(result.count).toBe(6);\n        expect(result.max).toBe(5);\n        expect(result.min).toBe(1);\n        expect(result.domain).toEqual([1, 5]);\n    });\n\n    it('analyze an object of objects', () => {\n        const result = analyze(\n            {\n                a: { k: 1 },\n                b: { k: 2 },\n                c: { k: 2 },\n                d: { k: 3 },\n                e: { k: 4 },\n                f: { k: 5 }\n            },\n            'k'\n        );\n        expect(result.sum).toBe(17);\n        expect(result.count).toBe(6);\n        expect(result.max).toBe(5);\n        expect(result.min).toBe(1);\n        expect(result.domain).toEqual([1, 5]);\n    });\n});\n"
  },
  {
    "path": "test/autodetect.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('autodetect color', () => {\n    it('autodetect named color', () => {\n        const result = chroma('red');\n        expect(result.hex()).toBe('#ff0000');\n        expect(result.rgb()).toStrictEqual([255, 0, 0]);\n    });\n\n    it('autodetect hex color', () => {\n        const result = chroma('#00ff00');\n        expect(result.name()).toBe('lime');\n    });\n\n    it('autodetect hex color, no #', () => {\n        const result = chroma('00ff00');\n        expect(result.name()).toBe('lime');\n    });\n\n    it('autodetect 3-digit hex color, no #', () => {\n        const result = chroma('0F0');\n        expect(result.name()).toBe('lime');\n        expect(result.alpha()).toBe(1);\n    });\n\n    it('autodetect 4-digit hex color', () => {\n        const result = chroma('#0F09');\n        expect(result.name()).toBe('lime');\n        expect(result.alpha()).toBe(0.6);\n    });\n\n    it('autodetect RGB color', () => {\n        const result = chroma(0, 0, 255);\n        expect(result.hex()).toBe('#0000ff');\n    });\n\n    it('autodetect rgba color', () => {\n        const result = chroma(255, 0, 0, 0.5);\n        expect(result.css()).toBe('rgb(255 0 0 / 0.5)');\n    });\n\n    it('autodetect legacy hsl color', () => {\n        const result = chroma('hsl(120, 100%, 50%)');\n        expect(result.name()).toBe('lime');\n    });\n\n    it('autodetect modern hsl color', () => {\n        const result = chroma('hsl(120deg 100% 50%)');\n        expect(result.name()).toBe('lime');\n    });\n});\n"
  },
  {
    "path": "test/average.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst average = chroma.average;\n\nconst colors = ['red', 'blue', 'white'];\n\ndescribe('Tests for average color', () => {\n    it('average in RGB', () => {\n        const result = average(colors, 'rgb');\n        expect(result.hex()).toBe('#aa55aa');\n    });\n\n    it('average with alpha channel', () => {\n        const result = average([chroma('red').alpha(0.5), chroma('blue').alpha(0.5)], 'rgb');\n        expect(result.rgba()).toEqual([128, 0, 128, 0.5]);\n    });\n\n    it('average in lab', () => {\n        const result = average(colors, 'lab');\n        expect(result.hex()).toBe('#e26daf');\n    });\n\n    it('average h in lch', () => {\n        const result = average([chroma.lch(50, 50, 0), chroma.lch(50, 50, 90)], 'lch').get('lch.h');\n        expect(Math.round(result)).toBe(45);\n    });\n\n    it('average in hsl of same colors', () => {\n        const result = average(['#02c03a', '#02c03a'], 'hsl');\n        expect(result.hex()).toBe('#02c03a');\n    });\n\n    it('average same color', () => {\n        const result = average(['#02c03a', '#02c03a'], 'hsl');\n        expect(result.hex()).toBe('#02c03a');\n    });\n\n    it('lrgb average', () => {\n        const result = average(colors, 'lrgb');\n        expect(result.hex()).toBe('#d093d0');\n    });\n\n    it('three colors, weighted rgb average', () => {\n        const result = average(colors, 'rgb', [1, 1, 2]);\n        expect(result.hex()).toBe('#bf80bf');\n    });\n\n    it('three colors, weighted lrgb average', () => {\n        const result = average(colors, 'lrgb', [1, 3, 2]);\n        expect(result.hex()).toBe('#b493e9');\n    });\n\n    it('three colors, weighted hsl average', () => {\n        const result = average(colors, 'hsl', [0.25, 1, 0.5]);\n        expect(result.hex()).toBe('#8163e5');\n    });\n});\n"
  },
  {
    "path": "test/bezier.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst bezier = chroma.bezier;\n\ndescribe('Testing bezier interpolation', () => {\n    describe('simple two color linear interpolation', () => {\n        const f = bezier(['white', 'black']);\n\n        it('starts from white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('ends in black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n\n        it('center is grey', () => {\n            expect(f(0.5).hex()).toBe('#777777');\n        });\n    });\n\n    describe('three color quadratic bezier interpolation', () => {\n        const f = bezier(['white', 'red', 'black']);\n\n        it('starts from white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('ends in black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n\n        it('center is a greyish red', () => {\n            expect(f(0.5).hex()).toBe('#c45c44');\n        });\n    });\n\n    describe('four color cubic bezier interpolation', () => {\n        const f = bezier(['white', 'yellow', 'red', 'black']);\n\n        it('starts from white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('ends in black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n\n        it('1st quarter', () => {\n            expect(f(0.25).hex()).toBe('#ffe085');\n        });\n\n        it('center', () => {\n            expect(f(0.5).hex()).toBe('#e69735');\n        });\n\n        it('3rd quarter', () => {\n            expect(f(0.75).hex()).toBe('#914213');\n        });\n    });\n\n    describe('five color diverging quadratic bezier interpolation', () => {\n        const f = bezier(['darkred', 'orange', 'snow', 'lightgreen', 'royalblue']);\n\n        it('starts from darkred', () => {\n            expect(f(0).hex()).toBe('#8b0000');\n        });\n\n        it('ends in royalblue', () => {\n            expect(f(1).hex()).toBe('#4169e1');\n        });\n\n        it('1st quarter', () => {\n            expect(f(0.25).hex()).toBe('#dd8d49');\n        });\n\n        it('center', () => {\n            expect(f(0.5).hex()).toBe('#dfcb98');\n        });\n\n        it('3rd quarter', () => {\n            expect(f(0.75).hex()).toBe('#a7c1bd');\n        });\n    });\n\n    describe('using bezier in a chroma.scale', () => {\n        const f = chroma\n            .scale(bezier(['darkred', 'orange', 'snow', 'lightgreen', 'royalblue']))\n            .domain([0, 1], 5)\n            .out('hex');\n\n        it('starts from darkred', () => {\n            expect(f(0)).toBe('#8b0000');\n        });\n\n        it('ends in royalblue', () => {\n            expect(f(1)).toBe('#4169e1');\n        });\n\n        it('center is snow', () => {\n            expect(f(0.5)).toBe('#dfcb98');\n        });\n\n        it('1st quarter', () => {\n            expect(f(0.25)).toBe('#dd8d49');\n        });\n\n        it('3rd quarter', () => {\n            expect(f(0.75)).toBe('#a7c1bd');\n        });\n    });\n});\n"
  },
  {
    "path": "test/blend.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst blend = chroma.blend;\n\ndescribe('Testing blend modes', () => {\n    it('multiply 1', () => {\n        const result = blend('red', '#5a9f37', 'multiply');\n        expect(result.hex()).toBe('#5a0000');\n    });\n\n    it('multiply 2', () => {\n        const result = blend('#33b16f', '#857590', 'multiply');\n        expect(result.hex()).toBe('#1b513f');\n    });\n\n    it('screen', () => {\n        const result = blend('#b83d31', '#0da671', 'screen');\n        expect(result.hex()).toBe('#bcbb8c');\n    });\n\n    it('overlay', () => {\n        const result = blend('#b83d31', '#0da671', 'overlay');\n        expect(result.hex()).toBe('#784f2b');\n    });\n});\n"
  },
  {
    "path": "test/color.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst Color = chroma.Color;\n\nconst hexColors = ['#ff9900', '#FF9900', '#F90', 'f90', 'FF9900', 'FF9900F0', 'F90F', '#F90F'];\n\ndescribe('Testing Color', () => {\n    it('re-use existing color instance', () => {\n        const c0 = new Color('red');\n        expect(c0).toBe(new Color(c0));\n    });\n\n    it('autodetect named colors', () => {\n        const createColor = () => new Color('mediumslateblue');\n        expect(createColor).not.toThrow();\n        expect(createColor().hex()).toBe('#7b68ee');\n    });\n\n    it('throw err on wrong color name', () => {\n        const createColor = () => new Color('fakecolor');\n        expect(createColor).toThrow();\n    });\n\n    describe('autodetect correct hex colors', () => {\n        hexColors.forEach((hex) => {\n            it(`detect hex ${hex}`, () => {\n                const createColor = () => new Color(hex);\n                expect(createColor).not.toThrow();\n                expect(createColor().hex('rgb')).toBe('#ff9900');\n            });\n        });\n    });\n\n    it('create lch color from object', () => {\n        expect(new Color({ l: 80, c: 25, h: 200 }).hex()).toBe('#85d4d5');\n        expect(chroma({ l: 80, c: 25, h: 200 }).hex()).toBe('#85d4d5');\n        expect(chroma.lch(80, 25, 200).hex()).toBe('#85d4d5');\n        expect(chroma(80, 25, 200, 'lch').hex()).toBe('#85d4d5');\n    });\n\n    it('create hcl color from object', () => {\n        expect(new Color({ h: 200, c: 25, l: 80 }).hex()).toBe('#85d4d5');\n        expect(chroma({ h: 200, c: 25, l: 80 }).hex()).toBe('#85d4d5');\n        expect(chroma.hcl(200, 25, 80).hex()).toBe('#85d4d5');\n        expect(chroma(200, 25, 80, 'hcl').hex()).toBe('#85d4d5');\n    });\n});\n"
  },
  {
    "path": "test/colorbrewer.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('ColorBrewer palettes', () => {\n    it('should have all palettes', () => {\n        expect(chroma.brewer).toBeDefined();\n        expect(Object.keys(chroma.brewer).length).toBe(36);\n    });\n\n    it('brewer keys are camel-cased', () => {\n        expect(Object.keys(chroma.brewer)[0]).toBe('OrRd');\n        expect(Object.keys(chroma.brewer)[1]).toBe('PuBu');\n    });\n\n    it('supports case-insensitive access to palettes', () => {\n        expect(chroma.brewer.RdYlBu).toBeDefined();\n        expect(chroma.brewer.rdylbu).toBeDefined();\n        expect(chroma.brewer.RdylBU).toBeDefined();\n    });\n\n    it('non existing palettes are still undefined', () => {\n        expect(chroma.brewer.notHere).toBeUndefined();\n    });\n});\n"
  },
  {
    "path": "test/contrast.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport contrastAPCA from '../src/utils/contrastAPCA.js';\nimport chroma from 'chroma-js';\n\nconst contrast = chroma.contrast;\n\ndescribe('Testing contrast ratio', () => {\n    it('maximum contrast is 21:1', () => {\n        expect(contrast('black', 'white')).toBe(21);\n    });\n\n    it('minimum contrast is 1:1', () => {\n        expect(contrast('white', 'white')).toBe(1);\n    });\n\n    it('contrast between white and red is 4:1', () => {\n        expect(contrast('white', 'red').toFixed(1)).toBe('4.0');\n    });\n\n    it('contrast between black and red is 5.25:1', () => {\n        expect(contrast('black', 'red').toFixed(2)).toBe('5.25');\n    });\n\n    it('contrast between black and darkgrey is 1.32:1', () => {\n        expect(contrast('black', '#222').toFixed(2)).toBe('1.32');\n    });\n});\n\ndescribe('Testing contrast ratio with APCA', () => {\n    it('maximum contrast', () => {\n        expect(+contrastAPCA('black', 'white').toFixed(1)).toBe(106);\n        expect(+contrastAPCA('white', 'black').toFixed(1)).toBe(-107.9);\n    });\n\n    it('minimum contrast', () => {\n        expect(+contrastAPCA('gray', 'gray').toFixed(1)).toBe(0);\n    });\n\n    it('contrast without alpha', () => {\n        expect(+contrastAPCA('#594d45', '#ffd4d4').toFixed(1)).toBe(69.0);\n        expect(+contrastAPCA('#b04646', '#d6d6d6').toFixed(1)).toBe(52.9);\n        expect(+contrastAPCA('#c2afaf', '#d6d6d6').toFixed(1)).toBe(17.0);\n    });\n\n    it('contrast with alpha', () => {\n        // todo: there's a slight difference to the values shown in the APCA demo\n        // when computing contrast between colors with alpha\n        expect(+contrastAPCA('#00000044', 'white').toFixed(1)).toBe(37.3); // 36.7\n        expect(+contrastAPCA('#ffffffc0', 'black').toFixed(1)).toBe(-68); // -68.6\n    });\n\n    it('inverse contrast', () => {\n        expect(+contrastAPCA('#f5f5b3', '#614f63').toFixed(1)).toBe(-81.7);\n        expect(+contrastAPCA('#67a7d6', '#4f6357').toFixed(1)).toBe(-30.5);\n        expect(+contrastAPCA('#d667cb', '#b04646').toFixed(1)).toBe(-17.9);\n    });\n});\n"
  },
  {
    "path": "test/converters.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Testing color conversions', () => {\n    for (const colorName in chroma.colors) {\n        const hexValue = chroma.colors[colorName];\n\n        it(`should convert ${colorName} to hsl and back`, () => {\n            expect(chroma.hsl(chroma(hexValue).hsl()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to cmyk and back`, () => {\n            expect(chroma.cmyk(chroma(hexValue).cmyk()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to css and back`, () => {\n            expect(chroma.css(chroma(hexValue).css()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to hsi and back`, () => {\n            expect(chroma.hsi(chroma(hexValue).hsi()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to hsv and back`, () => {\n            expect(chroma.hsv(chroma(hexValue).hsv()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to lab and back`, () => {\n            expect(chroma.lab(chroma(hexValue).lab()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to oklab and back`, () => {\n            expect(chroma.oklab(chroma(hexValue).oklab()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to lch and back`, () => {\n            expect(chroma.lch(chroma(hexValue).lch()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to oklch and back`, () => {\n            expect(chroma.oklch(chroma(hexValue).oklch()).hex()).toBe(hexValue);\n        });\n\n        it(`should convert ${colorName} to num and back`, () => {\n            expect(chroma.num(chroma(hexValue).num()).hex()).toBe(hexValue);\n        });\n    }\n});\n"
  },
  {
    "path": "test/cubehelix.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst cubehelix = chroma.cubehelix;\n\ndescribe('Testing cubehelix colors', () => {\n    it('default helix', () => {\n        const color = cubehelix();\n        // starts in black\n        expect(color(0).hex()).toBe('#000000');\n        // at 0.25\n        expect(color(0.25).hex()).toBe('#16534c');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#a07949');\n        // at 0.75\n        expect(color(0.75).hex()).toBe('#c7b3ed');\n        // ends in white\n        expect(color(1).hex()).toBe('#ffffff');\n    });\n\n    it('red helix', () => {\n        const color = cubehelix(0, 1, 1, 1);\n        // starts in black\n        expect(color(0).hex()).toBe('#000000');\n        // at 0.25\n        expect(color(0.25).hex()).toBe('#2e5117');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#4c949f');\n        // at 0.75\n        expect(color(0.75).hex()).toBe('#d1aee8');\n        // ends in white\n        expect(color(1).hex()).toBe('#ffffff');\n    });\n\n    it('red helix - partial l range', () => {\n        const color = cubehelix(0, 1, 1, 1, [0.25, 0.75]);\n        // starts\n        expect(color(0).hex()).toBe('#663028');\n        // at 0.25\n        expect(color(0.25).hex()).toBe('#49752d');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#4c949f');\n        // at 0.75\n        expect(color(0.75).hex()).toBe('#b68ad2');\n        // ends\n        expect(color(1).hex()).toBe('#e6b0a8');\n    });\n\n    it('red helix - gamma', () => {\n        const color = cubehelix(0, 1, 1, 0.8);\n        // starts in black\n        expect(color(0).hex()).toBe('#000000');\n        // at 0.25\n        expect(color(0.25).hex()).toBe('#3f6824');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#60a6b1');\n        // at 0.75\n        expect(color(0.75).hex()).toBe('#dabcee');\n        // ends in white\n        expect(color(1).hex()).toBe('#ffffff');\n    });\n\n    it('red helix - no saturation', () => {\n        const color = cubehelix(0, 1, 0, 1, [0, 1]);\n        // starts in black\n        expect(color(0).hex()).toBe('#000000');\n        // at 0.25\n        expect(color(0.25).hex()).toBe('#404040');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#808080');\n        // at 0.75\n        expect(color(0.75).hex()).toBe('#bfbfbf');\n        // ends in white\n        expect(color(1).hex()).toBe('#ffffff');\n    });\n\n    it('red helix - saturation range', () => {\n        const color = cubehelix(0, 1, [1, 0], 1);\n        // starts in black\n        expect(color(0).hex()).toBe('#000000');\n        // at 0.25\n        expect(color(0.25).hex()).toBe('#324c21');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#668a8f');\n        // at 0.75\n        expect(color(0.75).hex()).toBe('#c4bbc9');\n        // ends in white\n        expect(color(1).hex()).toBe('#ffffff');\n        // saturation decreases\n        expect(color(0.33).hsl()[1]).toBeGreaterThan(color(0.66).hsl()[1]);\n    });\n\n    it('non-array lightness', () => {\n        const color = cubehelix(300, -1.5, 1, 1, 0.5);\n        // starts\n        expect(color(0).hex()).toBe('#ae629f');\n        // at 0.5\n        expect(color(0.5).hex()).toBe('#a07949');\n        // ends\n        expect(color(1).hex()).toBe('#519d60');\n    });\n});\n"
  },
  {
    "path": "test/delta-e.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst deltaE = chroma.deltaE;\n\n// due to floating-point arithmetic on different devices, differences in decimals may be found.\n// Running http://www.brucelindbloom.com/index.html?ColorDifferenceCalc.html JS code locally\n// on the same device as the delta-e code in this library will provide the exact same results.\nconst tests = {\n    nodifference: { in: [0x000000, 0x000000], out: 0 },\n    maxdifference: { in: [0xffffff, 0x000000], out: 100 },\n    redgreen: { in: [0xff0000, 0x00ff00], out: 86.6082374535373 },\n    greenred: { in: [0x00ff00, 0xff0000], out: 86.6082374535373 },\n    beef: { in: [0x00beef, 0x00beee], out: 0.28076037937072745 },\n    yellowblue: { in: [0xffff00, 0x0000ff], out: 100 }\n};\n\ndescribe('Testing delta-e color difference calculations', () => {\n    for (const key in tests) {\n        const { in: input, out } = tests[key];\n        it(key, () => {\n            const result = deltaE(...input);\n            expect(result).toBeCloseTo(out, 4);\n        });\n    }\n});\n"
  },
  {
    "path": "test/docs/index.test.js",
    "content": "/* eslint-disable no-unused-vars */\nimport { describe, it, expect } from 'vitest';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport chroma_ from '../../index.js';\n\nconst docsPath = path.resolve(__dirname, '../../docs/src/index.md');\nconst DOCS = fs.readFileSync(docsPath, 'utf-8');\n\nconst snippets = Array.from(DOCS.matchAll(/```js([\\s\\S]+?)```/g))\n    .map((m) => m[1])\n    .filter((s) => s.indexOf('import') === -1);\n\n// is used in eval;\nconst data = [\n    2.0, 3.5, 3.6, 3.8, 3.8, 4.1, 4.3, 4.4, 4.6, 4.9, 5.2, 5.3, 5.4, 5.7, 5.8,\n    5.9, 6.2, 6.5, 6.8, 7.2, 8\n];\n\ndescribe('Tests all snippets in the documentation', () => {\n    // referenced in code snippets\n    let colors, f, c;\n\n    snippets.forEach((code, i) => {\n        if (code.indexOf('function') > -1 || code.indexOf('### ') > -1) return;\n\n        it(`run code snippet ${i}`, () => {\n            expect(() => {\n                const chroma = chroma_;\n                eval(code);\n            }).not.toThrow();\n        });\n    });\n});\n"
  },
  {
    "path": "test/html/bezier.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>Bezier Interpolation</title>\n        <script type=\"text/javascript\" src=\"../../chroma.js\"></script>\n    </head>\n    <body>\n        <script type=\"text/javascript\">\n            function showGradient(I, outerdiv, steps) {\n                var I = chroma.interpolate.bezier(c),\n                    c = document.createElement('div'),\n                    totalW = 800;\n                for (var i = 0; i < steps; i++) {\n                    var d = document.createElement('div');\n                    d.style.display = 'inline-block';\n                    d.style.width = totalW / steps + 'px';\n                    d.style.height = '60px';\n                    d.style.background = I(i / (steps - 1)).hex();\n                    c.appendChild(d);\n                }\n                outerdiv.appendChild(c);\n            }\n\n            var colors = [\n                ['white', 'black'],\n                ['white', 'red', 'black'],\n                ['white', 'yellow', 'red', 'black'],\n                ['red', 'white', 'blue'],\n                ['#fdfdf9', 'darkred'],\n                ['#fdfdf9', 'orange', 'darkred'],\n                ['#fdfdf9', 'yellow', 'orange', 'darkred'],\n                ['darkred', 'orange', 'snow', 'lightgreen', 'royalblue']\n            ];\n\n            for (var c in colors) {\n                c = colors[c];\n                d = document.createElement('div');\n                d.style.position = 'relative';\n                document.body.appendChild(d);\n                d.innerHTML =\n                    \"<div style='position:absolute;top:5px;left:10px;opacity:0.9;color:#000;text-shadow:-1px -1px 0px rgba(255,255,255,0.3), 1px 1px 0px rgba(255,255,255,0.3), -1px 1px 0px rgba(255,255,255,0.3), 1px -1px 0px rgba(255,255,255,0.3)'><code>\" +\n                    c +\n                    '</code></div>';\n                showGradient(colors, d, 13);\n            }\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/html/blend.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>Chroma.js</title>\n        <script type=\"text/javascript\" src=\"../../chroma.js\"></script>\n    </head>\n    <style type=\"text/css\">\n        body {\n            font-family: Helvetica;\n        }\n        h1 {\n            font-size: 18px;\n        }\n        .row {\n            margin-bottom: 10px;\n        }\n        .left,\n        .right {\n            display: inline-block;\n            margin-right: 20px;\n            position: relative;\n        }\n        .swatch {\n            display: inline-block;\n            width: 60px;\n            height: 50px;\n        }\n        .swatch.overlay {\n            position: absolute;\n            left: 60px;\n        }\n    </style>\n    <body>\n        <script type=\"text/javascript\">\n            var names = Object.keys(chroma.colors),\n                l = names.length;\n\n            var cssMap = {\n                dodge: 'color-dodge',\n                burn: 'color-burn'\n            };\n\n            Object.keys(chroma.blend).forEach(function (mode) {\n                document.write('<h1>' + mode + '</h1>');\n                for (var i = 0; i < 10; i++) {\n                    var row = el('div.row'),\n                        left = el('div.left'),\n                        right = el('div.right');\n\n                    var bottom = names[Math.floor(Math.random() * l)],\n                        top = names[Math.floor(Math.random() * l)];\n\n                    swatch(bottom, left);\n                    swatch(chroma.blend(bottom, top, mode).hex(), left);\n                    swatch(top, left);\n\n                    swatch(bottom, right);\n                    swatch(bottom, right);\n                    var s = swatch(top, right);\n                    s.className = 'swatch overlay';\n                    s.style.mixBlendMode = cssMap[mode] || mode;\n                    swatch(top, right);\n                }\n            });\n\n            function swatch(color, parent) {\n                var s = el('div.swatch', parent);\n                s.setAttribute('title', color);\n                s.style.background = color;\n                return s;\n            }\n\n            function el(t, parent) {\n                var p = t.split('.');\n                var e = document.createElement(p[0]);\n                if (p[1]) e.className = p[1];\n                (parent || document.body).appendChild(e);\n                return e;\n            }\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/html/colorscales.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>Chroma.js</title>\n        <script type=\"text/javascript\" src=\"../chroma.js\"></script>\n    </head>\n    <body>\n        <script type=\"text/javascript\">\n            function showScale(cs, outerdiv, log) {\n                cs.out('hex');\n                var c = document.createElement('div');\n                for (var i = 0; i <= 1; i += 0.002) {\n                    var d = document.createElement('div');\n                    d.style.display = 'inline-block';\n                    d.style.width = '2px';\n                    d.style.height = '60px';\n                    d.style.background = cs(log ? Math.pow(10, i * 3) : i);\n                    c.appendChild(d);\n                }\n                outerdiv.appendChild(c);\n            }\n\n            var scales = [\n                \"scale(['lightyellow', 'navy'])\",\n                \"scale(['lightyellow', 'navy']).mode('lab')\",\n                \"scale(['lightyellow', 'navy']).mode('lch')\",\n                \"scale(['lightyellow', 'navy']).mode('hsv')\",\n                \"scale(['lightyellow', 'navy']).mode('hsl')\",\n                \"scale(['lightyellow', 'navy']).mode('hsi')\",\n                \"scale(['white', 'darkred']).mode('lab')\",\n                \"scale(['white', 'yellow', 'orange', 'darkred']).mode('lab')\",\n                \"scale(['darkred', 'orange', 'snow', 'lightgreen', 'royalblue']).mode('lab')\",\n                \"scale('RdYlGn').domain([0, 1], 9)\",\n                \"scale('RdYlGn')\",\n                \"scale(['#A50026', '#FFFFBF', '#006837']).mode('lab')\",\n                \"scale(['#A50026', '#FFFFBF', '#006837']).mode('lch')\",\n                \"scale(['#A50026', '#FFFFBF', '#006837']).mode('hsv')\",\n                \"scale(['#A50026', '#FFFFBF', '#006837']).mode('rgb')\",\n                \"scale(['#A50026', '#FFFFBF', '#006837']).mode('hsl')\",\n                \"scale(['#A50026', '#FFFFBF', '#006837']).mode('hsi')\",\n                \"scale(['#ffeeef', '#CD002F', '#400001']).domain([0,1], 9).mode('lab')\",\n                \"scale(['#ffeeef', '#CD002F', '#400001']).domain([0,1], 9).mode('lch')\",\n                \"scale(['#ffeeef', '#CD002F', '#400001']).domain([0,1], 9).mode('rgb')\",\n                \"scale(['#ffeeef', '#CD002F', '#400001']).domain([0,1], 9).mode('hsv')\",\n                \"scale(['#ffeeef', '#CD002F', '#400001']).domain([0,1], 9).mode('hsl')\",\n                \"scale(['#ffffff', '#000000']).domain([0,1], 9).mode('lab')\",\n                \"scale(['#ffffff', '#000000']).domain([0,1], 9).mode('lch')\",\n                \"scale(['#ffffff', '#000000']).domain([0,1], 9).mode('rgb')\",\n                \"scale(['#ffffff', '#000000']).domain([0,1], 9).mode('hsv')\",\n                \"scale(['#ffffff', '#000000']).domain([1,1000], 9, 'log').mode('hsl')\",\n                \"scale('RdYlGn').domain([0,1], 7).mode('lab')\",\n                \"scale(['a01','F7F7F7','25a']).domain([0, 0.14285714285714285, 0.2857142857142857, 0.42857142857142855, 0.5714285714285714, 0.7142857142857143, 0.8571428571428571, 1]).mode('lab')\",\n                \"scale(['a01','F7F7F7','25a']).domain([0,.05,.13,.2,.4,.6,.8,1]).mode('lab')\",\n                \"scale(['a01','F7F7F7','25a']).domain([0,.05,.13,.2,.4,.6,.8,1]).fixed(true).mode('lab')\"\n            ];\n\n            for (var s in scales) {\n                s = scales[s];\n                d = document.createElement('div');\n                d.style.position = 'relative';\n                document.body.appendChild(d);\n                d.innerHTML =\n                    \"<div style='position:absolute;top:5px;left:10px;opacity:0.9;color:#000;text-shadow:-1px -1px 0px rgba(255,255,255,0.3), 1px 1px 0px rgba(255,255,255,0.3), -1px 1px 0px rgba(255,255,255,0.3), 1px -1px 0px rgba(255,255,255,0.3)'><code>chroma.\" +\n                    s +\n                    '</code></div>';\n                showScale(eval('chroma.' + s), d, s.indexOf('log') > 0);\n            }\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/html/cubehelix.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>Cubejelix Interpolation</title>\n        <script\n            type=\"text/javascript\"\n            src=\"//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js\"\n        ></script>\n        <script type=\"text/javascript\" src=\"../../chroma.js\"></script>\n        <style type=\"text/css\">\n            input[type='number'] {\n                width: 40px;\n            }\n        </style>\n    </head>\n    <body>\n        start <input type=\"number\" id=\"start\" value=\"300\" step=\"20\" /> rotations\n        <input type=\"number\" id=\"rot\" value=\"-1.5\" step=\"0.1\" /> gamma\n        <input type=\"number\" id=\"gamma\" value=\"1\" step=\"0.1\" /> l min/max\n        <input type=\"number\" id=\"lmin\" value=\"0\" step=\"0.1\" min=\"0\" />\n        <input type=\"number\" id=\"lmax\" value=\"1\" step=\"0.1\" max=\"1\" />\n        sat min/max\n        <input type=\"number\" id=\"smin\" value=\"1\" step=\"0.1\" min=\"0\" />\n        <input type=\"number\" id=\"smax\" value=\"1\" step=\"0.1\" min=\"0\" />\n\n        <hr />\n        <script type=\"text/javascript\">\n            $('input').on('change', update);\n\n            function v(id) {\n                return +$('#' + id).val();\n            }\n\n            var j = 0;\n\n            function update() {\n                var steps = 200,\n                    totalW = 800;\n                var sat = [v('smin'), v('smax')];\n                $('.scale').remove();\n                var ch = chroma.cubehelix(\n                    v('start'),\n                    v('rot'),\n                    sat[0] != sat[1] ? sat : sat[0],\n                    v('gamma'),\n                    [v('lmin'), v('lmax')]\n                );\n                var c = $('<div />').addClass('scale').appendTo('body');\n                for (var i = 0; i < steps; i++) {\n                    $('<div/>')\n                        .css({\n                            display: 'inline-block',\n                            width: totalW / steps + 'px',\n                            height: '100px',\n                            background: ch(i / (steps - 1)).hex()\n                        })\n                        .appendTo(c);\n                }\n            }\n\n            update();\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/html/luminance.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>Chroma.js</title>\n        <script type=\"text/javascript\" src=\"../../chroma.js\"></script>\n    </head>\n    <body>\n        <script type=\"text/javascript\">\n            function showScale(color, mode, odiv) {\n                var c = document.createElement('div');\n                for (var i = 0; i <= 1; i += 0.01) {\n                    var col = chroma(color).luminance(i, mode);\n                    var d = document.createElement('div');\n                    d.style.display = 'inline-block';\n                    d.style.width = '10px';\n                    d.style.height = '60px';\n                    d.style.background = col.hex();\n                    c.appendChild(d);\n                }\n                odiv.appendChild(c);\n            }\n\n            var scales = [\n                ['red', 'rgb'],\n                ['red', 'lab'],\n                ['yellow', 'lab'],\n                ['black', 'rgb'],\n                ['blue', 'rgb'],\n                ['green', 'rgb'],\n                ['green', 'lab'],\n                ['orange', 'rgb']\n            ];\n\n            for (var s in scales) {\n                s = scales[s];\n                d = document.createElement('div');\n                d.style.position = 'relative';\n                document.body.appendChild(d);\n                d.innerHTML =\n                    \"<div style='position:absolute;top:5px;left:10px;opacity:0.9;color:#000;text-shadow:-1px -1px 0px rgba(255,255,255,0.3), 1px 1px 0px rgba(255,255,255,0.3), -1px 1px 0px rgba(255,255,255,0.3), 1px -1px 0px rgba(255,255,255,0.3)'><code>\" +\n                    s.join(' / ') +\n                    '</code></div>';\n                showScale(s[0], s[1], d);\n            }\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/io/cmyk2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport cmyk2rgb from '../../src/io/cmyk/cmyk2rgb.js';\n\nconst cmykColors = [\n    [0, 0, 0, 1],\n    [0, 0, 0, 0],\n    [0, 1, 1, 0],\n    [1, 0, 1, 0],\n    [1, 1, 0, 0],\n    [0, 0, 1, 0],\n    [1, 0, 0, 0],\n    [0, 1, 0, 0]\n];\nconst rgbColors = [\n    [0, 0, 0, 1],\n    [255, 255, 255, 1],\n    [255, 0, 0, 1],\n    [0, 255, 0, 1],\n    [0, 0, 255, 1],\n    [255, 255, 0, 1],\n    [0, 255, 255, 1],\n    [255, 0, 255, 1]\n];\n\ndescribe('Testing CMYK color conversions', () => {\n    describe('parse simple CMYK colors', () => {\n        it('black', () => {\n            expect(cmyk2rgb(cmykColors[0])).toEqual(rgbColors[0]);\n        });\n\n        it('white', () => {\n            expect(cmyk2rgb(cmykColors[1])).toEqual(rgbColors[1]);\n        });\n\n        it('red', () => {\n            expect(cmyk2rgb(cmykColors[2])).toEqual(rgbColors[2]);\n        });\n\n        it('green', () => {\n            expect(cmyk2rgb(cmykColors[3])).toEqual(rgbColors[3]);\n        });\n\n        it('blue', () => {\n            expect(cmyk2rgb(cmykColors[4])).toEqual(rgbColors[4]);\n        });\n\n        it('yellow', () => {\n            expect(cmyk2rgb(cmykColors[5])).toEqual(rgbColors[5]);\n        });\n\n        it('cyan', () => {\n            expect(cmyk2rgb(cmykColors[6])).toEqual(rgbColors[6]);\n        });\n\n        it('magenta', () => {\n            expect(cmyk2rgb(cmykColors[7])).toEqual(rgbColors[7]);\n        });\n    });\n\n    describe('test unpacked arguments', () => {\n        it('black', () => {\n            expect(cmyk2rgb(...cmykColors[0])).toEqual(rgbColors[0]);\n        });\n\n        it('white', () => {\n            expect(cmyk2rgb(...cmykColors[1])).toEqual(rgbColors[1]);\n        });\n\n        it('red', () => {\n            expect(cmyk2rgb(...cmykColors[2])).toEqual(rgbColors[2]);\n        });\n\n        it('green', () => {\n            expect(cmyk2rgb(...cmykColors[3])).toEqual(rgbColors[3]);\n        });\n\n        it('blue', () => {\n            expect(cmyk2rgb(...cmykColors[4])).toEqual(rgbColors[4]);\n        });\n\n        it('yellow', () => {\n            expect(cmyk2rgb(...cmykColors[5])).toEqual(rgbColors[5]);\n        });\n\n        it('cyan', () => {\n            expect(cmyk2rgb(...cmykColors[6])).toEqual(rgbColors[6]);\n        });\n\n        it('magenta', () => {\n            expect(cmyk2rgb(...cmykColors[7])).toEqual(rgbColors[7]);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/css2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst css2rgb = chroma.input.format.css;\n\ndescribe('Testing CSS2RGB color conversions', () => {\n    const testCases = {\n        // modern css colors\n        'rgb(0 0 0)': [0, 0, 0, 1],\n        'rgb(0% 0% 0%)': [0, 0, 0, 1],\n        'rgb(0% 100% 100%)': [0, 255, 255, 1],\n        'rgb(none none 50%)': [0, 0, 128, 1],\n        'rgb(0% 100% 100% / 0.5)': [0, 255, 255, 0.5],\n        'rgb(0% 100% 100%/0.5)': [0, 255, 255, 0.5],\n        'rgba(0% 100% 100%/0.5)': [0, 255, 255, 0.5],\n\n        // legacy css colors\n        'rgb(0,0,0)': [0, 0, 0, 1],\n        'rgb(100%,100%,100%)': [255, 255, 255, 1],\n        'foobarrgb(100%,100%,100%)': undefined,\n        'rgba(255,0,0,0.5)': [255, 0, 0, 0.5],\n        'RGBA(255, 0, 0  , 0.5)': [255, 0, 0, 0.5],\n        'RGBA (255, 0, 0  , 0.5)': undefined,\n        'rgba(0%,100%,0%,.5)': [0, 255, 0, 0.5],\n        'hsl(240,100%,50%) ': [0, 0, 255, 1],\n        'hsl(240 none 50%) ': [128, 128, 128, 1],\n        'hsl(60,100%,50%)': [255, 255, 0, 1],\n        'hsla(180,100%,50%,1)': [0, 255, 255, 1],\n        'hsla(300,100%,50%,.25)': [255, 0, 255, 0.25],\n        'hsl(240deg 100% 50% / 25%)': [0, 0, 255, 0.25],\n        'rgba(32, 48, 96, 0.5)': [32, 48, 96, 0.5],\n        blanchedalmond: [255, 235, 205, 1],\n        blue: [0, 0, 255, 1],\n        BlueViolet: [138, 43, 226, 1],\n        BROWN: [165, 42, 42, 1],\n        unknownColor: undefined,\n        'lab(47.99% -30.39 -8.98)': [0, 128, 128, 1],\n        'lab(47.99% -30.39 -8.98 / 0.25)': [0, 128, 128, 0.25],\n        'lab(47.99% -24.312% -7.18%)': [0, 128, 128, 1],\n        'lch(93.12 58.8 115.62)': [212, 248, 128, 1], // #d4f880\n        'lch(93.12 58.8 115.62 / .5)': [212, 248, 128, 0.5], // #d4f880\n        'lch(93.12 58.8 115.62 / 0.5)': [212, 248, 128, 0.5], // #d4f880\n        'lch(93.12 58.8 115.62 /50%)': [212, 248, 128, 0.5], // #d4f880\n        'lch(93.12% 58.8 115.62deg)': [212, 248, 128, 1], // #d4f880\n        'lch(93.12% 39.2% 115.62deg)': [212, 248, 128, 1], // #d4f880\n        'lch(93.12% none none)': [235, 235, 235, 1],\n        'oklch(92.83% 0.15 123.12deg)': [212, 248, 130, 1],\n        'oklch(92.83% none none)': [231, 231, 231, 1],\n        transparent: [0, 0, 0, 0]\n    };\n\n    Object.keys(testCases).forEach((name) => {\n        it(`should correctly parse ${name}`, () => {\n            expect(css2rgb(name)).toEqual(testCases[name]);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/hcg2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport hcg2rgb from '../../src/io/hcg/hcg2rgb.js';\n\ndescribe('Testing HCG color conversions', () => {\n    const colors = {\n        black: { in: [0, 0, 0], out: [0, 0, 0, 1] },\n        white: { in: [0, 0, 1], out: [255, 255, 255, 1] },\n        gray: { in: [0, 0, 0.5], out: [127.5, 127.5, 127.5, 1] },\n        red: { in: [0, 1, 0], out: [255, 0, 0, 1] },\n        yellow: { in: [60, 1, 0], out: [255, 255, 0, 1] },\n        green: { in: [120, 1, 0], out: [0, 255, 0, 1] },\n        cyan: { in: [180, 1, 0], out: [0, 255, 255, 1] },\n        blue: { in: [240, 1, 0], out: [0, 0, 255, 1] },\n        magenta: { in: [300, 1, 0], out: [255, 0, 255, 1] },\n        red_again: { in: [360, 1, 0], out: [255, 0, 0, 1] }\n    };\n\n    it('parse simple HCG colors (array)', () => {\n        Object.keys(colors).forEach((key) => {\n            expect(hcg2rgb(colors[key].in)).toEqual(colors[key].out);\n        });\n    });\n\n    it('parse simple HCG colors (arguments)', () => {\n        Object.keys(colors).forEach((key) => {\n            expect(hcg2rgb(...colors[key].in)).toEqual(colors[key].out);\n        });\n    });\n\n    it('parse simple HCG colors (object)', () => {\n        Object.keys(colors).forEach((key) => {\n            const [h, c, g] = colors[key].in;\n            expect(hcg2rgb({ h, c, g })).toEqual(colors[key].out);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/hex2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport hex2rgb from '../../src/io/hex/hex2rgb.js';\n\ndescribe('Testing HEX2RGB color conversions', () => {\n    const testCases = {\n        'parse simple #rrggbb HEX colors': [\n            '#000000',\n            '#ffffff',\n            '#ff0000',\n            '#00ff00',\n            '#0000ff',\n            '#ffff00',\n            '#00ffff',\n            '#ff00ff'\n        ],\n        'parse simple rrggbb HEX colors without #': [\n            '000000',\n            'ffffff',\n            'ff0000',\n            '00ff00',\n            '0000ff',\n            'ffff00',\n            '00ffff',\n            'ff00ff'\n        ],\n        'parse simple short-hand HEX colors': [\n            '#000',\n            '#fff',\n            '#f00',\n            '#0f0',\n            '#00f',\n            '#ff0',\n            '#0ff',\n            '#f0f'\n        ],\n        'parse simple short-hand HEX colors without #': [\n            '000',\n            'fff',\n            'f00',\n            '0f0',\n            '00f',\n            'ff0',\n            '0ff',\n            'f0f'\n        ],\n        'parse different #rrggbbaa HEX colors': [\n            '#00000000',\n            '#ffffff80',\n            '#ff000040',\n            '#00FF00C0',\n            '#FF00FFFF'\n        ],\n        'parse different rrggbbaa HEX colors without #': [\n            '00000000',\n            'ffffff80',\n            'ff000040',\n            '00FF00C0',\n            'FF00FFFF'\n        ],\n        'parse different #rgba HEX colors': [\n            '#0000',\n            '#fff8',\n            '#f004',\n            '#0F0C',\n            '#F0FF'\n        ],\n        'parse different rgba HEX colors without #': [\n            '0000',\n            'fff8',\n            'f004',\n            '0F0C',\n            'F0FF'\n        ]\n    };\n\n    const expectedResults = {\n        'parse simple #rrggbb HEX colors': [\n            [0, 0, 0, 1],\n            [255, 255, 255, 1],\n            [255, 0, 0, 1],\n            [0, 255, 0, 1],\n            [0, 0, 255, 1],\n            [255, 255, 0, 1],\n            [0, 255, 255, 1],\n            [255, 0, 255, 1]\n        ],\n        'parse simple rrggbb HEX colors without #': [\n            [0, 0, 0, 1],\n            [255, 255, 255, 1],\n            [255, 0, 0, 1],\n            [0, 255, 0, 1],\n            [0, 0, 255, 1],\n            [255, 255, 0, 1],\n            [0, 255, 255, 1],\n            [255, 0, 255, 1]\n        ],\n        'parse simple short-hand HEX colors': [\n            [0, 0, 0, 1],\n            [255, 255, 255, 1],\n            [255, 0, 0, 1],\n            [0, 255, 0, 1],\n            [0, 0, 255, 1],\n            [255, 255, 0, 1],\n            [0, 255, 255, 1],\n            [255, 0, 255, 1]\n        ],\n        'parse simple short-hand HEX colors without #': [\n            [0, 0, 0, 1],\n            [255, 255, 255, 1],\n            [255, 0, 0, 1],\n            [0, 255, 0, 1],\n            [0, 0, 255, 1],\n            [255, 255, 0, 1],\n            [0, 255, 255, 1],\n            [255, 0, 255, 1]\n        ],\n        'parse different #rrggbbaa HEX colors': [\n            [0, 0, 0, 0],\n            [255, 255, 255, 0.5],\n            [255, 0, 0, 0.25],\n            [0, 255, 0, 0.75],\n            [255, 0, 255, 1]\n        ],\n        'parse different rrggbbaa HEX colors without #': [\n            [0, 0, 0, 0],\n            [255, 255, 255, 0.5],\n            [255, 0, 0, 0.25],\n            [0, 255, 0, 0.75],\n            [255, 0, 255, 1]\n        ],\n        'parse different #rgba HEX colors': [\n            [0, 0, 0, 0],\n            [255, 255, 255, 0.53],\n            [255, 0, 0, 0.27],\n            [0, 255, 0, 0.8],\n            [255, 0, 255, 1]\n        ],\n        'parse different rgba HEX colors without #': [\n            [0, 0, 0, 0],\n            [255, 255, 255, 0.53],\n            [255, 0, 0, 0.27],\n            [0, 255, 0, 0.8],\n            [255, 0, 255, 1]\n        ]\n    };\n\n    for (const [testName, hexColors] of Object.entries(testCases)) {\n        it(testName, () => {\n            const results = expectedResults[testName];\n            hexColors.forEach((color, index) => {\n                expect(hex2rgb(color)).toEqual(results[index]);\n            });\n        });\n    }\n});\n"
  },
  {
    "path": "test/io/hsi2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport hsi2rgb from '../../src/io/hsi/hsi2rgb.js';\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => Math.round(v * d) / d;\n};\n\nconst testCases = [\n    { name: 'black', hsi: [0, 0, 0], rgb: [0, 0, 0, 1] },\n    { name: 'black2', hsi: [NaN, 0, 0], rgb: [0, 0, 0, 1] },\n    { name: 'white', hsi: [0, 0, 1], rgb: [255, 255, 255, 1] },\n    { name: 'gray', hsi: [0, 0, 0.5], rgb: [127.5, 127.5, 127.5, 1] },\n    { name: 'red', hsi: [0, 1, 1 / 3], rgb: [255, 0, 0, 1] },\n    { name: 'yellow', hsi: [60, 1, 2 / 3], rgb: [255, 255, 0, 1] },\n    { name: 'green', hsi: [120, 1, 1 / 3], rgb: [0, 255, 0, 1] },\n    { name: 'cyan', hsi: [180, 1, 2 / 3], rgb: [0, 255, 255, 1] },\n    { name: 'blue', hsi: [240, 1, 1 / 3], rgb: [0, 0, 255, 1] },\n    { name: 'magenta', hsi: [300, 1, 2 / 3], rgb: [255, 0, 255, 1] },\n    { name: 'red_again', hsi: [360, 1, 1 / 3], rgb: [255, 0, 0, 1] }\n];\n\ndescribe('Testing HSI to RGB color conversions', () => {\n    testCases.forEach(({ name, hsi, rgb }) => {\n        it(`${name}: should convert ${hsi} to ${rgb}`, () => {\n            expect(hsi2rgb(hsi).map(round(4))).toEqual(rgb);\n        });\n    });\n\n    testCases.forEach(({ name, hsi, rgb }) => {\n        it(`${name}: should convert unpacked arguments ${hsi} to ${rgb}`, () => {\n            expect(hsi2rgb(...hsi).map(round(4))).toEqual(rgb);\n        });\n    });\n\n    testCases.forEach(({ name, hsi, rgb }) => {\n        it(`${name}: should convert object ${hsi} to ${rgb}`, () => {\n            const [h, s, i] = hsi;\n            expect(hsi2rgb({ h, s, i }).map(round(4))).toEqual(rgb);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/hsl2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport hsl2rgb from '../../src/io/hsl/hsl2rgb.js';\n\ndescribe('Testing HSL to RGB color conversions', () => {\n    const testCases = [\n        { name: 'black', hsl: [0, 0, 0], rgb: [0, 0, 0, 1] },\n        { name: 'white', hsl: [0, 0, 1], rgb: [255, 255, 255, 1] },\n        { name: 'gray', hsl: [0, 0, 0.5], rgb: [127.5, 127.5, 127.5, 1] },\n        { name: 'red', hsl: [0, 1, 0.5], rgb: [255, 0, 0, 1] },\n        {\n            name: 'yellow',\n            hsl: [60, 1, 0.5],\n            rgb: [254.99999999999994, 255, 0, 1]\n        },\n        { name: 'green', hsl: [120, 1, 0.5], rgb: [0, 255, 0, 1] },\n        {\n            name: 'cyan',\n            hsl: [180, 1, 0.5],\n            rgb: [0, 254.99999999999994, 255, 1]\n        },\n        { name: 'blue', hsl: [240, 1, 0.5], rgb: [0, 0, 255, 1] },\n        {\n            name: 'magenta',\n            hsl: [300, 1, 0.5],\n            rgb: [255, 0, 254.99999999999994, 1]\n        },\n        { name: 'red again', hsl: [360, 1, 0.5], rgb: [255, 0, 0, 1] }\n    ];\n\n    testCases.forEach(({ name, hsl, rgb }) => {\n        describe(name, () => {\n            it(`convert hsl2rgb([${hsl}]) to rgb(${rgb})`, () => {\n                expect(hsl2rgb(hsl)).toEqual(rgb);\n            });\n            it(`convert hsl2rgb(${hsl}) to rgb(${rgb})`, () => {\n                expect(hsl2rgb(...hsl)).toEqual(rgb);\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/hsv2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport hsv2rgb from '../../src/io/hsv/hsv2rgb.js';\n\ndescribe('Testing HSV to RGB color conversions', () => {\n    const testCases = [\n        { hsv: [0, 0, 0], rgb: [0, 0, 0, 1] },\n        { hsv: [0, 0, 1], rgb: [255, 255, 255, 1] },\n        { hsv: [0, 1, 1], rgb: [255, 0, 0, 1] },\n        { hsv: [120, 1, 1], rgb: [0, 255, 0, 1] },\n        { hsv: [240, 1, 1], rgb: [0, 0, 255, 1] },\n        { hsv: [60, 1, 1], rgb: [255, 255, 0, 1] },\n        { hsv: [180, 1, 1], rgb: [0, 255, 255, 1] },\n        { hsv: [300, 1, 1], rgb: [255, 0, 255, 1] }\n    ];\n\n    testCases.forEach(({ hsv, rgb }) => {\n        it(`should convert hsv(${hsv}) to rgb(${rgb})`, () => {\n            expect(hsv2rgb(hsv)).toEqual(rgb);\n        });\n    });\n\n    testCases.forEach(({ hsv, rgb }) => {\n        it(`should convert unpacked arguments hsv(${hsv}) to rgb(${rgb})`, () => {\n            expect(hsv2rgb(...hsv)).toEqual(rgb);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/lab2lch.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport lab2lch from '../../src/io/lch/lab2lch.js';\n\nconst testCases = [\n    { name: 'black', lab: [0, 0, 0], lch: [0, 0, NaN] },\n    { name: 'white', lab: [100, 0, 0], lch: [100, 0, NaN] },\n    { name: 'gray', lab: [53.59, 0, 0], lch: [53.59, 0, NaN] },\n    { name: 'red', lab: [53.24, 80.09, 67.2], lch: [53.24, 104.55, 40] },\n    {\n        name: 'yellow',\n        lab: [97.14, -21.55, 94.48],\n        lch: [97.14, 96.91, 102.85]\n    },\n    {\n        name: 'green',\n        lab: [87.73, -86.18, 83.18],\n        lch: [87.73, 119.77, 136.01]\n    },\n    { name: 'cyan', lab: [91.11, -48.09, -14.13], lch: [91.11, 50.12, 196.37] },\n    { name: 'blue', lab: [32.3, 79.19, -107.86], lch: [32.3, 133.81, 306.29] },\n    {\n        name: 'magenta',\n        lab: [60.32, 98.23, -60.82],\n        lch: [60.32, 115.53, 328.24]\n    }\n];\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => Math.round(v * d) / d;\n};\n\nconst rnd = round(2);\n\ndescribe('Testing lab2lch color conversions', () => {\n    testCases.forEach(({ name, lab, lch }) => {\n        it(`${name}: should convert ${lab} to ${lch}`, () => {\n            expect(lab2lch(lab).map(rnd)).toEqual(lch);\n        });\n    });\n\n    testCases.forEach(({ name, lab, lch }) => {\n        it(`${name}: should convert unpacked arguments ${lab} to ${lch}`, () => {\n            expect(lab2lch(...lab).map(rnd)).toEqual(lch);\n        });\n    });\n\n    testCases.forEach(({ name, lab, lch }) => {\n        it(`${name}: should convert object ${lab} to ${lch}`, () => {\n            const [l, a, b] = lab;\n            expect(lab2lch({ l, a, b }).map(rnd)).toEqual(lch);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/lab2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\nimport lab2rgb from '../../src/io/lab/lab2rgb.js';\nimport limit from '../../src/utils/limit.js';\n\nconst round = (v) => limit(Math.round(v), +0, 255);\n\ndescribe('Testing LAB to RGB color conversions', () => {\n    const testCases = [\n        { name: 'black', lab: [0, 0, 0], rgb: [0, 0, 0, 1] },\n        { name: 'white', lab: [100, 0, 0], rgb: [255, 255, 255, 1] },\n        { name: 'gray', lab: [53.59, 0, 0], rgb: [128, 128, 128, 1] },\n        { name: 'red', lab: [53.24, 80.09, 67.2], rgb: [255, 0, 0, 1] },\n        { name: 'yellow', lab: [97.14, -21.55, 94.48], rgb: [255, 255, 0, 1] },\n        { name: 'green', lab: [87.73, -86.18, 83.18], rgb: [0, 255, 0, 1] },\n        { name: 'cyan', lab: [91.11, -48.09, -14.13], rgb: [0, 255, 255, 1] },\n        { name: 'blue', lab: [32.3, 79.19, -107.86], rgb: [0, 0, 255, 1] },\n        { name: 'magenta', lab: [60.32, 98.23, -60.82], rgb: [255, 0, 255, 1] }\n    ];\n\n    testCases.forEach(({ name, lab, rgb }) => {\n        it(`${name}: should convert ${lab} to ${rgb}`, () => {\n            expect(lab2rgb(lab).map(round)).toStrictEqual(rgb);\n        });\n    });\n\n    testCases.forEach(({ name, lab, rgb }) => {\n        it(`${name}: should convert unpacked arguments ${lab} to ${rgb}`, () => {\n            expect(lab2rgb(...lab).map(round)).toStrictEqual(rgb);\n        });\n    });\n});\n\ndescribe('Test switching of Lab illuminant', () => {\n    it('defaults to D65', () => {\n        expect(chroma.lab(97.14, -21.55, 94.48).rgb()).toStrictEqual([\n            255, 255, 0\n        ]);\n    });\n\n    it('change to D50', () => {\n        expect(chroma.lab(97.14, -21.55, 94.48).rgb()).toStrictEqual([\n            255, 255, 0\n        ]);\n\n        chroma.setLabWhitePoint('d50');\n        // not D50 Lab yellow\n        expect(chroma.lab(97.14, -21.55, 94.48).rgb()).toStrictEqual([\n            243, 255, 0\n        ]);\n        // actual D50 Lab yellow\n        expect(chroma.lab(97.61, -15.75, 93.39).rgb()).toStrictEqual([\n            255, 255, 0\n        ]);\n\n        // change back to not mess up other tests\n        chroma.setLabWhitePoint('d65');\n        expect(chroma.lab(97.14, -21.55, 94.48).rgb()).toStrictEqual([\n            255, 255, 0\n        ]);\n    });\n});\n"
  },
  {
    "path": "test/io/lch2lab.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport lch2lab from '../../src/io/lch/lch2lab.js';\n\nconst testCases = [\n    { name: 'black', lab: [0, 0, 0], lch: [0, 0, NaN] },\n    { name: 'white', lab: [100, 0, 0], lch: [100, 0, NaN] },\n    { name: 'gray', lab: [53.59, 0, 0], lch: [53.59, 0, NaN] },\n    { name: 'red', lab: [53.24, 80.09, 67.2], lch: [53.24, 104.55, 40] },\n    {\n        name: 'yellow',\n        lab: [97.14, -21.55, 94.48],\n        lch: [97.14, 96.91, 102.85]\n    },\n    {\n        name: 'green',\n        lab: [87.73, -86.17, 83.18],\n        lch: [87.73, 119.77, 136.01]\n    },\n    { name: 'cyan', lab: [91.11, -48.09, -14.13], lch: [91.11, 50.12, 196.37] },\n    { name: 'blue', lab: [32.3, 79.2, -107.86], lch: [32.3, 133.81, 306.29] },\n    {\n        name: 'magenta',\n        lab: [60.32, 98.23, -60.81],\n        lch: [60.32, 115.53, 328.24]\n    }\n];\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => Math.round(v * d) / d;\n};\n\nconst rnd = round(2);\n\ndescribe('Testing lch2lab color conversions', () => {\n    testCases.forEach(({ name, lab, lch }) => {\n        it(`${name}: should convert array ${lch} to ${lab}`, () => {\n            expect(lch2lab(lch).map(rnd)).toEqual(lab);\n        });\n    });\n\n    testCases.forEach(({ name, lab, lch }) => {\n        it(`${name}: should convert unpacked arguments ${lch} to ${lab}`, () => {\n            expect(lch2lab(...lch).map(rnd)).toEqual(lab);\n        });\n    });\n\n    testCases.forEach(({ name, lab, lch }) => {\n        it(`${name}: should convert object ${lch} to ${lab}`, () => {\n            const [l, c, h] = lch;\n            expect(lch2lab({ l, c, h }).map(rnd)).toEqual(lab);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/lch2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport limit from '../../src/utils/limit.js';\nimport lch2rgb from '../../src/io/lch/lch2rgb.js';\n\nconst testCases = [\n    { name: 'black', in: [0, 0, NaN], out: [0, 0, 0, 1] },\n    { name: 'white', in: [100, 0, NaN], out: [255, 255, 255, 1] },\n    { name: 'gray', in: [53.59, 0, NaN], out: [128, 128, 128, 1] },\n    { name: 'red', in: [53.24, 104.55, 40], out: [255, 0, 0, 1] },\n    { name: 'yellow', in: [97.14, 96.91, 102.85], out: [255, 255, 0, 1] },\n    { name: 'green', in: [87.73, 119.78, 136.02], out: [0, 255, 0, 1] },\n    { name: 'cyan', in: [91.11, 50.12, 196.38], out: [0, 255, 255, 1] },\n    { name: 'blue', in: [32.3, 133.81, 306.28], out: [0, 0, 255, 1] },\n    { name: 'magenta', in: [60.32, 115.54, 328.23], out: [255, 0, 255, 1] }\n];\n\nconst round = (v) => limit(Math.round(v), 0, 255);\n\ndescribe('Testing LCH to RGB color conversions', () => {\n    testCases.forEach(({ name, in: lchIn, out: rgbOut }) => {\n        it(`${name}: should convert ${lchIn} to ${rgbOut}`, () => {\n            expect(lch2rgb(lchIn).map(round)).toEqual(rgbOut);\n        });\n    });\n\n    testCases.forEach(({ name, in: lchIn, out: rgbOut }) => {\n        it(`${name}: should convert unpacked arguments ${lchIn} to ${rgbOut}`, () => {\n            expect(lch2rgb(...lchIn).map(round)).toEqual(rgbOut);\n        });\n    });\n\n    testCases.forEach(({ name, in: lchIn, out: rgbOut }) => {\n        it(`${name}: should convert object ${lchIn} to ${rgbOut}`, () => {\n            const [l, c, h] = lchIn;\n            expect(lch2rgb({ l, c, h }).map(round)).toEqual(rgbOut);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/num2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport num2rgb from '../../src/io/num/num2rgb.js';\n\ndescribe('Testing num2rgb color conversions', () => {\n    const testCases = {\n        black: { in: 0x000000, out: [0, 0, 0, 1] },\n        white: { in: 0xffffff, out: [255, 255, 255, 1] },\n        red: { in: 0xff0000, out: [255, 0, 0, 1] },\n        green: { in: 0x00ff00, out: [0, 255, 0, 1] },\n        blue: { in: 0x0000ff, out: [0, 0, 255, 1] },\n        yellow: { in: 0xffff00, out: [255, 255, 0, 1] },\n        cyan: { in: 0x00ffff, out: [0, 255, 255, 1] },\n        magenta: { in: 0xff00ff, out: [255, 0, 255, 1] }\n    };\n\n    Object.keys(testCases).forEach((key) => {\n        const { in: input, out: expected } = testCases[key];\n\n        it(`should convert numeric HEX to RGB for ${key}`, () => {\n            const result = num2rgb(input);\n            expect(result).toEqual(expected);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/oklab2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport limit from '../../src/utils/limit.js';\nimport oklab2rgb from '../../src/io/oklab/oklab2rgb.js';\n\nconst round = (v) => limit(Math.round(v), 0, 255);\n\ndescribe('Testing OKLab to RGB color conversions', () => {\n    const testCases = {\n        black: { in: [0.0, 0.0, 0.0], out: [0, 0, 0] },\n        white: { in: [1.0, 0.0, 0.0], out: [255, 255, 255] },\n        gray: { in: [0.59987, 0.0, 0.0], out: [128, 128, 128] },\n        red: { in: [0.62796, 0.22486, 0.12585], out: [255, 0, 0] },\n        yellow: { in: [0.96798, -0.07137, 0.19857], out: [255, 255, 0] },\n        green: { in: [0.51975, -0.1403, 0.10768], out: [0, 128, 0] },\n        cyan: { in: [0.9054, -0.14944, -0.0394], out: [0, 255, 255] },\n        blue: { in: [0.45201, -0.03246, -0.31153], out: [0, 0, 255] },\n        magenta: { in: [0.70167, 0.27457, -0.16916], out: [255, 0, 255] }\n    };\n\n    Object.keys(testCases).forEach((key) => {\n        const { in: input, out: expected } = testCases[key];\n\n        it(`should convert OKLab to RGB for ${key} with array input`, () => {\n            const result = oklab2rgb(input).map(round);\n            expect(result).toEqual(expected);\n        });\n\n        it(`should convert OKLab to RGB for ${key} with arguments`, () => {\n            const result = oklab2rgb(...input).map(round);\n            expect(result).toEqual(expected);\n        });\n\n        it(`should convert OKLab to RGB for ${key} with object input`, () => {\n            const [l, a, b] = input;\n            const result = oklab2rgb({ l, a, b }).map(round);\n            expect(result).toEqual(expected);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/oklch2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport limit from '../../src/utils/limit.js';\nimport oklch2rgb from '../../src/io/oklch/oklch2rgb.js';\n\nconst round = (v) => limit(Math.round(v), 0, 255);\n\ndescribe('Testing LCH conversions', () => {\n    const testCases = {\n        black: { in: [0.0, 0.0, NaN], out: [0, 0, 0] },\n        white: { in: [1.0, 0.0, NaN], out: [255, 255, 255] },\n        gray: { in: [0.59987, 0.0, NaN], out: [128, 128, 128] },\n        red: {\n            in: [0.62796, 0.25768, 29.233885192342633],\n            out: [255, 0, 0]\n        },\n        yellow: {\n            in: [0.96798, 0.21101, 109.76923207652125],\n            out: [255, 255, 0]\n        },\n        green: {\n            in: [0.51975, 0.17686, 142.49533888780996],\n            out: [0, 128, 0]\n        },\n        cyan: {\n            in: [0.9054, 0.15455, 194.76894793196382],\n            out: [0, 255, 255]\n        },\n        blue: { in: [0.45201, 0.31321, 264.052020638055], out: [0, 0, 255] },\n        magenta: {\n            in: [0.70167, 0.32249, 328.36341792345144],\n            out: [255, 0, 255]\n        }\n    };\n\n    Object.keys(testCases).forEach((key) => {\n        const { in: input, out: expected } = testCases[key];\n\n        it(`should convert oklch to rgb for ${key}`, () => {\n            const result = oklch2rgb(input).map(round);\n            expect(result).toEqual(expected);\n        });\n\n        it(`should convert oklch to rgb for ${key} using arguments`, () => {\n            const result = oklch2rgb(...input).map(round);\n            expect(result).toEqual(expected);\n        });\n\n        it(`should convert oklch to rgb for ${key} using object`, () => {\n            const [l, c, h] = input;\n            const result = oklch2rgb({ l, c, h }).map(round);\n            expect(result).toEqual(expected);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2cmyk.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2cmyk from '../../src/io/cmyk/rgb2cmyk.js';\n\nconst tests = {\n    black: { cmyk: [0, 0, 0, 1], rgb: [0, 0, 0, 1] },\n    white: { cmyk: [0, 0, 0, 0], rgb: [255, 255, 255, 1] },\n    red: { cmyk: [0, 1, 1, 0], rgb: [255, 0, 0, 1] },\n    green: { cmyk: [1, 0, 1, 0], rgb: [0, 255, 0, 1] },\n    blue: { cmyk: [1, 1, 0, 0], rgb: [0, 0, 255, 1] },\n    yellow: { cmyk: [0, 0, 1, 0], rgb: [255, 255, 0, 1] },\n    cyan: { cmyk: [1, 0, 0, 0], rgb: [0, 255, 255, 1] },\n    magenta: { cmyk: [0, 1, 0, 0], rgb: [255, 0, 255, 1] }\n};\n\ndescribe('Testing CMYK color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { cmyk, rgb } = tests[key];\n\n        it(`rgb2cmyk ${key} converts RGB to CMYK`, () => {\n            expect(rgb2cmyk(rgb)).toEqual(cmyk);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2css.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2css from '../../src/io/css/rgb2css.js';\n\nconst tests = {\n    black: { rgb: [0, 0, 0], css: 'rgb(0 0 0)' },\n    red: { rgb: [255, 0, 0], css: 'rgb(255 0 0)' },\n    auto_rgba: { rgb: [255, 0, 0, 0.25], css: 'rgb(255 0 0 / 0.25)' },\n    force_rgba: { rgb: [255, 0, 0], mode: 'rgba', css: 'rgb(255 0 0 / 1)' },\n    hsl: { rgb: [255, 0, 0], mode: 'hsl', css: 'hsl(0deg 100% 50%)' },\n    auto_hsla: {\n        rgb: [255, 0, 0, 0.5],\n        mode: 'hsl',\n        css: 'hsl(0deg 100% 50% / 0.5)'\n    },\n    force_hsla: {\n        rgb: [255, 255, 0, 0.75],\n        mode: 'hsl',\n        css: 'hsl(60deg 100% 50% / 0.75)'\n    },\n    lab: { rgb: [255, 0, 0], mode: 'lab', css: 'lab(54.29% 80.81 69.89)' },\n    laba: {\n        rgb: [255, 0, 0, 0.5],\n        mode: 'lab',\n        css: 'lab(54.29% 80.81 69.89 / 0.5)'\n    },\n    lch: { rgb: [255, 0, 0], mode: 'lch', css: 'lch(54.29% 106.84 40.85deg)' },\n    lcha: {\n        rgb: [255, 0, 0, 0.25],\n        mode: 'lch',\n        css: 'lch(54.29% 106.84 40.85deg / 0.25)'\n    },\n    oklab: {\n        rgb: [212, 248, 128],\n        mode: 'oklab',\n        css: 'oklab(92.83% -0.08 0.13)'\n    },\n    oklaba: {\n        rgb: [212, 248, 128, 0.4],\n        mode: 'oklab',\n        css: 'oklab(92.83% -0.08 0.13 / 0.4)'\n    },\n    oklch: {\n        rgb: [212, 248, 128],\n        mode: 'oklch',\n        css: 'oklch(92.83% 0.15 123.12deg)'\n    },\n    oklcha: {\n        rgb: [212, 248, 128, 0.6],\n        mode: 'oklch',\n        css: 'oklch(92.83% 0.15 123.12deg / 0.6)'\n    },\n    white_rgb: { rgb: [255, 255, 255], css: 'rgb(255 255 255)' },\n    white_lab: { rgb: [255, 255, 255], mode: 'lab', css: 'lab(100% 0 0)' },\n    white_lch: { rgb: [255, 255, 255], mode: 'lch', css: 'lch(100% 0 none)' },\n    gray_lch: { rgb: [120, 120, 120], mode: 'lch', css: 'lch(50.43% 0 none)' },\n    black_lch: { rgb: [0, 0, 0], mode: 'lch', css: 'lch(0% 0 none)' },\n    white_oklab: {\n        rgb: [255, 255, 255],\n        mode: 'oklab',\n        css: 'oklab(100% 0 0)'\n    },\n    white_oklch: {\n        rgb: [255, 255, 255],\n        mode: 'oklch',\n        css: 'oklch(100% 0 none)'\n    },\n    gray_oklch: {\n        rgb: [120, 120, 120],\n        mode: 'oklch',\n        css: 'oklch(57.27% 0 none)'\n    },\n    black_oklch: { rgb: [0, 0, 0], mode: 'oklch', css: 'oklch(0% 0 none)' }\n};\n\ndescribe('Testing rgb2css color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { rgb, mode, css } = tests[key];\n\n        it(`rgb2css ${key} converts array`, () => {\n            expect(rgb2css(rgb, mode || 'rgb')).toBe(css);\n        });\n\n        it(`rgb2css ${key} converts object`, () => {\n            const [r, g, b, a] = rgb;\n            const obj = {\n                r,\n                g,\n                b,\n                ...(a !== undefined ? { a } : {})\n            };\n            expect(rgb2css(obj, mode)).toBe(css);\n        });\n\n        it(`rgb2css ${key} converts arguments`, () => {\n            if (mode !== 'rgb') return;\n            expect(rgb2css(...rgb)).toBe(css);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2hex.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2hex from '../../src/io/hex/rgb2hex.js';\n\nconst tests = {\n    black: { rgb: [0, 0, 0, 1], mode: 'auto', hex: '#000000' },\n    white: { rgb: [255, 255, 255, 1], mode: 'auto', hex: '#ffffff' },\n    gray: { rgb: [128, 128, 128, 1], mode: 'auto', hex: '#808080' },\n    red: { rgb: [255, 0, 0, 1], mode: 'auto', hex: '#ff0000' },\n    yellow: { rgb: [0, 255, 0, 1], mode: 'auto', hex: '#00ff00' },\n    green: { rgb: [0, 0, 255, 1], mode: 'auto', hex: '#0000ff' },\n    cyan: { rgb: [255, 255, 0, 1], mode: 'auto', hex: '#ffff00' },\n    blue: { rgb: [0, 255, 255, 1], mode: 'auto', hex: '#00ffff' },\n    magenta: { rgb: [255, 0, 255], mode: 'rgb', hex: '#ff00ff' },\n    auto_rgba: { rgb: [255, 0, 255, 0.5], mode: 'auto', hex: '#ff00ff80' },\n    force_rgba: { rgb: [255, 0, 255], mode: 'rgba', hex: '#ff00ffff' },\n    force_rgb: { rgb: [255, 0, 255, 0.5], mode: 'rgb', hex: '#ff00ff' }\n};\n\ndescribe('Test rgb2hex color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { rgb, mode, hex } = tests[key];\n\n        it(`rgb2hex ${key} converts array`, () => {\n            expect(rgb2hex(rgb, mode || 'auto')).toBe(hex);\n        });\n\n        it(`rgb2hex ${key} converts object`, () => {\n            const [r, g, b, a] = rgb;\n            const obj = {\n                r,\n                g,\n                b,\n                ...(a !== undefined ? { a } : {})\n            };\n            expect(rgb2hex(obj, mode || 'auto')).toBe(hex);\n        });\n\n        it(`rgb2hex ${key} converts arguments`, () => {\n            if (mode !== 'auto') return;\n            expect(rgb2hex(...rgb)).toBe(hex);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2hsi.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2hsi from '../../src/io/hsi/rgb2hsi.js';\n\nconst tests = {\n    black2: { hsi: [NaN, 0, 0], rgb: [0, 0, 0, 1] },\n    white: { hsi: [NaN, 0, 1], rgb: [255, 255, 255, 1] },\n    gray: { hsi: [NaN, 0, 0.5], rgb: [127.5, 127.5, 127.5, 1] },\n    red: { hsi: [0, 1, 1 / 3], rgb: [255, 0, 0, 1] },\n    yellow: { hsi: [60, 1, 2 / 3], rgb: [255, 255, 0, 1] },\n    green: { hsi: [120, 1, 1 / 3], rgb: [0, 255, 0, 1] },\n    cyan: { hsi: [180, 1, 2 / 3], rgb: [0, 255, 255, 1] },\n    blue: { hsi: [240, 1, 1 / 3], rgb: [0, 0, 255, 1] },\n    magenta: { hsi: [300, 1, 2 / 3], rgb: [255, 0, 255, 1] }\n};\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => Math.round(v * d) / d;\n};\nconst rnd = round(5);\n\ndescribe('Test rgb2hsi color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { hsi, rgb } = tests[key];\n\n        it(`rgb2hsi ${key} converts array`, () => {\n            expect(rgb2hsi(rgb).map(rnd)).toEqual(hsi.map(rnd));\n        });\n\n        it(`rgb2hsi ${key} converts object`, () => {\n            const [r, g, b] = rgb;\n            expect(rgb2hsi({ r, g, b }).map(rnd)).toEqual(hsi.map(rnd));\n        });\n\n        it(`rgb2hsi ${key} converts arguments`, () => {\n            // Assuming `mode` is always 'auto' based on original code\n            expect(rgb2hsi(...rgb).map(rnd)).toEqual(hsi.map(rnd));\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2hsv.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2hsv from '../../src/io/hsv/rgb2hsv.js';\n\nconst tests = {\n    black: { hsv: [NaN, 0, 0], rgb: [0, 0, 0, 1] },\n    white: { hsv: [NaN, 0, 1], rgb: [255, 255, 255, 1] },\n    gray: { hsv: [NaN, 0, 0.5], rgb: [127.5, 127.5, 127.5, 1] },\n    red: { hsv: [0, 1, 1], rgb: [255, 0, 0, 1] },\n    yellow: { hsv: [60, 1, 1], rgb: [255, 255, 0, 1] },\n    green: { hsv: [120, 1, 1], rgb: [0, 255, 0, 1] },\n    cyan: { hsv: [180, 1, 1], rgb: [0, 255, 255, 1] },\n    blue: { hsv: [240, 1, 1], rgb: [0, 0, 255, 1] },\n    magenta: { hsv: [300, 1, 1], rgb: [255, 0, 255, 1] }\n};\n\ndescribe('Test rgb2hsv color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { hsv, rgb } = tests[key];\n\n        it(`rgb2hsv ${key} converts array`, () => {\n            expect(rgb2hsv(rgb)).toEqual(hsv);\n        });\n\n        it(`rgb2hsv ${key} converts object`, () => {\n            const [r, g, b] = rgb;\n            expect(rgb2hsv({ r, g, b })).toEqual(hsv);\n        });\n\n        it(`rgb2hsv ${key} converts arguments`, () => {\n            expect(rgb2hsv(...rgb)).toEqual(hsv);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2lab.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2lab from '../../src/io/lab/rgb2lab.js';\nimport chroma from 'chroma-js';\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => {\n        if (v > -1e-3 && v < 1e-3) v = 0;\n        return Math.round(v * d) / d;\n    };\n};\nconst rnd = round(2);\n\nconst tests = {\n    black: { lab: [0, 0, 0], rgb: [0, 0, 0] },\n    white: { lab: [100, 0, 0], rgb: [255, 255, 255] },\n    gray: { lab: [53.59, 0, 0], rgb: [128, 128, 128] },\n    red: { lab: [53.24, 80.09, 67.2], rgb: [255, 0, 0] },\n    yellow: { lab: [97.14, -21.55, 94.48], rgb: [255, 255, 0] },\n    green: { lab: [87.73, -86.18, 83.18], rgb: [0, 255, 0] },\n    cyan: { lab: [91.11, -48.09, -14.13], rgb: [0, 255, 255] },\n    blue: { lab: [32.3, 79.19, -107.86], rgb: [0, 0, 255] },\n    magenta: { lab: [60.32, 98.23, -60.82], rgb: [255, 0, 255] }\n};\n\ndescribe('Test rgb2lab color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { lab, rgb } = tests[key];\n\n        it(`rgb2lab ${key} converts array`, () => {\n            expect(rgb2lab(rgb).map(rnd)).toEqual(lab);\n        });\n\n        it(`rgb2lab ${key} converts object`, () => {\n            const [r, g, b] = rgb;\n            expect(rgb2lab({ r, g, b }).map(rnd)).toEqual(lab);\n        });\n\n        it(`rgb2lab ${key} converts arguments`, () => {\n            expect(rgb2lab(...rgb).map(rnd)).toEqual(lab);\n        });\n    });\n});\n\ndescribe('Test switching of Lab illuminant', () => {\n    it('defaults to D65', () => {\n        expect(chroma('yellow').lab().map(rnd)).toStrictEqual([\n            97.14, -21.55, 94.48\n        ]);\n    });\n\n    it('change to D50', () => {\n        expect(chroma('yellow').lab().map(rnd)).toEqual([97.14, -21.55, 94.48]);\n        chroma.setLabWhitePoint('d50');\n        expect(chroma('yellow').lab().map(rnd)).toEqual([97.61, -15.75, 93.39]);\n        // change back to not mess up other tests\n        chroma.setLabWhitePoint('d65');\n        expect(chroma('yellow').lab().map(rnd)).toEqual([97.14, -21.55, 94.48]);\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2lch.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2lch from '../../src/io/lch/rgb2lch.js';\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => {\n        if (v > -1e-3 && v < 1e-3) v = 0;\n        return Math.round(v * d) / d;\n    };\n};\nconst rnd = round(2);\n\nconst tests = {\n    black: { lch: [0, 0, NaN], rgb: [0, 0, 0] },\n    white: { lch: [100, 0, NaN], rgb: [255, 255, 255] },\n    gray: { lch: [53.59, 0, NaN], rgb: [128, 128, 128] },\n    red: { lch: [53.24, 104.55, 40], rgb: [255, 0, 0] },\n    yellow: { lch: [97.14, 96.91, 102.85], rgb: [255, 255, 0] },\n    green: { lch: [87.73, 119.78, 136.02], rgb: [0, 255, 0] },\n    cyan: { lch: [91.11, 50.12, 196.38], rgb: [0, 255, 255] },\n    blue: { lch: [32.3, 133.81, 306.28], rgb: [0, 0, 255] },\n    magenta: { lch: [60.32, 115.54, 328.23], rgb: [255, 0, 255] }\n};\n\ndescribe('Test rgb2lch color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const { lch, rgb } = tests[key];\n\n        it(`rgb2lch ${key} converts array`, () => {\n            expect(rgb2lch(rgb).map(rnd)).toEqual(lch);\n        });\n\n        it(`rgb2lch ${key} converts object`, () => {\n            const [r, g, b] = rgb;\n            expect(rgb2lch({ r, g, b }).map(rnd)).toEqual(lch);\n        });\n\n        it(`rgb2lch ${key} converts arguments`, () => {\n            expect(rgb2lch(...rgb).map(rnd)).toEqual(lch);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2oklab.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2oklab from '../../src/io/oklab/rgb2oklab.js';\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => {\n        if (v > -1e-3 && v < 1e-3) v = 0;\n        return Math.round(v * d) / d;\n    };\n};\nconst rnd = round(3);\n\ndescribe('Testing OKLab color conversions', () => {\n    const colors = {\n        black: { out: [0.0, 0.0, 0.0], in: [0, 0, 0, 1] },\n        white: { out: [1.0, 0.0, 0.0], in: [255, 255, 255, 1] },\n        gray: { out: [0.6, 0.0, 0.0], in: [128, 128, 128, 1] },\n        red: { out: [0.628, 0.225, 0.126], in: [255, 0, 0, 1] },\n        yellow: { out: [0.968, -0.071, 0.199], in: [255, 255, 0, 1] },\n        green: { out: [0.52, -0.14, 0.108], in: [0, 128, 0, 1] },\n        cyan: { out: [0.905, -0.149, -0.039], in: [0, 255, 255, 1] },\n        blue: { out: [0.452, -0.032, -0.312], in: [0, 0, 255, 1] },\n        magenta: { out: [0.702, 0.275, -0.169], in: [255, 0, 255, 1] }\n    };\n\n    Object.keys(colors).forEach((key) => {\n        const { out, in: rgb } = colors[key];\n\n        it(`rgb2oklab ${key} converts array`, () => {\n            expect(rgb2oklab(rgb).map(rnd)).toEqual(out);\n        });\n\n        it(`rgb2oklab ${key} converts arguments`, () => {\n            expect(rgb2oklab(...rgb).map(rnd)).toEqual(out);\n        });\n\n        it(`rgb2oklab ${key} converts object`, () => {\n            const [r, g, b] = rgb;\n            expect(rgb2oklab({ r, g, b }).map(rnd)).toEqual(out);\n        });\n    });\n});\n"
  },
  {
    "path": "test/io/rgb2oklch.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport rgb2oklch from '../../src/io/oklch/rgb2oklch.js';\n\nconst tests = {\n    black: { oklch: [0.0, 0.0, NaN], rgb: [0, 0, 0, 1] },\n    white: { oklch: [1.0, 0.0, NaN], rgb: [255, 255, 255, 1] },\n    gray: { oklch: [0.6, 0.0, NaN], rgb: [128, 128, 128, 1] },\n    red: { oklch: [0.628, 0.258, 29.234], rgb: [255, 0, 0, 1] },\n    yellow: { oklch: [0.968, 0.211, 109.763], rgb: [255, 255, 0, 1] },\n    green: { oklch: [0.52, 0.177, 142.495], rgb: [0, 128, 0, 1] },\n    cyan: { oklch: [0.905, 0.155, 194.757], rgb: [0, 255, 255, 1] },\n    blue: { oklch: [0.452, 0.313, 264.052], rgb: [0, 0, 255, 1] },\n    magenta: { oklch: [0.702, 0.322, 328.373], rgb: [255, 0, 255, 1] }\n};\n\nconst round = (digits) => {\n    const d = Math.pow(10, digits);\n    return (v) => {\n        if (v > -1e-3 && v < 1e-3) v = 0;\n        return Math.round(v * d) / d;\n    };\n};\nconst rnd = round(3);\n\ndescribe('Test rgb2oklch color conversions', () => {\n    Object.keys(tests).forEach((key) => {\n        const test = tests[key];\n\n        describe(`rgb2oklch ${key}`, () => {\n            it('converts array', () => {\n                expect(rgb2oklch(test.rgb).map(rnd)).toEqual(test.oklch);\n            });\n\n            it('converts object', () => {\n                const [r, g, b] = test.rgb;\n                expect(rgb2oklch({ r, g, b }).map(rnd)).toEqual(test.oklch);\n            });\n\n            it('converts arguments', () => {\n                expect(rgb2oklch.apply(null, test.rgb).map(rnd)).toEqual(\n                    test.oklch\n                );\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/lch.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Some tests for chroma.lch()', () => {\n    describe('lch grayscale', () => {\n        const grays = [\n            { l: 0, hex: '#000000' },\n            { l: 25, hex: '#3b3b3b' },\n            { l: 50, hex: '#777777' },\n            { l: 75, hex: '#b9b9b9' },\n            { l: 100, hex: '#ffffff' }\n        ];\n\n        for (const { l, hex } of grays) {\n            it(`l = ${l}`, () => {\n                expect(chroma.lch(l, 0, 0).hex()).toEqual(hex);\n            });\n        }\n    });\n\n    describe('lch hues', () => {\n        const hues = [\n            { h: 0, hex: '#dc2c7a' },\n            { h: 60, hex: '#bd5c00' },\n            { h: 120, hex: '#548400' },\n            { h: 180, hex: '#009175' },\n            { h: 240, hex: '#008cde' },\n            { h: 300, hex: '#6f67df' },\n            { h: 360, hex: '#dc2c7a' }\n        ];\n\n        for (const { h, hex } of hues) {\n            it(`h = ${h}`, () => {\n                expect(chroma.lch(50, 70, h).hex()).toEqual(hex);\n            });\n        }\n    });\n\n    describe('lch clipping', () => {\n        it('20 not clipped', () => {\n            expect(chroma.lch(90, 20, 80).clipped()).toEqual(false);\n        });\n\n        it('40 clipped', () => {\n            expect(chroma.lch(90, 40, 80).clipped()).toEqual(true);\n        });\n\n        it('60 clipped', () => {\n            expect(chroma.lch(90, 60, 80).clipped()).toEqual(true);\n        });\n\n        it('80 clipped', () => {\n            expect(chroma.lch(90, 80, 80).clipped()).toEqual(true);\n        });\n\n        it('100 clipped', () => {\n            expect(chroma.lch(90, 100, 80).clipped()).toEqual(true);\n        });\n    });\n});\n"
  },
  {
    "path": "test/limits.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport { limits } from '../src/utils/analyze.js';\n\ndescribe('Some tests for chroma.limits()', () => {\n    const testCases = [\n        {\n            name: 'simple continuous domain',\n            input: [[1, 2, 3, 4, 5], 'continuous'],\n            expected: [1, 5]\n        },\n        {\n            name: 'continuous domain, negative values',\n            input: [[1, -2, -3, 4, 5], 'continuous'],\n            expected: [-3, 5]\n        },\n        {\n            name: 'continuous domain, null values',\n            input: [[1, undefined, null, 4, 5], 'continuous'],\n            expected: [1, 5]\n        },\n        {\n            name: 'equidistant domain',\n            input: [[0, 10], 'equidistant', 5],\n            expected: [0, 2, 4, 6, 8, 10]\n        },\n        {\n            name: 'equidistant domain, NaN values',\n            input: [[0, 9, 3, 6, 3, 5, undefined, Number.NaN, 10], 'equidistant', 5],\n            expected: [0, 2, 4, 6, 8, 10]\n        },\n        {\n            name: 'logarithmic domain',\n            input: [[1, 10000], 'log', 4],\n            expected: [1, 10, 100, 1000, 10000]\n        },\n        {\n            name: 'quantiles domain',\n            input: [[1, 2, 3, 4, 5, 10, 20, 100], 'quantiles', 2],\n            expected: [1, 4.5, 100]\n        },\n        {\n            name: 'quantiles not enough values',\n            input: [[0, 1], 'quantiles', 5],\n            expected: [0, 0.2, 0.4, 0.6, 0.8, 1]\n        }\n    ];\n\n    testCases.forEach(({ name, input, expected }) => {\n        it(`${name}`, () => {\n            expect(limits(...input)).toEqual(expected);\n        });\n    });\n\n    it('logarithmic domain - non-positive values', () => {\n        expect(() => limits([-1, 10000], 'log', 4)).toThrow('Logarithmic scales are only possible for values > 0');\n    });\n});\n"
  },
  {
    "path": "test/luminance.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst rnd = function (f, d) {\n    d = Math.pow(10, d);\n    return Math.round(f * d) / d;\n};\n\ndescribe('Testing relative luminance', () => {\n    const testCases = [\n        {\n            name: 'black',\n            topic: chroma('black'),\n            expectedLuminance: 0\n        },\n        {\n            name: 'white',\n            topic: chroma('white'),\n            expectedLuminance: 1\n        },\n        {\n            name: 'red',\n            topic: chroma('red'),\n            precision: 4,\n            expectedLuminance: 0.2126\n        },\n        {\n            name: 'yellow brighter than blue',\n            topic: [chroma('yellow').luminance(), chroma('blue').luminance()],\n            comparison: 'greater'\n        },\n        {\n            name: 'green darker than red',\n            topic: [chroma('green').luminance(), chroma('red').luminance()],\n            comparison: 'less'\n        },\n        {\n            name: 'set red luminance to 0.4',\n            topic: chroma('red').luminance(0.4),\n            expectedLuminance: 0.4,\n            precision: 3\n        },\n        {\n            name: 'set red luminance to 0',\n            topic: chroma('red').luminance(0),\n            expectedLuminance: 0,\n            precision: 2,\n            expectedHex: '#000000'\n        },\n        {\n            name: 'set black luminance to 0.5',\n            topic: chroma('black').luminance(0.5),\n            expectedLuminance: 0.5,\n            expectedHex: '#bcbcbc'\n        },\n        {\n            name: 'set black luminance to 0.5 (lab)',\n            topic: chroma('black').luminance(0.5, 'lab'),\n            expectedLuminance: 0.5,\n            expectedHex: '#bcbcbc'\n        }\n    ];\n\n    testCases.forEach(({ name, topic, expectedLuminance, expectedHex, precision = 2, comparison, steps }) => {\n        if (comparison) {\n            it(`${name}`, () => {\n                if (comparison === 'greater') {\n                    expect(topic[0]).toBeGreaterThan(topic[1]);\n                } else if (comparison === 'less') {\n                    expect(topic[0]).toBeLessThan(topic[1]);\n                }\n            });\n        } else {\n            it(`${name}: luminance = ${expectedLuminance}`, () => {\n                expect(rnd(topic.luminance(), precision)).toBe(expectedLuminance);\n            });\n            if (expectedHex) {\n                it(`${name}: hex = ${expectedHex}`, () => {\n                    expect(topic.hex()).toBe(expectedHex);\n                });\n            }\n        }\n    });\n\n    it('setting luminance returns new color', () => {\n        const red = chroma('red');\n        expect(rnd(red.luminance(), 2)).toBe(0.21);\n        expect(red.hex()).toBe('#ff0000');\n        const red2 = red.luminance(0.4);\n        expect(red2.hex()).toBe('#ff8686');\n    });\n});\n"
  },
  {
    "path": "test/manipulate.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Manipulating colors', () => {\n    it('darken', () => {\n        const red = chroma('red');\n        expect(red.darken().hex()).toEqual('#c20000');\n        expect(red.darker().hex()).toEqual('#c20000');\n    });\n\n    it('darken more', () => {\n        const red = chroma('red');\n        expect(red.darken(2).hex()).toEqual('#890000');\n        expect(red.darken(10).hex()).toEqual('#000000');\n    });\n\n    it('brighten', () => {\n        const red = chroma('red');\n        expect(red.brighten().hex()).toEqual('#ff5a36');\n        expect(red.brighter().hex()).toEqual('#ff5a36');\n    });\n\n    it('brighten more', () => {\n        const red = chroma('red');\n        expect(red.brighten(2).hex()).toEqual('#ff9264');\n        expect(red.brighter(10).hex()).toEqual('#ffffff');\n    });\n\n    it('saturate', () => {\n        const red = chroma('red');\n        expect(red.saturate().hex()).toEqual('#ff0000');\n    });\n\n    it('desaturate', () => {\n        const red = chroma('red');\n        expect(red.desaturate().hex()).toEqual('#ee3a20');\n    });\n\n    it('desaturate more', () => {\n        const red = chroma('red');\n        expect(red.desaturate(2).hex()).toEqual('#db5136');\n    });\n\n    it('desaturate too much', () => {\n        const red = chroma('red');\n        expect(red.desaturate(400).hex()).toEqual('#7f7f7f');\n    });\n\n    it('shade a color', () => {\n        const red = chroma('red');\n        expect(red.shade().hex()).toEqual('#b40000');\n        expect(red.shade(0.25).hex()).toEqual('#dd0000');\n        expect(red.shade(0.75).hex()).toEqual('#800000');\n    });\n\n    it('shade a color in different spaces', () => {\n        const red = chroma('red');\n        expect(red.shade(0.5).hex()).toEqual('#b40000'); // default lrgb\n        expect(red.shade(0.5, 'rgb').hex()).toEqual('#800000');\n        expect(red.shade(0.5, 'lch').hex()).toEqual('#a60000');\n        expect(red.shade(0.5, 'lab').hex()).toEqual('#7a1b0c');\n    });\n\n    it('tint a color', () => {\n        const red = chroma('red');\n        expect(red.tint().hex()).toEqual('#ffb4b4');\n        expect(red.tint(0.25).hex()).toEqual('#ff8080');\n        expect(red.tint(0.75).hex()).toEqual('#ffdddd');\n    });\n\n    it('tint a color in different spaces', () => {\n        const red = chroma('red');\n        expect(red.tint(0.5).hex()).toEqual('#ffb4b4'); // default lrgb\n        expect(red.tint(0.5, 'rgb').hex()).toEqual('#ff8080');\n        expect(red.tint(0.5, 'lch').hex()).toEqual('#ff9e81');\n        expect(red.tint(0.5, 'lab').hex()).toEqual('#ff9e81');\n    });\n});\n"
  },
  {
    "path": "test/misc.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst round = function (digits) {\n    var d;\n    d = Math.pow(10, digits);\n    return function (v) {\n        return Math.round(v * d) / d;\n    };\n};\n\ndescribe('Some tests for chroma.lch()', () => {\n    describe('hsv black', () => {\n        it('hue is NaN', () => {\n            expect(chroma('black').hsv()[0]).toBeNaN();\n        });\n\n        it('but hue is defined', () => {\n            expect(chroma('black').hsv()[0] != null).toBeTruthy();\n        });\n    });\n\n    describe('toString', () => {\n        it('explicit .toString()', () => {\n            expect(chroma('greenyellow').toString()).toEqual('#adff2f');\n        });\n\n        it('implicit add to string', () => {\n            expect('' + chroma('greenyellow')).toEqual('#adff2f');\n        });\n\n        it('implicit cast as String', () => {\n            expect(String(chroma('greenyellow'))).toEqual('#adff2f');\n        });\n    });\n\n    describe('constructing numeric color', () => {\n        it('from 0xRRGGBB', () => {\n            expect(chroma.num(0xadff2f).name()).toEqual('greenyellow');\n        });\n\n        it('alpha is 100%', () => {\n            expect(chroma.num(0xadff2f).alpha()).toEqual(1);\n        });\n    });\n\n    describe('normalize hue', () => {\n        it('hue > 0', () => {\n            expect(chroma.rgb(0, 255, 255).lch()[2] >= 0).toBeTruthy();\n        });\n\n        it('hue < 360', () => {\n            expect(chroma.rgb(0, 255, 255).lch()[2] <= 360).toBeTruthy();\n        });\n    });\n\n    describe('lab conversions', () => {\n        /** @type {[string, [number, number, number]][]} */\n        const colors = [\n            ['red', [53.241, 80.092, 67.203]],\n            ['blue', [32.297, 79.188, -107.86]],\n            ['green', [46.227, -51.698, 49.897]],\n            ['yellow', [97.139, -21.554, 94.478]],\n            ['magenta', [60.324, 98.234, -60.825]]\n        ];\n\n        colors.forEach(([name, lab]) => {\n            it(name, () => {\n                expect(chroma(name).lab().map(round(3))).toEqual(lab);\n            });\n        });\n    });\n});\n\n// vows.describe('Some tests for chroma.color()')\n//     .addBatch({\n\n//         'hueless css hsl colors': {\n//             topic: [chroma('black'), chroma('gray'), chroma('white')],\n//             black: function (topic) {\n//                 return assert.equal(topic[0].css('hsl'), 'hsl(0,0%,0%)');\n//             },\n//             gray: function (topic) {\n//                 return assert.equal(topic[1].css('hsl'), 'hsl(0,0%,50.2%)');\n//             },\n//             white: function (topic) {\n//                 return assert.equal(topic[2].css('hsl'), 'hsl(0,0%,100%)');\n//             }\n//         },\n//         'hcl.h is NaN for hue-less colors': {\n//             topic: [chroma('black'), chroma('gray'), chroma('white')],\n//             black: function (topic) {\n//                 return assert.isNaN(topic[0].hcl()[0]);\n//             },\n//             gray: function (topic) {\n//                 return assert.isNaN(topic[1].hcl()[0]);\n//             },\n//             white: function (topic) {\n//                 return assert.isNaN(topic[2].hcl()[0]);\n//             }\n//         },\n//         'lab-rgb precision': {\n//             topic: [74, 24, 78],\n//             to_rgb_to_lab: function (topic) {\n//                 return assert.deepEqual(\n//                     chroma\n//                         .rgb(chroma.lab(topic).rgb(false))\n//                         .lab()\n//                         .map(round(3)),\n//                     topic\n//                 );\n//             }\n//         },\n//         'cmyk-rgb precision': {\n//             topic: [0, 1, 1, 0.02],\n//             to_rgb_to_cmyk: function (topic) {\n//                 return assert.deepEqual(\n//                     chroma\n//                         .rgb(chroma.cmyk(topic).rgb(false))\n//                         .cmyk()\n//                         .map(round(3)),\n//                     topic\n//                 );\n//             }\n//         },\n//         'auto-detect rgba in hex output': {\n//             topic: ['rgba(255,0,0,1)', 'rgba(255,0,0,0.5)'],\n//             'rgb if alpha == 1': function (topic) {\n//                 return assert.equal(chroma(topic[0]).hex(), '#ff0000');\n//             },\n//             'rgba if alpha != 1': function (topic) {\n//                 return assert.equal(chroma(topic[1]).hex(), '#ff000080');\n//             }\n//         }\n//     })\n//     ['export'](module);\n"
  },
  {
    "path": "test/mix.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Some tests for chroma.color()', () => {\n    it('hsv interpolation white <-> red', () => {\n        const result = chroma('white').interpolate('red', 0.5, 'hsv');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('use mix as alias', () => {\n        const result = chroma('white').mix('red', 0.5, 'hsv');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('alternative mix syntax', () => {\n        const result = chroma.mix('red', 'blue', 0.25, 'rgb');\n        expect(result.hex()).toBe('#bf0040');\n    });\n\n    it('hsl interpolation white <-> red', () => {\n        const result = chroma('white').interpolate('red', 0.5, 'hsl');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('rgb interpolation white <-> red', () => {\n        const result = chroma('white').interpolate('red', 0.5, 'rgb');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('hsv interpolation red <-> white', () => {\n        const result = chroma('red').interpolate('white', 0.5, 'hsv');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('hsl interpolation red <-> white', () => {\n        const result = chroma('red').interpolate('white', 0.5, 'hsl');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('rgb interpolation red <-> white', () => {\n        const result = chroma('red').interpolate('white', 0.5, 'rgb');\n        expect(result.hex()).toBe('#ff8080');\n    });\n\n    it('interpolation short function', () => {\n        const interpolateFn = (t) => chroma.interpolate('#ff0000', '#ffffff', t, 'hsv').hex();\n\n        expect(interpolateFn(0)).toBe('#ff0000');\n        expect(interpolateFn(0.5)).toBe('#ff8080');\n        expect(interpolateFn(1)).toBe('#ffffff');\n    });\n\n    it('num interpolation white <-> red', () => {\n        const result = chroma(0xffffff).interpolate(0xff0000, 0.5, 'num');\n        expect(result.hex()).toBe('#ff7fff');\n    });\n\n    it('num interpolation red <-> white', () => {\n        const result = chroma(0xff0000).interpolate(0xffffff, 0.5, 'num');\n        expect(result.hex()).toBe('#ff7fff');\n    });\n\n    it('interpolation short function with num provided', () => {\n        const interpolateFn = (t) => chroma.interpolate(0xff0000, 0xffffff, t, 'num').hex();\n\n        expect(interpolateFn(0)).toBe('#ff0000');\n        expect(interpolateFn(0.5)).toBe('#ff7fff');\n        expect(interpolateFn(1)).toBe('#ffffff');\n    });\n\n    it('interpolate in num', () => {\n        const result = chroma.interpolate(chroma.num(0xffffe0), chroma.num(0x102180), 0.5, 'num');\n        expect(result.hex()).toBe('#8810b0');\n        expect(result.num()).toBe(8917168);\n    });\n\n    it('interpolate in hsv', () => {\n        const result = chroma.interpolate('white', 'black', 0.5, 'hsv');\n        expect(result.hex()).toBe('#808080');\n    });\n\n    it('interpolate in hsl', () => {\n        const result = chroma.interpolate('lightyellow', 'navy', 0.5, 'hsl');\n        expect(result.hex()).toBe('#31ff98');\n    });\n\n    it('interpolate in lrgb', () => {\n        const result = chroma.interpolate('red', 'blue', 0.5, 'lrgb');\n        expect(result.hex()).toBe('#b400b4');\n    });\n\n    it('mix gray and black', () => {\n        const result = chroma.mix('#666666', '#000000', 0.5, 'lch');\n        expect(result.hex()).toBe('#343434');\n        expect(result.css()).toBe('rgb(52 52 52)');\n    });\n\n    it('mix transparent gray and black', () => {\n        const result = chroma.mix('#66666600', '#000000', 0.5, 'lch');\n        expect(result.hex()).toBe('#34343480');\n        expect(result.css()).toBe('rgb(52 52 52 / 0.5)');\n    });\n});\n"
  },
  {
    "path": "test/num.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Some tests for chroma.num()', () => {\n    it('number output', () => {\n        const color = chroma.hsl(0, 1, 0.5, 0.5);\n        expect(color.num()).toBe(0xff0000);\n    });\n\n    it('num color', () => {\n        const colors = [chroma(0xff0000), chroma(0x000000), chroma(0xffffff), chroma(0x31ff98), chroma('red')];\n\n        expect(colors[0].hex()).toBe('#ff0000');\n        expect(colors[0].num()).toBe(0xff0000);\n\n        expect(colors[1].hex()).toBe('#000000');\n        expect(colors[1].num()).toBe(0x000000);\n\n        expect(colors[2].hex()).toBe('#ffffff');\n        expect(colors[2].num()).toBe(0xffffff);\n\n        expect(colors[3].hex()).toBe('#31ff98');\n        expect(colors[3].num()).toBe(0x31ff98);\n\n        expect(colors[4].num()).toBe(0xff0000);\n    });\n});\n"
  },
  {
    "path": "test/premultiply.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\ndescribe('Premultiply colors', () => {\n    it('premultiply rgba', () => {\n        const c = chroma('rgba(32, 48, 96, 0.5)');\n        expect(c.premultiply().rgba()).toEqual([16, 24, 48, 0.5]);\n    });\n\n    it('premultiply hex', () => {\n        const c = chroma('rgba(32, 48, 96, 0.5)');\n        expect(c.premultiply().hex()).toEqual('#10183080');\n    });\n\n    it('premultiply num', () => {\n        const c = chroma('rgba(32, 48, 96, 0.5)');\n        expect(c.premultiply().num()).toEqual(0x101830);\n    });\n});\n"
  },
  {
    "path": "test/random.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\nimport { vitest } from 'vitest';\n\ndescribe('Some tests for random colors', () => {\n    it('should generate valid hex codes for random colors', () => {\n        const color = chroma.random();\n        expect(/^#[0-9a-f]{6}$/i.test(color.hex())).toBe(true);\n    });\n\n    it('should accept random number generator function as first argument', () => {\n        const rng = vitest.fn(() => 0.5);\n        const color = chroma.random(rng);\n        expect(color.hex()).toBe('#888888');\n        expect(rng).toHaveBeenCalled();\n    });\n});\n"
  },
  {
    "path": "test/scale.lcorrection.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst scale = chroma.scale;\n\ndescribe('Testing lightness correction', () => {\n    describe('simple two color linear interpolation', () => {\n        const f = scale(['white', 'black']).mode('lab');\n\n        it('center L is 50', () => {\n            expect(Math.round(f(0.5).lab()[0])).toBe(50);\n        });\n    });\n\n    describe('hot - w/o correction', () => {\n        const f = scale(['white', 'yellow', 'red', 'black']).mode('lab');\n\n        it('center L is 74', () => {\n            expect(Math.round(f(0.5).lab()[0])).toBe(74);\n        });\n    });\n\n    describe('hot - with correction', () => {\n        const f = scale(['white', 'yellow', 'red', 'black']).mode('lab').correctLightness(true);\n\n        it('center L is 50', () => {\n            expect(Math.round(f(0.5).lab()[0])).toBe(50);\n        });\n    });\n\n    describe('hot - w/o correction - domained [0,100]', () => {\n        const f = scale(['white', 'yellow', 'red', 'black']).domain([0, 100]).mode('lab');\n\n        it('center L is 74', () => {\n            expect(Math.round(f(50).lab()[0])).toBe(74);\n        });\n    });\n\n    describe('hot - with correction - domained [0,100]', () => {\n        const f = scale(['white', 'yellow', 'red', 'black']).domain([0, 100]).mode('lab').correctLightness(true);\n\n        it('center L is 50', () => {\n            expect(Math.round(f(50).lab()[0])).toBe(50);\n        });\n    });\n\n    describe('hot - w/o correction - domained [0,20,40,60,80,100]', () => {\n        const f = scale(['white', 'yellow', 'red', 'black']).domain([0, 20, 40, 60, 80, 100]).mode('lab');\n\n        it('center L is 74', () => {\n            expect(Math.round(f(50).lab()[0])).toBe(74);\n        });\n    });\n\n    describe('hot - with correction - domained [0,20,40,60,80,100]', () => {\n        const f = scale(['white', 'yellow', 'red', 'black']).domain([0, 20, 40, 60, 80, 100]).mode('lab').correctLightness(true);\n\n        it('center L is 50', () => {\n            expect(Math.round(f(50).lab()[0])).toBe(50);\n        });\n    });\n});\n"
  },
  {
    "path": "test/scales.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst scale = chroma.scale;\n\ndescribe('Some tests for scale()', () => {\n    describe('simple rgb scale (white-->black)', () => {\n        const f = scale(['white', 'black']);\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('mid gray', () => {\n            expect(f(0.5).hex()).toBe('#808080');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n    });\n\n    describe('simple hsv scale (white-->black)', () => {\n        const f = scale(['white', 'black']).mode('hsv');\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('mid gray', () => {\n            expect(f(0.5).hex()).toBe('#808080');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n\n        it('colors', () => {\n            expect(f.colors()).toEqual(['#ffffff', '#000000']);\n        });\n\n        it('colors start and end', () => {\n            expect(f.colors(2)).toEqual(['#ffffff', '#000000']);\n        });\n\n        it('color mode', () => {\n            expect(f.colors(2, 'rgb')[1]).toEqual([0, 0, 0]);\n        });\n\n        it('color mode null len', () => {\n            expect(f.colors(2, null).length).toBe(2);\n        });\n\n        it('color mode null', () => {\n            expect(f.colors(2, null)[0]._rgb).toBeDefined();\n        });\n    });\n\n    describe('simple hsv scale (white-->black), classified', () => {\n        const f = scale(['white', 'black']).classes(7).mode('hsv');\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('mid gray', () => {\n            expect(f(0.5).hex()).toBe('#808080');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n\n        it('colors', () => {\n            expect(f.colors(7)).toEqual(['#ffffff', '#d5d5d5', '#aaaaaa', '#808080', '#555555', '#2a2a2a', '#000000']);\n        });\n    });\n\n    describe('simple lab scale (white-->black)', () => {\n        const f = scale(['white', 'black']).mode('lab');\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('mid gray', () => {\n            expect(f(0.5).hex()).toBe('#777777');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n    });\n\n    describe('colorbrewer scale', () => {\n        const f = scale('RdYlGn');\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#a50026');\n        });\n\n        it('mid gray', () => {\n            expect(f(0.5).hex()).toBe('#ffffbf');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#006837');\n        });\n    });\n\n    describe('colorbrewer scale - domained', () => {\n        const f = scale('RdYlGn').domain([0, 100]);\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#a50026');\n        });\n\n        it('foo', () => {\n            expect(f(10).hex()).not.toBe('#ffffbf');\n        });\n\n        it('mid gray', () => {\n            expect(f(50).hex()).toBe('#ffffbf');\n        });\n\n        it('ends black', () => {\n            expect(f(100).hex()).toBe('#006837');\n        });\n\n        it('returns domain', () => {\n            expect(f.domain()).toEqual([0, 100]);\n        });\n    });\n\n    describe('colorbrewer scale - lowercase', () => {\n        const f = scale('rdylgn');\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#a50026');\n        });\n\n        it('mid gray', () => {\n            expect(f(0.5).hex()).toBe('#ffffbf');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#006837');\n        });\n    });\n\n    describe('colorbrewer scale - domained - classified', () => {\n        const f = scale('RdYlGn').domain([0, 100]).classes(5);\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#a50026');\n        });\n\n        it('10', () => {\n            expect(f(10).hex()).toBe('#a50026');\n        });\n\n        it('mid gray', () => {\n            expect(f(50).hex()).toBe('#ffffbf');\n        });\n\n        it('ends black', () => {\n            expect(f(100).hex()).toBe('#006837');\n        });\n\n        it('get colors', () => {\n            expect(f.colors(5)).toEqual(['#a50026', '#f98e52', '#ffffbf', '#86cb67', '#006837']);\n        });\n    });\n\n    describe('calling domain with no arguments', () => {\n        const f = scale('RdYlGn').domain([0, 100]).classes(5);\n\n        it('returns domain', () => {\n            expect(f.domain()).toEqual([0, 100]);\n        });\n\n        it('returns classes', () => {\n            expect(f.classes()).toEqual([0, 20, 40, 60, 80, 100]);\n        });\n    });\n\n    describe('source array keeps untouched', () => {\n        const colors = chroma.brewer.Blues.slice(0);\n\n        it('colors are an array', () => {\n            expect(colors.length).toBe(9);\n        });\n\n        it('colors are strings', () => {\n            expect(typeof colors[0]).toBe('string');\n        });\n\n        it('creating a color scale', () => {\n            scale(colors);\n            expect(true).toBe(true);\n        });\n\n        it('colors are still strings', () => {\n            expect(typeof colors[0]).toBe('string');\n        });\n    });\n\n    describe('domain with same min and max', () => {\n        const f = scale(['white', 'black']).domain([1, 1]);\n\n        it('returns color', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n    });\n\n    describe('simple num scale (white-->black)', () => {\n        const f = scale(['white', 'black']).mode('num');\n\n        it('starts white', () => {\n            expect(f(0).hex()).toBe('#ffffff');\n        });\n\n        it('25%', () => {\n            expect(f(0.25).hex()).toBe('#bfffff');\n        });\n\n        it('50%', () => {\n            expect(f(0.5).hex()).toBe('#7fffff');\n        });\n\n        it('75%', () => {\n            expect(f(0.75).hex()).toBe('#3fffff');\n        });\n\n        it('95%', () => {\n            expect(f(0.95).hex()).toBe('#0ccccc');\n        });\n\n        it('ends black', () => {\n            expect(f(1).hex()).toBe('#000000');\n        });\n    });\n\n    describe('css rgb colors', () => {\n        const color = scale('YlGnBu')(0.3).css();\n\n        it('have rounded rgb() values', () => {\n            expect(color).toBe('rgb(170 222 183)');\n        });\n    });\n\n    describe('css rgba colors', () => {\n        const color = scale('YlGnBu')(0.3).alpha(0.675).css();\n\n        it('dont round alpha value', () => {\n            expect(color).toBe('rgb(170 222 183 / 0.675)');\n        });\n    });\n\n    describe('get colors from a scale', () => {\n        const f = scale(['yellow', 'darkgreen']);\n\n        it('just colors', () => {\n            expect(f.colors()).toEqual(['#ffff00', '#006400']);\n        });\n\n        it('five hex colors', () => {\n            expect(f.colors(5)).toEqual(['#ffff00', '#bfd800', '#80b200', '#408b00', '#006400']);\n        });\n\n        it('three css colors', () => {\n            expect(f.colors(3, 'css')).toEqual(['rgb(255 255 0)', 'rgb(128 178 0)', 'rgb(0 100 0)']);\n        });\n    });\n\n    describe('get colors from a scale', () => {\n        const f = scale(['yellow', 'darkgreen']);\n\n        it('just colors', () => {\n            expect(f.colors()).toEqual(['#ffff00', '#006400']);\n        });\n\n        it('five hex colors', () => {\n            expect(f.colors(5)).toEqual(['#ffff00', '#bfd800', '#80b200', '#408b00', '#006400']);\n        });\n\n        it('three css colors', () => {\n            expect(f.colors(3, 'css')).toEqual(['rgb(255 255 0)', 'rgb(128 178 0)', 'rgb(0 100 0)']);\n        });\n    });\n\n    describe('get colors from a scale with more than two colors', () => {\n        const f = scale(['yellow', 'orange', 'darkgreen']);\n\n        it('just original colors', () => {\n            expect(f.colors()).toEqual(['#ffff00', '#ffa500', '#006400']);\n        });\n    });\n\n    describe('test example in readme', () => {\n        const f = scale('RdYlGn');\n\n        it('five hex colors (new)', () => {\n            expect(f.colors(5)).toEqual(['#a50026', '#f98e52', '#ffffbf', '#86cb67', '#006837']);\n        });\n    });\n\n    describe('weird result', () => {\n        const f = scale([\n            [0, 0, 0, 1],\n            [255, 255, 255, 1]\n        ])\n            .domain([0, 10])\n            .mode('rgb');\n\n        it('has hex function at 0.5', () => {\n            expect(typeof f(0.5).hex).toBe('function');\n        });\n\n        it('has hex function at 0', () => {\n            expect(typeof f(0).hex).toBe('function');\n        });\n    });\n\n    describe('scale padding, simple', () => {\n        const f = scale('RdYlBu').padding(0.15);\n\n        it('0', () => {\n            expect(f(0).hex()).toBe('#e64f35');\n        });\n\n        it('0.5', () => {\n            expect(f(0.5).hex()).toBe('#ffffbf');\n        });\n\n        it('1', () => {\n            expect(f(1).hex()).toBe('#5d91c3');\n        });\n    });\n\n    describe('scale padding, one-sided', () => {\n        const f = scale('OrRd').padding([0.2, 0]);\n\n        it('0', () => {\n            expect(f(0).hex()).toBe('#fddcaf');\n        });\n\n        it('0.5', () => {\n            expect(f(0.5).hex()).toBe('#f26d4b');\n        });\n\n        it('1', () => {\n            expect(f(1).hex()).toBe('#7f0000');\n        });\n    });\n\n    describe('colors return original colors', () => {\n        const f = scale(['red', 'white', 'blue']);\n\n        it('same colors', () => {\n            expect(f.colors()).toEqual(['#ff0000', '#ffffff', '#0000ff']);\n        });\n    });\n\n    describe('scale with one color', () => {\n        const f = scale(['red']);\n\n        it('should return that color', () => {\n            expect(f(0.3).hex()).toBe('#ff0000');\n        });\n    });\n\n    describe('scale() no data color', () => {\n        const f = scale('OrRd');\n\n        it('null --> nodata', () => {\n            expect(f(null).hex()).toBe('#cccccc');\n        });\n\n        it('undefined --> nodata', () => {\n            expect(f(undefined).hex()).toBe('#cccccc');\n        });\n\n        it('custom nodata color', () => {\n            expect(f.nodata('#eee')(undefined).hex()).toBe('#eeeeee');\n        });\n    });\n\n    describe('scale wrapped in a scale', () => {\n        const f1 = scale('OrRd');\n        const f = scale('OrRd').domain([0, 0.25, 1]);\n\n        it('start', () => {\n            expect(f(0).hex()).toBe(f1(0).hex());\n        });\n\n        it('end', () => {\n            expect(f(1).hex()).toBe(f1(1).hex());\n        });\n\n        it('center', () => {\n            expect(f(0.125).hex()).toBe(f1(0.25).hex());\n        });\n\n        it('center2', () => {\n            expect(f(0.25).hex()).toBe(f1(0.5).hex());\n        });\n\n        it('center3', () => {\n            expect(f(0.625).hex()).toBe(f1(0.75).hex());\n        });\n    });\n\n    describe('multi-stop domain with matching ramp stops', () => {\n        const f = scale(['yellow', 'red', 'black']).domain([0, 25, 100]);\n\n        it('returns the original domain positions', () => {\n            expect(f.domain()).toEqual([0, 25, 100]);\n        });\n\n        it('maps first stop', () => {\n            expect(f(0).hex()).toBe('#ffff00');\n        });\n\n        it('maps mid stop', () => {\n            expect(f(25).hex()).toBe('#ff0000');\n        });\n\n        it('maps last stop', () => {\n            expect(f(100).hex()).toBe('#000000');\n        });\n    });\n});\n"
  },
  {
    "path": "test/temperature2rgb.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport temperature2rgb from '../src/io/temp/temperature2rgb.js';\n\nconst { round } = Math;\n\ndescribe('Testing Kelvin color conversions', () => {\n    const cases = {\n        '1k': { in: 1000, out: [255, 58, 0, 1] },\n        '4k': { in: 4000, out: [255, 208, 164, 1] },\n        '5k': { in: 5000, out: [255, 228, 205, 1] },\n        '7k': { in: 7000, out: [245, 243, 255, 1] },\n        '10k': { in: 10000, out: [204, 220, 255, 1] },\n        '20k': { in: 20000, out: [168, 197, 255, 1] },\n        '30k': { in: 30000, out: [159, 190, 255, 1] }\n    };\n\n    Object.keys(cases).forEach((key) => {\n        it(`parse simple kelvin colors for ${key}`, () => {\n            expect(temperature2rgb(cases[key].in).map(round)).toEqual(cases[key].out);\n        });\n    });\n});\n"
  },
  {
    "path": "test/unpack.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport unpack from '../src/utils/unpack.js';\n\ndescribe('Testing unpack', () => {\n    it('parse simple CMYK colors (args)', () => {\n        expect(unpack([1, 2, 3, 4])).toEqual([1, 2, 3, 4]);\n    });\n\n    it('parse simple CMYK colors (array)', () => {\n        expect(unpack([[1, 2, 3, 4]])).toEqual([1, 2, 3, 4]);\n    });\n\n    it('parse simple CMYK colors (object)', () => {\n        expect(unpack([{ c: 1, m: 2, y: 3, k: 4 }], 'cmyk')).toEqual([1, 2, 3, 4]);\n    });\n});\n"
  },
  {
    "path": "test/valid.test.js",
    "content": "import { describe, it, expect } from 'vitest';\nimport chroma from 'chroma-js';\n\nconst valid = chroma.valid;\n\ndescribe('Some tests for chroma.valid', () => {\n    it('valid color', () => {\n        expect(valid('red')).toBe(true);\n        expect(valid('transparent')).toBe(true);\n    });\n\n    it('invalid color', () => {\n        expect(valid('bread')).toBe(false);\n        expect(valid('unset')).toBe(false);\n        expect(valid('inherit')).toBe(false);\n    });\n});\n"
  },
  {
    "path": "vitest.config.mjs",
    "content": "import { resolve } from 'node:path'\nimport { defineConfig } from 'vitest/config'\nimport { name } from './package.json'\n\nconst r = (p) => resolve(__dirname, p)\n\nexport default defineConfig({\n  resolve: {\n    alias: {\n      [name]: r('./index.js'),\n    },\n  },\n})\n"
  }
]