diff --git a/package/src/import_map.json b/package/src/import_map.json index e876a9ca167..5996351399e 100644 --- a/package/src/import_map.json +++ b/package/src/import_map.json @@ -12,6 +12,7 @@ "streams/": "https://deno.land/std@0.138.0/streams/", "textproto/": "https://deno.land/std@0.138.0/textproto/", "uuid/": "https://deno.land/std@0.138.0/uuid/", + "node/": "https://deno.land/std@0.138.0/node/", "cliffy/": "https://deno.land/x/cliffy@v0.24.2/", "dayjs/": "https://cdn.skypack.dev/dayjs@1.8.21/", "moment-guess": "https://cdn.skypack.dev/moment-guess@1.2.4", @@ -19,6 +20,7 @@ "cache/": "https://deno.land/x/cache@0.2.12/", "media_types/": "https://deno.land/x/media_types@v2.10.1/", "observablehq/parser": "https://cdn.skypack.dev/@observablehq/parser@4.5.0", + "events/": "https://deno.land/x/events@v1.0.0/", "xmlp/": "https://deno.land/x/xmlp@v0.2.8/", "ajv": "https://cdn.skypack.dev/ajv@8.8.2", "blueimpMd5": "https://cdn.skypack.dev/blueimp-md5@2.19.0", @@ -104,4 +106,4 @@ "/npm:set-immediate-shim@1.0?dew": "./resources/vendor/dev.jspm.io/npm_set-immediate-shim@1.0.js" } } -} \ No newline at end of file +} diff --git a/src/core/cri/cri.ts b/src/core/cri/cri.ts new file mode 100644 index 00000000000..bbc75db368f --- /dev/null +++ b/src/core/cri/cri.ts @@ -0,0 +1,171 @@ +/** + * cri.ts + * + * Chrome Remote Interface + * + * Copyright (c) 2022 by RStudio, PBC. + */ + +import { decode } from "encoding/base64.ts"; +import cdp from "./deno-cri/index.js"; +import { getBrowserExecutablePath } from "../puppeteer.ts"; +import { Semaphore } from "../lib/semaphore.ts"; +import { findOpenPort } from "../port.ts"; + +async function waitForServer(port: number, timeout = 3000) { + const interval = 50; + let soFar = 0; + + do { + try { + const response = await fetch(`http://localhost:${port}/json/list`); + if (response.status !== 200) { + soFar += interval; + await new Promise((resolve) => setTimeout(resolve, interval)); + continue; + } else { + return true; + } + } catch (_e) { + soFar += interval; + await new Promise((resolve) => setTimeout(resolve, interval)); + } + } while (soFar < timeout); + return false; +} + +const criSemaphore = new Semaphore(1); + +export function withCriClient( + fn: (client: Awaited>) => Promise, + appPath?: string, + port?: number, +): Promise { + if (port === undefined) { + port = findOpenPort(9222); + } + + return criSemaphore.runExclusive(async () => { + const client = await criClient(appPath, port); + try { + const result = await fn(client); + await client.close(); + return result; + } catch (e) { + await client.close(); + throw e; + } + }); +} + +export async function criClient(appPath?: string, port?: number) { + if (port === undefined) { + port = findOpenPort(9222); + } + if (appPath === undefined) { + appPath = await getBrowserExecutablePath(); + } + + const cmd = [ + appPath as string, + "--headless", + "--no-sandbox", + "--single-process", + "--disable-gpu", + `--remote-debugging-port=${port}`, + ]; + const browser = Deno.run({ cmd, stdout: "piped", stderr: "piped" }); + + if (!(await waitForServer(port))) { + throw new Error("Couldn't find open server"); + } + + // deno-lint-ignore no-explicit-any + let client: any; + + const result = { + close: async () => { + await client.close(); + browser.close(); + }, + + rawClient: () => client, + + open: async (url: string) => { + client = await cdp(); + const { Network, Page } = client; + await Network.enable(); + await Page.enable(); + await Page.navigate({ url }); + return new Promise((fulfill, _reject) => { + Page.loadEventFired(() => { + fulfill(null); + }); + }); + }, + + docQuerySelectorAll: async (cssSelector: string): Promise => { + await client.DOM.enable(); + const doc = await client.DOM.getDocument(); + const nodeIds = await client.DOM.querySelectorAll({ + nodeId: doc.root.nodeId, + selector: cssSelector, + }); + return nodeIds.nodeIds; + }, + + contents: async (cssSelector: string): Promise => { + const nodeIds = await result.docQuerySelectorAll(cssSelector); + return Promise.all( + // deno-lint-ignore no-explicit-any + nodeIds.map(async (nodeId: any) => { + return (await client.DOM.getOuterHTML({ nodeId })).outerHTML; + }), + ); + }, + + // defaults to screenshotting at 4x scale = 392dpi. + screenshots: async ( + cssSelector: string, + scale = 4, + ): Promise<{ nodeId: number; data: Uint8Array }[]> => { + const nodeIds = await result.docQuerySelectorAll(cssSelector); + const lst: { nodeId: number; data: Uint8Array }[] = []; + for (const nodeId of nodeIds) { + // the docs say that inline elements might return more than one box + // TODO what do we do in that case? + let quad; + try { + quad = (await client.DOM.getContentQuads({ nodeId })).quads[0]; + } catch (_e) { + // TODO report error? + continue; + } + const minX = Math.min(quad[0], quad[2], quad[4], quad[6]); + const maxX = Math.max(quad[0], quad[2], quad[4], quad[6]); + const minY = Math.min(quad[1], quad[3], quad[5], quad[7]); + const maxY = Math.max(quad[1], quad[3], quad[5], quad[7]); + try { + const screenshot = await client.Page.captureScreenshot({ + clip: { + x: minX, + y: minY, + width: maxX - minX, + height: maxY - minY, + scale, + }, + fromSurface: true, + captureBeyondViewport: true, + }); + const buf = decode(screenshot.data); + lst.push({ nodeId, data: buf }); + } catch (_e) { + // TODO report error? + continue; + } + } + return lst; + }, + }; + return result; +} diff --git a/src/core/cri/deno-cri/README.md b/src/core/cri/deno-cri/README.md new file mode 100644 index 00000000000..a0d44eb185d --- /dev/null +++ b/src/core/cri/deno-cri/README.md @@ -0,0 +1,3 @@ +## A deno port of https://github.com/cyrus-and/chrome-remote-interface + +This directory contains a minimal, self-contained deno port of https://github.com/cyrus-and/chrome-remote-interface diff --git a/src/core/cri/deno-cri/api.js b/src/core/cri/deno-cri/api.js new file mode 100644 index 00000000000..252c6410065 --- /dev/null +++ b/src/core/cri/deno-cri/api.js @@ -0,0 +1,98 @@ +/* + * api.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +function arrayToObject(parameters) { + const keyValue = {}; + parameters.forEach((parameter) => { + const name = parameter.name; + delete parameter.name; + keyValue[name] = parameter; + }); + return keyValue; +} + +function decorate(to, category, object) { + to.category = category; + Object.keys(object).forEach((field) => { + // skip the 'name' field as it is part of the function prototype + if (field === "name") { + return; + } + // commands and events have parameters whereas types have properties + if ( + (category === "type" && field === "properties") || + field === "parameters" + ) { + to[field] = arrayToObject(object[field]); + } else { + to[field] = object[field]; + } + }); +} + +function addCommand(chrome, domainName, command) { + const commandName = `${domainName}.${command.name}`; + const handler = (params, sessionId, callback) => { + return chrome.send(commandName, params, sessionId, callback); + }; + decorate(handler, "command", command); + chrome[commandName] = chrome[domainName][command.name] = handler; +} + +function addEvent(chrome, domainName, event) { + const eventName = `${domainName}.${event.name}`; + const handler = (sessionId, handler) => { + if (typeof sessionId === "function") { + handler = sessionId; + sessionId = undefined; + } + const rawEventName = sessionId ? `${eventName}.${sessionId}` : eventName; + if (typeof handler === "function") { + chrome.on(rawEventName, handler); + return () => chrome.removeListener(rawEventName, handler); + } else { + return new Promise((fulfill, _reject) => { + chrome.once(rawEventName, fulfill); + }); + } + }; + decorate(handler, "event", event); + chrome[eventName] = chrome[domainName][event.name] = handler; +} + +function addType(chrome, domainName, type) { + const typeName = `${domainName}.${type.id}`; + const help = {}; + decorate(help, "type", type); + chrome[typeName] = chrome[domainName][type.id] = help; +} + +export function prepare(object, protocol) { + // assign the protocol and generate the shorthands + object.protocol = protocol; + protocol.domains.forEach((domain) => { + const domainName = domain.domain; + object[domainName] = {}; + // add commands + (domain.commands || []).forEach((command) => { + addCommand(object, domainName, command); + }); + // add events + (domain.events || []).forEach((event) => { + addEvent(object, domainName, event); + }); + // add types + (domain.types || []).forEach((type) => { + addType(object, domainName, type); + }); + // add utility listener for each domain + object[domainName].on = (eventName, handler) => { + return object[domainName][eventName](handler); + }; + }); +} diff --git a/src/core/cri/deno-cri/chrome.js b/src/core/cri/deno-cri/chrome.js new file mode 100644 index 00000000000..00d862b569e --- /dev/null +++ b/src/core/cri/deno-cri/chrome.js @@ -0,0 +1,307 @@ +/* + * chrome.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +import EventEmitter from "events/mod.ts"; + +// const util = require('util'); +import { format as formatUrl, parse as parseUrl } from "node/url.ts"; +import { nextTick } from "node/_next_tick.ts"; + +/* const formatUrl = require('url').format; +const parseUrl = require('url').parse; + */ +// const WebSocket = require('ws'); + +import * as api from "./api.js"; +import * as defaults from "./defaults.js"; +import * as devtools from "./devtools.js"; + +/* const api = require('./api.js'); +const defaults = require('./defaults.js'); +const devtools = require('./devtools.js'); + */ + +class ProtocolError extends Error { + constructor(request, response) { + let { message } = response; + if (response.data) { + message += ` (${response.data})`; + } + super(message); + // attach the original response as well + this.request = request; + this.response = response; + } +} + +export default class Chrome extends EventEmitter { + constructor(options, notifier) { + super(); + // options + const defaultTarget = (targets) => { + // prefer type = 'page' inspectable targets as they represents + // browser tabs (fall back to the first inspectable target + // otherwise) + let backup; + let target = targets.find((target) => { + if (target.webSocketDebuggerUrl) { + backup = backup || target; + return target.type === "page"; + } else { + return false; + } + }); + target = target || backup; + if (target) { + return target; + } else { + throw new Error("No inspectable targets"); + } + }; + options = options || {}; + this.host = options.host || defaults.HOST; + this.port = options.port || defaults.PORT; + this.secure = !!options.secure; + this.useHostName = !!options.useHostName; + this.alterPath = options.alterPath || ((path) => path); + this.protocol = options.protocol; + this.local = !!options.local; + this.target = options.target || defaultTarget; + // locals + this._notifier = notifier; + this._callbacks = {}; + this._nextCommandId = 1; + // properties + this.webSocketUrl = undefined; + // operations + this._start(); + } + + // avoid misinterpreting protocol's members as custom util.inspect functions + inspect(_depth, options) { + options.customInspect = false; + return Deno.inspect(this, options); + } + + send(method, params, sessionId, callback) { + // handle optional arguments + const optionals = Array.from(arguments).slice(1); + params = optionals.find((x) => typeof x === "object"); + sessionId = optionals.find((x) => typeof x === "string"); + callback = optionals.find((x) => typeof x === "function"); + // return a promise when a callback is not provided + if (typeof callback === "function") { + this._enqueueCommand(method, params, sessionId, callback); + return undefined; + } else { + return new Promise((fulfill, reject) => { + this._enqueueCommand(method, params, sessionId, (error, response) => { + if (error) { + const request = { method, params, sessionId }; + reject( + error instanceof Error + ? error // low-level WebSocket error + : new ProtocolError(request, response) + ); + } else { + fulfill(response); + } + }); + }); + } + } + + close(callback) { + const closeWebSocket = (callback) => { + // don't close if it's already closed + if (this._ws.readyState === 3) { + callback(); + } else { + // don't notify on user-initiated shutdown ('disconnect' event) + //this._ws.removeAllListeners('close'); + const onclose = this._ws.onclose; + this._ws.onclose = () => { + // this._ws.removeAllListeners(); + callback(); + this._ws.onclose = onclose; + this._ws.onclose && this._ws.onclose(); + }; + this._ws.close(); + } + }; + if (typeof callback === "function") { + closeWebSocket(callback); + return undefined; + } else { + return new Promise((fulfill, _reject) => { + closeWebSocket(fulfill); + }); + } + } + + // initiate the connection process + async _start() { + const options = { + host: this.host, + port: this.port, + secure: this.secure, + useHostName: this.useHostName, + alterPath: this.alterPath, + }; + try { + // fetch the WebSocket debugger URL + const url = await this._fetchDebuggerURL(options); + // allow the user to alter the URL + const urlObject = parseUrl(url); + urlObject.pathname = options.alterPath(urlObject.pathname); + this.webSocketUrl = formatUrl(urlObject); + // update the connection parameters using the debugging URL + options.host = urlObject.hostname; + options.port = urlObject.port || options.port; + // fetch the protocol and prepare the API + const protocol = await this._fetchProtocol(options); + api.prepare(this, protocol); + // finally connect to the WebSocket + await this._connectToWebSocket(); + // since the handler is executed synchronously, the emit() must be + // performed in the next tick so that uncaught errors in the client code + // are not intercepted by the Promise mechanism and therefore reported + // via the 'error' event + nextTick(() => { + this._notifier.emit("connect", this); + }); + } catch (err) { + this._notifier.emit("error", err); + } + } + + // fetch the WebSocket URL according to 'target' + async _fetchDebuggerURL(options) { + const userTarget = this.target; + switch (typeof userTarget) { + case "string": { + let idOrUrl = userTarget; + // use default host and port if omitted (and a relative URL is specified) + if (idOrUrl.startsWith("/")) { + idOrUrl = `ws://${this.host}:${this.port}${idOrUrl}`; + } + // a WebSocket URL is specified by the user (e.g., node-inspector) + if (idOrUrl.match(/^wss?:/i)) { + return idOrUrl; // done! + } + // a target id is specified by the user + else { + const targets = await devtools.List(options); + const object = targets.find((target) => target.id === idOrUrl); + return object.webSocketDebuggerUrl; + } + } + case "object": { + const object = userTarget; + return object.webSocketDebuggerUrl; + } + case "function": { + const func = userTarget; + const targets = await devtools.List(options); + const result = func(targets); + const object = typeof result === "number" ? targets[result] : result; + return object.webSocketDebuggerUrl; + } + default: + throw new Error(`Invalid target argument "${this.target}"`); + } + } + + // fetch the protocol according to 'protocol' and 'local' + async _fetchProtocol(options) { + // if a protocol has been provided then use it + if (this.protocol) { + return this.protocol; + } + // otherwise user either the local or the remote version + else { + options.local = this.local; + return await devtools.Protocol(options); + } + } + + // establish the WebSocket connection and start processing user commands + _connectToWebSocket() { + return new Promise((fulfill, reject) => { + // create the WebSocket + try { + if (this.secure) { + this.webSocketUrl = this.webSocketUrl.replace(/^ws:/i, "wss:"); + } + this._ws = new WebSocket(this.webSocketUrl); + } catch (err) { + // handles bad URLs + reject(err); + return; + } + // set up event handlers + this._ws.onopen = () => { + fulfill(); + }; + this._ws.onmessage = (data) => { + const message = JSON.parse(data.data); + this._handleMessage(message); + }; + this._ws.onclose = (_code) => { + this.emit("disconnect"); + }; + this._ws.onerror = (err) => { + reject(err); + }; + }); + } + + // handle the messages read from the WebSocket + _handleMessage(message) { + // command response + if (message.id) { + const callback = this._callbacks[message.id]; + if (!callback) { + return; + } + // interpret the lack of both 'error' and 'result' as success + // (this may happen with node-inspector) + if (message.error) { + callback(true, message.error); + } else { + callback(false, message.result || {}); + } + // unregister command response callback + delete this._callbacks[message.id]; + // notify when there are no more pending commands + if (Object.keys(this._callbacks).length === 0) { + this.emit("ready"); + } + } + // event + else if (message.method) { + const { method, params, sessionId } = message; + this.emit("event", message); + this.emit(method, params, sessionId); + this.emit(`${method}.${sessionId}`, params, sessionId); + } + } + + // send a command to the remote endpoint and register a callback for the reply + _enqueueCommand(method, params, sessionId, callback) { + const id = this._nextCommandId++; + const message = { + id, + method, + sessionId, + params: params || {}, + }; + this._ws.send(JSON.stringify(message)); + this._callbacks[id] = callback; + } +} diff --git a/src/core/cri/deno-cri/defaults.js b/src/core/cri/deno-cri/defaults.js new file mode 100644 index 00000000000..2f585f19ef3 --- /dev/null +++ b/src/core/cri/deno-cri/defaults.js @@ -0,0 +1,10 @@ +/* + * defaults.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +export const HOST = "localhost"; +export const PORT = 9222; diff --git a/src/core/cri/deno-cri/devtools.js b/src/core/cri/deno-cri/devtools.js new file mode 100644 index 00000000000..749df195e33 --- /dev/null +++ b/src/core/cri/deno-cri/devtools.js @@ -0,0 +1,73 @@ +/* + * devtools.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +import * as defaults from "./defaults.js"; +import externalRequest from "./external-request.js"; + +// don't load localDescriptor for now +// import localDescriptor from "./protocol.json" assert { type: "json" }; + +// options.path must be specified; callback(err, data) +function devToolsInterface(options, callback) { + options.host = options.host || defaults.HOST; + options.port = options.port || defaults.PORT; + options.secure = !!options.secure; + options.useHostName = !!options.useHostName; + options.alterPath = options.alterPath || ((path) => path); + // allow the user to alter the path + const newOptions = { ...options }; + newOptions.path = options.alterPath(options.path); + newOptions.protocol = options.secure ? "https" : "http"; + return externalRequest(newOptions, callback); +} + +export async function Protocol(options) { + // this version doesn't support options.local + /*if (options.local) { + return localDescriptor; + }*/ + + // try to fetch the protocol remotely + options.path = "/json/protocol"; + const result = await devToolsInterface(options); + return JSON.parse(result); +} + +export async function List(options) { + options.path = "/json/list"; + + const result = await devToolsInterface(options); + return JSON.parse(result); +} + +export async function New(options) { + options.path = "/json/new"; + if (Object.prototype.hasOwnProperty.call(options, "url")) { + options.path += `?${options.url}`; + } + const result = await devToolsInterface(options); + return JSON.parse(result); +} + +export async function Activate(options) { + options.path = "/json/activate/" + options.id; + await devToolsInterface(options); + return null; +} + +export async function Close(options) { + options.path = "/json/close/" + options.id; + await devToolsInterface(options); + return null; +} + +export async function Version(options) { + options.path = "/json/version"; + const result = await devToolsInterface(options); + return JSON.parse(result); +} diff --git a/src/core/cri/deno-cri/external-request.js b/src/core/cri/deno-cri/external-request.js new file mode 100644 index 00000000000..e788fd17d31 --- /dev/null +++ b/src/core/cri/deno-cri/external-request.js @@ -0,0 +1,28 @@ +/* + * external-request.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +export default async function externalRequest(options) { + // perform the DNS lookup manually so that the HTTP host header generated by + // http.get will contain the IP address, this is needed because since Chrome + // 66 the host header cannot contain an host name different than localhost + // (see https://github.com/cyrus-and/chrome-remote-interface/issues/340) + /*if (!options.useHostName) { + const addresses = await Deno.resolveDns(options.host, "A"); + options = Object.assign({}, options); + options.host = addresses[0]; + }*/ + + const url = `${options.protocol}:/${options.host}:${options.port}${options.path}`; + const response = await fetch(url); + const text = await response.text(); + + if (response.status !== 200) { + throw new Error(text); + } + return text; +} diff --git a/src/core/cri/deno-cri/index.js b/src/core/cri/deno-cri/index.js new file mode 100644 index 00000000000..d4f13ac8f1f --- /dev/null +++ b/src/core/cri/deno-cri/index.js @@ -0,0 +1,49 @@ +/* + * index.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +import EventEmitter from "events/mod.ts"; +import Chrome from "./chrome.js"; + +export default CDP; +export { Protocol, List, New, Activate, Close, Version } from "./devtools.js"; +import { nextTick } from "node/_next_tick.ts"; + +// const EventEmitter = require('events'); +// const dns = require('dns'); + +/* const devtools = require('./lib/devtools.js'); +const Chrome = require('./lib/chrome.js'); + */ +// XXX reset the default that has been changed in +// (https://github.com/nodejs/node/pull/39987) to prefer IPv4. since +// implementations alway bind on 127.0.0.1 this solution should be fairly safe +// (see #467) +/*if (dns.setDefaultResultOrder) { + dns.setDefaultResultOrder('ipv4first'); +}*/ + +function CDP(options, callback) { + if (typeof options === "function") { + callback = options; + options = undefined; + } + const notifier = new EventEmitter(); + if (typeof callback === "function") { + // allow to register the error callback later + nextTick(() => { + new Chrome(options, notifier); + }); + return notifier.once("connect", callback); + } else { + return new Promise((fulfill, reject) => { + notifier.once("connect", fulfill); + notifier.once("error", reject); + new Chrome(options, notifier); + }); + } +} diff --git a/src/core/cri/deno-cri/websocket-wrapper.js b/src/core/cri/deno-cri/websocket-wrapper.js new file mode 100644 index 00000000000..377a1757684 --- /dev/null +++ b/src/core/cri/deno-cri/websocket-wrapper.js @@ -0,0 +1,43 @@ +/* + * websocket-wrapper.js + * + * Copyright (c) 2021 Andrea Cardaci + * + * Deno port Copyright (C) 2022 by RStudio, PBC + */ + +import EventEmitter from "events/mod.ts"; + +// wrapper around the Node.js ws module +// for use in browsers +export class WebSocketWrapper extends EventEmitter { + constructor(url) { + super(); + this._ws = new WebSocket(url); // eslint-disable-line no-undef + this._ws.onopen = () => { + this.emit("open"); + }; + this._ws.onclose = () => { + this.emit("close"); + }; + this._ws.onmessage = (event) => { + this.emit("message", event.data); + }; + this._ws.onerror = () => { + this.emit("error", new Error("WebSocket error")); + }; + } + + close() { + this._ws.close(); + } + + send(data, callback) { + try { + this._ws.send(data); + callback(); + } catch (err) { + callback(err); + } + } +} diff --git a/src/core/handlers/base.ts b/src/core/handlers/base.ts index 48cad5de5e0..0af9226b14a 100644 --- a/src/core/handlers/base.ts +++ b/src/core/handlers/base.ts @@ -71,11 +71,12 @@ import { figuresDir, inputFilesDir } from "../render.ts"; import { ensureDirSync } from "fs/mod.ts"; import { mappedStringFromFile } from "../mapped-text.ts"; import { error } from "log/mod.ts"; -import { +import { withCriClient } from "../cri/cri.ts"; +/* import { extractHtmlFromElements, extractImagesFromElements, } from "../puppeteer.ts"; - + */ const handlers: Record = {}; let globalFigureCounter: Record = {}; @@ -118,11 +119,11 @@ function makeHandlerContext( const fileName = join(dirName, "index.html"); Deno.writeTextFileSync(fileName, content); const url = `file://${fileName}`; - const result = await extractHtmlFromElements( - url, - selector, - ); - return result; + + return await withCriClient(async (client) => { + await client.open(url); + return await client.contents(selector); + }); }, async createPngsFromHtml(opts: { @@ -130,7 +131,6 @@ function makeHandlerContext( html: string; deviceScaleFactor: number; selector: string; - count: number; resources?: [string, string][]; }): Promise<{ filenames: string[]; @@ -141,21 +141,11 @@ function makeHandlerContext( html: content, deviceScaleFactor, selector, - count, } = opts; const nonEmptyHtmlResources: [string, string][] = opts.resources || []; const dirName = context.options.temp.createDir(); - // create temporary figure names - const tempNames: string[] = [], - sourceNames: string[] = []; - for (let i = 0; i < count; ++i) { - const { sourceName, fullName: tempName } = context - .uniqueFigureName(prefix, ".png"); - sourceNames.push(sourceName); - tempNames.push(tempName); - } // create temporary resources for (const [name, content] of nonEmptyHtmlResources) { Deno.writeTextFileSync(join(dirName, name), content); @@ -163,18 +153,30 @@ function makeHandlerContext( const fileName = join(dirName, "index.html"); Deno.writeTextFileSync(fileName, content); const url = `file://${fileName}`; - const elements = await extractImagesFromElements( - { - url, - viewport: { - width: 800, - height: 600, - deviceScaleFactor, - }, - }, - selector, - tempNames, - ); + + const { elements, images } = await withCriClient(async (client) => { + await client.open(url); + const elements = await client.contents(selector); + const screenshots = await client.screenshots( + selector, + deviceScaleFactor, + ); + return { + elements, + images: screenshots.map((x) => x.data), + }; + }); + + // write figures to disk + const sourceNames: string[] = []; + + for (let i = 0; i < images.length; ++i) { + const { sourceName, fullName } = context + .uniqueFigureName(prefix, ".png"); + sourceNames.push(sourceName); + Deno.writeFileSync(fullName, images[i]); + } + return { filenames: sourceNames, elements, diff --git a/src/core/lib/semaphore.ts b/src/core/lib/semaphore.ts index ccdf669c551..48b210348cd 100644 --- a/src/core/lib/semaphore.ts +++ b/src/core/lib/semaphore.ts @@ -36,13 +36,15 @@ export class Semaphore { await this.acquire(); } - // deno-lint-ignore no-explicit-any - async runExclusive(fun: () => any) { + async runExclusive(fun: () => Promise) { await this.acquire(); try { - fun(); - } finally { + const result = await fun(); + this.release(); + return result; + } catch (e) { this.release(); + throw e; } } } diff --git a/src/core/puppeteer.ts b/src/core/puppeteer.ts index 11f6d8a8869..9be85e89fef 100644 --- a/src/core/puppeteer.ts +++ b/src/core/puppeteer.ts @@ -225,7 +225,7 @@ async function findChrome(): Promise { return path; } -async function fetchBrowser() { +export async function getBrowserExecutablePath() { // Cook up a new instance const browserFetcher = await fetcher(); const availableRevisions = await browserFetcher.localRevisions(); @@ -247,11 +247,16 @@ async function fetchBrowser() { if (executablePath === undefined) { error("Chrome not found"); info( - "\nNo Chromium installation was detected.\n\nPlease run 'quarto tools install chromium' to install Chromium.\n", + "\nNo Chrome or Chromium installation was detected.\n\nPlease run 'quarto tools install chromium' to install Chromium.\n", ); throw new Error(); } + return executablePath; +} + +async function fetchBrowser() { + const executablePath = await getBrowserExecutablePath(); const puppeteer = await getPuppeteer(); return await puppeteer.launch({ product: "chrome", diff --git a/src/import_map.json b/src/import_map.json index 4385e294dc8..798c71e101c 100644 --- a/src/import_map.json +++ b/src/import_map.json @@ -18,7 +18,9 @@ "testing/": "https://deno.land/std@0.138.0/testing/", "textproto/": "https://deno.land/std@0.138.0/textproto/", "uuid/": "https://deno.land/std@0.138.0/uuid/", + "node/": "https://deno.land/std@0.138.0/node/", "ws/": "https://deno.land/std@0.138.0/ws/", + "events/": "https://deno.land/x/events@v1.0.0/", "cache/": "https://deno.land/x/cache@0.2.12/", "cliffy/": "https://deno.land/x/cliffy@v0.24.2/", "dayjs/": "https://cdn.skypack.dev/dayjs@1.8.21/", diff --git a/src/resources/formats/html/ojs/quarto-ojs-runtime.js b/src/resources/formats/html/ojs/quarto-ojs-runtime.js index 074c699a80e..b0dfbf3df69 100644 --- a/src/resources/formats/html/ojs/quarto-ojs-runtime.js +++ b/src/resources/formats/html/ojs/quarto-ojs-runtime.js @@ -19231,14 +19231,10 @@ class QuartoOJSConnector extends OJSConnector { p.appendChild(tt); p.appendChild(document.createTextNode(" " + rest.join(" "))); message = p; - } else if (message === "circular definition") { + } else { const p = document.createElement("p"); - p.appendChild(document.createTextNode("circular definition")); + p.appendChild(document.createTextNode(message)); message = p; - } else { - throw new Error( - `Internal error, could not parse OJS error message "${message}"` - ); } } else { heading = "OJS Error"; diff --git a/src/resources/formats/html/ojs/quarto-ojs-runtime.min.js b/src/resources/formats/html/ojs/quarto-ojs-runtime.min.js index 8a675efe4c2..d3083e7ecfc 100644 --- a/src/resources/formats/html/ojs/quarto-ojs-runtime.min.js +++ b/src/resources/formats/html/ojs/quarto-ojs-runtime.min.js @@ -1,3 +1,3 @@ var e={},t={};function i(e){return new Function("d","return {"+e.map((function(e,t){return JSON.stringify(e)+": d["+t+'] || ""'})).join(",")+"}")}function n(e){var t=Object.create(null),i=[];return e.forEach((function(e){for(var n in e)n in t||i.push(t[n]=n)})),i}function s(e,t){var i=e+"",n=i.length;return n9999?"+"+s(t,6):s(t,4))+"-"+s(e.getUTCMonth()+1,2)+"-"+s(e.getUTCDate(),2)+(a?"T"+s(i,2)+":"+s(n,2)+":"+s(r,2)+"."+s(a,3)+"Z":r?"T"+s(i,2)+":"+s(n,2)+":"+s(r,2)+"Z":n||i?"T"+s(i,2)+":"+s(n,2)+"Z":"")}function a(s){var a=new RegExp('["'+s+"\n\r]"),o=s.charCodeAt(0);function c(i,n){var s,r=[],a=i.length,c=0,h=0,l=a<=0,p=!1;function u(){if(l)return t;if(p)return p=!1,e;var n,s,r=c;if(34===i.charCodeAt(r)){for(;c++=a?l=!0:10===(s=i.charCodeAt(c++))?p=!0:13===s&&(p=!0,10===i.charCodeAt(c)&&++c),i.slice(r+1,n-1).replace(/""/g,'"')}for(;c{if(!e.ok)throw new S("unable to load package.json");return e.redirected&&!m.has(e.url)&&m.set(e.url,i),e.json()}))),i}S.prototype.name=S.name;var P=I((async function(e,t){if(e.startsWith(b)&&(e=e.substring(b.length)),/^(\w+:)|\/\//i.test(e))return e;if(/^[.]{0,2}\//i.test(e))return new URL(e,null==t?location:t).href;if(!e.length||/^[\s._]/.test(e)||/\s$/.test(e))throw new S("illegal name");const i=C(e);if(!i)return`${b}${e}`;if(!i.version&&null!=t&&t.startsWith(b)){const e=await A(C(t.substring(b.length)));i.version=e.dependencies&&e.dependencies[i.name]||e.peerDependencies&&e.peerDependencies[i.name]}if(i.path&&!E.test(i.path)&&(i.path+=".js"),i.path&&i.version&&_.test(i.version))return`${b}${i.name}@${i.version}/${i.path}`;const n=await A(i);return`${b}${n.name}@${n.version}/${i.path||function(e){for(const t of k){const i=e[t];if("string"==typeof i)return E.test(i)?i:`${i}.js`}}(n)||"index.js"}`}));function I(e){const t=new Map,i=a(null);let n,s=0;function r(e){if("string"!=typeof e)return e;let i=t.get(e);return i||t.set(e,i=new Promise(((t,i)=>{const r=document.createElement("script");r.onload=()=>{try{t(g.pop()(a(e)))}catch(e){i(new S("invalid module"))}r.remove(),s--,0===s&&(window.define=n)},r.onerror=()=>{i(new S("unable to load module")),r.remove(),s--,0===s&&(window.define=n)},r.async=!0,r.src=e,0===s&&(n=window.define,window.define=V),s++,document.head.appendChild(r)}))),i}function a(t){return i=>Promise.resolve(e(i,t)).then(r)}function o(e){return arguments.length>1?Promise.all(v.call(arguments,i)).then(N):i(e)}return o.alias=function(t){return I(((i,n)=>i in t&&(n=null,"string"!=typeof(i=t[i]))?i:e(i,n)))},o.resolve=e,o}function N(e){const t={};for(const i of e)for(const e in i)y.call(i,e)&&(null==i[e]?Object.defineProperty(t,e,{get:L(i,e)}):t[e]=i[e]);return t}function L(e,t){return()=>e[t]}function T(e){return"exports"===(e+="")||"module"===e}function V(e,t,i){const n=arguments.length;n<2?(i=e,t=[]):n<3&&(i=t,t="string"==typeof e?[]:e),g.push(x.call(t,T)?e=>{const n={},s={exports:n};return Promise.all(v.call(t,(t=>"exports"===(t+="")?n:"module"===t?s:e(t)))).then((e=>(i.apply(null,e),s.exports)))}:e=>Promise.all(v.call(t,e)).then((e=>"function"==typeof i?i.apply(null,e):i)))}function O(e,t,i){return{resolve:(n=i)=>`https://cdn.jsdelivr.net/npm/${e}@${t}/${n}`}}V.amd={};const M=O("d3","7.4.4","dist/d3.min.js"),R=O("@observablehq/inputs","0.10.4","dist/inputs.min.js"),D=O("@observablehq/plot","0.4.3","dist/plot.umd.min.js"),j=O("@observablehq/graphviz","0.2.1","dist/graphviz.min.js"),B=O("@observablehq/highlight.js","2.0.0","highlight.min.js"),F=O("@observablehq/katex","0.11.1","dist/katex.min.js"),U=O("lodash","4.17.21","lodash.min.js"),q=O("htl","0.3.1","dist/htl.min.js"),W=O("jszip","3.9.1","dist/jszip.min.js"),H=O("marked","0.3.12","marked.min.js"),z=O("sql.js","1.6.2","dist/sql-wasm.js"),G=O("vega","5.22.1","build/vega.min.js"),K=O("vega-lite","5.2.0","build/vega-lite.min.js"),Q=O("vega-lite-api","5.0.0","build/vega-lite-api.min.js"),Y=O("apache-arrow","4.0.1","Arrow.es2015.min.js"),X=O("arquero","4.8.8","dist/arquero.min.js"),Z=O("topojson-client","3.1.0","dist/topojson-client.min.js"),J=O("exceljs","4.3.0","dist/exceljs.min.js"),ee=O("mermaid","9.0.0","dist/mermaid.min.js");function te(e){const t={};for(const[i,n]of e)t[i]=n;return t}async function ie(e){return(await e(z.resolve()))({locateFile:e=>z.resolve(`dist/${e}`)})}class ne{constructor(e){Object.defineProperties(this,{_db:{value:e}})}static async open(e){const[t,i]=await Promise.all([ie(P),Promise.resolve(e).then(se)]);return new ne(new t.Database(i))}async query(e,t){return await async function(e,t,i){const[n]=await e.exec(t,i);if(!n)return[];const{columns:s,values:r}=n,a=r.map((e=>te(e.map(((e,t)=>[s[t],e])))));return a.columns=s,a}(this._db,e,t)}async queryRow(e,t){return(await this.query(e,t))[0]||null}async explain(e,t){return re("pre",{className:"observablehq--inspect"},[ae((await this.query(`EXPLAIN QUERY PLAN ${e}`,t)).map((e=>e.detail)).join("\n"))])}async describe(e){const t=await(void 0===e?this.query("SELECT name FROM sqlite_master WHERE type = 'table'"):this.query("SELECT * FROM pragma_table_info(?)",[e]));if(!t.length)throw new Error("Not found");const{columns:i}=t;return re("table",{value:t},[re("thead",[re("tr",i.map((e=>re("th",[ae(e)]))))]),re("tbody",t.map((e=>re("tr",i.map((t=>re("td",[ae(e[t])])))))))])}async sql(e,...t){return this.query(e.join("?"),t)}}function se(e){return"string"==typeof e?fetch(e).then(se):e instanceof Response||e instanceof Blob?e.arrayBuffer().then(se):e instanceof ArrayBuffer?new Uint8Array(e):e}function re(e,t,i){2===arguments.length&&(i=t,t=void 0);const n=document.createElement(e);if(void 0!==t)for(const e in t)n[e]=t[e];if(void 0!==i)for(const e of i)n.appendChild(e);return n}function ae(e){return document.createTextNode(e)}Object.defineProperty(ne.prototype,"dialect",{value:"sqlite"});class oe{constructor(e){Object.defineProperties(this,{_:{value:e},sheetNames:{value:e.worksheets.map((e=>e.name)),enumerable:!0}})}sheet(e,t){const i="number"==typeof e?this.sheetNames[e]:this.sheetNames.includes(e+="")?e:null;if(null==i)throw new Error(`Sheet not found: ${e}`);return function(e,{range:t,headers:i}={}){let[[n,s],[r,a]]=function(e=":",{columnCount:t,rowCount:i}){if(!(e+="").match(/^[A-Z]*\d*:[A-Z]*\d*$/))throw new Error("Malformed range specifier");const[[n=0,s=0],[r=t-1,a=i-1]]=e.split(":").map(pe);return[[n,s],[r,a]]}(t,e);const o=i?e._rows[s++]:null;let c=new Set(["#"]);for(let e=n;e<=r;e++){const t=o?ce(o.findCell(e+1)):null;let i=t&&t+""||le(e);for(;c.has(i);)i+="_";c.add(i)}c=new Array(n).concat(Array.from(c));const h=new Array(a-s+1);for(let t=s;t<=a;t++){const i=h[t-s]=Object.create(null,{"#":{value:t+1}}),a=e.getRow(t+1);if(a.hasValues)for(let e=n;e<=r;e++){const t=ce(a.findCell(e+1));null!=t&&(i[c[e+1]]=t)}}return h.columns=c.filter((()=>!0)),h}(this._.getWorksheet(i),t)}}function ce(e){if(!e)return;const{value:t}=e;if(t&&"object"==typeof t&&!(t instanceof Date)){if(t.formula||t.sharedFormula)return t.result&&t.result.error?NaN:t.result;if(t.richText)return he(t);if(t.text){let{text:e}=t;return e.richText&&(e=he(e)),t.hyperlink&&t.hyperlink!==e?`${t.hyperlink} ${e}`:e}return t}return t}function he(e){return e.richText.map((e=>e.text)).join("")}function le(e){let t="";e++;do{t=String.fromCharCode(64+(e%26||26))+t}while(e=Math.floor((e-1)/26));return t}function pe(e){const[,t,i]=e.match(/^([A-Z]*)(\d*)$/);let n=0;if(t)for(let e=0;e{const s=new Image;new URL(t,document.baseURI).origin!==new URL(location).origin&&(s.crossOrigin="anonymous"),Object.assign(s,e),s.onload=()=>i(s),s.onerror=()=>n(new Error(`Unable to load file: ${this.name}`)),s.src=t}))}async arrow(){const[e,t]=await Promise.all([P(Y.resolve()),ue(this)]);return e.Table.from(t)}async sqlite(){return ne.open(ue(this))}async zip(){const[e,t]=await Promise.all([P(W.resolve()),this.arrayBuffer()]);return new ve(await e.loadAsync(t))}async xml(e="application/xml"){return(new DOMParser).parseFromString(await this.text(),e)}async html(){return this.xml("text/html")}async xlsx(){const[e,t]=await Promise.all([P(J.resolve()),this.arrayBuffer()]);return new oe(await(new e.Workbook).xlsx.load(t))}}class me extends fe{constructor(e,t,i){super(t,i),Object.defineProperty(this,"_url",{value:e})}async url(){return await this._url+""}}function ge(e){throw new Error(`File not found: ${e}`)}class ve{constructor(e){Object.defineProperty(this,"_",{value:e}),this.filenames=Object.keys(e.files).filter((t=>!e.files[t].dir))}file(e){const t=this._.file(e+="");if(!t||t.dir)throw new Error(`file not found: ${e}`);return new xe(t)}}class xe extends fe{constructor(e){super(e.name),Object.defineProperty(this,"_",{value:e}),Object.defineProperty(this,"_url",{writable:!0})}async url(){return this._url||(this._url=this.blob().then(URL.createObjectURL))}async blob(){return this._.async("blob")}async arrayBuffer(){return this._.async("arraybuffer")}async text(){return this._.async("text")}async json(){return JSON.parse(await this.text())}}var ye={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};var be=0;function we(e){return new _e("O-"+(null==e?"":e+"-")+ ++be)}function _e(e){this.id=e,this.href=new URL(`#${e}`,location)+""}_e.prototype.toString=function(){return"url("+this.href+")"};var Ee={canvas:function(e,t){var i=document.createElement("canvas");return i.width=e,i.height=t,i},context2d:function(e,t,i){null==i&&(i=devicePixelRatio);var n=document.createElement("canvas");n.width=e*i,n.height=t*i,n.style.width=e+"px";var s=n.getContext("2d");return s.scale(i,i),s},download:function(e,t="untitled",i="Save"){const n=document.createElement("a"),s=n.appendChild(document.createElement("button"));async function r(){await new Promise(requestAnimationFrame),URL.revokeObjectURL(n.href),n.removeAttribute("href"),s.textContent=i,s.disabled=!1}return s.textContent=i,n.download=t,n.onclick=async t=>{if(s.disabled=!0,n.href)return r();s.textContent="Saving…";try{const t=await("function"==typeof e?e():e);s.textContent="Download",n.href=URL.createObjectURL(t)}catch(e){s.textContent=i}if(t.eventPhase)return r();s.disabled=!1},n},element:function(e,t){var i,n=e+="",s=n.indexOf(":");s>=0&&"xmlns"!==(n=e.slice(0,s))&&(e=e.slice(s+1));var r=ye.hasOwnProperty(n)?document.createElementNS(ye[n],e):document.createElement(e);if(t)for(var a in t)s=(n=a).indexOf(":"),i=t[a],s>=0&&"xmlns"!==(n=a.slice(0,s))&&(a=a.slice(s+1)),ye.hasOwnProperty(n)?r.setAttributeNS(ye[n],a,i):r.setAttribute(a,i);return r},input:function(e){var t=document.createElement("input");return null!=e&&(t.type=e),t},range:function(e,t,i){1===arguments.length&&(t=e,e=null);var n=document.createElement("input");return n.min=e=null==e?0:+e,n.max=t=null==t?1:+t,n.step=null==i?"any":i=+i,n.type="range",n},select:function(e){var t=document.createElement("select");return Array.prototype.forEach.call(e,(function(e){var i=document.createElement("option");i.value=i.textContent=e,t.appendChild(i)})),t},svg:function(e,t){var i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox",[0,0,e,t]),i.setAttribute("width",e),i.setAttribute("height",t),i},text:function(e){return document.createTextNode(e)},uid:we};var ke={buffer:function(e){return new Promise((function(t,i){var n=new FileReader;n.onload=function(){t(n.result)},n.onerror=i,n.readAsArrayBuffer(e)}))},text:function(e){return new Promise((function(t,i){var n=new FileReader;n.onload=function(){t(n.result)},n.onerror=i,n.readAsText(e)}))},url:function(e){return new Promise((function(t,i){var n=new FileReader;n.onload=function(){t(n.result)},n.onerror=i,n.readAsDataURL(e)}))}};function Se(){return this}function Ce(e,t){let i=!1;if("function"!=typeof t)throw new Error("dispose is not a function");return{[Symbol.iterator]:Se,next:()=>i?{done:!0}:(i=!0,{done:!1,value:e}),return:()=>(i=!0,t(e),{done:!0}),throw:()=>({done:i=!0})}}function Ae(e){let t,i,n=!1;const s=e((function(e){i?(i(e),i=null):n=!0;return t=e}));if(null!=s&&"function"!=typeof s)throw new Error("function"==typeof s.then?"async initializers are not supported":"initializer returned something, but not a dispose function");return{[Symbol.iterator]:Se,throw:()=>({done:!0}),return:()=>(null!=s&&s(),{done:!0}),next:function(){return{done:!1,value:n?(n=!1,Promise.resolve(t)):new Promise((e=>i=e))}}}}function Pe(e){switch(e.type){case"range":case"number":return e.valueAsNumber;case"date":return e.valueAsDate;case"checkbox":return e.checked;case"file":return e.multiple?e.files:e.files[0];case"select-multiple":return Array.from(e.selectedOptions,(e=>e.value));default:return e.value}}var Ie={disposable:Ce,filter:function*(e,t){for(var i,n=-1;!(i=e.next()).done;)t(i.value,++n)&&(yield i.value)},input:function(e){return Ae((function(t){var i=function(e){switch(e.type){case"button":case"submit":case"checkbox":return"click";case"file":return"change";default:return"input"}}(e),n=Pe(e);function s(){t(Pe(e))}return e.addEventListener(i,s),void 0!==n&&t(n),function(){e.removeEventListener(i,s)}}))},map:function*(e,t){for(var i,n=-1;!(i=e.next()).done;)yield t(i.value,++n)},observe:Ae,queue:function(e){let t;const i=[],n=e((function(e){i.push(e),t&&(t(i.shift()),t=null);return e}));if(null!=n&&"function"!=typeof n)throw new Error("function"==typeof n.then?"async initializers are not supported":"initializer returned something, but not a dispose function");return{[Symbol.iterator]:Se,throw:()=>({done:!0}),return:()=>(null!=n&&n(),{done:!0}),next:function(){return{done:!1,value:i.length?Promise.resolve(i.shift()):new Promise((e=>t=e))}}}},range:function*(e,t,i){e=+e,t=+t,i=(s=arguments.length)<2?(t=e,e=0,1):s<3?1:+i;for(var n=-1,s=0|Math.max(0,Math.ceil((t-e)/i));++n{i.terminate(),URL.revokeObjectURL(t)}))}};function Ne(e,t){return function(i){var n,s,r,a,o,c,h,l,p=i[0],u=[],d=null,f=-1;for(o=1,c=arguments.length;o0){for(r=new Array(f),a=document.createTreeWalker(d,NodeFilter.SHOW_COMMENT,null,!1);a.nextNode();)s=a.currentNode,/^o:/.test(s.nodeValue)&&(r[+s.nodeValue.slice(2)]=s);for(o=0;o{t=e}))},value:{get:()=>e,set:i=>t(e=i)}}),void 0!==e&&t(e)}function*Ve(){for(;;)yield Date.now()}var Oe=new Map;function Me(e,t){var i;return(i=Oe.get(e=+e))?i.then((()=>t)):(i=Date.now())>=e?Promise.resolve(t):function(e,t){var i=new Promise((function(i){Oe.delete(t);var n=t-e;if(!(n>0))throw new Error("invalid time");if(n>2147483647)throw new Error("too long to wait");setTimeout(i,n)}));return Oe.set(t,i),i}(i,e).then((()=>t))}var Re={delay:function(e,t){return new Promise((function(i){setTimeout((function(){i(t)}),e)}))},tick:function(e,t){return Me(Math.ceil((Date.now()+1)/e)*e,t)},when:Me};function De(e,t){if(/^(\w+:)|\/\//i.test(e))return e;if(/^[.]{0,2}\//i.test(e))return new URL(e,null==t?location:t).href;if(!e.length||/^[\s._]/.test(e)||/\s$/.test(e))throw new Error("illegal name");return"https://unpkg.com/"+e}var je=Ne((function(e){var t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.innerHTML=e.trim(),t}),(function(){return document.createElementNS("http://www.w3.org/2000/svg","g")})),Be=String.raw;function Fe(){return Ae((function(e){var t=e(document.body.clientWidth);function i(){var i=document.body.clientWidth;i!==t&&e(t=i)}return window.addEventListener("resize",i),function(){window.removeEventListener("resize",i)}}))}var Ue=Object.assign((function(e){const t=function(e){return null==e?P:I(e)}(e);var i;Object.defineProperties(this,(i={FileAttachment:()=>ge,Arrow:()=>t(Y.resolve()),Inputs:()=>t(R.resolve()).then((e=>({...e,file:e.fileOf(fe)}))),Mutable:()=>Te,Plot:()=>t(D.resolve()),SQLite:()=>ie(t),SQLiteDatabaseClient:()=>ne,_:()=>t(U.resolve()),aq:()=>t.alias({"apache-arrow":Y.resolve()})(X.resolve()),d3:()=>t(M.resolve()),dot:()=>t(j.resolve()),htl:()=>t(q.resolve()),html:()=>Le,md:()=>function(e){return e(H.resolve()).then((function(t){return Ne((function(i){var n=document.createElement("div");n.innerHTML=t(i,{langPrefix:""}).trim();var s=n.querySelectorAll("pre code[class]");return s.length>0&&e(B.resolve()).then((function(t){s.forEach((function(i){function n(){t.highlightBlock(i),i.parentNode.classList.add("observablehq--md-pre")}t.getLanguage(i.className)?n():e(B.resolve("async-languages/index.js")).then((n=>{if(n.has(i.className))return e(B.resolve("async-languages/"+n.get(i.className))).then((e=>{t.registerLanguage(i.className,e)}))})).then(n,n)}))})),n}),(function(){return document.createElement("div")}))}))}(t),mermaid:()=>async function(e){const t=await e(ee.resolve());return t.initialize({securityLevel:"loose",theme:"neutral"}),function(){const e=document.createElement("div");return e.innerHTML=t.render(we().id,String.raw.apply(String,arguments)),e.removeChild(e.firstChild)}}(t),now:Ve,require:()=>t,resolve:()=>De,svg:()=>je,tex:()=>function(e){return Promise.all([e(F.resolve()),(t=F.resolve("dist/katex.min.css"),new Promise((function(e,i){var n=document.createElement("link");n.rel="stylesheet",n.href=t,n.onerror=i,n.onload=e,document.head.appendChild(n)})))]).then((function(e){var t=e[0],i=n();function n(e){return function(){var i=document.createElement("div");return t.render(Be.apply(String,arguments),i,e),i.removeChild(i.firstChild)}}return i.options=n,i.block=n({displayMode:!0}),i}));var t}(t),topojson:()=>t(Z.resolve()),vl:()=>async function(e){const[t,i,n]=await Promise.all([G,K,Q].map((t=>e(t.resolve()))));return n.register(t,i)}(t),width:Fe,DOM:Ee,Files:ke,Generators:Ie,Promises:Re},te(Object.entries(i).map(qe))))}),{resolve:P.resolve});function qe([e,t]){return[e,{value:t,writable:!0,enumerable:!0}]}class $e{constructor(e){this._node=e,this._spans=[],this.normalizeCodeRange(),this.initializeEntryPoints()}normalizeCodeRange(){const e=this._node.querySelectorAll("code > span");for(const t of e)Array.from(t.childNodes).filter((e=>e.nodeType===e.TEXT_NODE)).forEach((e=>{const t=document.createElement("span");t.textContent=e.wholeText,e.replaceWith(t)}))}initializeEntryPoints(){const e=this._node.querySelectorAll("code > span");let t=[],i=this._node.parentElement.dataset.sourceOffset&&-Number(this._node.parentElement.dataset.sourceOffset)||0;for(const n of e){let e=Number(n.id.split("-").pop()),s=1;Array.from(n.childNodes).filter((e=>e.nodeType===e.ELEMENT_NODE&&"SPAN"===e.nodeName)).forEach((n=>{t.push({offset:i,line:e,column:s,node:n}),i+=n.textContent.length,s+=n.textContent.length})),i+=1}this._elementEntryPoints=t}locateEntry(e){let t;if(e!==1/0){for(let i=0;ie)return{entry:t,index:i-1};t=n}return e{const i=document.createElement("span");for(const t of e.node.classList)i.classList.add(t);const n=e.node.textContent.slice(0,t-e.offset),s=e.node.textContent.slice(t-e.offset);e.node.textContent=n,i.textContent=s,e.node.after(i),this._elementEntryPoints.push({column:e.column+t-e.offset,line:e.line,node:i,offset:t}),this._elementEntryPoints.sort(((e,t)=>e.offset-t.offset))},n=this.locateEntry(e);void 0!==n&&n.entry.offset!=e&&i(n.entry,e);const s=this.locateEntry(t);void 0!==s&&s.entry.offset!==t&&i(s.entry,t)}clearSpan(e,t,i){this.ensureExactSpan(e,t);const n=this.locateEntry(e),s=this.locateEntry(t);if(void 0===n)return;const r=n.index,a=s&&s.index||this._elementEntryPoints.length;for(let e=r;e!0===e[t]));if(!t.length)return;const i=t.find((e=>!e.modifier)),n="Map"===i.name&&t.find((e=>e.modifier&&e.prefix)),s=t.some((e=>e.arrayish)),r=t.some((e=>e.setish));return{name:`${n?n.name:""}${i.name}`,symbols:t,arrayish:s&&!r,setish:r}}catch(e){return null}}const{getPrototypeOf:at,getOwnPropertyDescriptors:ot}=Object,ct=at({});function ht(e,t,i,n){let s,r,a,o,c=He(e);e instanceof Map?e instanceof e.constructor?(s=`Map(${e.size})`,r=lt):(s="Map()",r=gt):e instanceof Set?e instanceof e.constructor?(s=`Set(${e.size})`,r=pt):(s="Set()",r=gt):c?(s=`${e.constructor.name}(${e.length})`,r=dt):(o=rt(e))?(s=`Immutable.${o.name}${"Record"===o.name?"":`(${e.size})`}`,c=o.arrayish,r=o.arrayish?ft:o.setish?ut:vt):n?(s=it(e),r=mt):(s=it(e),r=gt);const h=document.createElement("span");h.className="observablehq--expanded",i&&h.appendChild(Ge(i));const l=h.appendChild(document.createElement("a"));l.innerHTML="\n \n ",l.appendChild(document.createTextNode(`${s}${c?" [":" {"}`)),l.addEventListener("mouseup",(function(t){t.stopPropagation(),Yt(h,Et(e,null,i,n))})),r=r(e);for(let e=0;!(a=r.next()).done&&e<20;++e)h.appendChild(a.value);if(!a.done){const e=h.appendChild(document.createElement("a"));e.className="observablehq--field",e.style.display="block",e.appendChild(document.createTextNode(" … more")),e.addEventListener("mouseup",(function(e){e.stopPropagation(),h.insertBefore(a.value,h.lastChild.previousSibling);for(let e=0;!(a=r.next()).done&&e<19;++e)h.insertBefore(a.value,h.lastChild.previousSibling);a.done&&h.removeChild(h.lastChild.previousSibling),We(h,"load")}))}return h.appendChild(document.createTextNode(c?"]":"}")),h}function*lt(e){for(const[t,i]of e)yield bt(t,i);yield*gt(e)}function*pt(e){for(const t of e)yield wt(t);yield*gt(e)}function*ut(e){for(const t of e)yield wt(t)}function*dt(e){for(let t=0,i=e.length;t ")),i.appendChild(Qt(t)),i}function wt(e){const t=document.createElement("div");return t.className="observablehq--field",t.appendChild(document.createTextNode(" ")),t.appendChild(Qt(e)),t}function _t(e){const t=window.getSelection();return"Range"===t.type&&(t.containsNode(e,!0)||t.anchorNode.isSelfOrDescendant(e)||t.focusNode.isSelfOrDescendant(e))}function Et(e,t,i,n){let s,r,a,o,c=He(e);if(e instanceof Map?e instanceof e.constructor?(s=`Map(${e.size})`,r=kt):(s="Map()",r=It):e instanceof Set?e instanceof e.constructor?(s=`Set(${e.size})`,r=St):(s="Set()",r=It):c?(s=`${e.constructor.name}(${e.length})`,r=Pt):(o=rt(e))?(s=`Immutable.${o.name}${"Record"===o.name?"":`(${e.size})`}`,c=o.arrayish,r=o.arrayish?At:o.setish?Ct:Nt):(s=it(e),r=It),t){const t=document.createElement("span");return t.className="observablehq--shallow",i&&t.appendChild(Ge(i)),t.appendChild(document.createTextNode(s)),t.addEventListener("mouseup",(function(i){_t(t)||(i.stopPropagation(),Yt(t,Et(e)))})),t}const h=document.createElement("span");h.className="observablehq--collapsed",i&&h.appendChild(Ge(i));const l=h.appendChild(document.createElement("a"));l.innerHTML="\n \n ",l.appendChild(document.createTextNode(`${s}${c?" [":" {"}`)),h.addEventListener("mouseup",(function(t){_t(h)||(t.stopPropagation(),Yt(h,ht(e,0,i,n)))}),!0),r=r(e);for(let e=0;!(a=r.next()).done&&e<20;++e)e>0&&h.appendChild(document.createTextNode(", ")),h.appendChild(a.value);return a.done||h.appendChild(document.createTextNode(", …")),h.appendChild(document.createTextNode(c?"]":"}")),h}function*kt(e){for(const[t,i]of e)yield Vt(t,i);yield*It(e)}function*St(e){for(const t of e)yield Qt(t,!0);yield*It(e)}function*Ct(e){for(const t of e)yield Qt(t,!0)}function*At(e){let t=-1,i=0;for(const n=e.size;it+1&&(yield Lt(i-t-1)),yield Qt(e.get(i),!0),t=i;i>t+1&&(yield Lt(i-t-1))}function*Pt(e){let t=-1,i=0;for(const n=e.length;it+1&&(yield Lt(i-t-1)),yield Qt(nt(e,i),!0),t=i);i>t+1&&(yield Lt(i-t-1));for(const t in e)!ze(t)&&tt(e,t)&&(yield Tt(t,nt(e,t),"observablehq--key"));for(const t of et(e))yield Tt(Qe(t),nt(e,t),"observablehq--symbol")}function*It(e){for(const t in e)tt(e,t)&&(yield Tt(t,nt(e,t),"observablehq--key"));for(const t of et(e))yield Tt(Qe(t),nt(e,t),"observablehq--symbol")}function*Nt(e){for(const[t,i]of e)yield Tt(t,i,"observablehq--key")}function Lt(e){const t=document.createElement("span");return t.className="observablehq--empty",t.textContent=1===e?"empty":`empty × ${e}`,t}function Tt(e,t,i){const n=document.createDocumentFragment(),s=n.appendChild(document.createElement("span"));return s.className=i,s.textContent=e,n.appendChild(document.createTextNode(": ")),n.appendChild(Qt(t,!0)),n}function Vt(e,t){const i=document.createDocumentFragment();return i.appendChild(Qt(e,!0)),i.appendChild(document.createTextNode(" => ")),i.appendChild(Qt(t,!0)),i}function Ot(e,t){if(e instanceof Date||(e=new Date(+e)),isNaN(e))return"function"==typeof t?t(e):t;const i=e.getUTCHours(),n=e.getUTCMinutes(),s=e.getUTCSeconds(),r=e.getUTCMilliseconds();return`${a=e.getUTCFullYear(),a<0?`-${Mt(-a,6)}`:a>9999?`+${Mt(a,6)}`:Mt(a,4)}-${Mt(e.getUTCMonth()+1,2)}-${Mt(e.getUTCDate(),2)}${i||n||s||r?`T${Mt(i,2)}:${Mt(n,2)}${s||r?`:${Mt(s,2)}${r?`.${Mt(r,3)}`:""}`:""}Z`:""}`;var a}function Mt(e,t){return`${e}`.padStart(t,"0")}var Rt=Error.prototype.toString;var Dt=RegExp.prototype.toString;function jt(e){return e.replace(/[\\`\x00-\x09\x0b-\x19]|\${/g,Bt)}function Bt(e){var t=e.charCodeAt(0);switch(t){case 8:return"\\b";case 9:return"\\t";case 11:return"\\v";case 12:return"\\f";case 13:return"\\r"}return t<16?"\\x0"+t.toString(16):t<32?"\\x"+t.toString(16):"\\"+e}function Ft(e,t){for(var i=0;t.exec(e);)++i;return i}var Ut=Function.prototype.toString,qt={prefix:"async ƒ"},$t={prefix:"async ƒ*"},Wt={prefix:"class"},Ht={prefix:"ƒ"},zt={prefix:"ƒ*"};function Gt(e,t,i){var n=document.createElement("span");n.className="observablehq--function",i&&n.appendChild(Ge(i));var s=n.appendChild(document.createElement("span"));return s.className="observablehq--keyword",s.textContent=e.prefix,n.appendChild(document.createTextNode(t)),n}const{prototype:{toString:Kt}}=Object;function Qt(e,t,i,n,s){let r=typeof e;switch(r){case"boolean":case"undefined":e+="";break;case"number":e=0===e&&1/e<0?"-0":e+"";break;case"bigint":e+="n";break;case"symbol":e=Qe(e);break;case"function":return function(e,t){var i,n,s=Ut.call(e);switch(e.constructor&&e.constructor.name){case"AsyncFunction":i=qt;break;case"AsyncGeneratorFunction":i=$t;break;case"GeneratorFunction":i=zt;break;default:i=/^class\b/.test(s)?Wt:Ht}return i===Wt?Gt(i,"",t):(n=/^(?:async\s*)?(\w+)\s*=>/.exec(s))?Gt(i,"("+n[1]+")",t):(n=/^(?:async\s*)?\(\s*(\w+(?:\s*,\s*\w+)*)?\s*\)/.exec(s))||(n=/^(?:async\s*)?function(?:\s*\*)?(?:\s*\w+)?\s*\(\s*(\w+(?:\s*,\s*\w+)*)?\s*\)/.exec(s))?Gt(i,n[1]?"("+n[1].replace(/\s*,\s*/g,", ")+")":"()",t):Gt(i,"(…)",t)}(e,n);case"string":return function(e,t,i,n){if(!1===t){if(Ft(e,/["\n]/g)<=Ft(e,/`|\${/g)){const t=document.createElement("span");n&&t.appendChild(Ge(n));const i=t.appendChild(document.createElement("span"));return i.className="observablehq--string",i.textContent=JSON.stringify(e),t}const s=e.split("\n");if(s.length>20&&!i){const i=document.createElement("div");n&&i.appendChild(Ge(n));const r=i.appendChild(document.createElement("span"));r.className="observablehq--string",r.textContent="`"+jt(s.slice(0,20).join("\n"));const a=i.appendChild(document.createElement("span")),o=s.length-20;return a.textContent=`Show ${o} truncated line${o>1?"s":""}`,a.className="observablehq--string-expand",a.addEventListener("mouseup",(function(s){s.stopPropagation(),Yt(i,Qt(e,t,!0,n))})),i}const r=document.createElement("span");n&&r.appendChild(Ge(n));const a=r.appendChild(document.createElement("span"));return a.className="observablehq--string"+(i?" observablehq--expanded":""),a.textContent="`"+jt(e)+"`",r}const s=document.createElement("span");n&&s.appendChild(Ge(n));const r=s.appendChild(document.createElement("span"));return r.className="observablehq--string",r.textContent=JSON.stringify(e.length>100?`${e.slice(0,50)}…${e.slice(-49)}`:e),s}(e,t,i,n);default:if(null===e){r=null,e="null";break}if(e instanceof Date){r="date",e=Ot(e,"Invalid Date");break}if(e===Je){r="forbidden",e="[forbidden]";break}switch(Kt.call(e)){case"[object RegExp]":r="regexp",e=function(e){return Dt.call(e)}(e);break;case"[object Error]":case"[object DOMException]":r="error",e=function(e){return e.stack||Rt.call(e)}(e);break;default:return(i?ht:Et)(e,t,n,s)}}const a=document.createElement("span");n&&a.appendChild(Ge(n));const o=a.appendChild(document.createElement("span"));return o.className=`observablehq--${r}`,o.textContent=e,a}function Yt(e,t){e.classList.contains("observablehq--inspect")&&t.classList.add("observablehq--inspect"),e.parentNode.replaceChild(t,e),We(t,"load")}const Xt=/\s+\(\d+:\d+\)$/m;class Zt{constructor(e){if(!e)throw new Error("invalid node");this._node=e,e.classList.add("observablehq")}pending(){const{_node:e}=this;e.classList.remove("observablehq--error"),e.classList.add("observablehq--running")}fulfilled(e,t){const{_node:i}=this;if((!function(e){return(e instanceof Element||e instanceof Text)&&e instanceof e.constructor}(e)||e.parentNode&&e.parentNode!==i)&&(e=Qt(e,!1,i.firstChild&&i.firstChild.classList&&i.firstChild.classList.contains("observablehq--expanded"),t)).classList.add("observablehq--inspect"),i.classList.remove("observablehq--running","observablehq--error"),i.firstChild!==e)if(i.firstChild){for(;i.lastChild!==i.firstChild;)i.removeChild(i.lastChild);i.replaceChild(e,i.firstChild)}else i.appendChild(e);We(i,"update")}rejected(e,t){const{_node:i}=this;for(i.classList.remove("observablehq--running"),i.classList.add("observablehq--error");i.lastChild;)i.removeChild(i.lastChild);var n=document.createElement("div");n.className="observablehq--inspect",t&&n.appendChild(Ge(t)),n.appendChild(document.createTextNode((e+"").replace(Xt,""))),i.appendChild(n),We(i,"error",{error:e})}}Zt.into=function(e){if("string"==typeof e&&null==(e=document.querySelector(e)))throw new Error("container not found");return function(){return new Zt(e.appendChild(document.createElement("div")))}};const Jt=new Map,ei=[],ti=ei.map,ii=ei.some,ni=ei.hasOwnProperty,si="https://cdn.jsdelivr.net/npm/",ri=/^((?:@[^/@]+\/)?[^/@]+)(?:@([^/]+))?(?:\/(.*))?$/,ai=/^\d+\.\d+\.\d+(-[\w-.+]+)?$/,oi=/\.[^/]*$/,ci=["unpkg","jsdelivr","browser","main"];class RequireError extends Error{constructor(e){super(e)}}function hi(e){const t=ri.exec(e);return t&&{name:t[1],version:t[2],path:t[3]}}function li(e){const t=`${si}${e.name}${e.version?`@${e.version}`:""}/package.json`;let i=Jt.get(t);return i||Jt.set(t,i=fetch(t).then((e=>{if(!e.ok)throw new RequireError("unable to load package.json");return e.redirected&&!Jt.has(e.url)&&Jt.set(e.url,i),e.json()}))),i}RequireError.prototype.name=RequireError.name;var pi=ui((async function(e,t){if(e.startsWith(si)&&(e=e.substring(si.length)),/^(\w+:)|\/\//i.test(e))return e;if(/^[.]{0,2}\//i.test(e))return new URL(e,null==t?location:t).href;if(!e.length||/^[\s._]/.test(e)||/\s$/.test(e))throw new RequireError("illegal name");const i=hi(e);if(!i)return`${si}${e}`;if(!i.version&&null!=t&&t.startsWith(si)){const e=await li(hi(t.substring(si.length)));i.version=e.dependencies&&e.dependencies[i.name]||e.peerDependencies&&e.peerDependencies[i.name]}if(i.path&&!oi.test(i.path)&&(i.path+=".js"),i.path&&i.version&&ai.test(i.version))return`${si}${i.name}@${i.version}/${i.path}`;const n=await li(i);return`${si}${n.name}@${n.version}/${i.path||function(e){for(const t of ci){const i=e[t];if("string"==typeof i)return oi.test(i)?i:`${i}.js`}}(n)||"index.js"}`}));function ui(e){const t=new Map,i=s(null);function n(e){if("string"!=typeof e)return e;let i=t.get(e);return i||t.set(e,i=new Promise(((t,i)=>{const n=document.createElement("script");n.onload=()=>{try{t(ei.pop()(s(e)))}catch(e){i(new RequireError("invalid module"))}n.remove()},n.onerror=()=>{i(new RequireError("unable to load module")),n.remove()},n.async=!0,n.src=e,window.define=gi,document.head.appendChild(n)}))),i}function s(t){return i=>Promise.resolve(e(i,t)).then(n)}function r(e){return arguments.length>1?Promise.all(ti.call(arguments,i)).then(di):i(e)}return r.alias=function(t){return ui(((i,n)=>i in t&&(n=null,"string"!=typeof(i=t[i]))?i:e(i,n)))},r.resolve=e,r}function di(e){const t={};for(const i of e)for(const e in i)ni.call(i,e)&&(null==i[e]?Object.defineProperty(t,e,{get:fi(i,e)}):t[e]=i[e]);return t}function fi(e,t){return()=>e[t]}function mi(e){return"exports"===(e+="")||"module"===e}function gi(e,t,i){const n=arguments.length;n<2?(i=e,t=[]):n<3&&(i=t,t="string"==typeof e?[]:e),ei.push(ii.call(t,mi)?e=>{const n={},s={exports:n};return Promise.all(ti.call(t,(t=>"exports"===(t+="")?n:"module"===t?s:e(t)))).then((e=>(i.apply(null,e),s.exports)))}:e=>Promise.all(ti.call(t,e)).then((e=>"function"==typeof i?i.apply(null,e):i)))}function vi(e,t,i){return{resolve:(n=i)=>`https://cdn.jsdelivr.net/npm/${e}@${t}/${n}`}}gi.amd={};const xi=vi("d3","7.4.4","dist/d3.min.js"),yi=vi("@observablehq/inputs","0.10.4","dist/inputs.min.js"),bi=vi("@observablehq/plot","0.4.3","dist/plot.umd.min.js"),wi=vi("@observablehq/graphviz","0.2.1","dist/graphviz.min.js"),_i=vi("@observablehq/highlight.js","2.0.0","highlight.min.js"),Ei=vi("@observablehq/katex","0.11.1","dist/katex.min.js"),ki=vi("lodash","4.17.21","lodash.min.js"),Si=vi("htl","0.3.1","dist/htl.min.js"),Ci=vi("jszip","3.9.1","dist/jszip.min.js"),Ai=vi("marked","0.3.12","marked.min.js"),Pi=vi("sql.js","1.6.2","dist/sql-wasm.js"),Ii=vi("vega","5.22.1","build/vega.min.js"),Ni=vi("vega-lite","5.2.0","build/vega-lite.min.js"),Li=vi("vega-lite-api","5.0.0","build/vega-lite-api.min.js"),Ti=vi("apache-arrow","4.0.1","Arrow.es2015.min.js"),Vi=vi("arquero","4.8.8","dist/arquero.min.js"),Oi=vi("topojson-client","3.1.0","dist/topojson-client.min.js"),Mi=vi("exceljs","4.3.0","dist/exceljs.min.js"),Ri=vi("mermaid","9.0.0","dist/mermaid.min.js");async function Di(e){return(await e(Pi.resolve()))({locateFile:e=>Pi.resolve(`dist/${e}`)})}class ji{constructor(e){Object.defineProperties(this,{_db:{value:e}})}static async open(e){const[t,i]=await Promise.all([Di(pi),Promise.resolve(e).then(Bi)]);return new ji(new t.Database(i))}async query(e,t){return await async function(e,t,i){const[n]=await e.exec(t,i);if(!n)return[];const{columns:s,values:r}=n,a=r.map((e=>Object.fromEntries(e.map(((e,t)=>[s[t],e])))));return a.columns=s,a}(this._db,e,t)}async queryRow(e,t){return(await this.query(e,t))[0]||null}async explain(e,t){return Fi("pre",{className:"observablehq--inspect"},[Ui((await this.query(`EXPLAIN QUERY PLAN ${e}`,t)).map((e=>e.detail)).join("\n"))])}async describe(e){const t=await(void 0===e?this.query("SELECT name FROM sqlite_master WHERE type = 'table'"):this.query("SELECT * FROM pragma_table_info(?)",[e]));if(!t.length)throw new Error("Not found");const{columns:i}=t;return Fi("table",{value:t},[Fi("thead",[Fi("tr",i.map((e=>Fi("th",[Ui(e)]))))]),Fi("tbody",t.map((e=>Fi("tr",i.map((t=>Fi("td",[Ui(e[t])])))))))])}async sql(e,...t){return this.query(e.join("?"),t)}}function Bi(e){return"string"==typeof e?fetch(e).then(Bi):e instanceof Response||e instanceof Blob?e.arrayBuffer().then(Bi):e instanceof ArrayBuffer?new Uint8Array(e):e}function Fi(e,t,i){2===arguments.length&&(i=t,t=void 0);const n=document.createElement(e);if(void 0!==t)for(const e in t)n[e]=t[e];if(void 0!==i)for(const e of i)n.appendChild(e);return n}function Ui(e){return document.createTextNode(e)}Object.defineProperty(ji.prototype,"dialect",{value:"sqlite"});class qi{constructor(e){Object.defineProperties(this,{_:{value:e},sheetNames:{value:e.worksheets.map((e=>e.name)),enumerable:!0}})}sheet(e,t){const i="number"==typeof e?this.sheetNames[e]:this.sheetNames.includes(e+="")?e:null;if(null==i)throw new Error(`Sheet not found: ${e}`);return function(e,{range:t,headers:i}={}){let[[n,s],[r,a]]=function(e=":",{columnCount:t,rowCount:i}){if(!(e+="").match(/^[A-Z]*\d*:[A-Z]*\d*$/))throw new Error("Malformed range specifier");const[[n=0,s=0],[r=t-1,a=i-1]]=e.split(":").map(zi);return[[n,s],[r,a]]}(t,e);const o=i?e._rows[s++]:null;let c=new Set(["#"]);for(let e=n;e<=r;e++){const t=o?$i(o.findCell(e+1)):null;let i=t&&t+""||Hi(e);for(;c.has(i);)i+="_";c.add(i)}c=new Array(n).concat(Array.from(c));const h=new Array(a-s+1);for(let t=s;t<=a;t++){const i=h[t-s]=Object.create(null,{"#":{value:t+1}}),a=e.getRow(t+1);if(a.hasValues)for(let e=n;e<=r;e++){const t=$i(a.findCell(e+1));null!=t&&(i[c[e+1]]=t)}}return h.columns=c.filter((()=>!0)),h}(this._.getWorksheet(i),t)}}function $i(e){if(!e)return;const{value:t}=e;if(t&&"object"==typeof t&&!(t instanceof Date)){if(t.formula||t.sharedFormula)return t.result&&t.result.error?NaN:t.result;if(t.richText)return Wi(t);if(t.text){let{text:e}=t;return e.richText&&(e=Wi(e)),t.hyperlink&&t.hyperlink!==e?`${t.hyperlink} ${e}`:e}return t}return t}function Wi(e){return e.richText.map((e=>e.text)).join("")}function Hi(e){let t="";e++;do{t=String.fromCharCode(64+(e%26||26))+t}while(e=Math.floor((e-1)/26));return t}function zi(e){const[,t,i]=e.match(/^([A-Z]*)(\d*)$/);let n=0;if(t)for(let e=0;e{const s=new Image;new URL(t,document.baseURI).origin!==new URL(location).origin&&(s.crossOrigin="anonymous"),Object.assign(s,e),s.onload=()=>i(s),s.onerror=()=>n(new Error(`Unable to load file: ${this.name}`)),s.src=t}))}async arrow(){const[e,t]=await Promise.all([pi(Ti.resolve()),Gi(this)]);return e.Table.from(t)}async sqlite(){return ji.open(Gi(this))}async zip(){const[e,t]=await Promise.all([pi(Ci.resolve()),this.arrayBuffer()]);return new Zi(await e.loadAsync(t))}async xml(e="application/xml"){return(new DOMParser).parseFromString(await this.text(),e)}async html(){return this.xml("text/html")}async xlsx(){const[e,t]=await Promise.all([pi(Mi.resolve()),this.arrayBuffer()]);return new qi(await(new e.Workbook).xlsx.load(t))}}class Yi extends Qi{constructor(e,t,i){super(t,i),Object.defineProperty(this,"_url",{value:e})}async url(){return await this._url+""}}function Xi(e){throw new Error(`File not found: ${e}`)}class Zi{constructor(e){Object.defineProperty(this,"_",{value:e}),this.filenames=Object.keys(e.files).filter((t=>!e.files[t].dir))}file(e){const t=this._.file(e+="");if(!t||t.dir)throw new Error(`file not found: ${e}`);return new Ji(t)}}class Ji extends Qi{constructor(e){super(e.name),Object.defineProperty(this,"_",{value:e}),Object.defineProperty(this,"_url",{writable:!0})}async url(){return this._url||(this._url=this.blob().then(URL.createObjectURL))}async blob(){return this._.async("blob")}async arrayBuffer(){return this._.async("arraybuffer")}async text(){return this._.async("text")}async json(){return JSON.parse(await this.text())}}var en={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};var tn=0;function nn(e){return new sn("O-"+(null==e?"":e+"-")+ ++tn)}function sn(e){this.id=e,this.href=new URL(`#${e}`,location)+""}sn.prototype.toString=function(){return"url("+this.href+")"};var rn={canvas:function(e,t){var i=document.createElement("canvas");return i.width=e,i.height=t,i},context2d:function(e,t,i){null==i&&(i=devicePixelRatio);var n=document.createElement("canvas");n.width=e*i,n.height=t*i,n.style.width=e+"px";var s=n.getContext("2d");return s.scale(i,i),s},download:function(e,t="untitled",i="Save"){const n=document.createElement("a"),s=n.appendChild(document.createElement("button"));async function r(){await new Promise(requestAnimationFrame),URL.revokeObjectURL(n.href),n.removeAttribute("href"),s.textContent=i,s.disabled=!1}return s.textContent=i,n.download=t,n.onclick=async t=>{if(s.disabled=!0,n.href)return r();s.textContent="Saving…";try{const t=await("function"==typeof e?e():e);s.textContent="Download",n.href=URL.createObjectURL(t)}catch(e){s.textContent=i}if(t.eventPhase)return r();s.disabled=!1},n},element:function(e,t){var i,n=e+="",s=n.indexOf(":");s>=0&&"xmlns"!==(n=e.slice(0,s))&&(e=e.slice(s+1));var r=en.hasOwnProperty(n)?document.createElementNS(en[n],e):document.createElement(e);if(t)for(var a in t)s=(n=a).indexOf(":"),i=t[a],s>=0&&"xmlns"!==(n=a.slice(0,s))&&(a=a.slice(s+1)),en.hasOwnProperty(n)?r.setAttributeNS(en[n],a,i):r.setAttribute(a,i);return r},input:function(e){var t=document.createElement("input");return null!=e&&(t.type=e),t},range:function(e,t,i){1===arguments.length&&(t=e,e=null);var n=document.createElement("input");return n.min=e=null==e?0:+e,n.max=t=null==t?1:+t,n.step=null==i?"any":i=+i,n.type="range",n},select:function(e){var t=document.createElement("select");return Array.prototype.forEach.call(e,(function(e){var i=document.createElement("option");i.value=i.textContent=e,t.appendChild(i)})),t},svg:function(e,t){var i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox",[0,0,e,t]),i.setAttribute("width",e),i.setAttribute("height",t),i},text:function(e){return document.createTextNode(e)},uid:nn};var an={buffer:function(e){return new Promise((function(t,i){var n=new FileReader;n.onload=function(){t(n.result)},n.onerror=i,n.readAsArrayBuffer(e)}))},text:function(e){return new Promise((function(t,i){var n=new FileReader;n.onload=function(){t(n.result)},n.onerror=i,n.readAsText(e)}))},url:function(e){return new Promise((function(t,i){var n=new FileReader;n.onload=function(){t(n.result)},n.onerror=i,n.readAsDataURL(e)}))}};function on(){return this}function cn(e,t){let i=!1;if("function"!=typeof t)throw new Error("dispose is not a function");return{[Symbol.iterator]:on,next:()=>i?{done:!0}:(i=!0,{done:!1,value:e}),return:()=>(i=!0,t(e),{done:!0}),throw:()=>({done:i=!0})}}function hn(e){let t,i,n=!1;const s=e((function(e){i?(i(e),i=null):n=!0;return t=e}));if(null!=s&&"function"!=typeof s)throw new Error("function"==typeof s.then?"async initializers are not supported":"initializer returned something, but not a dispose function");return{[Symbol.iterator]:on,throw:()=>({done:!0}),return:()=>(null!=s&&s(),{done:!0}),next:function(){return{done:!1,value:n?(n=!1,Promise.resolve(t)):new Promise((e=>i=e))}}}}function ln(e){switch(e.type){case"range":case"number":return e.valueAsNumber;case"date":return e.valueAsDate;case"checkbox":return e.checked;case"file":return e.multiple?e.files:e.files[0];case"select-multiple":return Array.from(e.selectedOptions,(e=>e.value));default:return e.value}}var pn={disposable:cn,filter:function*(e,t){for(var i,n=-1;!(i=e.next()).done;)t(i.value,++n)&&(yield i.value)},input:function(e){return hn((function(t){var i=function(e){switch(e.type){case"button":case"submit":case"checkbox":return"click";case"file":return"change";default:return"input"}}(e),n=ln(e);function s(){t(ln(e))}return e.addEventListener(i,s),void 0!==n&&t(n),function(){e.removeEventListener(i,s)}}))},map:function*(e,t){for(var i,n=-1;!(i=e.next()).done;)yield t(i.value,++n)},observe:hn,queue:function(e){let t;const i=[],n=e((function(e){i.push(e),t&&(t(i.shift()),t=null);return e}));if(null!=n&&"function"!=typeof n)throw new Error("function"==typeof n.then?"async initializers are not supported":"initializer returned something, but not a dispose function");return{[Symbol.iterator]:on,throw:()=>({done:!0}),return:()=>(null!=n&&n(),{done:!0}),next:function(){return{done:!1,value:i.length?Promise.resolve(i.shift()):new Promise((e=>t=e))}}}},range:function*(e,t,i){e=+e,t=+t,i=(s=arguments.length)<2?(t=e,e=0,1):s<3?1:+i;for(var n=-1,s=0|Math.max(0,Math.ceil((t-e)/i));++n{i.terminate(),URL.revokeObjectURL(t)}))}};function un(e,t){return function(i){var n,s,r,a,o,c,h,l,p=i[0],u=[],d=null,f=-1;for(o=1,c=arguments.length;o0){for(r=new Array(f),a=document.createTreeWalker(d,NodeFilter.SHOW_COMMENT,null,!1);a.nextNode();)s=a.currentNode,/^o:/.test(s.nodeValue)&&(r[+s.nodeValue.slice(2)]=s);for(o=0;o{t=e}))},value:{get:()=>e,set:i=>t(e=i)}}),void 0!==e&&t(e)}function*mn(){for(;;)yield Date.now()}var gn=new Map;function vn(e,t){var i;return(i=gn.get(e=+e))?i.then((()=>t)):(i=Date.now())>=e?Promise.resolve(t):function(e,t){var i=new Promise((function(i){gn.delete(t);var n=t-e;if(!(n>0))throw new Error("invalid time");if(n>2147483647)throw new Error("too long to wait");setTimeout(i,n)}));return gn.set(t,i),i}(i,e).then((()=>t))}var xn={delay:function(e,t){return new Promise((function(i){setTimeout((function(){i(t)}),e)}))},tick:function(e,t){return vn(Math.ceil((Date.now()+1)/e)*e,t)},when:vn};function yn(e,t){if(/^(\w+:)|\/\//i.test(e))return e;if(/^[.]{0,2}\//i.test(e))return new URL(e,null==t?location:t).href;if(!e.length||/^[\s._]/.test(e)||/\s$/.test(e))throw new Error("illegal name");return"https://unpkg.com/"+e}function bn(e){return null==e?pi:ui(e)}var wn=un((function(e){var t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.innerHTML=e.trim(),t}),(function(){return document.createElementNS("http://www.w3.org/2000/svg","g")})),_n=String.raw;function En(){return hn((function(e){var t=e(document.body.clientWidth);function i(){var i=document.body.clientWidth;i!==t&&e(t=i)}return window.addEventListener("resize",i),function(){window.removeEventListener("resize",i)}}))}var kn=Object.assign((function(e){const t=bn(e);var i;Object.defineProperties(this,(i={FileAttachment:()=>Xi,Arrow:()=>t(Ti.resolve()),Inputs:()=>t(yi.resolve()).then((e=>({...e,file:e.fileOf(Qi)}))),Mutable:()=>fn,Plot:()=>t(bi.resolve()),SQLite:()=>Di(t),SQLiteDatabaseClient:()=>ji,_:()=>t(ki.resolve()),aq:()=>t.alias({"apache-arrow":Ti.resolve()})(Vi.resolve()),d3:()=>t(xi.resolve()),dot:()=>t(wi.resolve()),htl:()=>t(Si.resolve()),html:()=>dn,md:()=>function(e){return e(Ai.resolve()).then((function(t){return un((function(i){var n=document.createElement("div");n.innerHTML=t(i,{langPrefix:""}).trim();var s=n.querySelectorAll("pre code[class]");return s.length>0&&e(_i.resolve()).then((function(t){s.forEach((function(i){function n(){t.highlightBlock(i),i.parentNode.classList.add("observablehq--md-pre")}t.getLanguage(i.className)?n():e(_i.resolve("async-languages/index.js")).then((n=>{if(n.has(i.className))return e(_i.resolve("async-languages/"+n.get(i.className))).then((e=>{t.registerLanguage(i.className,e)}))})).then(n,n)}))})),n}),(function(){return document.createElement("div")}))}))}(t),mermaid:()=>async function(e){const t=await e(Ri.resolve());return t.initialize({securityLevel:"loose",theme:"neutral"}),function(){const e=document.createElement("div");return e.innerHTML=t.render(nn().id,String.raw.apply(String,arguments)),e.removeChild(e.firstChild)}}(t),now:mn,require:()=>t,resolve:()=>yn,svg:()=>wn,tex:()=>function(e){return Promise.all([e(Ei.resolve()),(t=Ei.resolve("dist/katex.min.css"),new Promise((function(e,i){var n=document.createElement("link");n.rel="stylesheet",n.href=t,n.onerror=i,n.onload=e,document.head.appendChild(n)})))]).then((function(e){var t=e[0],i=n();function n(e){return function(){var i=document.createElement("div");return t.render(_n.apply(String,arguments),i,e),i.removeChild(i.firstChild)}}return i.options=n,i.block=n({displayMode:!0}),i}));var t}(t),topojson:()=>t(Oi.resolve()),vl:()=>async function(e){const[t,i,n]=await Promise.all([Ii,Ni,Li].map((t=>e(t.resolve()))));return n.register(t,i)}(t),width:En,DOM:rn,Files:an,Generators:pn,Promises:xn},Object.fromEntries(Object.entries(i).map(Sn))))}),{resolve:pi.resolve});function Sn([e,t]){return[e,{value:t,writable:!0,enumerable:!0}]}function Cn(e,t){this.message=e+"",this.input=t}Cn.prototype=Object.create(Error.prototype),Cn.prototype.name="RuntimeError",Cn.prototype.constructor=Cn;var An=Array.prototype,Pn=An.map,In=An.forEach;function Nn(e){return function(){return e}}function Ln(e){return e}function Tn(){}var Vn={};function On(e,t,i){var n;i||(i=Vn),Object.defineProperties(this,{_observer:{value:i,writable:!0},_definition:{value:Dn,writable:!0},_duplicate:{value:void 0,writable:!0},_duplicates:{value:void 0,writable:!0},_indegree:{value:NaN,writable:!0},_inputs:{value:[],writable:!0},_invalidate:{value:Tn,writable:!0},_module:{value:t},_name:{value:null,writable:!0},_outputs:{value:new Set,writable:!0},_promise:{value:Promise.resolve(void 0),writable:!0},_reachable:{value:i!==Vn,writable:!0},_rejector:{value:(n=this,function(e){if(e===Dn)throw new Cn(n._name+" is not defined",n._name);if(e instanceof Error&&e.message)throw new Cn(e.message,n._name);throw new Cn(n._name+" could not be resolved",n._name)})},_type:{value:e},_value:{value:void 0,writable:!0},_version:{value:0,writable:!0}})}function Mn(e){e._module._runtime._dirty.add(e),e._outputs.add(this)}function Rn(e){e._module._runtime._dirty.add(e),e._outputs.delete(this)}function Dn(){throw Dn}function jn(e){return function(){throw new Cn(e+" is defined more than once")}}function Bn(e,t,i){var n=this._module._scope,s=this._module._runtime;if(this._inputs.forEach(Rn,this),t.forEach(Mn,this),this._inputs=t,this._definition=i,this._value=void 0,i===Tn?s._variables.delete(this):s._variables.add(this),e!==this._name||n.get(e)!==this){var r,a;if(this._name)if(this._outputs.size)n.delete(this._name),(a=this._module._resolve(this._name))._outputs=this._outputs,this._outputs=new Set,a._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(this)]=a}),this),a._outputs.forEach(s._updates.add,s._updates),s._dirty.add(a).add(this),n.set(this._name,a);else if((a=n.get(this._name))===this)n.delete(this._name);else{if(3!==a._type)throw new Error;a._duplicates.delete(this),this._duplicate=void 0,1===a._duplicates.size&&(a=a._duplicates.keys().next().value,r=n.get(this._name),a._outputs=r._outputs,r._outputs=new Set,a._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(r)]=a})),a._definition=a._duplicate,a._duplicate=void 0,s._dirty.add(r).add(a),s._updates.add(a),n.set(this._name,a))}if(this._outputs.size)throw new Error;e&&((a=n.get(e))?3===a._type?(this._definition=jn(e),this._duplicate=i,a._duplicates.add(this)):2===a._type?(this._outputs=a._outputs,a._outputs=new Set,this._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(a)]=this}),this),s._dirty.add(a).add(this),n.set(e,this)):(a._duplicate=a._definition,this._duplicate=i,(r=new On(3,this._module))._name=e,r._definition=this._definition=a._definition=jn(e),r._outputs=a._outputs,a._outputs=new Set,r._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(a)]=r})),r._duplicates=new Set([this,a]),s._dirty.add(a).add(r),s._updates.add(a).add(r),n.set(e,r)):n.set(e,this)),this._name=e}return s._updates.add(this),s._compute(),this}function Fn(e,t=[]){Object.defineProperties(this,{_runtime:{value:e},_scope:{value:new Map},_builtins:{value:new Map([["invalidation",$n],["visibility",Wn],...t])},_source:{value:null,writable:!0}})}function Un(e){return e._name}Object.defineProperties(On.prototype,{_pending:{value:function(){this._observer.pending&&this._observer.pending()},writable:!0,configurable:!0},_fulfilled:{value:function(e){this._observer.fulfilled&&this._observer.fulfilled(e,this._name)},writable:!0,configurable:!0},_rejected:{value:function(e){this._observer.rejected&&this._observer.rejected(e,this._name)},writable:!0,configurable:!0},define:{value:function(e,t,i){switch(arguments.length){case 1:i=e,e=t=null;break;case 2:i=t,"string"==typeof e?t=null:(t=e,e=null)}return Bn.call(this,null==e?null:e+"",null==t?[]:Pn.call(t,this._module._resolve,this._module),"function"==typeof i?i:Nn(i))},writable:!0,configurable:!0},delete:{value:function(){return Bn.call(this,null,[],Tn)},writable:!0,configurable:!0},import:{value:function(e,t,i){arguments.length<3&&(i=t,t=e);return Bn.call(this,t+"",[i._resolve(e+"")],Ln)},writable:!0,configurable:!0}}),Object.defineProperties(Fn.prototype,{_copy:{value:function(e,t){e._source=this,t.set(this,e);for(const[r,a]of this._scope){var i=e._scope.get(r);if(!i||1!==i._type)if(a._definition===Ln){var n=a._inputs[0],s=n._module;e.import(n._name,r,t.get(s)||(s._source?s._copy(new Fn(e._runtime,e._builtins),t):s))}else e.define(r,a._inputs.map(Un),a._definition)}return e},writable:!0,configurable:!0},_resolve:{value:function(e){var t,i=this._scope.get(e);if(!i)if(i=new On(2,this),this._builtins.has(e))i.define(e,Nn(this._builtins.get(e)));else if(this._runtime._builtin._scope.has(e))i.import(e,this._runtime._builtin);else{try{t=this._runtime._global(e)}catch(t){return i.define(e,(n=t,function(){throw n}))}void 0===t?this._scope.set(i._name=e,i):i.define(e,Nn(t))}var n;return i},writable:!0,configurable:!0},redefine:{value:function(e){var t=this._scope.get(e);if(!t)throw new Cn(e+" is not defined");if(3===t._type)throw new Cn(e+" is defined more than once");return t.define.apply(t,arguments)},writable:!0,configurable:!0},define:{value:function(){var e=new On(1,this);return e.define.apply(e,arguments)},writable:!0,configurable:!0},derive:{value:function(e,t){var i=new Fn(this._runtime,this._builtins);return i._source=this,In.call(e,(function(e){"object"!=typeof e&&(e={name:e+""}),null==e.alias&&(e.alias=e.name),i.import(e.name,e.alias,t)})),Promise.resolve().then((()=>{const e=new Set([this]);for(const t of e)for(const i of t._scope.values())if(i._definition===Ln){const t=i._inputs[0]._module,n=t._source||t;if(n===this)return void console.warn("circular module definition; ignoring");e.add(n)}this._copy(i,new Map)})),i},writable:!0,configurable:!0},import:{value:function(){var e=new On(1,this);return e.import.apply(e,arguments)},writable:!0,configurable:!0},value:{value:async function(e){var t=this._scope.get(e);if(!t)throw new Cn(e+" is not defined");t._observer===Vn&&(t._observer=!0,this._runtime._dirty.add(t));return await this._runtime._compute(),t._promise},writable:!0,configurable:!0},variable:{value:function(e){return new On(1,this,e)},writable:!0,configurable:!0},builtin:{value:function(e,t){this._builtins.set(e,t)},writable:!0,configurable:!0}});const qn="function"==typeof requestAnimationFrame?requestAnimationFrame:setImmediate;var $n={},Wn={};function Hn(e=new kn,t=es){var i=this.module();if(Object.defineProperties(this,{_dirty:{value:new Set},_updates:{value:new Set},_precomputes:{value:[],writable:!0},_computing:{value:null,writable:!0},_init:{value:null,writable:!0},_modules:{value:new Map},_variables:{value:new Set},_disposed:{value:!1,writable:!0},_builtin:{value:i},_global:{value:t}}),e)for(var n in e)new On(2,i).define(n,[],e[n])}function zn(e){const t=new Set(e._inputs);for(const i of t){if(i===e)return!0;i._inputs.forEach(t.add,t)}return!1}function Gn(e){++e._indegree}function Kn(e){--e._indegree}function Qn(e){return e._promise.catch(e._rejector)}function Yn(e){return new Promise((function(t){e._invalidate=t}))}function Xn(e,t){let i,n,s="function"==typeof IntersectionObserver&&t._observer&&t._observer._node,r=!s,a=Tn,o=Tn;return s&&(n=new IntersectionObserver((([e])=>(r=e.isIntersecting)&&(i=null,a()))),n.observe(s),e.then((()=>(n.disconnect(),n=null,o())))),function(e){return r?Promise.resolve(e):n?(i||(i=new Promise(((e,t)=>(a=e,o=t)))),i.then((()=>e))):Promise.reject()}}function Zn(e){e._invalidate(),e._invalidate=Tn,e._pending();const t=e._value,i=++e._version;let n=null;const s=e._promise=(e._inputs.length?Promise.all(e._inputs.map(Qn)).then((function(s){if(e._version!==i)return;for(var r=0,a=s.length;ri(e._definition.call(t))))).then((function(t){if(function(e){return e&&"function"==typeof e.next&&"function"==typeof e.return}(t))return e._version!==i?void t.return():((n||Yn(e)).then((s=t,function(){s.return()})),function(e,t,i){const n=e._module._runtime;function s(e){return new Promise((e=>e(i.next()))).then((({done:t,value:i})=>t?void 0:Promise.resolve(i).then(e)))}function r(){const i=s((s=>{if(e._version===t)return a(s,i).then((()=>n._precompute(r))),e._fulfilled(s),s}));i.catch((n=>{e._version===t&&(a(void 0,i),e._rejected(n))}))}function a(t,i){return e._value=t,e._promise=i,e._outputs.forEach(n._updates.add,n._updates),n._compute()}return s((i=>{if(e._version===t)return n._precompute(r),i}))}(e,i,t));var s;return t}));s.then((t=>{e._version===i&&(e._value=t,e._fulfilled(t))}),(t=>{e._version===i&&(e._value=void 0,e._rejected(t))}))}function Jn(e,t){e._invalidate(),e._invalidate=Tn,e._pending(),++e._version,e._indegree=NaN,(e._promise=Promise.reject(t)).catch(Tn),e._value=void 0,e._rejected(t)}function es(e){return window[e]}function ts(e){const t=document.createElement("template");return t.innerHTML=e,document.importNode(t.content,!0)}function is(e){const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.innerHTML=e,t}Object.defineProperties(Hn,{load:{value:function(e,t,i){if("function"==typeof t&&(i=t,t=null),"function"!=typeof i)throw new Error("invalid observer");null==t&&(t=new kn);const{modules:n,id:s}=e,r=new Map,a=new Hn(t),o=c(s);function c(e){let t=r.get(e);return t||r.set(e,t=a.module()),t}for(const e of n){const t=c(e.id);let n=0;for(const s of e.variables)s.from?t.import(s.remote,s.name,c(s.from)):t===o?t.variable(i(s,n,e.variables)).define(s.name,s.inputs,s.value):t.define(s.name,s.inputs,s.value),++n}return a},writable:!0,configurable:!0}}),Object.defineProperties(Hn.prototype,{_precompute:{value:function(e){this._precomputes.push(e),this._compute()},writable:!0,configurable:!0},_compute:{value:function(){return this._computing||(this._computing=this._computeSoon())},writable:!0,configurable:!0},_computeSoon:{value:function(){return new Promise(qn).then((()=>this._disposed?void 0:this._computeNow()))},writable:!0,configurable:!0},_computeNow:{value:async function(){var e,t,i=[],n=this._precomputes;if(n.length){this._precomputes=[];for(const e of n)e();await function(e=0){let t=Promise.resolve();for(let i=0;i{}));return t}(3)}(e=new Set(this._dirty)).forEach((function(t){t._inputs.forEach(e.add,e);const i=function(e){if(e._observer!==Vn)return!0;var t=new Set(e._outputs);for(const e of t){if(e._observer!==Vn)return!0;e._outputs.forEach(t.add,t)}return!1}(t);i>t._reachable?this._updates.add(t):i{e._invalidate(),e._version=NaN}))},writable:!0,configurable:!0},module:{value:function(e,t=Tn){let i;if(void 0===e)return(i=this._init)?(this._init=null,i):new Fn(this);if(i=this._modules.get(e),i)return i;this._init=i=new Fn(this),this._modules.set(e,i);try{e(this,t)}finally{this._init=null}return i},writable:!0,configurable:!0},fileAttachments:{value:function(e){return Object.assign((t=>{const i=e(t+="");if(null==i)throw new Error(`File not found: ${t}`);if("object"==typeof i&&"url"in i){const{url:e,mimeType:n}=i;return new Yi(e,t,n)}return new Yi(i,t)}),{prototype:Yi.prototype})},writable:!0,configurable:!0}});const ns=Object.assign(ls(ts,(e=>{if(null===e.firstChild)return null;if(e.firstChild===e.lastChild)return e.removeChild(e.firstChild);const t=document.createElement("span");return t.appendChild(e),t})),{fragment:ls(ts,(e=>e))});Object.assign(ls(is,(e=>null===e.firstChild?null:e.firstChild===e.lastChild?e.removeChild(e.firstChild):e)),{fragment:ls(is,(e=>{const t=document.createDocumentFragment();for(;e.firstChild;)t.appendChild(e.firstChild);return t}))});const ss="http://www.w3.org/2000/svg",rs="http://www.w3.org/1999/xlink",as="http://www.w3.org/XML/1998/namespace",os="http://www.w3.org/2000/xmlns/",cs=new Map(["attributeName","attributeType","baseFrequency","baseProfile","calcMode","clipPathUnits","diffuseConstant","edgeMode","filterUnits","glyphRef","gradientTransform","gradientUnits","kernelMatrix","kernelUnitLength","keyPoints","keySplines","keyTimes","lengthAdjust","limitingConeAngle","markerHeight","markerUnits","markerWidth","maskContentUnits","maskUnits","numOctaves","pathLength","patternContentUnits","patternTransform","patternUnits","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","refX","refY","repeatCount","repeatDur","requiredExtensions","requiredFeatures","specularConstant","specularExponent","spreadMethod","startOffset","stdDeviation","stitchTiles","surfaceScale","systemLanguage","tableValues","targetX","targetY","textLength","viewBox","viewTarget","xChannelSelector","yChannelSelector","zoomAndPan"].map((e=>[e.toLowerCase(),e]))),hs=new Map([["xlink:actuate",rs],["xlink:arcrole",rs],["xlink:href",rs],["xlink:role",rs],["xlink:show",rs],["xlink:title",rs],["xlink:type",rs],["xml:lang",as],["xml:space",as],["xmlns",os],["xmlns:xlink",os]]);function ls(e,t){return function({raw:i}){let n,s,r,a,o=1,c="",h=0;for(let e=0,t=arguments.length;e0){const n=arguments[e];switch(o){case 26:if(null!=n){const e=`${n}`;if(gs(s))c+=e.replace(/[<]/g,ps);else{if(new RegExp(`/]`,"i").test(c.slice(-s.length-2)+e))throw new Error("unsafe raw text");c+=e}}break;case 1:null==n||(n instanceof Node||"string"!=typeof n&&n[Symbol.iterator]||/(?:^|>)$/.test(i[e-1])&&/^(?:<|$)/.test(t)?(c+="\x3c!--::"+e+"--\x3e",h|=128):c+=`${n}`.replace(/[<&]/g,ps));break;case 9:{let s;if(o=12,/^[\s>]/.test(t)){if(null==n||!1===n){c=c.slice(0,r-i[e-1].length);break}if(!0===n||""==(s=`${n}`)){c+="''";break}if("style"===i[e-1].slice(r,a)&&fs(n)||"function"==typeof n){c+="::"+e,h|=1;break}}if(void 0===s&&(s=`${n}`),""===s)throw new Error("unsafe unquoted empty string");c+=s.replace(/^['"]|[\s>&]/g,ps);break}case 12:c+=`${n}`.replace(/[\s>&]/g,ps);break;case 11:c+=`${n}`.replace(/['&]/g,ps);break;case 10:c+=`${n}`.replace(/["&]/g,ps);break;case 6:if(fs(n)){c+="::"+e+"=''",h|=1;break}throw new Error("invalid binding");case 17:break;default:throw new Error("invalid binding")}}for(let e=0,i=t.length;e=0;--n)s=t.insertBefore(i[n],s);else for(const n of i)null!=n&&t.insertBefore(n instanceof Node?n:document.createTextNode(n),e);else t.insertBefore(document.createTextNode(i),e);u.push(e)}}}for(const e of u)e.parentNode.removeChild(e);return t(l)}}function ps(e){return`&#${e.charCodeAt(0).toString()};`}function us(e){return 65<=e&&e<=90||97<=e&&e<=122}function ds(e){return 9===e||10===e||12===e||32===e||13===e}function fs(e){return e&&e.toString===Object.prototype.toString}function ms(e){return"script"===e||"style"===e||gs(e)}function gs(e){return"textarea"===e||"title"===e}function vs(e,t,i){return e.slice(t,i).toLowerCase()}function xs(e,t,i){e.namespaceURI===ss&&(t=t.toLowerCase(),t=cs.get(t)||t,hs.has(t))?e.setAttributeNS(hs.get(t),t,i):e.setAttribute(t,i)}function ys(e,t){e.namespaceURI===ss&&(t=t.toLowerCase(),t=cs.get(t)||t,hs.has(t))?e.removeAttributeNS(hs.get(t),t):e.removeAttribute(t)}function bs(e,t){for(const i in t){const n=t[i];i.startsWith("--")?e.setProperty(i,n):e[i]=n}}const ws={bubbles:!0};function _s(e){e.preventDefault()}function Es(e){return e}let ks=0;function Ss(e,t){if(e)return e=ns`