diff --git a/coloraide/util.py b/coloraide/util.py index 6815b1348..e58d72e7d 100644 --- a/coloraide/util.py +++ b/coloraide/util.py @@ -23,7 +23,7 @@ DEF_MIX = 0.5 DEF_HUE_ADJ = "shorter" DEF_INTERPOLATE = "oklab" -DEF_FIT = "oklch-chroma" +DEF_FIT = "lch-chroma" DEF_DELTA_E = "76" ERR_MAP_MSG = """ diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md index a0f44d4c2..669982df4 100644 --- a/docs/src/markdown/about/changelog.md +++ b/docs/src/markdown/about/changelog.md @@ -2,6 +2,8 @@ ## 0.10.0 +- **NEW**: Switch back to using CIELCH for gamut mapping (`lch-chroma`). There are still some edge cases that make + `oklch-chroma` less desirable. - **FIX**: Fix an issue where when attempting to generate steps some ∆E distance apart, the maximum step range was not respected and could result in large hangs. diff --git a/docs/src/markdown/gamut.md b/docs/src/markdown/gamut.md index 431aa5f71..b14a26f88 100644 --- a/docs/src/markdown/gamut.md +++ b/docs/src/markdown/gamut.md @@ -131,15 +131,14 @@ There are various different ways to gamut map a color into a smaller gamut. Curr Method | Description -------------- | ----------- `clip` | Simple, naive clipping. -`oklch-chroma` | Compress chroma in the Oklch color space to bring a color in gamut. This is the default method used. -`lch-chroma` | This is like `oklch-chroma` but is done with CIELCH. This is what ColorAide originally used before `oklch-chroma`. Hue preservation is not as good. This method may be removed at some future point as it does not hold the hue as well as Oklch. +`lch-chroma` | Uses chroma reduction in the CIELCH color space to bring a color into gamut. This is the default method used. +`oklch-chroma` | Like `lch-chroma`, but uses the Oklch color space instead. Currently experimental. !!! note "CSS Level 4 Gamut Mapping" - We do currently use the algorithm as defined in the [CSS Level 4 specification](https://drafts.csswg.org/css-color/#binsearch). - This is because the current algorithm, as defined, has some issues that create gradients that are not smooth. - We currently feel the algorithm we are using does a better job as it creates smooth gradients without odd - discontinuities. If/when the CSS Level 4 specification is updated to address such concerns, we may align more - closely. + We do not currently use the algorithm as defined in the [CSS Level 4 specification](https://drafts.csswg.org/css-color/#binsearch). + This is because the current algorithm, as defined, has some issues that create gradients that are not smooth and + can create unexpected colors in some situations. If/when the CSS Level 4 specification is updated to address such + concerns, we may align more closely. In this example, we will take the color `#!color lch(100% 50 75)`. CIELCH's gamut is technically unbounded, but when we convert the color to sRGB, we find that the color is out of gamut. So, using the `fit` method, we can actually transform @@ -178,14 +177,14 @@ Color("lch(100% 50 75)").fit("srgb", method='clip') If we wanted to change the default "fitting" to `clip`, we can also just use a [class override](./color.md#override-default-settings). Doing this will cause the class to default to `clip` any time a -color needs to be mapped. Though, you can still use chroma compression by specifying `oklch-chroma` for the `method`. +color needs to be mapped. Though, you can still use chroma compression by specifying `lch-chroma` for the `method`. ```playground class Custom(Color): FIT = 'clip' Custom("lch(100% 50 75)").convert('srgb').fit() -Custom("lch(100% 50 75)").convert('srgb').fit(method='oklch-chroma') +Custom("lch(100% 50 75)").convert('srgb').fit(method='lch-chroma') ``` It is important to note that when using fit, there is no tolerance, so even if `in_gamut` allowed enough tolerance to diff --git a/docs/theme/assets/coloraide-extras/extra-notebook-8732391e.js b/docs/theme/assets/coloraide-extras/extra-notebook-678306ab.js similarity index 66% rename from docs/theme/assets/coloraide-extras/extra-notebook-8732391e.js rename to docs/theme/assets/coloraide-extras/extra-notebook-678306ab.js index 904305ea4..761fdb81f 100644 --- a/docs/theme/assets/coloraide-extras/extra-notebook-8732391e.js +++ b/docs/theme/assets/coloraide-extras/extra-notebook-678306ab.js @@ -1,2 +1,2 @@ -function _typeof(e){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_typeof(e)}var runtime=function(e){"use strict";var n,t=Object.prototype,o=t.hasOwnProperty,r="function"==typeof Symbol?Symbol:{},a=r.iterator||"@@iterator",i=r.asyncIterator||"@@asyncIterator",s=r.toStringTag||"@@toStringTag";function c(e,n,t){return Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}),e[n]}try{c({},"")}catch(e){c=function(e,n,t){return e[n]=t}}function l(e,n,t,o){var r=n&&n.prototype instanceof g?n:g,a=Object.create(r.prototype),i=new T(o||[]);return a._invoke=function(e,n,t){var o=d;return function(r,a){if(o===m)throw new Error("Generator is already running");if(o===f){if("throw"===r)throw a;return P()}for(t.method=r,t.arg=a;;){var i=t.delegate;if(i){var s=L(i,t);if(s){if(s===h)continue;return s}}if("next"===t.method)t.sent=t._sent=t.arg;else if("throw"===t.method){if(o===d)throw o=f,t.arg;t.dispatchException(t.arg)}else"return"===t.method&&t.abrupt("return",t.arg);o=m;var c=u(e,n,t);if("normal"===c.type){if(o=t.done?f:p,c.arg===h)continue;return{value:c.arg,done:t.done}}"throw"===c.type&&(o=f,t.method="throw",t.arg=c.arg)}}}(e,t,i),a}function u(e,n,t){try{return{type:"normal",arg:e.call(n,t)}}catch(e){return{type:"throw",arg:e}}}e.wrap=l;var d="suspendedStart",p="suspendedYield",m="executing",f="completed",h={};function g(){}function y(){}function _(){}var v={};c(v,a,(function(){return this}));var w=Object.getPrototypeOf,b=w&&w(w(R([])));b&&b!==t&&o.call(b,a)&&(v=b);var x=_.prototype=g.prototype=Object.create(v);function E(e){["next","throw","return"].forEach((function(n){c(e,n,(function(e){return this._invoke(n,e)}))}))}function k(e,n){function t(r,a,i,s){var c=u(e[r],e,a);if("throw"!==c.type){var l=c.arg,d=l.value;return d&&"object"===_typeof(d)&&o.call(d,"__await")?n.resolve(d.__await).then((function(e){t("next",e,i,s)}),(function(e){t("throw",e,i,s)})):n.resolve(d).then((function(e){l.value=e,i(l)}),(function(e){return t("throw",e,i,s)}))}s(c.arg)}var r;this._invoke=function(e,o){function a(){return new n((function(n,r){t(e,o,n,r)}))}return r=r?r.then(a,a):a()}}function L(e,t){var o=e.iterator[t.method];if(o===n){if(t.delegate=null,"throw"===t.method){if(e.iterator.return&&(t.method="return",t.arg=n,L(e,t),"throw"===t.method))return h;t.method="throw",t.arg=new TypeError("The iterator does not provide a 'throw' method")}return h}var r=u(o,e.iterator,t.arg);if("throw"===r.type)return t.method="throw",t.arg=r.arg,t.delegate=null,h;var a=r.arg;return a?a.done?(t[e.resultName]=a.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=n),t.delegate=null,h):a:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,h)}function S(e){var n={tryLoc:e[0]};1 in e&&(n.catchLoc=e[1]),2 in e&&(n.finallyLoc=e[2],n.afterLoc=e[3]),this.tryEntries.push(n)}function C(e){var n=e.completion||{};n.type="normal",delete n.arg,e.completion=n}function T(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(S,this),this.reset(!0)}function R(e){if(e){var t=e[a];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var r=-1,i=function t(){for(;++r=0;--a){var i=this.tryEntries[a],s=i.completion;if("root"===i.tryLoc)return r("end");if(i.tryLoc<=this.prev){var c=o.call(i,"catchLoc"),l=o.call(i,"finallyLoc");if(c&&l){if(this.prev=0;--t){var r=this.tryEntries[t];if(r.tryLoc<=this.prev&&o.call(r,"finallyLoc")&&this.prev=0;--n){var t=this.tryEntries[n];if(t.finallyLoc===e)return this.complete(t.completion,t.afterLoc),C(t),h}},catch:function(e){for(var n=this.tryEntries.length-1;n>=0;--n){var t=this.tryEntries[n];if(t.tryLoc===e){var o=t.completion;if("throw"===o.type){var r=o.arg;C(t)}return r}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,o){return this.delegate={iterator:R(e),resultName:t,nextLoc:o},"next"===this.method&&(this.arg=n),h}},e}("object"===("undefined"==typeof module?"undefined":_typeof(module))?module.exports:{});try{regeneratorRuntime=runtime}catch(e){"object"===("undefined"==typeof globalThis?"undefined":_typeof(globalThis))?globalThis.regeneratorRuntime=runtime:Function("r","regeneratorRuntime = r")(runtime)}!function(){"use strict";function e(e,n,t,o,r,a,i){try{var s=e[a](i),c=s.value}catch(e){return void t(e)}s.done?n(c):Promise.resolve(c).then(o,r)}function n(n){return function(){var t=this,o=arguments;return new Promise((function(r,a){var i=n.apply(t,o);function s(n){e(i,r,a,s,c,"next",n)}function c(n){e(i,r,a,s,c,"throw",n)}s(void 0)}))}}function t(e,n){return function(e){if(Array.isArray(e))return e}(e)||function(e,n){var t=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null==t)return;var o,r,a=[],i=!0,s=!1;try{for(t=t.call(e);!(i=(o=t.next()).done)&&(a.push(o.value),!n||a.length!==n);i=!0);}catch(e){s=!0,r=e}finally{try{i||null==t.return||t.return()}finally{if(s)throw r}}return a}(e,n)||o(e,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(e,n){if(e){if("string"==typeof e)return r(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?r(e,n):void 0}}function r(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,o=new Array(n);t\n
\n{results}\n
\n
\n
\n\n
\n
\n\n\n\n\n\n'''\n\ncode_id = 0\n\n\nclass ColorInterpolate(list):\n \"\"\"Color interpolate.\"\"\"\n\n\nclass ColorTuple(namedtuple('ColorTuple', ['string', 'color'])):\n \"\"\"Color tuple.\"\"\"\n\n\ndef _escape(txt):\n \"\"\"Basic HTML escaping.\"\"\"\n\n txt = txt.replace('&', '&')\n txt = txt.replace('<', '<')\n txt = txt.replace('>', '>')\n return txt\n\n\n@contextlib.contextmanager\ndef std_output(stdout=None):\n \"\"\"Capture standard out.\"\"\"\n old = sys.stdout\n if stdout is None:\n stdout = StringIO()\n sys.stdout = stdout\n yield stdout\n sys.stdout = old\n\n\ndef get_colors(result):\n \"\"\"Get color from results.\"\"\"\n\n from coloraide import Color\n from coloraide.interpolate import Interpolator\n\n colors = []\n if isinstance(result, Color):\n colors.append(ColorTuple(result.to_string(fit=False), result))\n elif isinstance(result, Interpolator):\n colors = ColorInterpolate(result.steps(steps=5, max_delta_e=4))\n elif isinstance(result, str):\n try:\n colors.append(ColorTuple(result, Color(result)))\n except Exception:\n pass\n elif isinstance(result, Sequence):\n for x in result:\n if isinstance(x, Color):\n colors.append(ColorTuple(x.to_string(fit=False), x))\n elif isinstance(x, str):\n try:\n colors.append(ColorTuple(x, Color(x)))\n except Exception:\n pass\n return colors\n\n\ndef find_colors(text):\n \"\"\"Find colors in text buffer.\"\"\"\n\n import coloraide\n\n colors = []\n for m in RE_COLOR_START.finditer(text):\n start = m.start()\n mcolor = coloraide.Color.match(text, start=start)\n if mcolor is not None:\n colors.append(ColorTuple(text[mcolor.start:mcolor.end], mcolor.color))\n return colors\n\n\ndef execute(cmd):\n \"\"\"Execute color commands.\"\"\"\n\n import coloraide\n\n g = {'Color': coloraide.Color, 'coloraide': coloraide, 'NaN': coloraide.NaN, 'Piecewise': coloraide.Piecewise}\n console = ''\n colors = []\n\n # Build AST tree\n src = cmd.strip()\n lines = src.split('\\n')\n try:\n tree = ast.parse(src)\n except Exception:\n import traceback\n return '{}'.format(traceback.format_exc()), colors\n\n for node in tree.body:\n result = None\n\n # Format source as Python console statements\n start = node.lineno\n end = node.end_lineno\n stmt = lines[start - 1: end]\n command = ''\n for i, line in enumerate(stmt, 0):\n if i == 0:\n stmt[i] = '>>> ' + line\n else:\n stmt[i] = '... ' + line\n command += '\\n'.join(stmt)\n if isinstance(node, AST_BLOCKS):\n command += '\\n... '\n\n try:\n # Capture anything sent to standard out\n text = ''\n with std_output() as s:\n # Execute code\n if isinstance(node, ast.Expr):\n _eval = ast.Expression(node.value)\n result = eval(compile(_eval, '', 'eval'), g)\n else:\n _exec = ast.Module([node], [])\n exec(compile(_exec, '', 'exec'), g)\n\n # Execution went well, so append command\n console += command\n\n # Output captured standard out after statements\n text = s.getvalue()\n if text:\n clist = find_colors(text)\n if clist:\n colors.append(clist)\n console += '\\n{}'.format(text)\n s.flush()\n except Exception:\n import traceback\n console += '{}\\n{}'.format(command, traceback.format_exc())\n # Failed for some reason, so quit\n break\n\n # If we got a result, output it as well\n if result is not None:\n clist = get_colors(result)\n if clist:\n colors.append(clist)\n console += '{}{}\\n'.format('\\n' if not text else '', str(result))\n else:\n console += '\\n' if not text else ''\n\n return console, colors\n\n\ndef colorize(src, lang, **options):\n \"\"\"Colorize.\"\"\"\n\n lexer = get_lexer_by_name(lang, **options)\n formatter = HtmlFormatter(cssclass=\"highlight\", wrapcode=True)\n return highlight(src, lexer, formatter).strip()\n\n\ndef color_command_validator(language, inputs, options, attrs, md):\n \"\"\"Color validator.\"\"\"\n\n valid_inputs = set()\n\n for k, v in inputs.items():\n if k in valid_inputs:\n options[k] = True\n continue\n attrs[k] = v\n return True\n\n\ndef _color_command_console(colors):\n \"\"\"Color command formatter.\"\"\"\n\n el = ''\n bar = False\n values = []\n for item in colors:\n if isinstance(item, ColorInterpolate):\n if bar:\n el += '
{}
'.format(' '.join(values))\n values = []\n sub_el1 = '
{}
'\n style = \"--swatch-stops: \"\n stops = []\n for color in item:\n color.fit(WEBSPACE, in_place=True)\n stops.append(color.convert(WEBSPACE).to_string())\n if not stops:\n stops.extend(['transparent'] * 2)\n if len(stops) == 1:\n stops.append(stops[0])\n style += ','.join(stops)\n sub_el2 = ''.format(style)\n el += sub_el1.format(sub_el2)\n bar = False\n else:\n bar = True\n base_classes = \"swatch\"\n for color in item:\n if not color.color.in_gamut(WEBSPACE):\n base_classes += \" out-of-gamut\"\n color.color.fit(WEBSPACE, in_place=True)\n srgb = color.color.convert(WEBSPACE)\n value1 = srgb.to_string(alpha=False)\n value2 = srgb.to_string()\n style = \"--swatch-stops: {} 50%, {} 50%\".format(value1, value2)\n title = color.string\n classes = base_classes\n c = ''.format(style=style)\n c = '{color}'.format(\n classes=classes,\n color=c,\n title=title\n )\n values.append(c)\n if bar:\n el += '
{}
'.format(' '.join(values))\n values = []\n\n return el\n\n\ndef color_command_formatter(src=\"\", language=\"\", class_name=None, options=None, md=\"\", **kwargs):\n \"\"\"Formatter wrapper.\"\"\"\n\n global code_id\n\n try:\n if len(md.preprocessors['fenced_code_block'].extension.stash) == 0:\n code_id = 0\n\n console, colors = execute(src.strip())\n el = _color_command_console(colors)\n\n el += md.preprocessors['fenced_code_block'].extension.superfences[0]['formatter'](\n src=console,\n class_name=\"highlight\",\n language='pycon',\n md=md,\n options=options,\n **kwargs\n )\n el = '
{}
'.format(el)\n el = template.format(el_id=code_id, raw_source=_escape(src), results=el)\n code_id += 1\n except Exception:\n from pymdownx import superfences\n import traceback\n print(traceback.format_exc())\n return superfences.fence_code_format(src, 'text', class_name, options, md, **kwargs)\n return el\n\n\ndef live_color_command_formatter(src):\n \"\"\"Formatter wrapper.\"\"\"\n\n try:\n console, colors = execute(src.strip())\n el = _color_command_console(colors)\n\n if not colors:\n el += '
'\n\n el += colorize(console, 'pycon', **{'python3': True, 'stripnl': False})\n el = '
{}
'.format(el)\n except Exception:\n return '
{}
'.format(colorize('', 'text'))\n return el\n\n\ndef color_formatter(src=\"\", language=\"\", class_name=None, md=\"\"):\n \"\"\"Formatter wrapper.\"\"\"\n\n from coloraide import Color\n\n try:\n result = src.strip()\n try:\n console, colors = execute(result)\n if len(colors) != 1 or len(colors[0]) != 1:\n raise ValueError('Need one color only')\n color = colors[0][0].color\n result = colors[0][0].string\n except Exception:\n color = Color(result.strip())\n el = Etree.Element('span')\n stops = []\n if not color.in_gamut(WEBSPACE):\n color.fit(WEBSPACE, in_place=True)\n attributes = {'class': \"swatch out-of-gamut\", \"title\": result}\n sub_el = Etree.SubElement(el, 'span', attributes)\n stops.append(color.convert(WEBSPACE).to_string(hex=True, alpha=False))\n if color.alpha < 1.0:\n stops[-1] += ' 50%'\n stops.append(color.convert(WEBSPACE).to_string(hex=True) + ' 50%')\n else:\n attributes = {'class': \"swatch\", \"title\": result}\n sub_el = Etree.SubElement(el, 'span', attributes)\n stops.append(color.convert(WEBSPACE).to_string(hex=True, alpha=False))\n if color.alpha < 1.0:\n stops[-1] += ' 50%'\n stops.append(color.convert(WEBSPACE).to_string(hex=True) + ' 50%')\n\n if not stops:\n stops.extend(['transparent'] * 2)\n if len(stops) == 1:\n stops.append(stops[0])\n\n Etree.SubElement(\n sub_el,\n 'span',\n {\n \"class\": \"swatch-color\",\n \"style\": \"--swatch-stops: {};\".format(','.join(stops))\n }\n )\n\n el.append(md.inlinePatterns['backtick'].handle_code('css-color', result))\n except Exception:\n import traceback\n print(traceback.format_exc())\n el = md.inlinePatterns['backtick'].handle_code('text', src)\n return el\n\n\ndef render_console(*args):\n \"\"\"Render console update.\"\"\"\n\n try:\n # Run code\n inputs = document.getElementById(\"__playground-inputs_{}\".format(globals()['id_num']))\n results = document.getElementById(\"__playground-results_{}\".format(globals()['id_num']))\n results.innerHTML = live_color_command_formatter(inputs.value)\n scrollingElement = results.querySelector('code')\n scrollingElement.scrollTop = scrollingElement.scrollHeight\n except Exception as e:\n print(e)\n\n\ndef render_notebook(*args):\n \"\"\"Render notebook.\"\"\"\n\n import markdown\n from pymdownx import slugs\n\n text = globals().get('content', '')\n extensions = [\n 'markdown.extensions.toc',\n 'markdown.extensions.admonition',\n 'markdown.extensions.smarty',\n 'pymdownx.betterem',\n 'markdown.extensions.attr_list',\n 'markdown.extensions.def_list',\n 'markdown.extensions.tables',\n 'markdown.extensions.abbr',\n 'markdown.extensions.footnotes',\n 'markdown.extensions.md_in_html',\n 'pymdownx.superfences',\n 'pymdownx.highlight',\n 'pymdownx.inlinehilite',\n 'pymdownx.magiclink',\n 'pymdownx.tilde',\n 'pymdownx.caret',\n 'pymdownx.smartsymbols',\n 'pymdownx.emoji',\n 'pymdownx.escapeall',\n 'pymdownx.tasklist',\n 'pymdownx.striphtml',\n 'pymdownx.snippets',\n 'pymdownx.keys',\n 'pymdownx.details',\n 'pymdownx.saneheaders',\n 'pymdownx.tabbed'\n ]\n extension_configs = {\n 'markdown.extensions.toc': {\n 'slugify': slugs.slugify(case=\"lower\"),\n 'permalink': \"\"\n },\n 'markdown.extensions.smarty': {\n \"smart_quotes\": False,\n },\n 'pymdownx.superfences': {\n 'preserve_tabs': True,\n 'custom_fences': [\n {\n \"name\": 'playground',\n \"class\": 'playground',\n \"format\": color_command_formatter,\n \"validator\": color_command_validator\n }\n ]\n },\n 'pymdownx.inlinehilite': {\n 'custom_inline': [\n {\n 'name': 'color',\n 'class': 'color',\n 'format': color_formatter\n }\n ]\n },\n 'pymdownx.magiclink': {\n 'repo_url_shortener': True,\n 'repo_url_shorthand': True,\n 'social_url_shorthand': True,\n 'user': 'facelessuser',\n 'repo': 'coloraide'\n },\n 'pymdownx.keys': {\n 'separator': \"\\uff0b\"\n }\n }\n\n try:\n html = markdown.markdown(text, extensions=extensions, extension_configs=extension_configs)\n except Exception:\n html = ''\n content = document.getElementById(\"__notebook-render\")\n content.innerHTML = html\n\n\n# Load up necessary wheels and then execute the appropriate payload\ncwheel = \"coloraide-0.9.0-py3-none-any.whl\"\nmwheel = \"Markdown-3.3.4-py3-none-any.whl\"\npwheel = \"pymdown_extensions-9.0-py3-none-any.whl\"\n\nwheels = [\n location.origin + '/coloraide/playground/' + cwheel\n]\n\naction = globals().get('action')\nif action == 'render':\n callback = render_notebook\n wheels.extend(\n [\n location.origin + '/coloraide/playground/' + mwheel,\n location.origin + '/coloraide/playground/' + pwheel\n ]\n )\nelse:\n callback = render_console\n\n# We run this from inside an async JavaScript function\n# so it is okay to call await outside a coroutine as\n# we are technically still inside one.\nawait micropip.install(wheels)\ncallback()\n",g=function(e){return'\n!!! new "This notebook is powered by [Pyodide](https://github.com/pyodide/pyodide). Learn more [here](?notebook=https://gist.githubusercontent.com/facelessuser/7c819668b5eb248ecb9ac608d91391cf/raw/playground.md). Preview, convert, interpolate, and explore!"\n\n````````playground\n'.concat(e,"\n````````\n")},y=function(){m=!0,window.document.dispatchEvent(new Event("DOMContentLoaded",{bubbles:!0,cancelable:!0}))},_=function(e){var n=window.pageXOffset||(document.documentElement||document.body.parentNode||document.body).scrollLeft,t=window.pageYOffset||(document.documentElement||document.body.parentNode||document.body).scrollTop;e.style.height="5px",e.style.height="".concat(e.scrollHeight,"px"),window.scrollTo(n,t)},v=function(e){return encodeURIComponent(e).replace(/[.!'()*]/g,(function(e){return"%".concat(e.charCodeAt(0).toString(16))}))},w=function(){var e=n(regeneratorRuntime.mark((function e(n){var t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return(t=document.getElementById("__playground-inputs_".concat(n))).setAttribute("readonly",""),a.globals.set("id_num",n),a.globals.set("action","notebook"),e.next=6,a.runPythonAsync(h);case 6:t.removeAttribute("readonly");case 7:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),b=function(){var e=n(regeneratorRuntime.mark((function e(n){var t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return a.globals.set("content",n),a.globals.set("action","render"),e.next=4,a.runPythonAsync(h);case 4:(t=document.getElementById("__notebook-input"))&&(s=n,t.value=n),window.location.hash&&(window.location.href=window.location.href);case 7:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),x=function(){var e=n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(d){e.next=7;break}return d=!0,e.next=4,loadPyodide({indexURL:"https://cdn.jsdelivr.net/pyodide/v0.19.0/full/",fullStdLib:!1});case 4:return a=e.sent,e.next=7,a.loadPackage(["micropip","Pygments"]);case 7:case"end":return e.stop()}}),e)})));return function(){return e.apply(this,arguments)}}(),E=function(e,n,t){var o=null==n?"Loading...":n,r=t?"loading relative":"loading",a=document.createElement("template");a.innerHTML='
').concat(o,"
"),e.appendChild(a.content.firstChild)},k=function(e){e.querySelector(".loading")&&e.removeChild(e.querySelector(".loading"))},L=function(e){if("Tab"===e.key){var n=e.target;if(n.selectionStart!==n.selectionEnd){e.preventDefault();for(var t=n.selectionStart,o=n.selectionEnd,r=n.value;t>0&&"\n"!==r[t-1];)t--;for(;o>0&&"\n"!==r[o-1]&&o1e3?alert("Code must be under a 1000 characters to generate a URL!"):navigator.clipboard.writeText(i).then(n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:alert("Link copied to clipboard :)");case 1:case"end":return e.stop()}}),e)}))),n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:alert("Failed to copy link clipboard!");case 1:case"end":return e.stop()}}),e)}))));case 6:case"end":return e.stop()}}),e)})))),h.addEventListener("click",n(regeneratorRuntime.mark((function e(){var n,t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!i){e.next=2;break}return e.abrupt("return");case 2:return i=!0,n=p.querySelector("form"),E(n,null,!0),(t=document.querySelectorAll(".playground .playground-run"))&&t.forEach((function(e){e.setAttribute("disabled","")})),e.next=9,x();case 9:return d.querySelector("code").innerHTML="",e.next=12,w(r);case 12:t&&t.forEach((function(e){e.removeAttribute("disabled")})),k(n),p.classList.toggle("hidden"),d.classList.toggle("hidden"),m.classList.toggle("hidden"),f.classList.toggle("hidden"),h.classList.toggle("hidden"),g.classList.toggle("hidden"),delete l[r],i=!1;case 22:case"end":return e.stop()}}),e)})))),g.addEventListener("click",(function(){a.value=l[r],delete l[r],p.classList.toggle("hidden"),d.classList.toggle("hidden"),m.classList.toggle("hidden"),f.classList.toggle("hidden"),h.classList.toggle("hidden"),g.classList.toggle("hidden")}))}));case 3:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),C=function(){var e=n(regeneratorRuntime.mark((function e(t){var o,r,a,i,s,u,d,m,f;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(l={},!window.location.pathname.endsWith("/playground/")){e.next=32;break}if(o=new URLSearchParams(window.location.search),r="Loading Pyodide...",a="Loading Notebook...",i=o.has("source")?o.get("source"):o.get("notebook"),s=document.querySelector("article"),null===i||!i.trim()){e.next=16;break}return E(s,r),e.next=11,x();case 11:k(s),E(s,a);try{u=o.has("source")?"source":"notebook",p=decodeURIComponent(o.toString()),d="",m=new XMLHttpRequest,c=i,m.open("GET",i,!0),m.onload=n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return 4===m.readyState&&200===m.status&&(d=m.responseText),"source"===u&&(d=g(d)),e.next=4,b(d);case 4:return e.next=6,S(t);case 6:k(s),y();case 8:case"end":return e.stop()}}),e)}))),m.send()}catch(e){}e.next=30;break;case 16:return c="",f=g(o.has("code")?o.get("code"):"import coloraide\ncoloraide.__version__\nColor('red')"),p=decodeURIComponent(o.toString()),E(s,r),e.next=22,x();case 22:return k(s),E(s,a),e.next=26,b(f);case 26:return e.next=28,S(t);case 28:k(s),y();case 30:e.next=35;break;case 32:c="",p="",S(t);case 35:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),document.addEventListener("click",(function(e){var n=e.target||e.srcElement;if("A"===n.tagName&&C&&n.getAttribute("href")&&n.host===window.location.host&&"/coloraide/playground/"===window.location.pathname&&window.location.pathname===n.pathname&&window.location.search!==n.search){e.preventDefault();var r,a={},i=function(e,n){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!t){if(Array.isArray(e)||(t=o(e))||n&&e&&"number"==typeof e.length){t&&(e=t);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,s=!0,c=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return s=e.done,e},e:function(e){c=!0,i=e},f:function(){try{s||null==t.return||t.return()}finally{if(c)throw i}}}}(new URLSearchParams(n.search));try{for(i.s();!(r=i.n()).done;){var s=t(r.value,2),c=s[0],l=s[1];a[c]=l}}catch(e){i.e(e)}finally{i.f()}history.pushState(a,"",n.href),C(!1)}})),window.addEventListener("popstate",(function(){"/coloraide/playground/"===window.location.pathname&&decodeURIComponent(new URLSearchParams(window.location.search).toString())!==p&&C(!1)})),window.addEventListener("unload",(function(){m=!0})),window.document$.subscribe((function(){m?m=!1:C(!0)}))}(); -//# sourceMappingURL=extra-notebook-8732391e.js.map +function _typeof(e){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_typeof(e)}var runtime=function(e){"use strict";var n,t=Object.prototype,o=t.hasOwnProperty,r="function"==typeof Symbol?Symbol:{},a=r.iterator||"@@iterator",i=r.asyncIterator||"@@asyncIterator",s=r.toStringTag||"@@toStringTag";function c(e,n,t){return Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}),e[n]}try{c({},"")}catch(e){c=function(e,n,t){return e[n]=t}}function l(e,n,t,o){var r=n&&n.prototype instanceof g?n:g,a=Object.create(r.prototype),i=new T(o||[]);return a._invoke=function(e,n,t){var o=d;return function(r,a){if(o===m)throw new Error("Generator is already running");if(o===f){if("throw"===r)throw a;return P()}for(t.method=r,t.arg=a;;){var i=t.delegate;if(i){var s=L(i,t);if(s){if(s===h)continue;return s}}if("next"===t.method)t.sent=t._sent=t.arg;else if("throw"===t.method){if(o===d)throw o=f,t.arg;t.dispatchException(t.arg)}else"return"===t.method&&t.abrupt("return",t.arg);o=m;var c=u(e,n,t);if("normal"===c.type){if(o=t.done?f:p,c.arg===h)continue;return{value:c.arg,done:t.done}}"throw"===c.type&&(o=f,t.method="throw",t.arg=c.arg)}}}(e,t,i),a}function u(e,n,t){try{return{type:"normal",arg:e.call(n,t)}}catch(e){return{type:"throw",arg:e}}}e.wrap=l;var d="suspendedStart",p="suspendedYield",m="executing",f="completed",h={};function g(){}function y(){}function _(){}var v={};c(v,a,(function(){return this}));var w=Object.getPrototypeOf,b=w&&w(w(R([])));b&&b!==t&&o.call(b,a)&&(v=b);var x=_.prototype=g.prototype=Object.create(v);function E(e){["next","throw","return"].forEach((function(n){c(e,n,(function(e){return this._invoke(n,e)}))}))}function k(e,n){function t(r,a,i,s){var c=u(e[r],e,a);if("throw"!==c.type){var l=c.arg,d=l.value;return d&&"object"===_typeof(d)&&o.call(d,"__await")?n.resolve(d.__await).then((function(e){t("next",e,i,s)}),(function(e){t("throw",e,i,s)})):n.resolve(d).then((function(e){l.value=e,i(l)}),(function(e){return t("throw",e,i,s)}))}s(c.arg)}var r;this._invoke=function(e,o){function a(){return new n((function(n,r){t(e,o,n,r)}))}return r=r?r.then(a,a):a()}}function L(e,t){var o=e.iterator[t.method];if(o===n){if(t.delegate=null,"throw"===t.method){if(e.iterator.return&&(t.method="return",t.arg=n,L(e,t),"throw"===t.method))return h;t.method="throw",t.arg=new TypeError("The iterator does not provide a 'throw' method")}return h}var r=u(o,e.iterator,t.arg);if("throw"===r.type)return t.method="throw",t.arg=r.arg,t.delegate=null,h;var a=r.arg;return a?a.done?(t[e.resultName]=a.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=n),t.delegate=null,h):a:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,h)}function S(e){var n={tryLoc:e[0]};1 in e&&(n.catchLoc=e[1]),2 in e&&(n.finallyLoc=e[2],n.afterLoc=e[3]),this.tryEntries.push(n)}function C(e){var n=e.completion||{};n.type="normal",delete n.arg,e.completion=n}function T(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(S,this),this.reset(!0)}function R(e){if(e){var t=e[a];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var r=-1,i=function t(){for(;++r=0;--a){var i=this.tryEntries[a],s=i.completion;if("root"===i.tryLoc)return r("end");if(i.tryLoc<=this.prev){var c=o.call(i,"catchLoc"),l=o.call(i,"finallyLoc");if(c&&l){if(this.prev=0;--t){var r=this.tryEntries[t];if(r.tryLoc<=this.prev&&o.call(r,"finallyLoc")&&this.prev=0;--n){var t=this.tryEntries[n];if(t.finallyLoc===e)return this.complete(t.completion,t.afterLoc),C(t),h}},catch:function(e){for(var n=this.tryEntries.length-1;n>=0;--n){var t=this.tryEntries[n];if(t.tryLoc===e){var o=t.completion;if("throw"===o.type){var r=o.arg;C(t)}return r}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,o){return this.delegate={iterator:R(e),resultName:t,nextLoc:o},"next"===this.method&&(this.arg=n),h}},e}("object"===("undefined"==typeof module?"undefined":_typeof(module))?module.exports:{});try{regeneratorRuntime=runtime}catch(e){"object"===("undefined"==typeof globalThis?"undefined":_typeof(globalThis))?globalThis.regeneratorRuntime=runtime:Function("r","regeneratorRuntime = r")(runtime)}!function(){"use strict";function e(e,n,t,o,r,a,i){try{var s=e[a](i),c=s.value}catch(e){return void t(e)}s.done?n(c):Promise.resolve(c).then(o,r)}function n(n){return function(){var t=this,o=arguments;return new Promise((function(r,a){var i=n.apply(t,o);function s(n){e(i,r,a,s,c,"next",n)}function c(n){e(i,r,a,s,c,"throw",n)}s(void 0)}))}}function t(e,n){return function(e){if(Array.isArray(e))return e}(e)||function(e,n){var t=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null==t)return;var o,r,a=[],i=!0,s=!1;try{for(t=t.call(e);!(i=(o=t.next()).done)&&(a.push(o.value),!n||a.length!==n);i=!0);}catch(e){s=!0,r=e}finally{try{i||null==t.return||t.return()}finally{if(s)throw r}}return a}(e,n)||o(e,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(e,n){if(e){if("string"==typeof e)return r(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?r(e,n):void 0}}function r(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,o=new Array(n);t\n
\n{results}\n
\n
\n
\n\n
\n
\n\n\n\n\n\n'''\n\ncode_id = 0\n\n\nclass ColorInterpolate(list):\n \"\"\"Color interpolate.\"\"\"\n\n\nclass ColorTuple(namedtuple('ColorTuple', ['string', 'color'])):\n \"\"\"Color tuple.\"\"\"\n\n\ndef _escape(txt):\n \"\"\"Basic HTML escaping.\"\"\"\n\n txt = txt.replace('&', '&')\n txt = txt.replace('<', '<')\n txt = txt.replace('>', '>')\n return txt\n\n\n@contextlib.contextmanager\ndef std_output(stdout=None):\n \"\"\"Capture standard out.\"\"\"\n old = sys.stdout\n if stdout is None:\n stdout = StringIO()\n sys.stdout = stdout\n yield stdout\n sys.stdout = old\n\n\ndef get_colors(result):\n \"\"\"Get color from results.\"\"\"\n\n from coloraide import Color\n from coloraide.interpolate import Interpolator\n\n colors = []\n if isinstance(result, Color):\n colors.append(ColorTuple(result.to_string(fit=False), result))\n elif isinstance(result, Interpolator):\n colors = ColorInterpolate(result.steps(steps=5, max_delta_e=4))\n elif isinstance(result, str):\n try:\n colors.append(ColorTuple(result, Color(result)))\n except Exception:\n pass\n elif isinstance(result, Sequence):\n for x in result:\n if isinstance(x, Color):\n colors.append(ColorTuple(x.to_string(fit=False), x))\n elif isinstance(x, str):\n try:\n colors.append(ColorTuple(x, Color(x)))\n except Exception:\n pass\n return colors\n\n\ndef find_colors(text):\n \"\"\"Find colors in text buffer.\"\"\"\n\n import coloraide\n\n colors = []\n for m in RE_COLOR_START.finditer(text):\n start = m.start()\n mcolor = coloraide.Color.match(text, start=start)\n if mcolor is not None:\n colors.append(ColorTuple(text[mcolor.start:mcolor.end], mcolor.color))\n return colors\n\n\ndef execute(cmd):\n \"\"\"Execute color commands.\"\"\"\n\n import coloraide\n\n g = {'Color': coloraide.Color, 'coloraide': coloraide, 'NaN': coloraide.NaN, 'Piecewise': coloraide.Piecewise}\n console = ''\n colors = []\n\n # Build AST tree\n src = cmd.strip()\n lines = src.split('\\n')\n try:\n tree = ast.parse(src)\n except Exception:\n import traceback\n return '{}'.format(traceback.format_exc()), colors\n\n for node in tree.body:\n result = None\n\n # Format source as Python console statements\n start = node.lineno\n end = node.end_lineno\n stmt = lines[start - 1: end]\n command = ''\n for i, line in enumerate(stmt, 0):\n if i == 0:\n stmt[i] = '>>> ' + line\n else:\n stmt[i] = '... ' + line\n command += '\\n'.join(stmt)\n if isinstance(node, AST_BLOCKS):\n command += '\\n... '\n\n try:\n # Capture anything sent to standard out\n text = ''\n with std_output() as s:\n # Execute code\n if isinstance(node, ast.Expr):\n _eval = ast.Expression(node.value)\n result = eval(compile(_eval, '', 'eval'), g)\n else:\n _exec = ast.Module([node], [])\n exec(compile(_exec, '', 'exec'), g)\n\n # Execution went well, so append command\n console += command\n\n # Output captured standard out after statements\n text = s.getvalue()\n if text:\n clist = find_colors(text)\n if clist:\n colors.append(clist)\n console += '\\n{}'.format(text)\n s.flush()\n except Exception:\n import traceback\n console += '{}\\n{}'.format(command, traceback.format_exc())\n # Failed for some reason, so quit\n break\n\n # If we got a result, output it as well\n if result is not None:\n clist = get_colors(result)\n if clist:\n colors.append(clist)\n console += '{}{}\\n'.format('\\n' if not text else '', str(result))\n else:\n console += '\\n' if not text else ''\n\n return console, colors\n\n\ndef colorize(src, lang, **options):\n \"\"\"Colorize.\"\"\"\n\n lexer = get_lexer_by_name(lang, **options)\n formatter = HtmlFormatter(cssclass=\"highlight\", wrapcode=True)\n return highlight(src, lexer, formatter).strip()\n\n\ndef color_command_validator(language, inputs, options, attrs, md):\n \"\"\"Color validator.\"\"\"\n\n valid_inputs = set()\n\n for k, v in inputs.items():\n if k in valid_inputs:\n options[k] = True\n continue\n attrs[k] = v\n return True\n\n\ndef _color_command_console(colors):\n \"\"\"Color command formatter.\"\"\"\n\n el = ''\n bar = False\n values = []\n for item in colors:\n if isinstance(item, ColorInterpolate):\n if bar:\n el += '
{}
'.format(' '.join(values))\n values = []\n sub_el1 = '
{}
'\n style = \"--swatch-stops: \"\n stops = []\n for color in item:\n color.fit(WEBSPACE, in_place=True)\n stops.append(color.convert(WEBSPACE).to_string())\n if not stops:\n stops.extend(['transparent'] * 2)\n if len(stops) == 1:\n stops.append(stops[0])\n style += ','.join(stops)\n sub_el2 = ''.format(style)\n el += sub_el1.format(sub_el2)\n bar = False\n else:\n bar = True\n base_classes = \"swatch\"\n for color in item:\n if not color.color.in_gamut(WEBSPACE):\n base_classes += \" out-of-gamut\"\n color.color.fit(WEBSPACE, in_place=True)\n srgb = color.color.convert(WEBSPACE)\n value1 = srgb.to_string(alpha=False)\n value2 = srgb.to_string()\n style = \"--swatch-stops: {} 50%, {} 50%\".format(value1, value2)\n title = color.string\n classes = base_classes\n c = ''.format(style=style)\n c = '{color}'.format(\n classes=classes,\n color=c,\n title=title\n )\n values.append(c)\n if bar:\n el += '
{}
'.format(' '.join(values))\n values = []\n\n return el\n\n\ndef color_command_formatter(src=\"\", language=\"\", class_name=None, options=None, md=\"\", **kwargs):\n \"\"\"Formatter wrapper.\"\"\"\n\n global code_id\n\n try:\n if len(md.preprocessors['fenced_code_block'].extension.stash) == 0:\n code_id = 0\n\n console, colors = execute(src.strip())\n el = _color_command_console(colors)\n\n el += md.preprocessors['fenced_code_block'].extension.superfences[0]['formatter'](\n src=console,\n class_name=\"highlight\",\n language='pycon',\n md=md,\n options=options,\n **kwargs\n )\n el = '
{}
'.format(el)\n el = template.format(el_id=code_id, raw_source=_escape(src), results=el)\n code_id += 1\n except Exception:\n from pymdownx import superfences\n import traceback\n print(traceback.format_exc())\n return superfences.fence_code_format(src, 'text', class_name, options, md, **kwargs)\n return el\n\n\ndef live_color_command_formatter(src):\n \"\"\"Formatter wrapper.\"\"\"\n\n try:\n console, colors = execute(src.strip())\n el = _color_command_console(colors)\n\n if not colors:\n el += '
'\n\n el += colorize(console, 'pycon', **{'python3': True, 'stripnl': False})\n el = '
{}
'.format(el)\n except Exception:\n return '
{}
'.format(colorize('', 'text'))\n return el\n\n\ndef color_formatter(src=\"\", language=\"\", class_name=None, md=\"\"):\n \"\"\"Formatter wrapper.\"\"\"\n\n from coloraide import Color\n\n try:\n result = src.strip()\n try:\n console, colors = execute(result)\n if len(colors) != 1 or len(colors[0]) != 1:\n raise ValueError('Need one color only')\n color = colors[0][0].color\n result = colors[0][0].string\n except Exception:\n color = Color(result.strip())\n el = Etree.Element('span')\n stops = []\n if not color.in_gamut(WEBSPACE):\n color.fit(WEBSPACE, in_place=True)\n attributes = {'class': \"swatch out-of-gamut\", \"title\": result}\n sub_el = Etree.SubElement(el, 'span', attributes)\n stops.append(color.convert(WEBSPACE).to_string(hex=True, alpha=False))\n if color.alpha < 1.0:\n stops[-1] += ' 50%'\n stops.append(color.convert(WEBSPACE).to_string(hex=True) + ' 50%')\n else:\n attributes = {'class': \"swatch\", \"title\": result}\n sub_el = Etree.SubElement(el, 'span', attributes)\n stops.append(color.convert(WEBSPACE).to_string(hex=True, alpha=False))\n if color.alpha < 1.0:\n stops[-1] += ' 50%'\n stops.append(color.convert(WEBSPACE).to_string(hex=True) + ' 50%')\n\n if not stops:\n stops.extend(['transparent'] * 2)\n if len(stops) == 1:\n stops.append(stops[0])\n\n Etree.SubElement(\n sub_el,\n 'span',\n {\n \"class\": \"swatch-color\",\n \"style\": \"--swatch-stops: {};\".format(','.join(stops))\n }\n )\n\n el.append(md.inlinePatterns['backtick'].handle_code('css-color', result))\n except Exception:\n import traceback\n print(traceback.format_exc())\n el = md.inlinePatterns['backtick'].handle_code('text', src)\n return el\n\n\ndef render_console(*args):\n \"\"\"Render console update.\"\"\"\n\n try:\n # Run code\n inputs = document.getElementById(\"__playground-inputs_{}\".format(globals()['id_num']))\n results = document.getElementById(\"__playground-results_{}\".format(globals()['id_num']))\n results.innerHTML = live_color_command_formatter(inputs.value)\n scrollingElement = results.querySelector('code')\n scrollingElement.scrollTop = scrollingElement.scrollHeight\n except Exception as e:\n print(e)\n\n\ndef render_notebook(*args):\n \"\"\"Render notebook.\"\"\"\n\n import markdown\n from pymdownx import slugs\n\n text = globals().get('content', '')\n extensions = [\n 'markdown.extensions.toc',\n 'markdown.extensions.admonition',\n 'markdown.extensions.smarty',\n 'pymdownx.betterem',\n 'markdown.extensions.attr_list',\n 'markdown.extensions.def_list',\n 'markdown.extensions.tables',\n 'markdown.extensions.abbr',\n 'markdown.extensions.footnotes',\n 'markdown.extensions.md_in_html',\n 'pymdownx.superfences',\n 'pymdownx.highlight',\n 'pymdownx.inlinehilite',\n 'pymdownx.magiclink',\n 'pymdownx.tilde',\n 'pymdownx.caret',\n 'pymdownx.smartsymbols',\n 'pymdownx.emoji',\n 'pymdownx.escapeall',\n 'pymdownx.tasklist',\n 'pymdownx.striphtml',\n 'pymdownx.snippets',\n 'pymdownx.keys',\n 'pymdownx.details',\n 'pymdownx.saneheaders',\n 'pymdownx.tabbed'\n ]\n extension_configs = {\n 'markdown.extensions.toc': {\n 'slugify': slugs.slugify(case=\"lower\"),\n 'permalink': \"\"\n },\n 'markdown.extensions.smarty': {\n \"smart_quotes\": False,\n },\n 'pymdownx.superfences': {\n 'preserve_tabs': True,\n 'custom_fences': [\n {\n \"name\": 'playground',\n \"class\": 'playground',\n \"format\": color_command_formatter,\n \"validator\": color_command_validator\n }\n ]\n },\n 'pymdownx.inlinehilite': {\n 'custom_inline': [\n {\n 'name': 'color',\n 'class': 'color',\n 'format': color_formatter\n }\n ]\n },\n 'pymdownx.magiclink': {\n 'repo_url_shortener': True,\n 'repo_url_shorthand': True,\n 'social_url_shorthand': True,\n 'user': 'facelessuser',\n 'repo': 'coloraide'\n },\n 'pymdownx.keys': {\n 'separator': \"\\uff0b\"\n }\n }\n\n try:\n html = markdown.markdown(text, extensions=extensions, extension_configs=extension_configs)\n except Exception:\n html = ''\n content = document.getElementById(\"__notebook-render\")\n content.innerHTML = html\n\n\n# Load up necessary wheels and then execute the appropriate payload\ncwheel = \"coloraide-0.10.0-py3-none-any.whl\"\nmwheel = \"Markdown-3.3.4-py3-none-any.whl\"\npwheel = \"pymdown_extensions-9.0-py3-none-any.whl\"\n\nwheels = [\n location.origin + '/coloraide/playground/' + cwheel\n]\n\naction = globals().get('action')\nif action == 'render':\n callback = render_notebook\n wheels.extend(\n [\n location.origin + '/coloraide/playground/' + mwheel,\n location.origin + '/coloraide/playground/' + pwheel\n ]\n )\nelse:\n callback = render_console\n\n# We run this from inside an async JavaScript function\n# so it is okay to call await outside a coroutine as\n# we are technically still inside one.\nawait micropip.install(wheels)\ncallback()\n",g=function(e){return'\n!!! new "This notebook is powered by [Pyodide](https://github.com/pyodide/pyodide). Learn more [here](?notebook=https://gist.githubusercontent.com/facelessuser/7c819668b5eb248ecb9ac608d91391cf/raw/playground.md). Preview, convert, interpolate, and explore!"\n\n````````playground\n'.concat(e,"\n````````\n")},y=function(){m=!0,window.document.dispatchEvent(new Event("DOMContentLoaded",{bubbles:!0,cancelable:!0}))},_=function(e){var n=window.pageXOffset||(document.documentElement||document.body.parentNode||document.body).scrollLeft,t=window.pageYOffset||(document.documentElement||document.body.parentNode||document.body).scrollTop;e.style.height="5px",e.style.height="".concat(e.scrollHeight,"px"),window.scrollTo(n,t)},v=function(e){return encodeURIComponent(e).replace(/[.!'()*]/g,(function(e){return"%".concat(e.charCodeAt(0).toString(16))}))},w=function(){var e=n(regeneratorRuntime.mark((function e(n){var t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return(t=document.getElementById("__playground-inputs_".concat(n))).setAttribute("readonly",""),a.globals.set("id_num",n),a.globals.set("action","notebook"),e.next=6,a.runPythonAsync(h);case 6:t.removeAttribute("readonly");case 7:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),b=function(){var e=n(regeneratorRuntime.mark((function e(n){var t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return a.globals.set("content",n),a.globals.set("action","render"),e.next=4,a.runPythonAsync(h);case 4:(t=document.getElementById("__notebook-input"))&&(s=n,t.value=n),window.location.hash&&(window.location.href=window.location.href);case 7:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),x=function(){var e=n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(d){e.next=7;break}return d=!0,e.next=4,loadPyodide({indexURL:"https://cdn.jsdelivr.net/pyodide/v0.19.0/full/",fullStdLib:!1});case 4:return a=e.sent,e.next=7,a.loadPackage(["micropip","Pygments"]);case 7:case"end":return e.stop()}}),e)})));return function(){return e.apply(this,arguments)}}(),E=function(e,n,t){var o=null==n?"Loading...":n,r=t?"loading relative":"loading",a=document.createElement("template");a.innerHTML='
').concat(o,"
"),e.appendChild(a.content.firstChild)},k=function(e){e.querySelector(".loading")&&e.removeChild(e.querySelector(".loading"))},L=function(e){if("Tab"===e.key){var n=e.target;if(n.selectionStart!==n.selectionEnd){e.preventDefault();for(var t=n.selectionStart,o=n.selectionEnd,r=n.value;t>0&&"\n"!==r[t-1];)t--;for(;o>0&&"\n"!==r[o-1]&&o1e3?alert("Code must be under a 1000 characters to generate a URL!"):navigator.clipboard.writeText(i).then(n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:alert("Link copied to clipboard :)");case 1:case"end":return e.stop()}}),e)}))),n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:alert("Failed to copy link clipboard!");case 1:case"end":return e.stop()}}),e)}))));case 6:case"end":return e.stop()}}),e)})))),h.addEventListener("click",n(regeneratorRuntime.mark((function e(){var n,t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!i){e.next=2;break}return e.abrupt("return");case 2:return i=!0,n=p.querySelector("form"),E(n,null,!0),(t=document.querySelectorAll(".playground .playground-run"))&&t.forEach((function(e){e.setAttribute("disabled","")})),e.next=9,x();case 9:return d.querySelector("code").innerHTML="",e.next=12,w(r);case 12:t&&t.forEach((function(e){e.removeAttribute("disabled")})),k(n),p.classList.toggle("hidden"),d.classList.toggle("hidden"),m.classList.toggle("hidden"),f.classList.toggle("hidden"),h.classList.toggle("hidden"),g.classList.toggle("hidden"),delete l[r],i=!1;case 22:case"end":return e.stop()}}),e)})))),g.addEventListener("click",(function(){a.value=l[r],delete l[r],p.classList.toggle("hidden"),d.classList.toggle("hidden"),m.classList.toggle("hidden"),f.classList.toggle("hidden"),h.classList.toggle("hidden"),g.classList.toggle("hidden")}))}));case 3:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),C=function(){var e=n(regeneratorRuntime.mark((function e(t){var o,r,a,i,s,u,d,m,f;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(l={},!window.location.pathname.endsWith("/playground/")){e.next=32;break}if(o=new URLSearchParams(window.location.search),r="Loading Pyodide...",a="Loading Notebook...",i=o.has("source")?o.get("source"):o.get("notebook"),s=document.querySelector("article"),null===i||!i.trim()){e.next=16;break}return E(s,r),e.next=11,x();case 11:k(s),E(s,a);try{u=o.has("source")?"source":"notebook",p=decodeURIComponent(o.toString()),d="",m=new XMLHttpRequest,c=i,m.open("GET",i,!0),m.onload=n(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return 4===m.readyState&&200===m.status&&(d=m.responseText),"source"===u&&(d=g(d)),e.next=4,b(d);case 4:return e.next=6,S(t);case 6:k(s),y();case 8:case"end":return e.stop()}}),e)}))),m.send()}catch(e){}e.next=30;break;case 16:return c="",f=g(o.has("code")?o.get("code"):"import coloraide\ncoloraide.__version__\nColor('red')"),p=decodeURIComponent(o.toString()),E(s,r),e.next=22,x();case 22:return k(s),E(s,a),e.next=26,b(f);case 26:return e.next=28,S(t);case 28:k(s),y();case 30:e.next=35;break;case 32:c="",p="",S(t);case 35:case"end":return e.stop()}}),e)})));return function(n){return e.apply(this,arguments)}}(),document.addEventListener("click",(function(e){var n=e.target||e.srcElement;if("A"===n.tagName&&C&&n.getAttribute("href")&&n.host===window.location.host&&"/coloraide/playground/"===window.location.pathname&&window.location.pathname===n.pathname&&window.location.search!==n.search){e.preventDefault();var r,a={},i=function(e,n){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!t){if(Array.isArray(e)||(t=o(e))||n&&e&&"number"==typeof e.length){t&&(e=t);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,s=!0,c=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return s=e.done,e},e:function(e){c=!0,i=e},f:function(){try{s||null==t.return||t.return()}finally{if(c)throw i}}}}(new URLSearchParams(n.search));try{for(i.s();!(r=i.n()).done;){var s=t(r.value,2),c=s[0],l=s[1];a[c]=l}}catch(e){i.e(e)}finally{i.f()}history.pushState(a,"",n.href),C(!1)}})),window.addEventListener("popstate",(function(){"/coloraide/playground/"===window.location.pathname&&decodeURIComponent(new URLSearchParams(window.location.search).toString())!==p&&C(!1)})),window.addEventListener("unload",(function(){m=!0})),window.document$.subscribe((function(){m?m=!1:C(!0)}))}(); +//# sourceMappingURL=extra-notebook-678306ab.js.map diff --git a/docs/theme/assets/coloraide-extras/extra-notebook-8732391e.js.map b/docs/theme/assets/coloraide-extras/extra-notebook-678306ab.js.map similarity index 99% rename from docs/theme/assets/coloraide-extras/extra-notebook-8732391e.js.map rename to docs/theme/assets/coloraide-extras/extra-notebook-678306ab.js.map index 5858d16e0..572de7e00 100644 --- a/docs/theme/assets/coloraide-extras/extra-notebook-8732391e.js.map +++ b/docs/theme/assets/coloraide-extras/extra-notebook-678306ab.js.map @@ -1 +1 @@ -{"version":3,"file":"extra-notebook-8732391e.js","sources":["../../../src/js/extra-notebook.js"],"sourcesContent":["(() => {\n let pyodide = null\n let busy = false\n let raw = \"\"\n let gist = \"\"\n let editTemp = {}\n const reIdNum = /.*?_(\\d+)$/\n let initialized = false\n let lastSearch = \"\"\n let fake = false\n const tabStart = /^( {1,4}|\\t)/\n // This is the Python payload that will be executed when the user\n // presses the `Run` button. It will execute the code, create a\n // Python console output, find color references, steps, and interpolation\n // references and render the appropriate preview.\n const pycode = \"{{pycode}}\"\n const defContent = \"import coloraide\\ncoloraide.__version__\\nColor('red')\"\n\n const getContent = content => {\n return `\n!!! new \"This notebook is powered by [Pyodide](https://github.com/pyodide/pyodide). \\\nLearn more [here](\\\n?notebook=https://gist.githubusercontent.com/facelessuser/7c819668b5eb248ecb9ac608d91391cf/raw/playground.md\\\n). Preview, convert, interpolate, and explore!\"\n\n\\`\\`\\`\\`\\`\\`\\`\\`playground\n${content}\n\\`\\`\\`\\`\\`\\`\\`\\`\n`\n }\n\n const fakeDOMContentLoaded = () => {\n // Send a fake `DOMContentLoaded`\n fake = true\n window.document.dispatchEvent(new Event(\"DOMContentLoaded\", {\n bubbles: true,\n cancelable: true\n }))\n }\n\n const textResize = inpt => {\n // Resize inputs based on text height.\n\n const scrollLeft = window.pageXOffset ||\n (document.documentElement || document.body.parentNode || document.body).scrollLeft\n\n const scrollTop = window.pageYOffset ||\n (document.documentElement || document.body.parentNode || document.body).scrollTop\n\n inpt.style.height = \"5px\"\n inpt.style.height = `${inpt.scrollHeight}px`\n\n window.scrollTo(scrollLeft, scrollTop)\n }\n\n const encodeuri = uri => {\n // Encode the URI component.\n\n return encodeURIComponent(uri).replace(/[.!'()*]/g, c => {\n return `%${c.charCodeAt(0).toString(16)}`\n })\n }\n\n const pyexecute = async currentID => {\n // Execute Python code\n\n const currentInputs = document.getElementById(`__playground-inputs_${currentID}`)\n currentInputs.setAttribute(\"readonly\", \"\")\n pyodide.globals.set(\"id_num\", currentID)\n pyodide.globals.set(\"action\", \"notebook\")\n await pyodide.runPythonAsync(pycode)\n currentInputs.removeAttribute(\"readonly\")\n }\n\n const pyrender = async text => {\n // Execute Python code\n\n pyodide.globals.set(\"content\", text)\n pyodide.globals.set(\"action\", \"render\")\n await pyodide.runPythonAsync(pycode)\n const src = document.getElementById(\"__notebook-input\")\n if (src) {\n raw = text\n src.value = text\n }\n if (window.location.hash) {\n // Force jumping to hashes\n window.location.href = window.location.href // eslint-disable-line no-self-assign\n }\n }\n\n const setupPyodide = async() => {\n // Load `Pyodide` and the any default packages we can need and can load.\n\n if (!initialized) {\n initialized = true\n pyodide = await loadPyodide({ // eslint-disable-line no-undef\n indexURL: \"https://cdn.jsdelivr.net/pyodide/v0.19.0/full/\",\n fullStdLib: false\n })\n await pyodide.loadPackage([\"micropip\", \"Pygments\"])\n }\n }\n\n const showBusy = (target, label, relative) => {\n // Show busy indicator\n\n const loaderLabel = (typeof label === \"undefined\" || label === null) ? \"Loading...\" : label\n const classes = relative ? \"loading relative\" : \"loading\"\n const template = document.createElement(\"template\")\n template.innerHTML = `
${loaderLabel}
`\n target.appendChild(template.content.firstChild)\n }\n\n const hideBusy = target => {\n // Hide busy indicator\n\n const loading = target.querySelector(\".loading\")\n if (loading) {\n target.removeChild(target.querySelector(\".loading\"))\n }\n }\n\n const popState = () => {\n // Handle notebook history\n\n if (\n window.location.pathname === \"/coloraide/playground/\"\n ) {\n const current = decodeURIComponent(new URLSearchParams(window.location.search).toString())\n if (current !== lastSearch) {\n main(false) // eslint-disable-line no-use-before-define\n }\n }\n }\n\n const interceptClickEvent = e => {\n // Catch links to other notebook pages and handle them\n\n const target = e.target || e.srcElement\n if (target.tagName === \"A\" && main) { // eslint-disable-line no-use-before-define\n if (\n target.getAttribute(\"href\") &&\n target.host === window.location.host &&\n window.location.pathname === \"/coloraide/playground/\" &&\n window.location.pathname === target.pathname &&\n window.location.search !== target.search\n ) {\n e.preventDefault()\n const search = new URLSearchParams(target.search)\n const state = {}\n for (const [key, value] of search) {\n state[key] = value\n }\n history.pushState(state, \"\", target.href)\n main(false) // eslint-disable-line no-use-before-define\n }\n }\n }\n\n const handleTab = e => {\n // Prevent tab from tabbing out.\n\n if (e.key === 'Tab') {\n const target = e.target\n\n if (target.selectionStart !== target.selectionEnd) {\n e.preventDefault()\n\n let start = target.selectionStart\n let end = target.selectionEnd\n\n const text = target.value\n\n while (start > 0 && text[start - 1] !== '\\n') {\n start--\n }\n while (end > 0 && text[end - 1] !== '\\n' && end < text.length) {\n end++\n }\n\n let lines = text.substr(start, end - start).split('\\n')\n\n for (let i = 0; i < lines.length; i++) {\n\n // Don't indent last line if cursor at start of line\n if (i === lines.length - 1 && lines[i].length === 0) {\n continue\n }\n\n // Indent or deindent\n if (e.shiftKey) {\n lines[i] = lines[i].replace(tabStart, '')\n } else {\n lines[i] = ` ${lines[i]}`\n }\n }\n lines = lines.join('\\n')\n\n // Update the text area\n target.value = text.substr(0, start) + lines + text.substr(end)\n target.selectionStart = start\n target.selectionEnd = start + lines.length\n }\n }\n }\n\n const init = async first => {\n // Setup input highlighting and events to run Python code blocks.\n\n const notebook = document.getElementById(\"__notebook-source\")\n const playgrounds = document.querySelectorAll(\".playground\")\n playgrounds.forEach(pg => {\n\n const currentID = pg.id.replace(reIdNum, \"$1\")\n const inputs = document.getElementById(`__playground-inputs_${currentID}`)\n const results = document.getElementById(`__playground-results_${currentID}`)\n const pgcode = document.getElementById(`__playground-code_${currentID}`)\n const buttonEdit = document.querySelector(`button#__playground-edit_${currentID}`)\n const buttonShare = document.querySelector(`button#__playground-share_${currentID}`)\n const buttonRun = document.querySelector(`button#__playground-run_${currentID}`)\n const buttonCancel = document.querySelector(`button#__playground-cancel_${currentID}`)\n\n inputs.addEventListener(\"input\", () => {\n // Adjust textarea height on text input.\n\n textResize(inputs)\n })\n\n inputs.addEventListener('keydown', handleTab)\n\n if (notebook && first) {\n const notebookInput = document.getElementById(\"__notebook-input\")\n\n notebookInput.addEventListener(\"input\", e => {\n // Adjust textarea height on text input.\n\n textResize(e.target)\n })\n\n notebookInput.addEventListener('keydown', handleTab)\n\n const editPage = document.getElementById(\"__notebook-edit\")\n editPage.addEventListener(\"click\", () => {\n editTemp[notebookInput.id] = notebookInput.value\n document.getElementById(\"__notebook-render\").classList.toggle(\"hidden\")\n document.getElementById(\"__notebook-source\").classList.toggle(\"hidden\")\n textResize(document.getElementById(\"__notebook-input\"))\n })\n\n document.getElementById(\"__notebook-md-gist\").addEventListener(\"click\", async e => {\n let uri = prompt(\"Please enter link to the Markdown page source:\", gist) // eslint-disable-line no-alert\n if (uri !== null) {\n uri = encodeuri(uri)\n e.preventDefault()\n history.pushState({notebook: uri}, \"\", `?${new URLSearchParams(`notebook=${uri}`).toString()}`)\n main(false) // eslint-disable-line no-use-before-define\n }\n })\n\n document.getElementById(\"__notebook-py-gist\").addEventListener(\"click\", async e => {\n let uri = prompt(\"Please enter the link to the Python code source:\", gist) // eslint-disable-line no-alert\n if (uri !== null) {\n uri = encodeuri(uri)\n e.preventDefault()\n history.pushState({source: uri}, \"\", `?${new URLSearchParams(`source=${uri}`).toString()}`)\n main(false) // eslint-disable-line no-use-before-define\n }\n })\n\n document.getElementById(\"__notebook-input\").value = raw\n document.getElementById(\"__notebook-cancel\").addEventListener(\"click\", () => {\n notebookInput.value = editTemp[notebookInput.id]\n delete editTemp[notebookInput.id]\n document.getElementById(\"__notebook-render\").classList.toggle(\"hidden\")\n document.getElementById(\"__notebook-source\").classList.toggle(\"hidden\")\n })\n\n document.getElementById(\"__notebook-submit\").addEventListener(\"click\", async() => {\n const render = document.getElementById(\"__notebook-render\")\n raw = document.getElementById(\"__notebook-input\").value\n render.classList.toggle(\"hidden\")\n document.getElementById(\"__notebook-source\").classList.toggle(\"hidden\")\n const article = document.querySelector(\"article\")\n showBusy(article, \"Loading Notebook...\")\n render.innerHTML = \"\"\n editTemp = {}\n await setupPyodide()\n await pyrender(raw)\n await init()\n hideBusy(article)\n })\n }\n\n inputs.addEventListener(\"touchmove\", e => {\n // Stop propagation on \"touchmove\".\n\n e.stopPropagation()\n })\n\n buttonEdit.addEventListener(\"click\", async() => {\n // Handle the button click: show source or execute source.\n\n editTemp[currentID] = inputs.value\n pgcode.classList.toggle(\"hidden\")\n results.classList.toggle(\"hidden\")\n buttonRun.classList.toggle(\"hidden\")\n buttonCancel.classList.toggle(\"hidden\")\n buttonEdit.classList.toggle(\"hidden\")\n buttonShare.classList.toggle(\"hidden\")\n textResize(inputs)\n inputs.focus()\n })\n\n buttonShare.addEventListener(\"click\", async() => {\n // Handle the share click: copy URL with code as parameter.\n\n const uri = encodeuri(inputs.value)\n const loc = window.location\n let pathname = \"/playground/\"\n if (loc.pathname.startsWith(\"/coloraide/\")) {\n pathname = \"/coloraide/playground/\"\n }\n const path = `${loc.protocol}//${loc.host}${pathname}?code=${uri}`\n if (uri.length > 1000) {\n alert(\"Code must be under a 1000 characters to generate a URL!\") // eslint-disable-line no-alert\n } else {\n navigator.clipboard.writeText(path).then(async() => {\n alert(\"Link copied to clipboard :)\") // eslint-disable-line no-alert\n }, async() => {\n alert(\"Failed to copy link clipboard!\") // eslint-disable-line no-alert\n })\n }\n })\n\n buttonRun.addEventListener(\"click\", async() => {\n // Handle the button click: show source or execute source.\n\n if (busy) {\n return\n }\n\n busy = true\n // Load Pyodide and related packages.\n const form = pgcode.querySelector(\"form\")\n showBusy(form, null, true)\n const buttons = document.querySelectorAll(\".playground .playground-run\")\n if (buttons) {\n buttons.forEach(b => {\n b.setAttribute(\"disabled\", \"\")\n })\n }\n await setupPyodide()\n results.querySelector(\"code\").innerHTML = \"\"\n await pyexecute(currentID)\n if (buttons) {\n buttons.forEach(b => {\n b.removeAttribute(\"disabled\")\n })\n }\n hideBusy(form)\n pgcode.classList.toggle(\"hidden\")\n results.classList.toggle(\"hidden\")\n buttonEdit.classList.toggle(\"hidden\")\n buttonShare.classList.toggle(\"hidden\")\n buttonRun.classList.toggle(\"hidden\")\n buttonCancel.classList.toggle(\"hidden\")\n\n delete editTemp[currentID]\n busy = false\n })\n\n buttonCancel.addEventListener(\"click\", () => {\n // Cancel edit.\n\n inputs.value = editTemp[currentID]\n delete editTemp[currentID]\n pgcode.classList.toggle(\"hidden\")\n results.classList.toggle(\"hidden\")\n buttonEdit.classList.toggle(\"hidden\")\n buttonShare.classList.toggle(\"hidden\")\n buttonRun.classList.toggle(\"hidden\")\n buttonCancel.classList.toggle(\"hidden\")\n })\n })\n }\n\n const main = async first => {\n // Load external source to render in a playground.\n // This can be something like a file on a gist we must read in (?source=)\n // or raw code (?code=).\n\n editTemp = {}\n\n if (window.location.pathname.endsWith(\"/playground/\")) {\n const params = new URLSearchParams(window.location.search)\n const loadMsg = \"Loading Pyodide...\"\n const pageMsg = \"Loading Notebook...\"\n const uri = params.has(\"source\") ? params.get(\"source\") : params.get(\"notebook\")\n const article = document.querySelector(\"article\")\n if (uri !== null && uri.trim()) {\n // A source was specified, so load it.\n showBusy(article, loadMsg)\n await setupPyodide()\n hideBusy(article)\n showBusy(article, pageMsg)\n try {\n const gistType = params.has(\"source\") ? \"source\" : \"notebook\"\n lastSearch = decodeURIComponent(params.toString())\n let value = \"\"\n const xhr = new XMLHttpRequest()\n gist = uri\n xhr.open(\"GET\", uri, true)\n xhr.onload = async() => {\n // Try and load the requested content\n if (xhr.readyState === 4) {\n if (xhr.status === 200) {\n value = xhr.responseText\n }\n }\n\n if (gistType === \"source\") {\n value = getContent(value)\n }\n await pyrender(value)\n await init(first)\n hideBusy(article)\n fakeDOMContentLoaded()\n }\n xhr.send()\n } catch (err) {} // eslint-disable-line no-empty\n } else {\n gist = \"\"\n const content = getContent(params.has(\"code\") ? params.get(\"code\") : defContent)\n lastSearch = decodeURIComponent(params.toString())\n showBusy(article, loadMsg)\n await setupPyodide()\n hideBusy(article)\n showBusy(article, pageMsg)\n await pyrender(content)\n await init(first)\n hideBusy(article)\n fakeDOMContentLoaded()\n }\n } else {\n gist = \"\"\n lastSearch = \"\"\n init(first)\n }\n }\n\n // Capture links in notebook pages so that we can make playgound links load instantly\n document.addEventListener(\"click\", interceptClickEvent)\n\n // Handle history of notebook pages as they are loaded dynamically\n window.addEventListener(\"popstate\", popState)\n\n // Before leaving, turn off fake, just in case we navigated away before finished\n window.addEventListener(\"unload\", () => {\n fake = true\n })\n\n // Attach main via subscribe (subscribes to Materials on page load and instant page loads)\n window.document$.subscribe(() => {\n // To get other libraries to to reload, we may create a fake `DOMContentLoaded`\n // No need to process these events.\n if (fake) {\n fake = false\n return\n }\n main(true)\n })\n})()\n"],"names":["pyodide","busy","raw","gist","editTemp","reIdNum","initialized","lastSearch","fake","tabStart","pycode","getContent","fakeDOMContentLoaded","textResize","encodeuri","pyexecute","pyrender","setupPyodide","showBusy","hideBusy","handleTab","init","main","content","window","document","dispatchEvent","Event","bubbles","cancelable","inpt","scrollLeft","pageXOffset","documentElement","body","parentNode","scrollTop","pageYOffset","style","height","scrollHeight","scrollTo","uri","encodeURIComponent","replace","c","charCodeAt","toString","currentID","currentInputs","getElementById","setAttribute","globals","set","runPythonAsync","removeAttribute","text","src","value","location","hash","href","loadPyodide","indexURL","fullStdLib","loadPackage","target","label","relative","loaderLabel","classes","template","createElement","innerHTML","appendChild","firstChild","querySelector","removeChild","e","key","selectionStart","selectionEnd","preventDefault","start","end","length","lines","substr","split","i","shiftKey","join","first","notebook","querySelectorAll","forEach","pg","id","inputs","results","pgcode","buttonEdit","buttonShare","buttonRun","buttonCancel","addEventListener","notebookInput","classList","toggle","prompt","history","pushState","URLSearchParams","source","render","article","stopPropagation","focus","loc","pathname","startsWith","path","protocol","host","alert","navigator","clipboard","writeText","then","form","buttons","b","endsWith","params","search","loadMsg","pageMsg","has","get","trim","gistType","decodeURIComponent","xhr","XMLHttpRequest","open","onload","readyState","status","responseText","send","err","srcElement","tagName","getAttribute","state","document$","subscribe"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"extra-notebook-678306ab.js","sources":["../../../src/js/extra-notebook.js"],"sourcesContent":["(() => {\n let pyodide = null\n let busy = false\n let raw = \"\"\n let gist = \"\"\n let editTemp = {}\n const reIdNum = /.*?_(\\d+)$/\n let initialized = false\n let lastSearch = \"\"\n let fake = false\n const tabStart = /^( {1,4}|\\t)/\n // This is the Python payload that will be executed when the user\n // presses the `Run` button. It will execute the code, create a\n // Python console output, find color references, steps, and interpolation\n // references and render the appropriate preview.\n const pycode = \"{{pycode}}\"\n const defContent = \"import coloraide\\ncoloraide.__version__\\nColor('red')\"\n\n const getContent = content => {\n return `\n!!! new \"This notebook is powered by [Pyodide](https://github.com/pyodide/pyodide). \\\nLearn more [here](\\\n?notebook=https://gist.githubusercontent.com/facelessuser/7c819668b5eb248ecb9ac608d91391cf/raw/playground.md\\\n). Preview, convert, interpolate, and explore!\"\n\n\\`\\`\\`\\`\\`\\`\\`\\`playground\n${content}\n\\`\\`\\`\\`\\`\\`\\`\\`\n`\n }\n\n const fakeDOMContentLoaded = () => {\n // Send a fake `DOMContentLoaded`\n fake = true\n window.document.dispatchEvent(new Event(\"DOMContentLoaded\", {\n bubbles: true,\n cancelable: true\n }))\n }\n\n const textResize = inpt => {\n // Resize inputs based on text height.\n\n const scrollLeft = window.pageXOffset ||\n (document.documentElement || document.body.parentNode || document.body).scrollLeft\n\n const scrollTop = window.pageYOffset ||\n (document.documentElement || document.body.parentNode || document.body).scrollTop\n\n inpt.style.height = \"5px\"\n inpt.style.height = `${inpt.scrollHeight}px`\n\n window.scrollTo(scrollLeft, scrollTop)\n }\n\n const encodeuri = uri => {\n // Encode the URI component.\n\n return encodeURIComponent(uri).replace(/[.!'()*]/g, c => {\n return `%${c.charCodeAt(0).toString(16)}`\n })\n }\n\n const pyexecute = async currentID => {\n // Execute Python code\n\n const currentInputs = document.getElementById(`__playground-inputs_${currentID}`)\n currentInputs.setAttribute(\"readonly\", \"\")\n pyodide.globals.set(\"id_num\", currentID)\n pyodide.globals.set(\"action\", \"notebook\")\n await pyodide.runPythonAsync(pycode)\n currentInputs.removeAttribute(\"readonly\")\n }\n\n const pyrender = async text => {\n // Execute Python code\n\n pyodide.globals.set(\"content\", text)\n pyodide.globals.set(\"action\", \"render\")\n await pyodide.runPythonAsync(pycode)\n const src = document.getElementById(\"__notebook-input\")\n if (src) {\n raw = text\n src.value = text\n }\n if (window.location.hash) {\n // Force jumping to hashes\n window.location.href = window.location.href // eslint-disable-line no-self-assign\n }\n }\n\n const setupPyodide = async() => {\n // Load `Pyodide` and the any default packages we can need and can load.\n\n if (!initialized) {\n initialized = true\n pyodide = await loadPyodide({ // eslint-disable-line no-undef\n indexURL: \"https://cdn.jsdelivr.net/pyodide/v0.19.0/full/\",\n fullStdLib: false\n })\n await pyodide.loadPackage([\"micropip\", \"Pygments\"])\n }\n }\n\n const showBusy = (target, label, relative) => {\n // Show busy indicator\n\n const loaderLabel = (typeof label === \"undefined\" || label === null) ? \"Loading...\" : label\n const classes = relative ? \"loading relative\" : \"loading\"\n const template = document.createElement(\"template\")\n template.innerHTML = `
${loaderLabel}
`\n target.appendChild(template.content.firstChild)\n }\n\n const hideBusy = target => {\n // Hide busy indicator\n\n const loading = target.querySelector(\".loading\")\n if (loading) {\n target.removeChild(target.querySelector(\".loading\"))\n }\n }\n\n const popState = () => {\n // Handle notebook history\n\n if (\n window.location.pathname === \"/coloraide/playground/\"\n ) {\n const current = decodeURIComponent(new URLSearchParams(window.location.search).toString())\n if (current !== lastSearch) {\n main(false) // eslint-disable-line no-use-before-define\n }\n }\n }\n\n const interceptClickEvent = e => {\n // Catch links to other notebook pages and handle them\n\n const target = e.target || e.srcElement\n if (target.tagName === \"A\" && main) { // eslint-disable-line no-use-before-define\n if (\n target.getAttribute(\"href\") &&\n target.host === window.location.host &&\n window.location.pathname === \"/coloraide/playground/\" &&\n window.location.pathname === target.pathname &&\n window.location.search !== target.search\n ) {\n e.preventDefault()\n const search = new URLSearchParams(target.search)\n const state = {}\n for (const [key, value] of search) {\n state[key] = value\n }\n history.pushState(state, \"\", target.href)\n main(false) // eslint-disable-line no-use-before-define\n }\n }\n }\n\n const handleTab = e => {\n // Prevent tab from tabbing out.\n\n if (e.key === 'Tab') {\n const target = e.target\n\n if (target.selectionStart !== target.selectionEnd) {\n e.preventDefault()\n\n let start = target.selectionStart\n let end = target.selectionEnd\n\n const text = target.value\n\n while (start > 0 && text[start - 1] !== '\\n') {\n start--\n }\n while (end > 0 && text[end - 1] !== '\\n' && end < text.length) {\n end++\n }\n\n let lines = text.substr(start, end - start).split('\\n')\n\n for (let i = 0; i < lines.length; i++) {\n\n // Don't indent last line if cursor at start of line\n if (i === lines.length - 1 && lines[i].length === 0) {\n continue\n }\n\n // Indent or deindent\n if (e.shiftKey) {\n lines[i] = lines[i].replace(tabStart, '')\n } else {\n lines[i] = ` ${lines[i]}`\n }\n }\n lines = lines.join('\\n')\n\n // Update the text area\n target.value = text.substr(0, start) + lines + text.substr(end)\n target.selectionStart = start\n target.selectionEnd = start + lines.length\n }\n }\n }\n\n const init = async first => {\n // Setup input highlighting and events to run Python code blocks.\n\n const notebook = document.getElementById(\"__notebook-source\")\n const playgrounds = document.querySelectorAll(\".playground\")\n playgrounds.forEach(pg => {\n\n const currentID = pg.id.replace(reIdNum, \"$1\")\n const inputs = document.getElementById(`__playground-inputs_${currentID}`)\n const results = document.getElementById(`__playground-results_${currentID}`)\n const pgcode = document.getElementById(`__playground-code_${currentID}`)\n const buttonEdit = document.querySelector(`button#__playground-edit_${currentID}`)\n const buttonShare = document.querySelector(`button#__playground-share_${currentID}`)\n const buttonRun = document.querySelector(`button#__playground-run_${currentID}`)\n const buttonCancel = document.querySelector(`button#__playground-cancel_${currentID}`)\n\n inputs.addEventListener(\"input\", () => {\n // Adjust textarea height on text input.\n\n textResize(inputs)\n })\n\n inputs.addEventListener('keydown', handleTab)\n\n if (notebook && first) {\n const notebookInput = document.getElementById(\"__notebook-input\")\n\n notebookInput.addEventListener(\"input\", e => {\n // Adjust textarea height on text input.\n\n textResize(e.target)\n })\n\n notebookInput.addEventListener('keydown', handleTab)\n\n const editPage = document.getElementById(\"__notebook-edit\")\n editPage.addEventListener(\"click\", () => {\n editTemp[notebookInput.id] = notebookInput.value\n document.getElementById(\"__notebook-render\").classList.toggle(\"hidden\")\n document.getElementById(\"__notebook-source\").classList.toggle(\"hidden\")\n textResize(document.getElementById(\"__notebook-input\"))\n })\n\n document.getElementById(\"__notebook-md-gist\").addEventListener(\"click\", async e => {\n let uri = prompt(\"Please enter link to the Markdown page source:\", gist) // eslint-disable-line no-alert\n if (uri !== null) {\n uri = encodeuri(uri)\n e.preventDefault()\n history.pushState({notebook: uri}, \"\", `?${new URLSearchParams(`notebook=${uri}`).toString()}`)\n main(false) // eslint-disable-line no-use-before-define\n }\n })\n\n document.getElementById(\"__notebook-py-gist\").addEventListener(\"click\", async e => {\n let uri = prompt(\"Please enter the link to the Python code source:\", gist) // eslint-disable-line no-alert\n if (uri !== null) {\n uri = encodeuri(uri)\n e.preventDefault()\n history.pushState({source: uri}, \"\", `?${new URLSearchParams(`source=${uri}`).toString()}`)\n main(false) // eslint-disable-line no-use-before-define\n }\n })\n\n document.getElementById(\"__notebook-input\").value = raw\n document.getElementById(\"__notebook-cancel\").addEventListener(\"click\", () => {\n notebookInput.value = editTemp[notebookInput.id]\n delete editTemp[notebookInput.id]\n document.getElementById(\"__notebook-render\").classList.toggle(\"hidden\")\n document.getElementById(\"__notebook-source\").classList.toggle(\"hidden\")\n })\n\n document.getElementById(\"__notebook-submit\").addEventListener(\"click\", async() => {\n const render = document.getElementById(\"__notebook-render\")\n raw = document.getElementById(\"__notebook-input\").value\n render.classList.toggle(\"hidden\")\n document.getElementById(\"__notebook-source\").classList.toggle(\"hidden\")\n const article = document.querySelector(\"article\")\n showBusy(article, \"Loading Notebook...\")\n render.innerHTML = \"\"\n editTemp = {}\n await setupPyodide()\n await pyrender(raw)\n await init()\n hideBusy(article)\n })\n }\n\n inputs.addEventListener(\"touchmove\", e => {\n // Stop propagation on \"touchmove\".\n\n e.stopPropagation()\n })\n\n buttonEdit.addEventListener(\"click\", async() => {\n // Handle the button click: show source or execute source.\n\n editTemp[currentID] = inputs.value\n pgcode.classList.toggle(\"hidden\")\n results.classList.toggle(\"hidden\")\n buttonRun.classList.toggle(\"hidden\")\n buttonCancel.classList.toggle(\"hidden\")\n buttonEdit.classList.toggle(\"hidden\")\n buttonShare.classList.toggle(\"hidden\")\n textResize(inputs)\n inputs.focus()\n })\n\n buttonShare.addEventListener(\"click\", async() => {\n // Handle the share click: copy URL with code as parameter.\n\n const uri = encodeuri(inputs.value)\n const loc = window.location\n let pathname = \"/playground/\"\n if (loc.pathname.startsWith(\"/coloraide/\")) {\n pathname = \"/coloraide/playground/\"\n }\n const path = `${loc.protocol}//${loc.host}${pathname}?code=${uri}`\n if (uri.length > 1000) {\n alert(\"Code must be under a 1000 characters to generate a URL!\") // eslint-disable-line no-alert\n } else {\n navigator.clipboard.writeText(path).then(async() => {\n alert(\"Link copied to clipboard :)\") // eslint-disable-line no-alert\n }, async() => {\n alert(\"Failed to copy link clipboard!\") // eslint-disable-line no-alert\n })\n }\n })\n\n buttonRun.addEventListener(\"click\", async() => {\n // Handle the button click: show source or execute source.\n\n if (busy) {\n return\n }\n\n busy = true\n // Load Pyodide and related packages.\n const form = pgcode.querySelector(\"form\")\n showBusy(form, null, true)\n const buttons = document.querySelectorAll(\".playground .playground-run\")\n if (buttons) {\n buttons.forEach(b => {\n b.setAttribute(\"disabled\", \"\")\n })\n }\n await setupPyodide()\n results.querySelector(\"code\").innerHTML = \"\"\n await pyexecute(currentID)\n if (buttons) {\n buttons.forEach(b => {\n b.removeAttribute(\"disabled\")\n })\n }\n hideBusy(form)\n pgcode.classList.toggle(\"hidden\")\n results.classList.toggle(\"hidden\")\n buttonEdit.classList.toggle(\"hidden\")\n buttonShare.classList.toggle(\"hidden\")\n buttonRun.classList.toggle(\"hidden\")\n buttonCancel.classList.toggle(\"hidden\")\n\n delete editTemp[currentID]\n busy = false\n })\n\n buttonCancel.addEventListener(\"click\", () => {\n // Cancel edit.\n\n inputs.value = editTemp[currentID]\n delete editTemp[currentID]\n pgcode.classList.toggle(\"hidden\")\n results.classList.toggle(\"hidden\")\n buttonEdit.classList.toggle(\"hidden\")\n buttonShare.classList.toggle(\"hidden\")\n buttonRun.classList.toggle(\"hidden\")\n buttonCancel.classList.toggle(\"hidden\")\n })\n })\n }\n\n const main = async first => {\n // Load external source to render in a playground.\n // This can be something like a file on a gist we must read in (?source=)\n // or raw code (?code=).\n\n editTemp = {}\n\n if (window.location.pathname.endsWith(\"/playground/\")) {\n const params = new URLSearchParams(window.location.search)\n const loadMsg = \"Loading Pyodide...\"\n const pageMsg = \"Loading Notebook...\"\n const uri = params.has(\"source\") ? params.get(\"source\") : params.get(\"notebook\")\n const article = document.querySelector(\"article\")\n if (uri !== null && uri.trim()) {\n // A source was specified, so load it.\n showBusy(article, loadMsg)\n await setupPyodide()\n hideBusy(article)\n showBusy(article, pageMsg)\n try {\n const gistType = params.has(\"source\") ? \"source\" : \"notebook\"\n lastSearch = decodeURIComponent(params.toString())\n let value = \"\"\n const xhr = new XMLHttpRequest()\n gist = uri\n xhr.open(\"GET\", uri, true)\n xhr.onload = async() => {\n // Try and load the requested content\n if (xhr.readyState === 4) {\n if (xhr.status === 200) {\n value = xhr.responseText\n }\n }\n\n if (gistType === \"source\") {\n value = getContent(value)\n }\n await pyrender(value)\n await init(first)\n hideBusy(article)\n fakeDOMContentLoaded()\n }\n xhr.send()\n } catch (err) {} // eslint-disable-line no-empty\n } else {\n gist = \"\"\n const content = getContent(params.has(\"code\") ? params.get(\"code\") : defContent)\n lastSearch = decodeURIComponent(params.toString())\n showBusy(article, loadMsg)\n await setupPyodide()\n hideBusy(article)\n showBusy(article, pageMsg)\n await pyrender(content)\n await init(first)\n hideBusy(article)\n fakeDOMContentLoaded()\n }\n } else {\n gist = \"\"\n lastSearch = \"\"\n init(first)\n }\n }\n\n // Capture links in notebook pages so that we can make playgound links load instantly\n document.addEventListener(\"click\", interceptClickEvent)\n\n // Handle history of notebook pages as they are loaded dynamically\n window.addEventListener(\"popstate\", popState)\n\n // Before leaving, turn off fake, just in case we navigated away before finished\n window.addEventListener(\"unload\", () => {\n fake = true\n })\n\n // Attach main via subscribe (subscribes to Materials on page load and instant page loads)\n window.document$.subscribe(() => {\n // To get other libraries to to reload, we may create a fake `DOMContentLoaded`\n // No need to process these events.\n if (fake) {\n fake = false\n return\n }\n main(true)\n })\n})()\n"],"names":["pyodide","busy","raw","gist","editTemp","reIdNum","initialized","lastSearch","fake","tabStart","pycode","getContent","fakeDOMContentLoaded","textResize","encodeuri","pyexecute","pyrender","setupPyodide","showBusy","hideBusy","handleTab","init","main","content","window","document","dispatchEvent","Event","bubbles","cancelable","inpt","scrollLeft","pageXOffset","documentElement","body","parentNode","scrollTop","pageYOffset","style","height","scrollHeight","scrollTo","uri","encodeURIComponent","replace","c","charCodeAt","toString","currentID","currentInputs","getElementById","setAttribute","globals","set","runPythonAsync","removeAttribute","text","src","value","location","hash","href","loadPyodide","indexURL","fullStdLib","loadPackage","target","label","relative","loaderLabel","classes","template","createElement","innerHTML","appendChild","firstChild","querySelector","removeChild","e","key","selectionStart","selectionEnd","preventDefault","start","end","length","lines","substr","split","i","shiftKey","join","first","notebook","querySelectorAll","forEach","pg","id","inputs","results","pgcode","buttonEdit","buttonShare","buttonRun","buttonCancel","addEventListener","notebookInput","classList","toggle","prompt","history","pushState","URLSearchParams","source","render","article","stopPropagation","focus","loc","pathname","startsWith","path","protocol","host","alert","navigator","clipboard","writeText","then","form","buttons","b","endsWith","params","search","loadMsg","pageMsg","has","get","trim","gistType","decodeURIComponent","xhr","XMLHttpRequest","open","onload","readyState","status","responseText","send","err","srcElement","tagName","getAttribute","state","document$","subscribe"],"mappings":"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"} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index d47c177d1..5c555e949 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -122,7 +122,7 @@ extra_css: - assets/coloraide-extras/extra-ccd229caf0.css extra_javascript: - https://cdn.jsdelivr.net/pyodide/v0.19.0/full/pyodide.js - - assets/coloraide-extras/extra-notebook-8732391e.js + - assets/coloraide-extras/extra-notebook-678306ab.js extra: social: diff --git a/tests/test_a98_rgb.py b/tests/test_a98_rgb.py index 7e9fc4bbe..0a6b93b4a 100644 --- a/tests/test_a98_rgb.py +++ b/tests/test_a98_rgb.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(a98-rgb 2 -1 0)').to_string(), - 'color(a98-rgb 1 1 1)' + 'color(a98-rgb 1 0.84447 0.82443)' ) self.assertEqual( diff --git a/tests/test_api.py b/tests/test_api.py index 1734a8392..a4d696510 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1502,7 +1502,7 @@ class Color2(Color): self.assertEqual( Color('color(srgb 2 -1 0)').fit().to_string(), - 'rgb(255 153.85 169.85)' + 'rgb(255 117.89 100.38)' ) self.assertEqual( diff --git a/tests/test_display_p3.py b/tests/test_display_p3.py index c32375753..182a26a28 100644 --- a/tests/test_display_p3.py +++ b/tests/test_display_p3.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(display-p3 2 -1 0)').to_string(), - 'color(display-p3 1 0.74486 0.77344)' + 'color(display-p3 1 0.60349 0.48071)' ) self.assertEqual( diff --git a/tests/test_hsl.py b/tests/test_hsl.py index 77b7d7d3b..3f5383bc9 100644 --- a/tests/test_hsl.py +++ b/tests/test_hsl.py @@ -168,7 +168,7 @@ def test_fit(self): self.assertEqual( Color('color(--hsl 20 150% 75%)').to_string(), - 'hsl(19.317 100% 77.159%)' + 'hsl(19.891 100% 76.496%)' ) self.assertEqual( diff --git a/tests/test_hsv.py b/tests/test_hsv.py index 8fed9f3d6..e72529ea7 100644 --- a/tests/test_hsv.py +++ b/tests/test_hsv.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(--hsv 20 1.5 0.75)').to_string(), - 'color(--hsv 51.799 1 0.45576)' + 'color(--hsv 40.492 1 0.50528)' ) self.assertEqual( diff --git a/tests/test_hwb.py b/tests/test_hwb.py index c81c3f8d0..07e711820 100644 --- a/tests/test_hwb.py +++ b/tests/test_hwb.py @@ -136,7 +136,7 @@ def test_fit(self): self.assertEqual( Color('color(--hwb 20 0% -55%)').to_string(), - 'hwb(14.271 82.804% 0%)' + 'hwb(17.844 77.324% 0%)' ) self.assertEqual( diff --git a/tests/test_okhsl.py b/tests/test_okhsl.py index ee81b00d1..68d65dda7 100644 --- a/tests/test_okhsl.py +++ b/tests/test_okhsl.py @@ -76,7 +76,7 @@ def test_fit(self): self.assertEqual( Color('color(--okhsl 50 40% 110%)').to_string(), - 'color(--okhsl 0 0 1)' + 'color(--okhsl 90 0 1)' ) self.assertEqual( diff --git a/tests/test_okhsv.py b/tests/test_okhsv.py index d68b0c4c5..650eb80b7 100644 --- a/tests/test_okhsv.py +++ b/tests/test_okhsv.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(--okhsv 20 150% 75%)').to_string(), - 'color(--okhsv 20 1 0.60559)' + 'color(--okhsv 23.815 1 0.57453)' ) self.assertEqual( diff --git a/tests/test_prophoto_rgb.py b/tests/test_prophoto_rgb.py index f536eea50..2ade493cb 100644 --- a/tests/test_prophoto_rgb.py +++ b/tests/test_prophoto_rgb.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(prophoto-rgb 2 -1 0)').to_string(), - 'color(prophoto-rgb 1 0.38166 0.5562)' + 'color(prophoto-rgb 1 0 0.25853)' ) self.assertEqual( diff --git a/tests/test_rec2020.py b/tests/test_rec2020.py index ac9e8f398..ea9c8f03f 100644 --- a/tests/test_rec2020.py +++ b/tests/test_rec2020.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(rec2020 2 -1 0)').to_string(), - 'color(rec2020 1 0.72306 0.76062)' + 'color(rec2020 1 0.51313 0.38209)' ) self.assertEqual( diff --git a/tests/test_srgb.py b/tests/test_srgb.py index 35fb4360e..db77da997 100644 --- a/tests/test_srgb.py +++ b/tests/test_srgb.py @@ -299,7 +299,7 @@ def test_fit(self): self.assertEqual( Color('color(srgb 2 -1 0)').to_string(), - 'rgb(255 153.85 169.85)' + 'rgb(255 117.89 100.38)' ) self.assertEqual( diff --git a/tests/test_srgb_linear.py b/tests/test_srgb_linear.py index 8823f3666..50b5cd51e 100644 --- a/tests/test_srgb_linear.py +++ b/tests/test_srgb_linear.py @@ -66,7 +66,7 @@ def test_fit(self): self.assertEqual( Color('color(srgb-linear 2 0 0)').to_string(), - 'color(srgb-linear 1 0.29816 0.22913)' + 'color(srgb-linear 1 0.26331 0.14651)' ) self.assertEqual(