From 8074d0b0887d8dc0520df637a916f912019790b3 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 11 Feb 2025 11:35:59 -0800 Subject: [PATCH 001/578] hide download and sim screenshot in time machine (#10380) --- webapp/src/editortoolbar.tsx | 5 +++-- webapp/src/simtoolbar.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/webapp/src/editortoolbar.tsx b/webapp/src/editortoolbar.tsx index 09ff7274ea2d..2e863b2f049b 100644 --- a/webapp/src/editortoolbar.tsx +++ b/webapp/src/editortoolbar.tsx @@ -358,6 +358,7 @@ export class EditorToolbar extends data.Component } {/* TODO clean this; make it just getCompileButton, and set the buttons fontsize to 0 / the icon itself back to normal to just hide text */} - {!headless &&
+ {!headless && !isTimeMachineEmbed &&
{compileBtn && this.getCompileButton(computer)}
} - {!headless &&
+ {!headless && !isTimeMachineEmbed &&
{compileBtn && this.getCompileButton(mobile)}
}
diff --git a/webapp/src/simtoolbar.tsx b/webapp/src/simtoolbar.tsx index 70fb73b22dec..57fb81de1f3f 100644 --- a/webapp/src/simtoolbar.tsx +++ b/webapp/src/simtoolbar.tsx @@ -119,7 +119,7 @@ export class SimulatorToolbar extends data.Component { const fullscreen = run && !simOpts.hideFullscreen && !sandbox; const audio = run && targetTheme.hasAudio; const isHeadless = simOpts.headless; - const screenshot = !!targetTheme.simScreenshot; + const screenshot = !!targetTheme.simScreenshot && !pxt.shell.isTimeMachineEmbed(); const screenshotClass = !!parentState.screenshoting ? "loading" : ""; const debugBtnEnabled = !isStarting && !isSimulatorPending && inCodeEditor; const runControlsEnabled = !debugging && !isStarting && !isSimulatorPending; From d43f4c8c3287aa59c57f714b15b028f5d4be0426 Mon Sep 17 00:00:00 2001 From: Robert Knight <95928279+microbit-robert@users.noreply.github.com> Date: Wed, 12 Feb 2025 18:13:52 +0000 Subject: [PATCH 002/578] Fix isFocusable for FocusList component (#10378) * Fix isFocusable for FocusList component * Use explicit check for null --- react-common/components/controls/FocusList.tsx | 2 +- react-common/components/util.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/react-common/components/controls/FocusList.tsx b/react-common/components/controls/FocusList.tsx index 8e316a2f7fe6..17944aaa640a 100644 --- a/react-common/components/controls/FocusList.tsx +++ b/react-common/components/controls/FocusList.tsx @@ -59,7 +59,7 @@ export const FocusList = (props: FocusListProps) => { const isFocusable = (e: HTMLElement) => { return e.getAttribute("data-isfocusable") === "true" - && getComputedStyle(e).display !== "none"; + && e.offsetParent !== null; } const onKeyDown = (e: React.KeyboardEvent) => { diff --git a/react-common/components/util.tsx b/react-common/components/util.tsx index 09c8c8e1ecfe..c29c5d542485 100644 --- a/react-common/components/util.tsx +++ b/react-common/components/util.tsx @@ -111,7 +111,7 @@ export function findNextFocusableElement(elements: HTMLElement[], focusedIndex: index += increment; } } - return findNextFocusableElement(elements, focusedIndex, index, forward); + return findNextFocusableElement(elements, focusedIndex, index, forward, isFocusable); } export function isFocusable(e: HTMLElement) { From 211c9324c87079e7310d975e01dca96270057359 Mon Sep 17 00:00:00 2001 From: Robert Knight <95928279+microbit-robert@users.noreply.github.com> Date: Wed, 12 Feb 2025 18:14:21 +0000 Subject: [PATCH 003/578] Make "Pair Now" button keyboard accessible (#10381) --- theme/common.less | 2 +- webapp/src/dialogs.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/common.less b/theme/common.less index 8bdca0bf59a5..9474f91eed26 100644 --- a/theme/common.less +++ b/theme/common.less @@ -1605,7 +1605,7 @@ p.ui.font.small { padding-top: .25rem; } - a.ui.button { + button.ui.button { margin-top: .5rem; } } diff --git a/webapp/src/dialogs.tsx b/webapp/src/dialogs.tsx index b94826ae5c03..3dec9d9611b6 100644 --- a/webapp/src/dialogs.tsx +++ b/webapp/src/dialogs.tsx @@ -790,7 +790,7 @@ export function renderBrowserDownloadInstructions(saveonly?: boolean, redeploy?:
{lf("Download your code faster by pairing with WebUSB!")} - {lf("Pair Now")} +
From 4c5f478d5f9456b516479e67094b52ab8e41aa4b Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 13 Feb 2025 10:04:07 -0800 Subject: [PATCH 004/578] A few blockly tutorial fixes (#10384) * fix class validation for numberdropdown with no min/max * don't cache flyot when tutorial has block config * fix tileset field in tutorial hint --- pxtblocks/fields/field_numberdropdown.ts | 7 ++++++- pxtblocks/fields/field_tileset.ts | 3 +++ webapp/src/blocks.tsx | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pxtblocks/fields/field_numberdropdown.ts b/pxtblocks/fields/field_numberdropdown.ts index 6b25f32887c4..43699ccf4e61 100644 --- a/pxtblocks/fields/field_numberdropdown.ts +++ b/pxtblocks/fields/field_numberdropdown.ts @@ -108,7 +108,12 @@ class BaseFieldNumberDropdown extends BaseFieldTextDropdown { return null; } // Get the value in range. - n = Math.min(Math.max(n, this.min_), this.max_); + if (this.min_ !== undefined) { + n = Math.max(n, this.min_); + } + if (this.max_ !== undefined) { + n = Math.min(n, this.max_); + } // Round to nearest multiple of precision. if (this.precision_ && isFinite(n)) { n = Math.round(n / this.precision_) * this.precision_; diff --git a/pxtblocks/fields/field_tileset.ts b/pxtblocks/fields/field_tileset.ts index 7bcf0fb03a1e..302e7109f0e3 100644 --- a/pxtblocks/fields/field_tileset.ts +++ b/pxtblocks/fields/field_tileset.ts @@ -201,6 +201,9 @@ export class FieldTileset extends FieldImages implements FieldCustom { else if (newValue.startsWith(pxt.sprite.TILE_NAMESPACE)) { tile = project.lookupAsset(pxt.AssetType.Tile, newValue.trim()); } + else { + tile = project.lookupAssetByName(pxt.AssetType.Tile, newValue.trim()); + } if (tile) { this.localTile = tile; diff --git a/webapp/src/blocks.tsx b/webapp/src/blocks.tsx index 7ca40e8afbca..4fac6af0c376 100644 --- a/webapp/src/blocks.tsx +++ b/webapp/src/blocks.tsx @@ -1611,7 +1611,7 @@ export class Editor extends toolboxeditor.ToolboxEditor { // Cache blocks xml list for later this.flyoutBlockXmlCache[cacheKey] = this.flyoutXmlList; } - this.showFlyoutInternal_(this.flyoutXmlList, cacheKey); + this.showFlyoutInternal_(this.flyoutXmlList, cachable && cacheKey); } } From aeae1b22e50cbb095e5928ad613b8f502be99f80 Mon Sep 17 00:00:00 2001 From: Abhijith Chatra Date: Thu, 13 Feb 2025 10:35:51 -0800 Subject: [PATCH 005/578] 11.3.19 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3703f77e5fb6..aebfea5e900b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pxt-core", - "version": "11.3.18", + "version": "11.3.19", "description": "Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors", "keywords": [ "TypeScript", From 9be171f09bf52faee4d230c65f53f658eb850be0 Mon Sep 17 00:00:00 2001 From: Abhijith Chatra Date: Thu, 13 Feb 2025 14:23:37 -0800 Subject: [PATCH 006/578] Adding Guarani language (#10385) --- pxtlib/util.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/pxtlib/util.ts b/pxtlib/util.ts index ec8b7818a957..668dfeb664b3 100644 --- a/pxtlib/util.ts +++ b/pxtlib/util.ts @@ -1224,6 +1224,7 @@ namespace ts.pxtc.Util { "fr-CA": { englishName: "French (Canada)", localizedName: "Français (Canada)" }, "ga-IE": { englishName: "Irish", localizedName: "Gaeilge" }, "gl": { englishName: "Galician", localizedName: "galego" }, + "gn": { englishName: "Guarani", localizedName: "Avañe'ẽ" }, "gu-IN": { englishName: "Gujarati", localizedName: "ગુજરાતી" }, "haw": { englishName: "Hawaiian", localizedName: "ʻŌlelo Hawaiʻi" }, "hi": { englishName: "Hindi", localizedName: "हिन्दी" }, From 1006dd1d5b5fdfd209af09e4d7f4f977a7cc4169 Mon Sep 17 00:00:00 2001 From: Abhijith Chatra Date: Thu, 13 Feb 2025 14:52:40 -0800 Subject: [PATCH 007/578] 11.3.20 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aebfea5e900b..35c5652ff94e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pxt-core", - "version": "11.3.19", + "version": "11.3.20", "description": "Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors", "keywords": [ "TypeScript", From 7114176d67b6447de6f1011fcd4aa77c30c35d75 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 13 Feb 2025 15:44:03 -0800 Subject: [PATCH 008/578] support copy/pasting assets between projects (#10383) * serialize assets for copy/paste * also support copying tileset fields * refactor and inflate * don't keep duplicating assets when pasting multiple times --- pxtblocks/fields/field_asset.ts | 25 +++- pxtblocks/fields/field_tileset.ts | 35 ++++- pxtblocks/fields/field_utils.ts | 221 ++++++++++++++++++++++++++++-- pxtlib/tilemap.ts | 45 ++++-- 4 files changed, 303 insertions(+), 23 deletions(-) diff --git a/pxtblocks/fields/field_asset.ts b/pxtblocks/fields/field_asset.ts index ae81ec2ae562..0d718cddbd86 100644 --- a/pxtblocks/fields/field_asset.ts +++ b/pxtblocks/fields/field_asset.ts @@ -4,7 +4,7 @@ import * as Blockly from "blockly"; import svg = pxt.svgUtil; import { FieldBase } from "./field_base"; -import { getTemporaryAssets, getTilesReferencedByTilesets, setMelodyEditorOpen, workspaceToScreenCoordinates, bitmapToImageURI, tilemapToImageURI, songToDataURI, setBlockDataForField } from "./field_utils"; +import { getTemporaryAssets, getTilesReferencedByTilesets, setMelodyEditorOpen, workspaceToScreenCoordinates, bitmapToImageURI, tilemapToImageURI, songToDataURI, setBlockDataForField, loadAssetFromSaveState, getAssetSaveState } from "./field_utils"; export interface FieldAssetEditorOptions { initWidth?: string; @@ -68,6 +68,27 @@ export abstract class FieldAssetEditor; } +export interface AssetSaveState { + version: number; + assetType: pxt.AssetType; + jres: pxt.Map; + assetId: string; +} + + export namespace svg { export function hasClass(el: SVGElement, cls: string): boolean { return pxt.BrowserUtils.containsClass(el, cls); @@ -283,18 +291,18 @@ export function updateTilemapXml(dom: Element, proj: pxt.TilemapProject) { const newData = new pxt.sprite.TilemapData( legacy.tilemap, { - tileWidth: legacy.tileset.tileWidth, - tiles: legacy.tileset.tiles.map((t, index) => { - if (t.projectId != null) { - return upgradedTileMapping["myTiles.tile" + t.projectId]; - } - if (!mapping[index]) { - mapping[index] = proj.resolveTile(t.qualifiedName) - } - - return mapping[index]; - }) - }, + tileWidth: legacy.tileset.tileWidth, + tiles: legacy.tileset.tiles.map((t, index) => { + if (t.projectId != null) { + return upgradedTileMapping["myTiles.tile" + t.projectId]; + } + if (!mapping[index]) { + mapping[index] = proj.resolveTile(t.qualifiedName) + } + + return mapping[index]; + }) + }, legacy.layers ); @@ -416,6 +424,129 @@ export function getTemporaryAssets(workspace: Blockly.Workspace, type: pxt.Asset } } +export function getAssetSaveState(asset: pxt.Asset) { + const serialized: AssetSaveState = { + version: 1, + assetType: asset.type, + assetId: asset.id, + jres: {} + }; + + const project = pxt.react.getTilemapProject(); + if (asset.type === pxt.AssetType.Tilemap) { + const jres = project.getProjectTilesetJRes(); + + for (const key of Object.keys(jres)) { + if (key === "*") continue; + const entry = jres[key]; + if (entry.mimeType === pxt.TILEMAP_MIME_TYPE) { + if (entry.id !== asset.id) { + delete jres[key]; + } + } + else { + const id = addDotToNamespace(jres["*"].namespace) + key; + + if (!asset.data.tileset.tiles.some(tile => tile.id === id)) { + delete jres[key]; + } + } + } + + serialized.jres = jres; + } + else { + const jres = asset.type === pxt.AssetType.Tile ? + project.getProjectTilesetJRes() : + project.getProjectAssetsJRes(); + + serialized.jres["*"] = jres["*"]; + const [key, entry] = findEntryInJres(jres, asset.id); + serialized.jres[key] = entry; + } + + return serialized; +} + + +export function loadAssetFromSaveState(serialized: AssetSaveState) { + let newId = serialized.assetId; + serialized.jres = inflateJRes(serialized.jres); + + const globalProject = pxt.react.getTilemapProject(); + const existing = globalProject.lookupAsset(serialized.assetType, serialized.assetId); + + // if this id is already in the project, we need to check to see + // if it's the same as what we're loading. if it isn't, we'll need + // to create new assets + if (existing) { + // load the jres into a throwaway project so that we don't pollute + // the actual one + const tempProject = new pxt.TilemapProject(); + + // if this is a tilemap, we need the gallery populated in case + // there are gallery tiles in the tileset + tempProject.loadGallerySnapshot(globalProject.saveGallerySnapshot()); + + if (serialized.assetType === "tilemap" || serialized.assetType === "tile") { + tempProject.loadTilemapJRes(serialized.jres); + } + else { + tempProject.loadAssetsJRes(serialized.jres); + } + + const tempAsset = tempProject.lookupAsset(serialized.assetType, serialized.assetId); + + if (pxt.assetEquals(tempAsset, existing, true)) { + return existing; + } + else { + // the asset ids collided! first try to find another asset in the + // project that has the same value. for example, if the same code + // is copy/pasted multiple times then we will have already created + // a new asset for this code + const valueMatch = globalProject.lookupAssetByValue(tempAsset.type, tempAsset); + + if (valueMatch) { + return valueMatch; + } + + // no existing asset, so remap the id in the jres before loading + // it in the project. in the case of a tilemap, we only need to + // remap the tilemap id because loadTilemapJRes automatically remaps + // tile ids and resolves duplicates + newId = globalProject.generateNewID(serialized.assetType); + + const [key, entry] = findEntryInJres(serialized.jres, serialized.assetId); + delete serialized.jres[key]; + + if (serialized.assetType === "tilemap") { + // tilemap ids don't have namespaces + entry.id = newId; + serialized.jres[newId] = entry; + } + else { + const [namespace, key] = newId.split("."); + if (addDotToNamespace(namespace) !== addDotToNamespace(serialized.jres["*"].namespace)) { + entry.namespace = addDotToNamespace(namespace); + } + entry.id = newId; + serialized.jres[key] = entry; + } + } + } + + + if (serialized.assetType === "tilemap" || serialized.assetType === "tile") { + globalProject.loadTilemapJRes(serialized.jres, true); + } + else { + globalProject.loadAssetsJRes(serialized.jres); + } + + return globalProject.lookupAsset(serialized.assetType, newId); +} + export const FIELD_EDITOR_OPEN_EVENT_TYPE = "field_editor_open"; export class FieldEditorOpenEvent extends Blockly.Events.UiBase { @@ -495,4 +626,70 @@ export function deleteBlockDataForField(block: Blockly.Block, field: string) { const blockData = getBlockData(block); delete blockData.fieldData[field]; setBlockData(block, blockData); +} + +function addDotToNamespace(namespace: string) { + if (namespace.endsWith(".")) { + return namespace; + } + return namespace + "."; +} + +function findEntryInJres(jres: pxt.Map, assetId: string): [string, any] { + const defaultNamespace = jres["*"].namespace; + + for (const key of Object.keys(jres)) { + if (key === "*") continue; + + const entry = jres[key]; + let id: string; + + if (entry.id) { + if (entry.namespace) { + id = addDotToNamespace(entry.namespace) + entry.id; + } + else { + id = entry.id; + } + } + else if (entry.namespace) { + id = addDotToNamespace(entry.namespace) + key; + } + else { + id = addDotToNamespace(defaultNamespace) + key; + } + + if (id === assetId) { + return [key, jres[key]]; + } + } + + // should never happen + return undefined; +} + +// simply replaces the string entries with objects; doesn't do a full inflate like pxt.inflateJRes +function inflateJRes(jres: pxt.Map): pxt.Map { + const meta = jres["*"] as pxt.JRes; + const result: pxt.Map = { + "*": meta + }; + + for (const key of Object.keys(jres)) { + if (key === "*") continue; + + const entry = jres[key]; + if (typeof entry === "string") { + result[key] = { + id: undefined, + data: entry, + mimeType: meta.mimeType + } + } + else { + result[key] = entry; + } + } + + return result; } \ No newline at end of file diff --git a/pxtlib/tilemap.ts b/pxtlib/tilemap.ts index 5d4a0edd751b..bb334718acfb 100644 --- a/pxtlib/tilemap.ts +++ b/pxtlib/tilemap.ts @@ -197,6 +197,15 @@ namespace pxt { return undefined; } + getByValue(toFind: U) { + for (const asset of this.assets) { + if (assetEquals(toFind, asset, true)) { + return asset; + } + } + return undefined; + } + isIDTaken(id: string) { return !!this.takenNames[id]; } @@ -896,6 +905,16 @@ namespace pxt { return getAssetCollection(this.state, assetType).getByDisplayName(name); } + public lookupAssetByValue(assetType: AssetType.Image, toFind: ProjectImage): ProjectImage; + public lookupAssetByValue(assetType: AssetType.Tile, toFind: Tile): Tile; + public lookupAssetByValue(assetType: AssetType.Tilemap, toFind: ProjectTilemap): ProjectTilemap; + public lookupAssetByValue(assetType: AssetType.Animation, toFind: Animation): Animation; + public lookupAssetByValue(assetType: AssetType.Song, toFind: Song): Song; + public lookupAssetByValue(assetType: AssetType, toFind: Asset): Asset; + public lookupAssetByValue(assetType: AssetType, toFind: Asset) { + return getAssetCollection(this.state, assetType).getByValue(toFind); + } + public getAssets(type: AssetType.Image): ProjectImage[]; public getAssets(type: AssetType.Tile): Tile[]; public getAssets(type: AssetType.Tilemap): ProjectTilemap[]; @@ -1206,6 +1225,14 @@ namespace pxt { return clone; } + saveGallerySnapshot() { + return this.gallery; + } + + loadGallerySnapshot(snapshot: AssetSnapshot) { + this.gallery = snapshot; + } + protected generateImage(entry: JRes, type: AssetType.Image): ProjectImage; protected generateImage(entry: JRes, type: AssetType.Tile): Tile; protected generateImage(entry: JRes, type: AssetType.Image | AssetType.Tile): ProjectImage | Tile { @@ -1778,15 +1805,17 @@ namespace pxt { } - export function assetEquals(a: Asset, b: Asset): boolean { + export function assetEquals(a: Asset, b: Asset, valueOnly = false): boolean { if (a == b) return true; - if (!a && b || !b && a) return false; - if (a.id !== b.id || a.type !== b.type || - !U.arrayEquals(a.meta.tags, b.meta.tags) || - !U.arrayEquals(a.meta.blockIDs, b.meta.blockIDs) || - a.meta.displayName !== b.meta.displayName - ) - return false; + if (!a && b || !b && a || a.type !== b.type) return false; + if (!valueOnly) { + if (a.id !== b.id || + !U.arrayEquals(a.meta.tags, b.meta.tags) || + !U.arrayEquals(a.meta.blockIDs, b.meta.blockIDs) || + a.meta.displayName !== b.meta.displayName + ) + return false; + } switch (a.type) { case AssetType.Image: From 3e1b7e126ef096e238a03335d865f4dc6d142f68 Mon Sep 17 00:00:00 2001 From: Robert Knight <95928279+microbit-robert@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:29:25 +0000 Subject: [PATCH 009/578] Align new project skip link behaviour with new project button (#10382) * Align new project skip link behaviour with new project button * Rename method, convert to async await --- localtypings/pxteditor.d.ts | 1 + webapp/src/accessibility.tsx | 6 ++++-- webapp/src/app.tsx | 10 ++++++++++ webapp/src/projects.tsx | 15 +-------------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/localtypings/pxteditor.d.ts b/localtypings/pxteditor.d.ts index 1c59d3192baa..0d031970141a 100644 --- a/localtypings/pxteditor.d.ts +++ b/localtypings/pxteditor.d.ts @@ -926,6 +926,7 @@ declare namespace pxt.editor { newEmptyProject(name?: string, documentation?: string, preferredEditor?: string): void; newProject(options?: pxt.editor.ProjectCreationOptions): void; + newUserCreatedProject(firstProject: boolean): Promise; createProjectAsync(options: pxt.editor.ProjectCreationOptions): Promise; importExampleAsync(options: ExampleImportOptions): Promise; showScriptManager(): void; diff --git a/webapp/src/accessibility.tsx b/webapp/src/accessibility.tsx index b232bb9fc4d4..5374bc8d0aab 100644 --- a/webapp/src/accessibility.tsx +++ b/webapp/src/accessibility.tsx @@ -104,9 +104,11 @@ export class HomeAccessibilityMenu extends data.Component { pxt.tickEvent("accmenu.home.new", undefined, { interactiveConsent: true }); - this.props.parent.newProject(); + const headers = this.getData(`headers:`); + const firstProject = (!headers || headers?.length == 0); + return this.props.parent.newUserCreatedProject(firstProject) } importProjectDialog() { diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index 772070ef0030..52b4ace5176c 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -2914,6 +2914,16 @@ export class ProjectView }); } + async newUserCreatedProject(firstProject: boolean): Promise { + if (pxt.appTarget.appTheme.nameProjectFirst || pxt.appTarget.appTheme.chooseLanguageRestrictionOnNewProject) { + const projectSettings = await this.askForProjectCreationOptionsAsync() + const { name, languageRestriction } = projectSettings + this.newProject({ name, languageRestriction, firstProject }); + } else { + this.newProject({ firstProject }); + } + } + async createProjectAsync(options: ProjectCreationOptions): Promise { pxt.perf.measureStart(Measurements.CreateProjectAsync) this.setSideDoc(undefined); diff --git a/webapp/src/projects.tsx b/webapp/src/projects.tsx index 00dcb7ea22c5..055b7f8dfb04 100644 --- a/webapp/src/projects.tsx +++ b/webapp/src/projects.tsx @@ -625,19 +625,6 @@ export class ProjectsCarousel extends data.Component { - const { name, languageRestriction } = projectSettings - this.props.parent.newProject({ name, languageRestriction, firstProject }); - }) - } else { - this.props.parent.newProject({ firstProject }); - } - } - showScriptManager() { pxt.tickEvent("projects.showscriptmanager", undefined, { interactiveConsent: true }); this.props.parent.showScriptManager(); @@ -768,7 +755,7 @@ export class ProjectsCarousel extends data.Component {showNewProject &&
this.newProject(isFirstProject)} onKeyDown={fireClickOnEnter} > + onClick={() => this.props.parent.newUserCreatedProject(isFirstProject)} onKeyDown={fireClickOnEnter} >
{lf("New Project")} From 4319736c9ba8e5055b605457755413ed37dbda90 Mon Sep 17 00:00:00 2001 From: Abhijith Chatra Date: Fri, 14 Feb 2025 09:30:14 -0800 Subject: [PATCH 010/578] 11.3.21 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35c5652ff94e..950d009fcbea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pxt-core", - "version": "11.3.20", + "version": "11.3.21", "description": "Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors", "keywords": [ "TypeScript", From 1747bd9a34b0c8d71e57636f880fe5e21e32f8c1 Mon Sep 17 00:00:00 2001 From: Sarah Rietkerk <49178322+srietkerk@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:29:39 -0800 Subject: [PATCH 011/578] OCV: add web config flag client side (#10386) * add webconfig flag clientside * add flag to test * change flag to be optional, remove from test files --- pxtlib/main.ts | 4 +++- webapp/src/app.tsx | 2 +- webapp/src/container.tsx | 2 +- webapp/src/projects.tsx | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pxtlib/main.ts b/pxtlib/main.ts index ca948bc25d43..02c70989b8d2 100644 --- a/pxtlib/main.ts +++ b/pxtlib/main.ts @@ -395,6 +395,7 @@ namespace pxt { teachertoolUrl?: string; // "/beta---eval" isStatic?: boolean; verprefix?: string; // "v1" + ocvEnabled?: boolean; } export function localWebConfig() { @@ -418,7 +419,8 @@ namespace pxt { simUrl: "/sim/simulator.html", simserviceworkerUrl: "/simulatorserviceworker.js", simworkerconfigUrl: "/sim/workerConfig.js", - partsUrl: "/sim/siminstructions.html" + partsUrl: "/sim/siminstructions.html", + ocvEnabled: true, } return r } diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index 52b4ace5176c..5e7e6d40e92c 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -5293,7 +5293,7 @@ export class ProjectView const hideMenuBar = targetTheme.hideMenuBar || hideTutorialIteration || (isTabTutorial && pxt.appTarget.appTheme.embeddedTutorial) || pxt.shell.isTimeMachineEmbed(); const isHeadless = simOpts && simOpts.headless; const selectLanguage = targetTheme.selectLanguage; - const feedbackEnabled = targetTheme.feedbackEnabled && targetTheme.ocvFrameUrl && targetTheme.ocvAppId; + const feedbackEnabled = pxt.webConfig.ocvEnabled && targetTheme.feedbackEnabled && targetTheme.ocvFrameUrl && targetTheme.ocvAppId; const showEditorToolbar = inEditor && !hideEditorToolbar && this.editor.hasEditorToolbar(); const useSerialEditor = pxt.appTarget.serial && !!pxt.appTarget.serial.useEditor; const showSideDoc = sideDocs && this.state.sideDocsLoadUrl && !this.state.sideDocsCollapsed; diff --git a/webapp/src/container.tsx b/webapp/src/container.tsx index b26740452d70..31fac0fe10ea 100644 --- a/webapp/src/container.tsx +++ b/webapp/src/container.tsx @@ -321,7 +321,7 @@ export class SettingsMenu extends data.Component this.dropdown = ref}> From 3baba2d3a6b78910cf9ec09bd50e92fe0aa2412b Mon Sep 17 00:00:00 2001 From: Robert Knight <95928279+microbit-robert@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:50:05 +0000 Subject: [PATCH 012/578] Implement keyboard and screen reader accessibility for LED matrix field (#10390) * Implement keyboard and screen reader accessibility for LED matrix field * Review feedback --- pxtblocks/fields/field_ledmatrix.ts | 187 ++++++++++++++++++++++++++-- pxtblocks/plugins/renderer/css.ts | 4 + 2 files changed, 179 insertions(+), 12 deletions(-) diff --git a/pxtblocks/fields/field_ledmatrix.ts b/pxtblocks/fields/field_ledmatrix.ts index 043976ce85fb..f488d1850ed9 100644 --- a/pxtblocks/fields/field_ledmatrix.ts +++ b/pxtblocks/fields/field_ledmatrix.ts @@ -45,6 +45,7 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { private elt: SVGSVGElement; private currentDragState_: boolean; + private selected: number[] | undefined = undefined; constructor(text: string, params: any, validator?: Blockly.FieldValidator) { super(text, validator); @@ -80,17 +81,121 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { this.scale = 0.9; } + private keyHandler(e: KeyboardEvent) { + if (!this.selected) { + return + } + const [x, y] = this.selected; + const ctrlCmd = pxt.BrowserUtils.isMac() ? e.metaKey : e.ctrlKey; + switch(e.code) { + case "KeyW": + case "ArrowUp": { + if (y !== 0) { + this.selected = [x, y - 1] + } + break; + } + case "KeyS": + case "ArrowDown": { + if (y !== this.cells[0].length - 1) { + this.selected = [x, y + 1] + } + break; + } + case "KeyA": + case "ArrowLeft": { + if (x !== 0) { + this.selected = [x - 1, y] + } else if (y !== 0){ + this.selected = [this.matrixWidth - 1, y - 1] + } + break; + } + case "KeyD": + case "ArrowRight": { + if (x !== this.cells.length - 1) { + this.selected = [x + 1, y] + } else if (y !== this.matrixHeight - 1) { + this.selected = [0, y + 1] + } + break; + } + case "Home": { + if (ctrlCmd) { + this.selected = [0, 0] + } else { + this.selected = [0, y] + } + break; + } + case "End": { + if (ctrlCmd) { + this.selected = [this.matrixWidth - 1, this.matrixHeight - 1] + } else { + this.selected = [this.matrixWidth - 1, y] + } + break; + } + case "Enter": + case "Space": { + this.toggleRect(x, y, !this.cellState[x][y]); + break; + } + case "Escape": { + (this.sourceBlock_.workspace as Blockly.WorkspaceSvg).markFocused(); + return; + } + default: { + return + } + } + const [newX, newY] = this.selected; + this.setFocusIndicator(this.cells[newX][newY], this.cellState[newX][newY]); + this.elt.setAttribute('aria-activedescendant', `${this.sourceBlock_.id}:${newX}${newY}`); + e.preventDefault(); + e.stopPropagation(); + } + + private clearSelection() { + if (this.selected) { + this.setFocusIndicator(); + this.selected = undefined; + } + this.elt.removeAttribute('aria-activedescendant'); + } + + private removeKeyboardFocusHandlers() { + this.elt.removeEventListener("keydown", this.keyHandler) + this.elt.removeEventListener("blur", this.blurHandler) + } + + private blurHandler() { + this.removeKeyboardFocusHandlers(); + this.clearSelection(); + } + + private setFocusIndicator(cell?: SVGRectElement, ledOn?: boolean) { + this.cells.forEach(cell => cell.forEach(cell => cell.nextElementSibling.firstElementChild.classList.remove("selectedLedOn", "selectedLedOff"))); + if (cell) { + const className = ledOn ? "selectedLedOn" : "selectedLedOff" + cell.nextElementSibling.firstElementChild.classList.add(className); + } + } + /** * Show the inline free-text editor on top of the text. * @private */ showEditor_() { - // Intentionally left empty + this.selected = [0, 0]; + this.setFocusIndicator(this.cells[0][0], this.cellState[0][0]) + this.elt.setAttribute('aria-activedescendant', this.sourceBlock_.id + ":00"); + this.elt.focus(); } private initMatrix() { if (!this.sourceBlock_.isInsertionMarker()) { - this.elt = pxsim.svg.parseString(``); + this.elt = pxsim.svg.parseString(``); // Initialize the matrix that holds the state for (let i = 0; i < this.matrixWidth; i++) { @@ -104,9 +209,10 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { this.restoreStateFromString(); // Create the cells of the matrix that is displayed - for (let i = 0; i < this.matrixWidth; i++) { - for (let j = 0; j < this.matrixHeight; j++) { - this.createCell(i, j); + for (let y = 0; y < this.matrixHeight; y++) { + const row = this.createRow() + for (let x = 0; x < this.matrixWidth; x++) { + this.createCell(x, y, row); } } @@ -132,6 +238,10 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { } this.fieldGroup_.replaceChild(this.elt, this.fieldGroup_.firstChild); + if (!this.sourceBlock_.isInFlyout) { + this.elt.addEventListener("keydown", this.keyHandler.bind(this)); + this.elt.addEventListener("blur", this.blurHandler.bind(this)); + } } } @@ -179,13 +289,21 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { super.updateEditable(); } - private createCell(x: number, y: number) { + private createRow() { + return pxsim.svg.child(this.elt, "g", { 'role': 'row' }); + } + + private createCell(x: number, y: number, row: SVGElement) { const tx = this.scale * x * (FieldMatrix.CELL_WIDTH + FieldMatrix.CELL_HORIZONTAL_MARGIN) + FieldMatrix.CELL_HORIZONTAL_MARGIN + this.getYAxisWidth(); const ty = this.scale * y * (FieldMatrix.CELL_WIDTH + FieldMatrix.CELL_VERTICAL_MARGIN) + FieldMatrix.CELL_VERTICAL_MARGIN; - const cellG = pxsim.svg.child(this.elt, "g", { transform: `translate(${tx} ${ty})` }) as SVGGElement; + const cellG = pxsim.svg.child(row, "g", { transform: `translate(${tx} ${ty})`, 'role': 'gridcell' }); const cellRect = pxsim.svg.child(cellG, "rect", { + 'id': `${this.sourceBlock_.id}:${x}${y}`, // For aria-activedescendant 'class': `blocklyLed${this.cellState[x][y] ? 'On' : 'Off'}`, + 'aria-label': lf("LED"), + 'role': 'switch', + 'aria-checked': this.cellState[x][y].toString(), width: this.scale * FieldMatrix.CELL_WIDTH, height: this.scale * FieldMatrix.CELL_WIDTH, fill: this.getColor(x, y), 'data-x': x, @@ -193,6 +311,20 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { rx: Math.max(2, this.scale * FieldMatrix.CELL_CORNER_RADIUS) }) as SVGRectElement; this.cells[x][y] = cellRect; + // Borders and box-shadow do not work in this context and outlines do not follow border-radius. + // Stroke is harder to manage given the difference in stroke for an LED when it is on vs off. + // This foreignObject/div is used to create a focus indicator for the LED when selected via keyboard navigation. + const foreignObject = pxsim.svg.child(cellG, "foreignObject", { + transform: 'translate(-4, -4)', + width: this.scale * FieldMatrix.CELL_WIDTH + 8, + height: this.scale * FieldMatrix.CELL_WIDTH + 8, + }); + foreignObject.style.pointerEvents = "none"; + const div = document.createElement("div"); + div.classList.add("blocklyLedFocusIndicator"); + div.style.borderRadius = `${Math.max(2, this.scale * FieldMatrix.CELL_CORNER_RADIUS)}px`; + foreignObject.append(div); + if ((this.sourceBlock_.workspace as any).isFlyout) return; pxsim.pointerEvents.down.forEach(evid => cellRect.addEventListener(evid, (ev: MouseEvent) => { @@ -217,11 +349,14 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { ev.stopPropagation(); ev.preventDefault(); + // Clear event listeners and selection used for keyboard navigation. + this.removeKeyboardFocusHandlers(); + this.clearSelection(); }, false)); } - private toggleRect = (x: number, y: number) => { - this.cellState[x][y] = this.currentDragState_; + private toggleRect = (x: number, y: number, value?: boolean) => { + this.cellState[x][y] = value ?? this.currentDragState_; this.updateValue(); } @@ -262,6 +397,7 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { cellRect.setAttribute("fill", this.getColor(x, y)); cellRect.setAttribute("fill-opacity", this.getOpacity(x, y)); cellRect.setAttribute('class', `blocklyLed${this.cellState[x][y] ? 'On' : 'Off'}`); + cellRect.setAttribute("aria-checked", this.cellState[x][y].toString()); } setValue(newValue: string | number, restoreState = true) { @@ -287,10 +423,9 @@ export class FieldMatrix extends Blockly.Field implements FieldCustom { this.initMatrix(); } - // The height and width must be set by the render function this.size_.height = this.scale * Number(this.matrixHeight) * (FieldMatrix.CELL_WIDTH + FieldMatrix.CELL_VERTICAL_MARGIN) + FieldMatrix.CELL_VERTICAL_MARGIN * 2 + FieldMatrix.BOTTOM_MARGIN + this.getXAxisHeight() - this.size_.width = this.scale * Number(this.matrixWidth) * (FieldMatrix.CELL_WIDTH + FieldMatrix.CELL_HORIZONTAL_MARGIN) + this.getYAxisWidth(); + this.size_.width = this.scale * Number(this.matrixWidth) * (FieldMatrix.CELL_WIDTH + FieldMatrix.CELL_HORIZONTAL_MARGIN) + FieldMatrix.CELL_HORIZONTAL_MARGIN + this.getYAxisWidth(); } // The return value of this function is inserted in the code @@ -365,4 +500,32 @@ function removeQuotes(str: string) { return str.substr(1, str.length - 2).trim(); } return str; -} \ No newline at end of file +} + +Blockly.Css.register(` +.blocklyMatrix:focus-visible { + outline: none; +} + +.blocklyMatrix .blocklyLedFocusIndicator { + border: 4px solid transparent; + height: 100%; +} + +.blocklyMatrix .blocklyLedFocusIndicator.selectedLedOn, +.blocklyMatrix .blocklyLedFocusIndicator.selectedLedOff { + border-color: white; + transform: translateZ(0); +} + +.blocklyMatrix .blocklyLedFocusIndicator.selectedLedOn:after { + content: ""; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + border: 2px solid black; + border-radius: inherit; +} +`) \ No newline at end of file diff --git a/pxtblocks/plugins/renderer/css.ts b/pxtblocks/plugins/renderer/css.ts index c6f62b7ee29b..b43e769f59c7 100644 --- a/pxtblocks/plugins/renderer/css.ts +++ b/pxtblocks/plugins/renderer/css.ts @@ -4,4 +4,8 @@ Blockly.Css.register(` .blocklyDropdownMenu .blocklyMenuItemCheckbox.goog-menuitem-checkbox { filter: contrast(0) brightness(100); } + +.blocklyVerticalMarker { + fill: none; +} `) \ No newline at end of file From 1498f083b98232b59c5ec7cc921da92b5be56b50 Mon Sep 17 00:00:00 2001 From: Sae <73706334+sae220@users.noreply.github.com> Date: Fri, 21 Feb 2025 02:37:08 +0900 Subject: [PATCH 013/578] Make "list" in for-of translatable (#10393) --- webapp/src/blocksSnippets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/blocksSnippets.ts b/webapp/src/blocksSnippets.ts index 48c8e9e52e68..336bf8c524f2 100644 --- a/webapp/src/blocksSnippets.ts +++ b/webapp/src/blocksSnippets.ts @@ -83,7 +83,7 @@ function cachedBuiltinCategories(): pxt.Map { - list + ${lf("{id:var}list")} ` From b50685dd5c8b19c7eeb0a0fad36e658c0e4c9008 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 20 Feb 2025 10:33:04 -0800 Subject: [PATCH 014/578] Collection of fixes for bugs found on livestream (#10392) * fix exception when commiting state after clearing floating layer * make sure image canvas is focused when interacting * don't skip first pixel when starting a drag * remove field variable monkey patch --- pxtblocks/builtins/loops.ts | 6 +- pxtblocks/builtins/variables.ts | 66 ++++++++++++++++--- pxtblocks/help.ts | 1 - .../duplicateOnDrag/variablesGetReporter.ts | 3 +- pxtlib/spriteutils.ts | 2 + .../components/ImageEditor/ImageCanvas.tsx | 9 ++- 6 files changed, 73 insertions(+), 14 deletions(-) diff --git a/pxtblocks/builtins/loops.ts b/pxtblocks/builtins/loops.ts index 6eb52bd4b7c1..3294c00ca03a 100644 --- a/pxtblocks/builtins/loops.ts +++ b/pxtblocks/builtins/loops.ts @@ -122,7 +122,8 @@ export function initLoops() { { "type": "field_variable", "name": "VAR", - "variable": controlsSimpleForDef.block["variable"] + "variable": controlsSimpleForDef.block["variable"], + "variableTypes": [""], // Please note that most multilingual characters // cannot be used as variable name at this point. // Translate or decide the default variable name @@ -328,7 +329,8 @@ export function initLoops() { { "type": "field_variable", "name": "VAR", - "variable": controlsForOfDef.block["variable"] + "variable": controlsForOfDef.block["variable"], + "variableTypes": [""], // Please note that most multilingual characters // cannot be used as variable name at this point. // Translate or decide the default variable name diff --git a/pxtblocks/builtins/variables.ts b/pxtblocks/builtins/variables.ts index 6a954ad9b1f3..9635aed07769 100644 --- a/pxtblocks/builtins/variables.ts +++ b/pxtblocks/builtins/variables.ts @@ -1,14 +1,8 @@ import * as Blockly from "blockly"; import { createFlyoutGroupLabel, createFlyoutHeadingLabel, mkVariableFieldBlock } from "../toolbox"; import { installBuiltinHelpInfo, setBuiltinHelpInfo } from "../help"; -import { provider } from "../constants"; export function initVariables() { - // We only give types to "special" variables like enum members and we don't - // want those showing up in the variable dropdown so filter the variables - // that show up to only ones that have an empty type - (Blockly.FieldVariable.prototype as any).getVariableTypes_ = () => [""]; - let varname = lf("{id:var}item"); Blockly.Variables.flyoutCategory = function (workspace: Blockly.WorkspaceSvg) { let xmlList: HTMLElement[] = []; @@ -103,7 +97,31 @@ export function initVariables() { const variablesGetId = "variables_get"; const variablesGetDef = pxt.blocks.getBlockDefinition(variablesGetId); msg.VARIABLES_GET_CREATE_SET = variablesGetDef.block["VARIABLES_GET_CREATE_SET"]; - installBuiltinHelpInfo(variablesGetId); + Blockly.Blocks[variablesGetId] = { + init: function() { + this.jsonInit( + { + "type": "variables_get", + "message0": "%1", + "args0": [ + { + "type": "field_variable", + "name": "VAR", + "variable": "%{BKY_VARIABLES_DEFAULT_NAME}", + "variableTypes": [""], + }, + ], + "output": null, + "style": "variable_blocks", + "helpUrl": "%{BKY_VARIABLES_GET_HELPURL}", + "tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}", + "extensions": ["contextMenu_variableSetterGetter"], + } + ); + + setBuiltinHelpInfo(this, variablesGetId); + } + }; const variablesReporterGetId = "variables_get_reporter"; installBuiltinHelpInfo(variablesReporterGetId); @@ -120,7 +138,36 @@ export function initVariables() { msg.VARIABLES_SET = variablesSetDef.block["VARIABLES_SET"]; msg.VARIABLES_DEFAULT_NAME = varname; msg.VARIABLES_SET_CREATE_GET = lf("Create 'get %1'"); - installBuiltinHelpInfo(variablesSetId); + Blockly.Blocks[variablesSetId] = { + init: function() { + this.jsonInit( + { + "type": "variables_set", + "message0": "%{BKY_VARIABLES_SET}", + "args0": [ + { + "type": "field_variable", + "name": "VAR", + "variable": "%{BKY_VARIABLES_DEFAULT_NAME}", + "variableTypes": [""], + }, + { + "type": "input_value", + "name": "VALUE", + }, + ], + "previousStatement": null, + "nextStatement": null, + "style": "variable_blocks", + "tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}", + "helpUrl": "%{BKY_VARIABLES_SET_HELPURL}", + "extensions": ["contextMenu_variableSetterGetter"], + } + ); + + setBuiltinHelpInfo(this, variablesSetId); + } + }; // pxt variables_change const variablesChangeId = "variables_change"; @@ -133,7 +180,8 @@ export function initVariables() { { "type": "field_variable", "name": "VAR", - "variable": varname + "variable": varname, + "variableTypes": [""] }, { "type": "input_value", diff --git a/pxtblocks/help.ts b/pxtblocks/help.ts index 21dd47582161..51bfe86002e2 100644 --- a/pxtblocks/help.ts +++ b/pxtblocks/help.ts @@ -58,7 +58,6 @@ export function installHelpResources(id: string, name: string, tooltip: any, url block.init = function () { old.call(this); - let block = this; setHelpResources(this, id, name, tooltip, url, colour, colourSecondary, colourTertiary); } } diff --git a/pxtblocks/plugins/duplicateOnDrag/variablesGetReporter.ts b/pxtblocks/plugins/duplicateOnDrag/variablesGetReporter.ts index f2f083982d02..f88b8ed360cd 100644 --- a/pxtblocks/plugins/duplicateOnDrag/variablesGetReporter.ts +++ b/pxtblocks/plugins/duplicateOnDrag/variablesGetReporter.ts @@ -39,7 +39,8 @@ Blockly.defineBlocksWithJsonArray([ { "type": "field_variable", "name": "VAR", - "variable": "%{BKY_VARIABLES_DEFAULT_NAME}" + "variable": "%{BKY_VARIABLES_DEFAULT_NAME}", + "variableTypes": [""], } ], "output": null, diff --git a/pxtlib/spriteutils.ts b/pxtlib/spriteutils.ts index bf009297d7d6..3b8f2b6db4e2 100644 --- a/pxtlib/spriteutils.ts +++ b/pxtlib/spriteutils.ts @@ -806,6 +806,8 @@ namespace pxt.sprite { } export function bitmapEquals(a: pxt.sprite.BitmapData, b: pxt.sprite.BitmapData) { + if (a === b) return true; + else if (!a && b || !b && a) return false; return pxt.sprite.Bitmap.fromData(a).equals(pxt.sprite.Bitmap.fromData(b)); } diff --git a/webapp/src/components/ImageEditor/ImageCanvas.tsx b/webapp/src/components/ImageEditor/ImageCanvas.tsx index 7b8bee1ea193..1cef364d866e 100644 --- a/webapp/src/components/ImageEditor/ImageCanvas.tsx +++ b/webapp/src/components/ImageEditor/ImageCanvas.tsx @@ -204,7 +204,7 @@ export class ImageCanvasImpl extends React.Component imple if (document.activeElement instanceof HTMLElement && document.activeElement !== this.refs["canvas-bounds"]) { document.activeElement.blur(); } - (this.refs["canvas-bounds"] as HTMLDivElement).focus(); + this.focus(); if (this.isColorSelect()) { this.selectCanvasColor(coord, isRightClick); @@ -230,6 +230,7 @@ export class ImageCanvasImpl extends React.Component imple onDragStart(coord: ClientCoordinates, isRightClick?: boolean): void { this.hasInteracted = true + this.focus(); if (this.touchesResize(coord.clientX, coord.clientY)) { this.isResizing = true; } @@ -243,10 +244,12 @@ export class ImageCanvasImpl extends React.Component imple else { this.updateCursorLocation(coord); this.startEdit(!!isRightClick); + this.updateEdit(this.cursorLocation[0], this.cursorLocation[1]); } } onDragMove(coord: ClientCoordinates): void { + this.focus(); if (this.isPanning() && this.lastPanX != undefined && this.lastPanY != undefined) { this.panX += this.lastPanX - coord.clientX; this.panY += this.lastPanY - coord.clientY; @@ -1091,6 +1094,10 @@ export class ImageCanvasImpl extends React.Component imple this.props.dispatchImageEdit(this.editState.toImageState()); } + + protected focus() { + (this.refs["canvas-bounds"] as HTMLDivElement).focus(); + } } function mapStateToProps({ store: { present }, editor }: ImageEditorStore, ownProps: any) { From ef3c69a4fcab785f3f491c4e794683e4c29eb86b Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 21 Feb 2025 09:53:39 -0800 Subject: [PATCH 015/578] 11.3.22 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 950d009fcbea..f2f2be4d8249 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pxt-core", - "version": "11.3.21", + "version": "11.3.22", "description": "Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors", "keywords": [ "TypeScript", From 6f8fab8b99ba8582b90722e8955ea275847e7a73 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Mon, 24 Feb 2025 11:30:21 -0800 Subject: [PATCH 016/578] refresh variable flyout when a new variable is created (#10396) --- webapp/src/blocks.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/webapp/src/blocks.tsx b/webapp/src/blocks.tsx index 4fac6af0c376..f21308294338 100644 --- a/webapp/src/blocks.tsx +++ b/webapp/src/blocks.tsx @@ -61,6 +61,7 @@ export class Editor extends toolboxeditor.ToolboxEditor { showCategories: boolean = true; breakpointsByBlock: pxt.Map; // Map block id --> breakpoint ID breakpointsSet: number[]; // the IDs of the breakpoints set. + currentFlyoutKey: string; private errorChangesListeners: pxt.Map<(errors: pxtblockly.BlockDiagnostic[]) => void> = {}; protected intersectionObserver: IntersectionObserver; @@ -667,6 +668,13 @@ export class Editor extends toolboxeditor.ToolboxEditor { this.hideFlyout(); } + if (ev.type === "var_create") { + if (this.currentFlyoutKey === "variables" && this.editor.getFlyout()?.isVisible()) { + // refresh the flyout when a new variable is created + this.showVariablesFlyout(); + } + } + if ((ignoredChanges.indexOf(ev.type) === -1) || this.markIncomplete) { this.changeCallback(); @@ -1696,6 +1704,7 @@ export class Editor extends toolboxeditor.ToolboxEditor { } private showFlyoutInternal_(xmlList: Element[], flyoutName: string = "default") { + this.currentFlyoutKey = flyoutName; const flyout = this.editor.getFlyout() as pxtblockly.VerticalFlyout; flyout.show(xmlList, flyoutName); flyout.scrollToStart(); From 016dd9f071211292c2cf8a27dd30b71870704829 Mon Sep 17 00:00:00 2001 From: Thomas Sparks <69657545+thsparks@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:02:04 -0800 Subject: [PATCH 017/578] Add Theme Support (#10391) First pass at adding color theme support for arcade (and other targets in the future). See the theme README.md file (theme\color-themes\README.md) for details on what each theme variable is for. --- cli/cli.ts | 67 +++ gulpfile.js | 13 +- localtypings/pxtarget.d.ts | 11 + localtypings/pxteditor.d.ts | 9 + pxteditor/editorcontroller.ts | 5 + pxtlib/auth.ts | 4 +- pxtservices/editorDriver.ts | 10 + react-common/components/Notification.tsx | 4 +- react-common/components/controls/Modal.tsx | 4 +- .../components/controls/TeachingBubble.tsx | 6 +- .../extensions/DeleteConfirmationModal.tsx | 2 +- .../components/profile/SignInModal.tsx | 2 +- react-common/components/share/ShareInfo.tsx | 6 +- react-common/components/theming/ThemeCard.tsx | 25 + .../components/theming/ThemePickerModal.tsx | 32 ++ .../components/theming/ThemePreview.tsx | 56 +++ .../components/theming/themeManager.ts | 118 +++++ react-common/styles/controls/Button.less | 124 +---- react-common/styles/controls/Card.less | 12 +- .../styles/controls/DraggableGraph.less | 8 +- react-common/styles/controls/Dropdown.less | 10 +- .../styles/controls/EditorToggle.less | 21 +- react-common/styles/controls/Input.less | 26 +- .../styles/controls/MenuDropdown.less | 11 +- react-common/styles/controls/Modal.less | 20 +- react-common/styles/controls/ProgressBar.less | 8 +- .../styles/controls/RadioButtonGroup.less | 12 +- react-common/styles/controls/Textarea.less | 13 +- react-common/styles/controls/Tree.less | 4 +- .../controls/VerticalResizeContainer.less | 6 +- .../styles/controls/VerticalSlider.less | 4 +- .../styles/extensions/ExtensionCard.less | 11 +- react-common/styles/language/language.less | 2 +- .../styles/onboarding/TeachingBubble.less | 32 +- .../styles/profile/UserAvatarDropdown.less | 6 +- react-common/styles/profile/profile.less | 29 +- .../styles/react-common-variables.less | 112 +---- react-common/styles/react-common.less | 5 +- react-common/styles/share/share.less | 19 +- .../styles/theming/ThemePickerModal.less | 182 +++++++ react-common/styles/theming/base-theme.less | 111 ++++ skillmap/public/index.html | 3 +- skillmap/src/App.css | 69 +-- skillmap/src/App.tsx | 41 +- skillmap/src/actions/dispatch.ts | 4 +- skillmap/src/actions/types.ts | 4 +- skillmap/src/arcade.css | 32 -- skillmap/src/components/ActivityActions.tsx | 4 +- skillmap/src/components/HeaderBar.tsx | 25 +- skillmap/src/components/RewardActions.tsx | 4 +- skillmap/src/components/makecodeFrame.tsx | 20 +- skillmap/src/lib/authClient.ts | 8 +- skillmap/src/lib/skillMapParser.ts | 12 +- skillmap/src/store/reducer.ts | 38 +- skillmap/src/styles/carousel.css | 13 +- skillmap/src/styles/graphnode.css | 4 +- skillmap/src/styles/infopanel.css | 29 +- skillmap/src/styles/makecode-editor.css | 9 +- skillmap/src/styles/skillcard.css | 50 +- theme/asset-editor.less | 60 +-- theme/blockly-core.less | 28 +- theme/color-themes/README.md | 41 ++ theme/color-themes/high-contrast.json | 112 +++++ .../overrides/high-contrast-overrides.css | 11 + theme/common-components.less | 10 +- theme/common.less | 204 +++++--- theme/debugger.less | 63 +-- theme/diff.less | 30 +- theme/errorList.less | 37 +- theme/github.less | 42 +- theme/greenscreen.less | 3 +- theme/highcontrast.less | 4 +- theme/home.less | 115 +++-- theme/image-editor/bottomBar.less | 6 +- theme/image-editor/button.less | 4 +- theme/image-editor/imageEditor.less | 13 +- theme/light.less | 4 +- theme/monaco.less | 66 ++- theme/music-editor/MusicEditor.less | 11 +- theme/music-editor/PlaybackControls.less | 8 +- theme/music-editor/Staff.less | 7 +- theme/music-editor/TrackSelector.less | 9 +- theme/pxt.less | 1 + theme/semantic-ui-overrides.less | 473 ++++++++++++++++++ theme/serial.less | 54 +- theme/soundeffecteditor.less | 41 +- theme/themes/pxt/collections/menu.overrides | 12 +- theme/themes/pxt/collections/menu.variables | 9 +- theme/themes/pxt/elements/loader.variables | 2 - theme/themes/pxt/elements/segment.variables | 3 - theme/themes/pxt/globals/site.variables | 196 +------- theme/themes/pxt/modules/modal.overrides | 27 +- theme/themes/pxt/views/card.overrides | 24 +- theme/themes/pxt/views/card.variables | 9 +- theme/timeMachine.less | 18 +- theme/toolbox.less | 31 +- theme/tooltips.less | 19 +- theme/tutorial-sidebar.less | 61 ++- theme/tutorial.less | 67 +-- theme/webusb.less | 4 +- webapp/public/index.html | 2 +- webapp/src/accessibility.tsx | 22 +- webapp/src/app.tsx | 66 ++- webapp/src/auth.ts | 20 + webapp/src/codecard.tsx | 2 +- .../components/assetEditor/assetPalette.tsx | 4 +- .../components/assetEditor/assetSidebar.tsx | 2 +- .../soundEffectEditor/SoundGallery.tsx | 1 - .../soundEffectEditor/SoundPreview.tsx | 3 - .../components/tutorial/TutorialContainer.tsx | 2 +- .../TutorialValidationErrorMessage.tsx | 4 +- webapp/src/container.tsx | 8 +- webapp/src/core.ts | 12 +- webapp/src/extensionsBrowser.tsx | 2 +- webapp/src/gitjson.tsx | 32 +- webapp/src/lang.tsx | 4 +- webapp/src/monaco.tsx | 20 +- webapp/src/projects.tsx | 10 +- webapp/src/scriptmanager.tsx | 16 +- webapp/src/serial.tsx | 17 +- webapp/src/sidepanel.tsx | 6 +- 121 files changed, 2452 insertions(+), 1273 deletions(-) create mode 100644 react-common/components/theming/ThemeCard.tsx create mode 100644 react-common/components/theming/ThemePickerModal.tsx create mode 100644 react-common/components/theming/ThemePreview.tsx create mode 100644 react-common/components/theming/themeManager.ts create mode 100644 react-common/styles/theming/ThemePickerModal.less create mode 100644 react-common/styles/theming/base-theme.less delete mode 100644 skillmap/src/arcade.css create mode 100644 theme/color-themes/README.md create mode 100644 theme/color-themes/high-contrast.json create mode 100644 theme/color-themes/overrides/high-contrast-overrides.css create mode 100644 theme/semantic-ui-overrides.less diff --git a/cli/cli.ts b/cli/cli.ts index 3bc6f224bfa3..ed3049bc758f 100644 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -2153,6 +2153,7 @@ function buildReactAppAsync(app: string, parsed: commandParser.ParsedCommand, op if (!opts.expandedPxtTarget) { // read pxtarget.json, save into 'pxtTargetBundle' global variable let cfg = readLocalPxTarget(); + updateColorThemes(cfg); nodeutil.writeFileSync(`${appRoot}/public/blb/target.js`, "// eslint-disable-next-line \n" + targetJsPrefix + JSON.stringify(cfg)); } else { nodeutil.cp("built/target.js", `${appRoot}/public/blb`); @@ -2169,6 +2170,7 @@ function buildReactAppAsync(app: string, parsed: commandParser.ParsedCommand, op nodeutil.cp("targetconfig.json", `${appRoot}/public/blb`); nodeutil.cp("node_modules/pxt-core/built/pxtlib.js", `${appRoot}/public/blb`); + nodeutil.cp("node_modules/pxt-core/built/web/pxtrcdeps.js", `${appRoot}/public/blb`); if (opts.includePxtSim) { nodeutil.cp("node_modules/pxt-core/built/pxtsim.js", `${appRoot}/public/blb`); nodeutil.cp("node_modules/pxt-core/built/web/worker.js", `${appRoot}/public/blb`); @@ -2282,6 +2284,70 @@ function updateTOC(cfg: pxt.TargetBundle) { } } +function updateColorThemes(cfg: pxt.TargetBundle) { + pxt.log("Loading color themes..."); + + const sharedThemeFiles = fs.existsSync("node_modules/pxt-core/theme/color-themes") + ? nodeutil + .allFiles("node_modules/pxt-core/theme/color-themes", { maxDepth: 1, includeDirs: false }) + .filter((f) => /\.json$/i.test(f)) + : []; + const targetThemeFiles = fs.existsSync("theme/color-themes") + ? nodeutil + .allFiles("theme/color-themes", { maxDepth: 1, includeDirs: false }) + .filter((f) => /\.json$/i.test(f)) + : []; + + // Target takes precedence, so include those at the end (will overwrite shared themes) + const themeFiles = sharedThemeFiles.concat(targetThemeFiles); + + for (const themeFile of themeFiles) { + const themeFileDir = path.dirname(themeFile); + const fileData = fs.readFileSync(themeFile, "utf8"); + const themeData = JSON.parse(fileData); + const theme: pxt.ColorThemeInfo = { + id: themeData.id, + name: themeData.name, + monacoBaseTheme: themeData.monacoBaseTheme, + colors: themeData.colors + }; + + for (const overrideFile of themeData.overrideFiles ?? []) { + if (!overrideFile) { + // Skip empty entries + continue; + } + + // Strip leading slashes, convert \ to /, and lowercase the path + const combinedPath = path.join(themeFileDir, overrideFile); + let cssText = fs.readFileSync(combinedPath, "utf8"); + theme.overrideCss = theme.overrideCss ? `${theme.overrideCss}\n${cssText}` : cssText; + } + + if (!cfg.colorThemeMap) { + cfg.colorThemeMap = {}; + } + + if (cfg.colorThemeMap[theme.id]) { + // Only overwrite specified fields + // This allows target themes to be built off of shared base themes and only specify certain changes. + const existingTheme = cfg.colorThemeMap[theme.id]; + cfg.colorThemeMap[theme.id] = { + ...existingTheme, + ...theme, + colors: { + ...existingTheme.colors, + ...theme.colors + } + }; + } else { + cfg.colorThemeMap[theme.id] = theme; + } + + pxt.log(`Loaded theme ${theme.id}`); + } +} + function rebundleAsync() { return buildTargetCoreAsync({ quick: true }) .then(() => buildSimAsync()); @@ -2355,6 +2421,7 @@ async function buildTargetCoreAsync(options: BuildTargetOptions = {}) { let cfg = readLocalPxTarget() updateDefaultProjects(cfg); updateTOC(cfg); + updateColorThemes(cfg); cfg.bundledpkgs = {} pxt.setAppTarget(cfg); diff --git a/gulpfile.js b/gulpfile.js index 45ca11761646..eea903fa03ec 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -102,6 +102,13 @@ const buildpxtjs = () => gulp.src([ `)) .pipe(gulp.dest("built")); +const pxtrcdeps = () => gulp.src([ + "node_modules/dompurify/dist/purify.min.js", +]) + .pipe(concat("pxtrcdeps.js")) + .pipe(gulp.dest("built/web")); + + const copySubappsConfig = () => gulp.src("cli/webapps-config.json") .pipe(gulp.dest("built")); @@ -122,7 +129,7 @@ function initWatch() { gulp.parallel(pxtblocks, pxteditor, pxtservices), gulp.parallel(pxtrunner, cli, pxtcommon), gulp.parallel(updatestrings, browserifyEmbed), - gulp.parallel(pxtjs, pxtdts, pxtapp, pxtworker, pxtembed), + gulp.parallel(pxtjs, pxtdts, pxtapp, pxtworker, pxtembed, pxtrcdeps), targetjs, reactCommon, webapp, @@ -547,7 +554,7 @@ function createWebappTasks(root, outname) { .pipe(concat(`${outname}.html`)) .pipe(gulp.dest("webapp/public"))); - const result = gulp.series(cleanWebapp, buildWebapp, gulp.series(copyWebappCss, copyWebappJs, copyWebappHtml)); + const result = gulp.series(cleanWebapp, pxtrcdeps, buildWebapp, gulp.series(copyWebappCss, copyWebappJs, copyWebappHtml)); exports[outname] = result; @@ -748,7 +755,7 @@ const buildAll = gulp.series( gulp.parallel(pxteditor, pxtblocks, pxtservices), gulp.parallel(pxtrunner, cli, pxtcommon), browserifyEmbed, - gulp.parallel(pxtjs, pxtdts, pxtapp, pxtworker, pxtembed), + gulp.parallel(pxtjs, pxtdts, pxtapp, pxtworker, pxtembed, pxtrcdeps), targetjs, reactCommon, gulp.parallel(buildcss, buildSVGIcons), diff --git a/localtypings/pxtarget.d.ts b/localtypings/pxtarget.d.ts index bfc8141d9c7a..9d87c74f0310 100644 --- a/localtypings/pxtarget.d.ts +++ b/localtypings/pxtarget.d.ts @@ -427,6 +427,8 @@ declare namespace pxt { blockColors?: Map; // block namespace colors, used for build in categories blockIcons?: Map; blocklyColors?: pxt.Map; // Overrides for the styles in the workspace Blockly.Theme.ComponentStyle + defaultColorTheme?: string; // default color theme id for the editor + highContrastColorTheme?: string; // theme id for high contrast mode socialOptions?: SocialOptions; // show social icons in share dialog, options like twitter handle and org handle noReloadOnUpdate?: boolean; // do not notify the user or reload the page when a new app cache is downloaded appPathNames?: string[]; // Authorized URL paths in embedded apps, all other paths will display a warning banner @@ -595,6 +597,7 @@ declare namespace pxt { versions: TargetVersions; // @derived apiInfo?: Map; tutorialInfo?: Map; // hash of tutorial code mapped to prebuilt info for each tutorial + colorThemeMap?: Map; // Color theme id mapped to color theme info } interface BuiltTutorialInfo { @@ -610,6 +613,14 @@ declare namespace pxt { apis: ts.pxtc.ApisInfo; } + interface ColorThemeInfo { + id: string; + name: string; + overrideCss?: string; + monacoBaseTheme?: string; // https://code.visualstudio.com/docs/getstarted/themes + colors: { [key: string]: string }; + } + interface ServiceWorkerEvent { type: "serviceworker"; state: "activated"; diff --git a/localtypings/pxteditor.d.ts b/localtypings/pxteditor.d.ts index 0d031970141a..101699610718 100644 --- a/localtypings/pxteditor.d.ts +++ b/localtypings/pxteditor.d.ts @@ -104,6 +104,7 @@ declare namespace pxt.editor { | "serviceworkerregistered" | "runeval" | "precachetutorial" + | "setcolortheme" // package extension messasges | ExtInitializeType @@ -510,6 +511,11 @@ declare namespace pxt.editor { body?: any; } + export interface EditorMessageSetColorThemeRequest extends EditorMessageRequest { + action: "setcolortheme"; + colorThemeId: string; + } + /** * Events are fired by the editor on the extension iFrame. Extensions * receive events, they don't send them. @@ -812,6 +818,7 @@ declare namespace pxt.editor { isMultiplayerGame?: boolean; // Arcade: Does the current project contain multiplayer blocks? onboarding?: pxt.tour.BubbleStep[]; feedback?: FeedbackState; + themePickerOpen?: boolean; } export interface EditorState { @@ -1053,6 +1060,7 @@ declare namespace pxt.editor { showReportAbuse(): void; showLanguagePicker(): void; + showThemePicker(): void; showShareDialog(title?: string, kind?: "multiplayer" | "vscode" | "share"): void; showAboutDialog(): void; showFeedbackDialog(kind: ocv.FeedbackKind): void; @@ -1092,6 +1100,7 @@ declare namespace pxt.editor { hasHeaderBeenPersistentShared(): boolean; getSharePreferenceForHeader(): boolean; saveSharePreferenceForHeaderAsync(anonymousByDefault: boolean): Promise; + setColorTheme(colorThemeId: string): void; } export interface IHexFileImporter { diff --git a/pxteditor/editorcontroller.ts b/pxteditor/editorcontroller.ts index ab319e72f525..ab9a6594a4d6 100644 --- a/pxteditor/editorcontroller.ts +++ b/pxteditor/editorcontroller.ts @@ -317,6 +317,11 @@ export function bindEditorMessages(getEditorAsync: () => Promise) } }); } + case "setcolortheme": { + const msg = data as pxt.editor.EditorMessageSetColorThemeRequest; + projectView.setColorTheme(msg.colorThemeId); + return Promise.resolve(); + } } return Promise.resolve(); }); diff --git a/pxtlib/auth.ts b/pxtlib/auth.ts index 234f281ee2c7..34426dc5f204 100644 --- a/pxtlib/auth.ts +++ b/pxtlib/auth.ts @@ -55,6 +55,7 @@ namespace pxt.auth { export type UserPreferences = { language?: string; highContrast?: boolean; + themeId?: string; reader?: string; skillmap?: UserSkillmapState; badges?: UserBadgeState; @@ -62,8 +63,9 @@ namespace pxt.auth { }; export const DEFAULT_USER_PREFERENCES: () => UserPreferences = () => ({ - highContrast: false, language: pxt.appTarget.appTheme.defaultLocale, + highContrast: false, + themeId: pxt.appTarget.appTheme.defaultColorTheme, reader: "", skillmap: { mapProgress: {}, completedTags: {} }, email: false diff --git a/pxtservices/editorDriver.ts b/pxtservices/editorDriver.ts index cbd094458580..b3673d0196ad 100644 --- a/pxtservices/editorDriver.ts +++ b/pxtservices/editorDriver.ts @@ -456,6 +456,16 @@ export class EditorDriver extends IframeDriver { ); } + async setColorTheme(colorThemeId: string) { + await this.sendRequest ( + { + type: "pxteditor", + action: "setcolortheme", + colorThemeId + } as pxt.editor.EditorMessageSetColorThemeRequest + ); + } + addEventListener(event: typeof MessageSentEvent, handler: (ev: pxt.editor.EditorMessage) => void): void; addEventListener(event: typeof MessageReceivedEvent, handler: (ev: pxt.editor.EditorMessage) => void): void; addEventListener(event: "event", handler: (ev: pxt.editor.EditorMessageEventRequest) => void): void; diff --git a/react-common/components/Notification.tsx b/react-common/components/Notification.tsx index 9a630dc4101b..c9c6151337a2 100644 --- a/react-common/components/Notification.tsx +++ b/react-common/components/Notification.tsx @@ -55,7 +55,7 @@ export class Notification extends React.Component{text}
@@ -79,4 +79,4 @@ export function pushNotificationMessage(options: NotificationOptions): void { } else if (notificationMessages) { notificationMessages.push(options); } -} \ No newline at end of file +} diff --git a/react-common/components/controls/Modal.tsx b/react-common/components/controls/Modal.tsx index 96d80b100578..b5955a193962 100644 --- a/react-common/components/controls/Modal.tsx +++ b/react-common/components/controls/Modal.tsx @@ -102,7 +102,7 @@ export const Modal = (props: ModalProps) => { {!fullscreen && !hideDismissButton &&
, parentElement || document.getElementById("root") || document.body) -} \ No newline at end of file +} diff --git a/react-common/components/controls/TeachingBubble.tsx b/react-common/components/controls/TeachingBubble.tsx index b072e1f06b86..64b53cdae2e9 100644 --- a/react-common/components/controls/TeachingBubble.tsx +++ b/react-common/components/controls/TeachingBubble.tsx @@ -389,21 +389,21 @@ export const TeachingBubble = (props: TeachingBubbleProps) => {
}
{hasPrevious &&
-
-} \ No newline at end of file +} diff --git a/react-common/components/theming/ThemeCard.tsx b/react-common/components/theming/ThemeCard.tsx new file mode 100644 index 000000000000..927fbb190d37 --- /dev/null +++ b/react-common/components/theming/ThemeCard.tsx @@ -0,0 +1,25 @@ +import { ThemePreview } from "./ThemePreview"; +import { Card } from "../controls/Card"; + +interface ThemeCardProps { + theme: pxt.ColorThemeInfo; + onClick?: (theme: pxt.ColorThemeInfo) => void; +} + +export const ThemeCard = (props: ThemeCardProps) => { + const { onClick, theme } = props; + + return ( + onClick(theme)} + > +
+ +
{theme.name}
+
+
+ ); +}; diff --git a/react-common/components/theming/ThemePickerModal.tsx b/react-common/components/theming/ThemePickerModal.tsx new file mode 100644 index 000000000000..887dacc6a82c --- /dev/null +++ b/react-common/components/theming/ThemePickerModal.tsx @@ -0,0 +1,32 @@ +import { Modal } from "../controls/Modal"; +import { ThemeCard } from "./ThemeCard"; + +export interface ThemePickerModalProps { + themes: pxt.ColorThemeInfo[]; + onThemeClicked(them: pxt.ColorThemeInfo): void; + onClose(): void; +} +export const ThemePickerModal = (props: ThemePickerModalProps) => { + return ( + +
+ {props.themes && props.themes.map(theme => + + )} +
+
+ ); +}; diff --git a/react-common/components/theming/ThemePreview.tsx b/react-common/components/theming/ThemePreview.tsx new file mode 100644 index 000000000000..c5f1965d3ed3 --- /dev/null +++ b/react-common/components/theming/ThemePreview.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { getFullColorThemeCss } from "./themeManager"; +import { classList } from "../util"; + +// Programmatically generate a preview of the theme using theme colors. +export const ThemePreview = (props: { theme: pxt.ColorThemeInfo }) => { + const { theme } = props; + const styleRef = React.useRef(null); + const uniqueContainerClassName = `theme-preview-container-${theme.id}`; + const uniqueInnerClassName = `theme-preview-${theme.id}`; // Useful for override css adjusting previews + + const miniLogo = Microsoft MakeCode Logo; + + React.useEffect(() => { + if (styleRef?.current) { + const themeCss = getFullColorThemeCss(theme); + // Set textContent instead of innerHTML to avoid XSS + styleRef.current.textContent = `.${uniqueContainerClassName} { ${themeCss} }`; + } + }, [theme]); + + return ( +
+ ",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[e],starts:{e:"<\/script>",rE:!0,sL:["actionscript","javascript","handlebars","xml","vbscript"]}},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},e]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^\\s*([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}|\t)",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});hljs.registerLanguage("typescript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class public private protected get set super static implements enum export import declare type namespace abstract as from extends async await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document any number boolean string void Promise"},n={cN:"meta",b:"@"+r},a={b:"\\(",e:/\)/,k:t,c:["self",e.QSM,e.ASM,e.NM]},s={cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:[e.CLCM,e.CBCM,n,a]},c={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},o={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},i={b:"html`",e:"",starts:{e:"`",rE:!1,c:[e.BE,o],sL:"xml"}},l={b:"css`",e:"",starts:{e:"`",rE:!1,c:[e.BE,o],sL:"css"}},b={cN:"string",b:"`",e:"`",c:[e.BE,o]};return o.c=[e.ASM,e.QSM,i,l,b,c,e.RM],{aliases:["ts"],k:t,c:[{cN:"meta",b:/^\s*['"]use strict['"]/},e.ASM,e.QSM,i,l,b,e.CLCM,e.CBCM,c,{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+e.IR+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:e.IR},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:["self",e.CLCM,e.CBCM]}]}]}],r:0},{cN:"function",b:"function",e:/[\{;]/,eE:!0,k:t,c:["self",e.inherit(e.TM,{b:r}),s],i:/%/,r:0},{bK:"constructor",e:/\{/,eE:!0,c:["self",s]},{b:/module\./,k:{built_in:"module"},r:0},{bK:"module",e:/\{/,eE:!0},{bK:"interface",e:/\{/,eE:!0,k:"interface extends"},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},n,a]}});hljs.registerLanguage("python",function(e){var r={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10",built_in:"Ellipsis NotImplemented",literal:"False None True"},b={cN:"meta",b:/^(>>>|\.\.\.) /},c={cN:"subst",b:/\{/,e:/\}/,k:r,i:/#/},a={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[e.BE,b],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[e.BE,b],r:10},{b:/(fr|rf|f)'''/,e:/'''/,c:[e.BE,b,c]},{b:/(fr|rf|f)"""/,e:/"""/,c:[e.BE,b,c]},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},{b:/(fr|rf|f)'/,e:/'/,c:[e.BE,c]},{b:/(fr|rf|f)"/,e:/"/,c:[e.BE,c]},e.ASM,e.QSM]},i={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},l={cN:"params",b:/\(/,e:/\)/,c:["self",b,i,a]};return c.c=[a,i,b],{aliases:["py","gyp","ipython"],k:r,i:/(<\/|->|\?)|=>/,c:[b,i,a,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,l,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}}); \ No newline at end of file +/*! + Highlight.js v11.11.1 (git: 08cb242e7d) + (c) 2006-2025 Josh Goebel and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(t){ +return t instanceof Map?t.clear=t.delete=t.set=()=>{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{ +const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) +})),t}class t{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope +;class r{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)} +closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const o=(e={})=>{const t={children:[]} +;return Object.assign(t,e),t};class a{constructor(){ +this.rootNode=o(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t=o({scope:e}) +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,t){const n=e.root +;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function l(e){ +return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} +function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} +function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], +"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)} +const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",_="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={ +begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, +contains:[]},n);s.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s +},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({ +__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ +scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:N, +C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number", +begin:_,relevance:0},C_NUMBER_RE:_,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function L(e,t){ +Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function P(e,t){ +void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ +relevance:0,contains:[Object.assign(n,{endsParent:!0})] +},e.relevance=0,delete n.beforeMatch +},H=["of","and","for","in","not","or","if","then","parent","list","value"] +;function C(e,t,n="keyword"){const i=Object.create(null) +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,C(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,$(n[0],n[1])]}))}}function $(e,t){ +return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{ +console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{ +U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0) +},G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={} +;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1]) +;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +G +;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"), +G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +G +;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"), +G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){ +function t(t,n){ +return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) +}class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r +;if(r.isCompiled)return a +;[I,B,F,D].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))), +r.__beforeBegin=null,[T,L,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +c=r.keywords.$pattern, +delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)), +a.keywordPatternRe=t(c,!0), +o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(a.endRe=t(a.end)), +a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)), +r.illegal&&(a.illegalRe=t(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{ +starts:e.starts?i(e.starts):null +}):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a) +})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){ +return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{ +constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} +const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{ +const i=Object.create(null),s=Object.create(null),r=[];let o=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function b(e){ +return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" +;"object"==typeof t?(i=e, +n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."), +X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};N("before:highlight",r) +;const o=r.result?r.result:E(r.language,r.code,n) +;return o.code=r.code,N("after:highlight",o),o}function E(e,n,s,r){ +const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R) +;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n="" +;for(;t;){n+=R.substring(e,t.index) +;const s=w.case_insensitive?t[0].toLowerCase():t[0],r=(i=s,N.keywords[i]);if(r){ +const[e,i]=r +;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{ +const n=w.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0] +;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i +;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{ +if(""===R)return;let e=null;if("string"==typeof N.subLanguage){ +if(!i[N.subLanguage])return void M.addText(R) +;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top +}else e=x(R,N.subLanguage.length?N.subLanguage:null) +;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language) +})():l(),R=""}function u(e,t){ +""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1 +;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} +const i=w.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}} +function h(e,t){ +return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(u(R,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{ +value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) +;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return f(e.parent,n,i)}function b(e){ +return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){ +const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return Q;const r=N +;N.endScope&&N.endScope._wrap?(g(), +u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(), +d(N.endScope,e)):r.skip?R+=t:(r.returnEnd||r.excludeEnd||(R+=t), +g(),r.excludeEnd&&(R=t));do{ +N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent +}while(N!==s.parent);return s.starts&&h(s.starts,e),r.returnEnd?0:t.length} +let y={};function _(i,r){const a=r&&r[0];if(R+=i,null==a)return g(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===a){ +if(R+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=y.rule,t}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]] +;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return b(n) +;return i.skip?R+=n:(i.excludeBegin&&(R+=n), +g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(r) +;if("illegal"===r.type&&!s){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"') +;throw e.mode=N,e}if("end"===r.type){const e=m(r);if(e!==Q)return e} +if("illegal"===r.type&&""===a)return R+="\n",1 +;if(I>1e5&&I>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return R+=a,a.length}const w=O(e) +;if(!w)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=Z(w);let k="",N=r||v;const S={},M=new p.__emitter(p);(()=>{const e=[] +;for(let t=N;t!==w;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{ +if(w.__emitTokens)w.__emitTokens(n,M);else{for(N.matcher.considerAll();;){ +I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A +;const e=N.matcher.exec(n);if(!e)break;const t=_(n.substring(A,e.index),e) +;A=e.index+t}_(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e, +value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A, +context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(o)return{ +language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N} +;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ +const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} +;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1))) +;s.unshift(n);const r=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 +;if(O(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=r,c=o +;return c.secondBest=a,c}function y(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) +;return t||(W(a.replace("{}",n[1])), +W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return +;if(N("before:highlightElement",{el:e,language:n +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML) +;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),N("after:highlightElement",{el:e,result:r,text:i})}let _=!1;function w(){ +if("loading"===document.readyState)return _||window.addEventListener("DOMContentLoaded",(()=>{ +w()}),!1),void(_=!0);document.querySelectorAll(p.cssSelector).forEach(y)} +function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} +function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=O(e) +;return t&&!t.disableAutodetect}function N(e,t){const n=e;r.forEach((e=>{ +e[n]&&e[n](t)}))}Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:w, +highlightElement:y, +highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"), +X("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, +initHighlighting:()=>{ +w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ +if(z("Language definition for '{}' could not be registered.".replace("{}",e)), +!o)throw t;z(t),s=l} +s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete i[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v, +autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)}, +removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{ +o=!1},n.safeMode=()=>{o=!0},n.versionString="11.11.1",n.regex={concat:h, +lookahead:g,either:f,optional:d,anyNumberOfTimes:u} +;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n +},te=ee({});return te.newInstance=()=>ee({}),te}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `javascript` grammar compiled for Highlight.js 11.11.1 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s) +;return o=>{const l=o.regex,d=e,b={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="e+"\\s*\\(")), +l.concat("(?!",T.join("|"),")")),d,l.lookahead(/\s*\(/)), +className:"title.function",relevance:0};var T;const C={ +begin:l.concat(/\./,l.lookahead(l.concat(d,/(?![0-9A-Za-z$_(])/))),end:d, +excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={ +match:[/get|set/,/\s+/,d,/(?=\()/],className:{1:"keyword",3:"title.function"}, +contains:[{begin:/\(\)/},R] +},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={ +match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,_,N,f,p,{match:/\$\d+/},A,k,{ +scope:"attr",match:d+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[p,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0 +},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:b.begin, +"on:begin":b.isTrulyOpeningTag,end:b.end}],subLanguage:"xml",contains:[{ +begin:b.begin,end:b.end,skip:!0,contains:["self"]}]}]},I,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:d, +className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+d, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})() +;hljs.registerLanguage("javascript",e)})();/*! `json` grammar compiled for Highlight.js 11.11.1 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],s={ +scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",aliases:["jsonc"], +keywords:{literal:a},contains:[{className:"attr", +begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/, +className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,s,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `markdown` grammar compiled for Highlight.js 11.11.1 */ +(()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, +end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, +relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] +}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) +;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) +})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal", +match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})() +;hljs.registerLanguage("markdown",e)})();/*! `python` grammar compiled for Highlight.js 11.11.1 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n=e.regex,a=/[\p{XID_Start}_]\p{XID_Continue}*/u,s=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],t={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:s, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},i={className:"meta",begin:/^(>>>|\.\.\.) /},r={className:"subst",begin:/\{/, +end:/\}/,keywords:t,illegal:/#/},l={begin:/\{\{/,relevance:0},o={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,i],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,i],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,i,l,r]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,i,l,r]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,l,r]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,l,r]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},b="[0-9](_?[0-9])*",c=`(\\b(${b}))?\\.(${b})|\\b(${b})\\.`,d="\\b|"+s.join("|"),g={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${b})|(${c}))[eE][+-]?(${b})[jJ]?(?=${d})`},{begin:`(${c})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${d})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${d})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${d})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${d})`},{begin:`\\b(${b})[jJ](?=${d})` +}]},p={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:t, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t, +contains:["self",i,g,o,e.HASH_COMMENT_MODE]}]};return r.contains=[o,g,i],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:t, +illegal:/(<\/|\?)|=>/,contains:[i,g,{scope:"variable.language",match:/\bself\b/ +},{beginKeywords:"if",relevance:0},{match:/\bor\b/,scope:"keyword" +},o,p,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,a],scope:{1:"keyword", +3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,a,/\s*/,/\(\s*/,a,/\s*\)/]},{match:[/\bclass/,/\s+/,a]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[g,m,o]}]}}})() +;hljs.registerLanguage("python",e)})();/*! `typescript` grammar compiled for Highlight.js 11.11.1 */ +(()=>{var e=(()=>{"use strict" +;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],c=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],r=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(c,t,s) +;function o(o){const l=o.regex,d=e,b={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const a=e[0].length+e.index,t=e.input[a] +;if("<"===t||","===t)return void n.ignoreMatch();let s +;">"===t&&(((e,{after:n})=>{const a="e+"\\s*\\(")), +l.concat("(?!",C.join("|"),")")),d,l.lookahead(/\s*\(/)), +className:"title.function",relevance:0};var C;const T={ +begin:l.concat(/\./,l.lookahead(l.concat(d,/(?![0-9A-Za-z$_(])/))),end:d, +excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={ +match:[/get|set/,/\s+/,d,/(?=\()/],className:{1:"keyword",3:"title.function"}, +contains:[{begin:/\(\)/},R] +},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={ +match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{ +PARAMS_CONTAINS:w,CLASS_REFERENCE:x},illegal:/#(?![$_A-z])/, +contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,p,N,f,_,h,{match:/\$\d+/},A,x,{ +scope:"attr",match:d+l.lookahead(":"),relevance:0},$,{ +begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[h,o.REGEXP_MODE,{ +className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/, +excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0 +},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:b.begin, +"on:begin":b.isTrulyOpeningTag,end:b.end}],subLanguage:"xml",contains:[{ +begin:b.begin,end:b.end,skip:!0,contains:["self"]}]}]},O,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:d, +className:"title.function"})]},{match:/\.\.\./,relevance:0},T,{match:"\\$"+d, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[R]},I,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},k,M,{match:/\$[(.]/}]}}return t=>{ +const s=t.regex,c=o(t),l=e,d=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],b={ +begin:[/namespace/,/\s+/,t.IDENT_RE],beginScope:{1:"keyword",3:"title.class"} +},g={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{ +keyword:"interface extends",built_in:d},contains:[c.exports.CLASS_REFERENCE] +},u={$pattern:e, +keyword:n.concat(["type","interface","public","private","protected","implements","declare","abstract","readonly","enum","override","satisfies"]), +literal:a,built_in:i.concat(d),"variable.language":r},m={className:"meta", +begin:"@"+l},E=(e,n,a)=>{const t=e.contains.findIndex((e=>e.label===n)) +;if(-1===t)throw Error("can not find mode to replace");e.contains.splice(t,1,a)} +;Object.assign(c.keywords,u),c.exports.PARAMS_CONTAINS.push(m) +;const A=c.contains.find((e=>"attr"===e.scope)),y=Object.assign({},A,{ +match:s.concat(l,s.lookahead(/\s*\?:/))}) +;return c.exports.PARAMS_CONTAINS.push([c.exports.CLASS_REFERENCE,A,y]), +c.contains=c.contains.concat([m,b,g,y]), +E(c,"shebang",t.SHEBANG()),E(c,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),c.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(c,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),c}})() +;hljs.registerLanguage("typescript",e)})();/*! `xml` grammar compiled for Highlight.js 11.11.1 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[t,i,l,c]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:a.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{ +className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{ +className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}} +})();hljs.registerLanguage("xml",e)})(); \ No newline at end of file diff --git a/webapp/public/highlight.js/styles/vs.css b/webapp/public/highlight.js/styles/vs.css index c5d07d3115d3..dc35c885635d 100644 --- a/webapp/public/highlight.js/styles/vs.css +++ b/webapp/public/highlight.js/styles/vs.css @@ -1,30 +1,32 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} /* Visual Studio-like style based on original C# coloring by Jason Diamond */ .hljs { - display: block; - overflow-x: auto; - padding: 0.5em; background: white; - color: black; + color: black } - .hljs-comment, .hljs-quote, .hljs-variable { - color: #008000; + color: #008000 } - .hljs-keyword, .hljs-selector-tag, .hljs-built_in, .hljs-name, .hljs-tag { - color: #00f; + color: #00f } - .hljs-string, .hljs-title, .hljs-section, @@ -34,35 +36,28 @@ Visual Studio-like style based on original C# coloring by Jason Diamond [^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]||""});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&(cap[1]==="pre"||cap[1]==="script"||cap[1]==="style"),text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(cap[0]):escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.text(escape(this.smartypants(cap[0])));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){if(!this.options.mangle)return text;var out="",l=text.length,i=0,ch;for(;i.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"
"+(escaped?code:escape(code,true))+"\n
"}return'
'+(escaped?code:escape(code,true))+"\n
\n"};Renderer.prototype.blockquote=function(quote){return"
\n"+quote+"
\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"'+text+"\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"\n"};Renderer.prototype.listitem=function(text){return"
  • "+text+"
  • \n"};Renderer.prototype.paragraph=function(text){return"

    "+text+"

    \n"};Renderer.prototype.table=function(header,body){return"\n"+"\n"+header+"\n"+"\n"+body+"\n"+"
    \n"};Renderer.prototype.tablerow=function(content){return"\n"+content+"\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"\n"};Renderer.prototype.strong=function(text){return""+text+""};Renderer.prototype.em=function(text){return""+text+""};Renderer.prototype.codespan=function(text){return""+text+""};Renderer.prototype.br=function(){return this.options.xhtml?"
    ":"
    "};Renderer.prototype.del=function(text){return""+text+""};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='
    ";return out};Renderer.prototype.image=function(href,title,text){var out=''+text+'":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;iAn error occured:

    "+escape(e.message+"",true)+"
    "}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}()); \ No newline at end of file + +/** + * DO NOT EDIT THIS FILE + * The code in this file is generated from files in ./src/ + */ +(function(g,f){if(typeof exports=="object"&&typeof module<"u"){module.exports=f()}else if("function"==typeof define && define.amd){define("marked",f)}else {g["marked"]=f()}}(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : this,function(){var exports={};var __exports=exports;var module={exports}; +"use strict";var G=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var Re=Object.getOwnPropertyNames;var Te=Object.prototype.hasOwnProperty;var Oe=(l,e)=>{for(var t in e)G(l,t,{get:e[t],enumerable:!0})},we=(l,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Re(e))!Te.call(l,r)&&r!==t&&G(l,r,{get:()=>e[r],enumerable:!(n=be(e,r))||n.enumerable});return l};var ye=l=>we(G({},"__esModule",{value:!0}),l);var dt={};Oe(dt,{Hooks:()=>S,Lexer:()=>x,Marked:()=>A,Parser:()=>b,Renderer:()=>P,TextRenderer:()=>$,Tokenizer:()=>y,defaults:()=>T,getDefaults:()=>_,lexer:()=>kt,marked:()=>d,options:()=>ot,parse:()=>ct,parseInline:()=>pt,parser:()=>ht,setOptions:()=>at,use:()=>lt,walkTokens:()=>ut});module.exports=ye(dt);function _(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=_();function N(l){T=l}var E={exec:()=>null};function k(l,e=""){let t=typeof l=="string"?l:l.source,n={replace:(r,i)=>{let s=typeof i=="string"?i:i.source;return s=s.replace(m.caret,"$1"),t=t.replace(r,s),n},getRegex:()=>new RegExp(t,e)};return n}var Pe=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^
    /i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:l=>new RegExp(`^( {0,3}${l})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}#`),htmlBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}<(?:[a-z].*>|!--)`,"i")},Se=/^(?:[ \t]*(?:\n|$))+/,$e=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,_e=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,I=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Le=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,j=/(?:[*+-]|\d{1,9}[.)])/,ie=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,oe=k(ie).replace(/bull/g,j).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Me=k(ie).replace(/bull/g,j).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),Q=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,ze=/^[^\n]+/,U=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,Ae=k(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",U).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),Ce=k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,j).getRegex(),v="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",K=/|$))/,Ee=k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",K).replace("tag",v).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),ae=k(Q).replace("hr",I).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex(),Ie=k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",ae).getRegex(),W={blockquote:Ie,code:$e,def:Ae,fences:_e,heading:Le,hr:I,html:Ee,lheading:oe,list:Ce,newline:Se,paragraph:ae,table:E,text:ze},re=k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",I).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex(),Be={...W,lheading:Me,table:re,paragraph:k(Q).replace("hr",I).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",re).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex()},qe={...W,html:k(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",K).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:E,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:k(Q).replace("hr",I).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",oe).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},ve=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,De=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,le=/^( {2,}|\\)\n(?!\s*$)/,He=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Pe?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),ce=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,je=k(ce,"u").replace(/punct/g,D).getRegex(),Qe=k(ce,"u").replace(/punct/g,pe).getRegex(),he="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",Ue=k(he,"gu").replace(/notPunctSpace/g,ue).replace(/punctSpace/g,X).replace(/punct/g,D).getRegex(),Ke=k(he,"gu").replace(/notPunctSpace/g,Ne).replace(/punctSpace/g,Ge).replace(/punct/g,pe).getRegex(),We=k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,ue).replace(/punctSpace/g,X).replace(/punct/g,D).getRegex(),Xe=k(/\\(punct)/,"gu").replace(/punct/g,D).getRegex(),Je=k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Ve=k(K).replace("(?:-->|$)","-->").getRegex(),Ye=k("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",Ve).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),q=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,et=k(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",q).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),ke=k(/^!?\[(label)\]\[(ref)\]/).replace("label",q).replace("ref",U).getRegex(),de=k(/^!?\[(ref)\](?:\[\])?/).replace("ref",U).getRegex(),tt=k("reflink|nolink(?!\\()","g").replace("reflink",ke).replace("nolink",de).getRegex(),se=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,J={_backpedal:E,anyPunctuation:Xe,autolink:Je,blockSkip:Fe,br:le,code:De,del:E,emStrongLDelim:je,emStrongRDelimAst:Ue,emStrongRDelimUnd:We,escape:ve,link:et,nolink:de,punctuation:Ze,reflink:ke,reflinkSearch:tt,tag:Ye,text:He,url:E},nt={...J,link:k(/^!?\[(label)\]\((.*?)\)/).replace("label",q).getRegex(),reflink:k(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",q).getRegex()},F={...J,emStrongRDelimAst:Ke,emStrongLDelim:Qe,url:k(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",se).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:k(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},ge=l=>st[l];function w(l,e){if(e){if(m.escapeTest.test(l))return l.replace(m.escapeReplace,ge)}else if(m.escapeTestNoEncode.test(l))return l.replace(m.escapeReplaceNoEncode,ge);return l}function V(l){try{l=encodeURI(l).replace(m.percentDecode,"%")}catch{return null}return l}function Y(l,e){let t=l.replace(m.findPipe,(i,s,a)=>{let o=!1,u=s;for(;--u>=0&&a[u]==="\\";)o=!o;return o?"|":" |"}),n=t.split(m.splitPipe),r=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length0?-2:-1}function me(l,e,t,n,r){let i=e.href,s=e.title||null,a=l[1].replace(r.other.outputLinkReplace,"$1");n.state.inLink=!0;let o={type:l[0].charAt(0)==="!"?"image":"link",raw:t,href:i,title:s,text:a,tokens:n.inlineTokens(a)};return n.state.inLink=!1,o}function it(l,e,t){let n=l.match(t.other.indentCodeCompensation);if(n===null)return e;let r=n[1];return e.split(` +`).map(i=>{let s=i.match(t.other.beginningSpace);if(s===null)return i;let[a]=s;return a.length>=r.length?i.slice(r.length):i}).join(` +`)}var y=class{options;rules;lexer;constructor(e){this.options=e||T}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:z(n,` +`)}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],r=it(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:r}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let r=z(n,"#");(this.options.pedantic||!r||this.rules.other.endingSpaceChar.test(r))&&(n=r.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:z(t[0],` +`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=z(t[0],` +`).split(` +`),r="",i="",s=[];for(;n.length>0;){let a=!1,o=[],u;for(u=0;u1,i={type:"list",raw:"",ordered:r,start:r?+n.slice(0,-1):"",loose:!1,items:[]};n=r?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=r?n:"[*+-]");let s=this.rules.other.listItemRegex(n),a=!1;for(;e;){let u=!1,p="",c="";if(!(t=s.exec(e))||this.rules.block.hr.test(e))break;p=t[0],e=e.substring(p.length);let g=t[2].split(` +`,1)[0].replace(this.rules.other.listReplaceTabs,H=>" ".repeat(3*H.length)),h=e.split(` +`,1)[0],R=!g.trim(),f=0;if(this.options.pedantic?(f=2,c=g.trimStart()):R?f=t[1].length+1:(f=t[2].search(this.rules.other.nonSpaceChar),f=f>4?1:f,c=g.slice(f),f+=t[1].length),R&&this.rules.other.blankLine.test(h)&&(p+=h+` +`,e=e.substring(h.length+1),u=!0),!u){let H=this.rules.other.nextBulletRegex(f),ee=this.rules.other.hrRegex(f),te=this.rules.other.fencesBeginRegex(f),ne=this.rules.other.headingBeginRegex(f),xe=this.rules.other.htmlBeginRegex(f);for(;e;){let Z=e.split(` +`,1)[0],C;if(h=Z,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting," "),C=h):C=h.replace(this.rules.other.tabCharGlobal," "),te.test(h)||ne.test(h)||xe.test(h)||H.test(h)||ee.test(h))break;if(C.search(this.rules.other.nonSpaceChar)>=f||!h.trim())c+=` +`+C.slice(f);else{if(R||g.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||te.test(g)||ne.test(g)||ee.test(g))break;c+=` +`+h}!R&&!h.trim()&&(R=!0),p+=Z+` +`,e=e.substring(Z.length+1),g=C.slice(f)}}i.loose||(a?i.loose=!0:this.rules.other.doubleBlankLine.test(p)&&(a=!0));let O=null;this.options.gfm&&(O=this.rules.other.listIsTask.exec(c),O&&(c=c.replace(this.rules.other.listReplaceTask,""))),i.items.push({type:"list_item",raw:p,task:!!O,loose:!1,text:c,tokens:[]}),i.raw+=p}let o=i.items.at(-1);if(o)o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd();else return;i.raw=i.raw.trimEnd();for(let u of i.items){if(this.lexer.state.top=!1,u.tokens=this.lexer.blockTokens(u.text,[]),u.task){let p=this.rules.other.listTaskCheckbox.exec(u.raw);if(p){let c={type:"checkbox",raw:p[0]+" ",checked:p[0]!=="[ ]"};u.checked=c.checked,i.loose?u.tokens[0]&&["paragraph","text"].includes(u.tokens[0].type)&&"tokens"in u.tokens[0]&&u.tokens[0].tokens?(u.tokens[0].raw=c.raw+u.tokens[0].raw,u.tokens[0].text=c.raw+u.tokens[0].text,u.tokens[0].tokens.unshift(c)):u.tokens.unshift({type:"paragraph",raw:c.raw,text:c.raw,tokens:[c]}):u.tokens.unshift(c)}}if(!i.loose){let p=u.tokens.filter(g=>g.type==="space"),c=p.length>0&&p.some(g=>this.rules.other.anyLine.test(g.raw));i.loose=c}}if(i.loose)for(let u of i.items){u.loose=!0;for(let p of u.tokens)p.type==="text"&&(p.type="paragraph")}return i}}html(e){let t=this.rules.block.html.exec(e);if(t)return{type:"html",block:!0,raw:t[0],pre:t[1]==="pre"||t[1]==="script"||t[1]==="style",text:t[0]}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),r=t[2]?t[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:n,raw:t[0],href:r,title:i}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=Y(t[1]),r=t[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,"").split(` +`):[],s={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let a of r)this.rules.other.tableAlignRight.test(a)?s.align.push("right"):this.rules.other.tableAlignCenter.test(a)?s.align.push("center"):this.rules.other.tableAlignLeft.test(a)?s.align.push("left"):s.align.push(null);for(let a=0;a({text:o,tokens:this.lexer.inline(o),header:!1,align:s.align[u]})));return s}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[2].charAt(0)==="="?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===` +`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let s=z(n.slice(0,-1),"\\");if((n.length-s.length)%2===0)return}else{let s=fe(t[2],"()");if(s===-2)return;if(s>-1){let o=(t[0].indexOf("!")===0?5:4)+t[1].length+s;t[2]=t[2].substring(0,s),t[0]=t[0].substring(0,o).trim(),t[3]=""}}let r=t[2],i="";if(this.options.pedantic){let s=this.rules.other.pedanticHrefTitle.exec(r);s&&(r=s[1],i=s[3])}else i=t[3]?t[3].slice(1,-1):"";return r=r.trim(),this.rules.other.startAngleBracket.test(r)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?r=r.slice(1):r=r.slice(1,-1)),me(t,{href:r&&r.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let r=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=t[r.toLowerCase()];if(!i){let s=n[0].charAt(0);return{type:"text",raw:s,text:s}}return me(n,i,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let r=this.rules.inline.emStrongLDelim.exec(e);if(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(r[1]||r[2]||"")||!n||this.rules.inline.punctuation.exec(n)){let s=[...r[0]].length-1,a,o,u=s,p=0,c=r[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(c.lastIndex=0,t=t.slice(-1*e.length+s);(r=c.exec(t))!=null;){if(a=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!a)continue;if(o=[...a].length,r[3]||r[4]){u+=o;continue}else if((r[5]||r[6])&&s%3&&!((s+o)%3)){p+=o;continue}if(u-=o,u>0)continue;o=Math.min(o,o+u+p);let g=[...r[0]][0].length,h=e.slice(0,s+r.index+g+o);if(Math.min(s,o)%2){let f=h.slice(1,-1);return{type:"em",raw:h,text:f,tokens:this.lexer.inlineTokens(f)}}let R=h.slice(2,-2);return{type:"strong",raw:h,text:R,tokens:this.lexer.inlineTokens(R)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," "),r=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return r&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,r;return t[2]==="@"?(n=t[1],r="mailto:"+n):(n=t[1],r=n),{type:"link",raw:t[0],text:n,href:r,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,r;if(t[2]==="@")n=t[0],r="mailto:"+n;else{let i;do i=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(i!==t[0]);n=t[0],t[1]==="www."?r="http://"+t[0]:r=t[0]}return{type:"link",raw:t[0],text:n,href:r,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}};var x=class l{tokens;options;state;tokenizer;inlineQueue;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||T,this.options.tokenizer=this.options.tokenizer||new y,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:B.normal,inline:M.normal};this.options.pedantic?(t.block=B.pedantic,t.inline=M.pedantic):this.options.gfm&&(t.block=B.gfm,this.options.breaks?t.inline=M.breaks:t.inline=M.gfm),this.tokenizer.rules=t}static get rules(){return{block:B,inline:M}}static lex(e,t){return new l(t).lex(e)}static lexInline(e,t){return new l(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,` +`),this.blockTokens(e,this.tokens);for(let t=0;t(r=s.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let s=t.at(-1);r.raw.length===1&&s!==void 0?s.raw+=` +`:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type==="paragraph"||s?.type==="text"?(s.raw+=(s.raw.endsWith(` +`)?"":` +`)+r.raw,s.text+=` +`+r.text,this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type==="paragraph"||s?.type==="text"?(s.raw+=(s.raw.endsWith(` +`)?"":` +`)+r.raw,s.text+=` +`+r.raw,this.inlineQueue.at(-1).src=s.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let s=1/0,a=e.slice(1),o;this.options.extensions.startBlock.forEach(u=>{o=u.call({lexer:this},a),typeof o=="number"&&o>=0&&(s=Math.min(s,o))}),s<1/0&&s>=0&&(i=e.substring(0,s+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let s=t.at(-1);n&&s?.type==="paragraph"?(s.raw+=(s.raw.endsWith(` +`)?"":` +`)+r.raw,s.text+=` +`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let s=t.at(-1);s?.type==="text"?(s.raw+=(s.raw.endsWith(` +`)?"":` +`)+r.raw,s.text+=` +`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=s.text):t.push(r);continue}if(e){let s="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(s);break}else throw new Error(s)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,r=null;if(this.tokens.links){let o=Object.keys(this.tokens.links);if(o.length>0)for(;(r=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)o.includes(r[0].slice(r[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,r.index)+"["+"a".repeat(r[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(r=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,r.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let i;for(;(r=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)i=r[2]?r[2].length:0,n=n.slice(0,r.index+i)+"["+"a".repeat(r[0].length-i-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let s=!1,a="";for(;e;){s||(a=""),s=!1;let o;if(this.options.extensions?.inline?.some(p=>(o=p.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);let p=t.at(-1);o.type==="text"&&p?.type==="text"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,a)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let u=e;if(this.options.extensions?.startInline){let p=1/0,c=e.slice(1),g;this.options.extensions.startInline.forEach(h=>{g=h.call({lexer:this},c),typeof g=="number"&&g>=0&&(p=Math.min(p,g))}),p<1/0&&p>=0&&(u=e.substring(0,p+1))}if(o=this.tokenizer.inlineText(u)){e=e.substring(o.raw.length),o.raw.slice(-1)!=="_"&&(a=o.raw.slice(-1)),s=!0;let p=t.at(-1);p?.type==="text"?(p.raw+=o.raw,p.text+=o.text):t.push(o);continue}if(e){let p="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(p);break}else throw new Error(p)}}return t}};var P=class{options;parser;constructor(e){this.options=e||T}space(e){return""}code({text:e,lang:t,escaped:n}){let r=(t||"").match(m.notSpaceStart)?.[0],i=e.replace(m.endingNewline,"")+` +`;return r?'
    '+(n?i:w(i,!0))+`
    +`:"
    "+(n?i:w(i,!0))+`
    +`}blockquote({tokens:e}){return`
    +${this.parser.parse(e)}
    +`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)} +`}hr(e){return`
    +`}list(e){let t=e.ordered,n=e.start,r="";for(let a=0;a +`+r+" +`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • +`}checkbox({checked:e}){return" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    +`}table(e){let t="",n="";for(let i=0;i${r}`),` + +`+t+` +`+r+`
    +`}tablerow({text:e}){return` +${e} +`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+` +`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${w(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),i=V(e);if(i===null)return r;e=i;let s='
    ",s}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=V(e);if(i===null)return w(n);e=i;let s=`${n}{let a=i[s].flat(1/0);n=n.concat(this.walkTokens(a,t))}):i.tokens&&(n=n.concat(this.walkTokens(i.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let r={...n};if(r.async=this.defaults.async||r.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let s=t.renderers[i.name];s?t.renderers[i.name]=function(...a){let o=i.renderer.apply(this,a);return o===!1&&(o=s.apply(this,a)),o}:t.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let s=t[i.level];s?s.unshift(i.tokenizer):t[i.level]=[i.tokenizer],i.start&&(i.level==="block"?t.startBlock?t.startBlock.push(i.start):t.startBlock=[i.start]:i.level==="inline"&&(t.startInline?t.startInline.push(i.start):t.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(t.childTokens[i.name]=i.childTokens)}),r.extensions=t),n.renderer){let i=this.defaults.renderer||new P(this.defaults);for(let s in n.renderer){if(!(s in i))throw new Error(`renderer '${s}' does not exist`);if(["options","parser"].includes(s))continue;let a=s,o=n.renderer[a],u=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=u.apply(i,p)),c||""}}r.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new y(this.defaults);for(let s in n.tokenizer){if(!(s in i))throw new Error(`tokenizer '${s}' does not exist`);if(["options","rules","lexer"].includes(s))continue;let a=s,o=n.tokenizer[a],u=i[a];i[a]=(...p)=>{let c=o.apply(i,p);return c===!1&&(c=u.apply(i,p)),c}}r.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new S;for(let s in n.hooks){if(!(s in i))throw new Error(`hook '${s}' does not exist`);if(["options","block"].includes(s))continue;let a=s,o=n.hooks[a],u=i[a];S.passThroughHooks.has(s)?i[a]=p=>{if(this.defaults.async&&S.passThroughHooksRespectAsync.has(s))return(async()=>{let g=await o.call(i,p);return u.call(i,g)})();let c=o.call(i,p);return u.call(i,c)}:i[a]=(...p)=>{if(this.defaults.async)return(async()=>{let g=await o.apply(i,p);return g===!1&&(g=await u.apply(i,p)),g})();let c=o.apply(i,p);return c===!1&&(c=u.apply(i,p)),c}}r.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,s=n.walkTokens;r.walkTokens=function(a){let o=[];return o.push(s.call(this,a)),i&&(o=o.concat(i.call(this,a))),o}}this.defaults={...this.defaults,...r}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,r)=>{let i={...r},s={...this.defaults,...i},a=this.onError(!!s.silent,!!s.async);if(this.defaults.async===!0&&i.async===!1)return a(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof n>"u"||n===null)return a(new Error("marked(): input parameter is undefined or null"));if(typeof n!="string")return a(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(s.hooks&&(s.hooks.options=s,s.hooks.block=e),s.async)return(async()=>{let o=s.hooks?await s.hooks.preprocess(n):n,p=await(s.hooks?await s.hooks.provideLexer():e?x.lex:x.lexInline)(o,s),c=s.hooks?await s.hooks.processAllTokens(p):p;s.walkTokens&&await Promise.all(this.walkTokens(c,s.walkTokens));let h=await(s.hooks?await s.hooks.provideParser():e?b.parse:b.parseInline)(c,s);return s.hooks?await s.hooks.postprocess(h):h})().catch(a);try{s.hooks&&(n=s.hooks.preprocess(n));let u=(s.hooks?s.hooks.provideLexer():e?x.lex:x.lexInline)(n,s);s.hooks&&(u=s.hooks.processAllTokens(u)),s.walkTokens&&this.walkTokens(u,s.walkTokens);let c=(s.hooks?s.hooks.provideParser():e?b.parse:b.parseInline)(u,s);return s.hooks&&(c=s.hooks.postprocess(c)),c}catch(o){return a(o)}}}onError(e,t){return n=>{if(n.message+=` +Please report this to https://github.com/markedjs/marked.`,e){let r="

    An error occurred:

    "+w(n.message+"",!0)+"
    ";return t?Promise.resolve(r):r}if(t)return Promise.reject(n);throw n}}};var L=new A;function d(l,e){return L.parse(l,e)}d.options=d.setOptions=function(l){return L.setOptions(l),d.defaults=L.defaults,N(d.defaults),d};d.getDefaults=_;d.defaults=T;d.use=function(...l){return L.use(...l),d.defaults=L.defaults,N(d.defaults),d};d.walkTokens=function(l,e){return L.walkTokens(l,e)};d.parseInline=L.parseInline;d.Parser=b;d.parser=b.parse;d.Renderer=P;d.TextRenderer=$;d.Lexer=x;d.lexer=x.lex;d.Tokenizer=y;d.Hooks=S;d.parse=d;var ot=d.options,at=d.setOptions,lt=d.use,ut=d.walkTokens,pt=d.parseInline,ct=d,ht=b.parse,kt=x.lex; + +if(__exports != exports)module.exports = exports;return module.exports})); +//# sourceMappingURL=marked.umd.js.map diff --git a/webapp/public/smoothie/smoothie.js b/webapp/public/smoothie/smoothie.js deleted file mode 100644 index 14774b5c1b6e..000000000000 --- a/webapp/public/smoothie/smoothie.js +++ /dev/null @@ -1,965 +0,0 @@ -// MIT License: -// -// Copyright (c) 2010-2013, Joe Walnes -// 2013-2017, Drew Noakes -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -/** - * Smoothie Charts - http://smoothiecharts.org/ - * (c) 2010-2013, Joe Walnes - * 2013-2017, Drew Noakes - * - * v1.0: Main charting library, by Joe Walnes - * v1.1: Auto scaling of axis, by Neil Dunn - * v1.2: fps (frames per second) option, by Mathias Petterson - * v1.3: Fix for divide by zero, by Paul Nikitochkin - * v1.4: Set minimum, top-scale padding, remove timeseries, add optional timer to reset bounds, by Kelley Reynolds - * v1.5: Set default frames per second to 50... smoother. - * .start(), .stop() methods for conserving CPU, by Dmitry Vyal - * options.interpolation = 'bezier' or 'line', by Dmitry Vyal - * options.maxValue to fix scale, by Dmitry Vyal - * v1.6: minValue/maxValue will always get converted to floats, by Przemek Matylla - * v1.7: options.grid.fillStyle may be a transparent color, by Dmitry A. Shashkin - * Smooth rescaling, by Kostas Michalopoulos - * v1.8: Set max length to customize number of live points in the dataset with options.maxDataSetLength, by Krishna Narni - * v1.9: Display timestamps along the bottom, by Nick and Stev-io - * (https://groups.google.com/forum/?fromgroups#!topic/smoothie-charts/-Ywse8FCpKI%5B1-25%5D) - * Refactored by Krishna Narni, to support timestamp formatting function - * v1.10: Switch to requestAnimationFrame, removed the now obsoleted options.fps, by Gergely Imreh - * v1.11: options.grid.sharpLines option added, by @drewnoakes - * Addressed warning seen in Firefox when seriesOption.fillStyle undefined, by @drewnoakes - * v1.12: Support for horizontalLines added, by @drewnoakes - * Support for yRangeFunction callback added, by @drewnoakes - * v1.13: Fixed typo (#32), by @alnikitich - * v1.14: Timer cleared when last TimeSeries removed (#23), by @davidgaleano - * Fixed diagonal line on chart at start/end of data stream, by @drewnoakes - * v1.15: Support for npm package (#18), by @dominictarr - * Fixed broken removeTimeSeries function (#24) by @davidgaleano - * Minor performance and tidying, by @drewnoakes - * v1.16: Bug fix introduced in v1.14 relating to timer creation/clearance (#23), by @drewnoakes - * TimeSeries.append now deals with out-of-order timestamps, and can merge duplicates, by @zacwitte (#12) - * Documentation and some local variable renaming for clarity, by @drewnoakes - * v1.17: Allow control over font size (#10), by @drewnoakes - * Timestamp text won't overlap, by @drewnoakes - * v1.18: Allow control of max/min label precision, by @drewnoakes - * Added 'borderVisible' chart option, by @drewnoakes - * Allow drawing series with fill but no stroke (line), by @drewnoakes - * v1.19: Avoid unnecessary repaints, and fixed flicker in old browsers having multiple charts in document (#40), by @asbai - * v1.20: Add SmoothieChart.getTimeSeriesOptions and SmoothieChart.bringToFront functions, by @drewnoakes - * v1.21: Add 'step' interpolation mode, by @drewnoakes - * v1.22: Add support for different pixel ratios. Also add optional y limit formatters, by @copacetic - * v1.23: Fix bug introduced in v1.22 (#44), by @drewnoakes - * v1.24: Fix bug introduced in v1.23, re-adding parseFloat to y-axis formatter defaults, by @siggy_sf - * v1.25: Fix bug seen when adding a data point to TimeSeries which is older than the current data, by @Nking92 - * Draw time labels on top of series, by @comolosabia - * Add TimeSeries.clear function, by @drewnoakes - * v1.26: Add support for resizing on high device pixel ratio screens, by @copacetic - * v1.27: Fix bug introduced in v1.26 for non whole number devicePixelRatio values, by @zmbush - * v1.28: Add 'minValueScale' option, by @megawac - * Fix 'labelPos' for different size of 'minValueString' 'maxValueString', by @henryn - * v1.29: Support responsive sizing, by @drewnoakes - * v1.29.1: Include types in package, and make property optional, by @TrentHouliston - * v1.30: Fix inverted logic in devicePixelRatio support, by @scanlime - * v1.31: Support tooltips, by @Sly1024 and @drewnoakes - * v1.32: Support frame rate limit, by @dpuyosa - */ - -;(function(exports) { - - var Util = { - extend: function() { - arguments[0] = arguments[0] || {}; - for (var i = 1; i < arguments.length; i++) - { - for (var key in arguments[i]) - { - if (arguments[i].hasOwnProperty(key)) - { - if (typeof(arguments[i][key]) === 'object') { - if (arguments[i][key] instanceof Array) { - arguments[0][key] = arguments[i][key]; - } else { - arguments[0][key] = Util.extend(arguments[0][key], arguments[i][key]); - } - } else { - arguments[0][key] = arguments[i][key]; - } - } - } - } - return arguments[0]; - }, - binarySearch: function(data, value) { - var low = 0, - high = data.length; - while (low < high) { - var mid = (low + high) >> 1; - if (value < data[mid][0]) - high = mid; - else - low = mid + 1; - } - return low; - } - }; - - /** - * Initialises a new TimeSeries with optional data options. - * - * Options are of the form (defaults shown): - * - *
    -       * {
    -       *   resetBounds: true,        // enables/disables automatic scaling of the y-axis
    -       *   resetBoundsInterval: 3000 // the period between scaling calculations, in millis
    -       * }
    -       * 
    - * - * Presentation options for TimeSeries are specified as an argument to SmoothieChart.addTimeSeries. - * - * @constructor - */ - function TimeSeries(options) { - this.options = Util.extend({}, TimeSeries.defaultOptions, options); - this.clear(); - } - - TimeSeries.defaultOptions = { - resetBoundsInterval: 3000, - resetBounds: true - }; - - /** - * Clears all data and state from this TimeSeries object. - */ - TimeSeries.prototype.clear = function() { - this.data = []; - this.maxValue = Number.NaN; // The maximum value ever seen in this TimeSeries. - this.minValue = Number.NaN; // The minimum value ever seen in this TimeSeries. - }; - - /** - * Recalculate the min/max values for this TimeSeries object. - * - * This causes the graph to scale itself in the y-axis. - */ - TimeSeries.prototype.resetBounds = function() { - if (this.data.length) { - // Walk through all data points, finding the min/max value - this.maxValue = this.data[0][1]; - this.minValue = this.data[0][1]; - for (var i = 1; i < this.data.length; i++) { - var value = this.data[i][1]; - if (value > this.maxValue) { - this.maxValue = value; - } - if (value < this.minValue) { - this.minValue = value; - } - } - } else { - // No data exists, so set min/max to NaN - this.maxValue = Number.NaN; - this.minValue = Number.NaN; - } - }; - - /** - * Adds a new data point to the TimeSeries, preserving chronological order. - * - * @param timestamp the position, in time, of this data point - * @param value the value of this data point - * @param sumRepeatedTimeStampValues if timestamp has an exact match in the series, this flag controls - * whether it is replaced, or the values summed (defaults to false.) - */ - TimeSeries.prototype.append = function(timestamp, value, sumRepeatedTimeStampValues) { - // Rewind until we hit an older timestamp - var i = this.data.length - 1; - while (i >= 0 && this.data[i][0] > timestamp) { - i--; - } - - if (i === -1) { - // This new item is the oldest data - this.data.splice(0, 0, [timestamp, value]); - } else if (this.data.length > 0 && this.data[i][0] === timestamp) { - // Update existing values in the array - if (sumRepeatedTimeStampValues) { - // Sum this value into the existing 'bucket' - this.data[i][1] += value; - value = this.data[i][1]; - } else { - // Replace the previous value - this.data[i][1] = value; - } - } else if (i < this.data.length - 1) { - // Splice into the correct position to keep timestamps in order - this.data.splice(i + 1, 0, [timestamp, value]); - } else { - // Add to the end of the array - this.data.push([timestamp, value]); - } - - this.maxValue = isNaN(this.maxValue) ? value : Math.max(this.maxValue, value); - this.minValue = isNaN(this.minValue) ? value : Math.min(this.minValue, value); - }; - - TimeSeries.prototype.dropOldData = function(oldestValidTime, maxDataSetLength) { - // We must always keep one expired data point as we need this to draw the - // line that comes into the chart from the left, but any points prior to that can be removed. - var removeCount = 0; - while (this.data.length - removeCount >= maxDataSetLength && this.data[removeCount + 1][0] < oldestValidTime) { - removeCount++; - } - if (removeCount !== 0) { - this.data.splice(0, removeCount); - } - }; - - /** - * Initialises a new SmoothieChart. - * - * Options are optional, and should be of the form below. Just specify the values you - * need and the rest will be given sensible defaults as shown: - * - *
    -       * {
    -       *   minValue: undefined,                      // specify to clamp the lower y-axis to a given value
    -       *   maxValue: undefined,                      // specify to clamp the upper y-axis to a given value
    -       *   maxValueScale: 1,                         // allows proportional padding to be added above the chart. for 10% padding, specify 1.1.
    -       *   minValueScale: 1,                         // allows proportional padding to be added below the chart. for 10% padding, specify 1.1.
    -       *   yRangeFunction: undefined,                // function({min: , max: }) { return {min: , max: }; }
    -       *   scaleSmoothing: 0.125,                    // controls the rate at which y-value zoom animation occurs
    -       *   millisPerPixel: 20,                       // sets the speed at which the chart pans by
    -       *   enableDpiScaling: true,                   // support rendering at different DPI depending on the device
    -       *   yMinFormatter: function(min, precision) { // callback function that formats the min y value label
    -       *     return parseFloat(min).toFixed(precision);
    -       *   },
    -       *   yMaxFormatter: function(max, precision) { // callback function that formats the max y value label
    -       *     return parseFloat(max).toFixed(precision);
    -       *   },
    -       *   maxDataSetLength: 2,
    -       *   interpolation: 'bezier'                   // one of 'bezier', 'linear', or 'step'
    -       *   timestampFormatter: null,                 // optional function to format time stamps for bottom of chart
    -       *                                             // you may use SmoothieChart.timeFormatter, or your own: function(date) { return ''; }
    -       *   scrollBackwards: false,                   // reverse the scroll direction of the chart
    -       *   horizontalLines: [],                      // [ { value: 0, color: '#ffffff', lineWidth: 1 } ]
    -       *   grid:
    -       *   {
    -       *     fillStyle: '#000000',                   // the background colour of the chart
    -       *     lineWidth: 1,                           // the pixel width of grid lines
    -       *     strokeStyle: '#777777',                 // colour of grid lines
    -       *     millisPerLine: 1000,                    // distance between vertical grid lines
    -       *     sharpLines: false,                      // controls whether grid lines are 1px sharp, or softened
    -       *     verticalSections: 2,                    // number of vertical sections marked out by horizontal grid lines
    -       *     borderVisible: true                     // whether the grid lines trace the border of the chart or not
    -       *   },
    -       *   labels
    -       *   {
    -       *     disabled: false,                        // enables/disables labels showing the min/max values
    -       *     fillStyle: '#ffffff',                   // colour for text of labels,
    -       *     fontSize: 15,
    -       *     fontFamily: 'sans-serif',
    -       *     precision: 2
    -       *   },
    -       *   tooltip: false                            // show tooltip when mouse is over the chart
    -       *   tooltipLine: {                            // properties for a vertical line at the cursor position
    -       *     lineWidth: 1,
    -       *     strokeStyle: '#BBBBBB'
    -       *   },
    -       *   tooltipFormatter: SmoothieChart.tooltipFormatter, // formatter function for tooltip text
    -       *   responsive: false,                        // whether the chart should adapt to the size of the canvas
    -       *   limitFPS: 0                         // maximum frame rate the chart will render at, in FPS (zero means no limit)
    -       * }
    -       * 
    - * - * @constructor - */ - function SmoothieChart(options) { - this.options = Util.extend({}, SmoothieChart.defaultChartOptions, options); - this.seriesSet = []; - this.currentValueRange = 1; - this.currentVisMinValue = 0; - this.lastRenderTimeMillis = 0; - - this.mousemove = this.mousemove.bind(this); - this.mouseout = this.mouseout.bind(this); - } - - /** Formats the HTML string content of the tooltip. */ - SmoothieChart.tooltipFormatter = function (timestamp, data) { - var timestampFormatter = this.options.timestampFormatter || SmoothieChart.timeFormatter, - lines = [timestampFormatter(new Date(timestamp))]; - - for (var i = 0; i < data.length; ++i) { - lines.push('' + - this.options.yMaxFormatter(data[i].value, this.options.labels.precision) + ''); - } - - return lines.join('
    '); - }; - - SmoothieChart.defaultChartOptions = { - millisPerPixel: 20, - enableDpiScaling: true, - yMinFormatter: function(min, precision) { - return parseFloat(min).toFixed(precision); - }, - yMaxFormatter: function(max, precision) { - return parseFloat(max).toFixed(precision); - }, - maxValueScale: 1, - minValueScale: 1, - interpolation: 'bezier', - scaleSmoothing: 0.125, - maxDataSetLength: 2, - scrollBackwards: false, - grid: { - fillStyle: '#000000', - strokeStyle: '#777777', - lineWidth: 1, - sharpLines: false, - millisPerLine: 1000, - verticalSections: 2, - borderVisible: true - }, - labels: { - fillStyle: '#ffffff', - disabled: false, - fontSize: 10, - fontFamily: 'monospace', - precision: 2 - }, - horizontalLines: [], - tooltip: false, - tooltipLine: { - lineWidth: 1, - strokeStyle: '#BBBBBB' - }, - tooltipFormatter: SmoothieChart.tooltipFormatter, - responsive: false, - limitFPS: 0 - }; - - // Based on http://inspirit.github.com/jsfeat/js/compatibility.js - SmoothieChart.AnimateCompatibility = (function() { - var requestAnimationFrame = function(callback, element) { - var requestAnimationFrame = - window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(function() { - callback(new Date().getTime()); - }, 16); - }; - return requestAnimationFrame.call(window, callback, element); - }, - cancelAnimationFrame = function(id) { - var cancelAnimationFrame = - window.cancelAnimationFrame || - function(id) { - clearTimeout(id); - }; - return cancelAnimationFrame.call(window, id); - }; - - return { - requestAnimationFrame: requestAnimationFrame, - cancelAnimationFrame: cancelAnimationFrame - }; - })(); - - SmoothieChart.defaultSeriesPresentationOptions = { - lineWidth: 1, - strokeStyle: '#ffffff' - }; - - /** - * Adds a TimeSeries to this chart, with optional presentation options. - * - * Presentation options should be of the form (defaults shown): - * - *
    -       * {
    -       *   lineWidth: 1,
    -       *   strokeStyle: '#ffffff',
    -       *   fillStyle: undefined
    -       * }
    -       * 
    - */ - SmoothieChart.prototype.addTimeSeries = function(timeSeries, options) { - this.seriesSet.push({timeSeries: timeSeries, options: Util.extend({}, SmoothieChart.defaultSeriesPresentationOptions, options)}); - if (timeSeries.options.resetBounds && timeSeries.options.resetBoundsInterval > 0) { - timeSeries.resetBoundsTimerId = setInterval( - function() { - timeSeries.resetBounds(); - }, - timeSeries.options.resetBoundsInterval - ); - } - }; - - /** - * Removes the specified TimeSeries from the chart. - */ - SmoothieChart.prototype.removeTimeSeries = function(timeSeries) { - // Find the correct timeseries to remove, and remove it - var numSeries = this.seriesSet.length; - for (var i = 0; i < numSeries; i++) { - if (this.seriesSet[i].timeSeries === timeSeries) { - this.seriesSet.splice(i, 1); - break; - } - } - // If a timer was operating for that timeseries, remove it - if (timeSeries.resetBoundsTimerId) { - // Stop resetting the bounds, if we were - clearInterval(timeSeries.resetBoundsTimerId); - } - }; - - /** - * Gets render options for the specified TimeSeries. - * - * As you may use a single TimeSeries in multiple charts with different formatting in each usage, - * these settings are stored in the chart. - */ - SmoothieChart.prototype.getTimeSeriesOptions = function(timeSeries) { - // Find the correct timeseries to remove, and remove it - var numSeries = this.seriesSet.length; - for (var i = 0; i < numSeries; i++) { - if (this.seriesSet[i].timeSeries === timeSeries) { - return this.seriesSet[i].options; - } - } - }; - - /** - * Brings the specified TimeSeries to the top of the chart. It will be rendered last. - */ - SmoothieChart.prototype.bringToFront = function(timeSeries) { - // Find the correct timeseries to remove, and remove it - var numSeries = this.seriesSet.length; - for (var i = 0; i < numSeries; i++) { - if (this.seriesSet[i].timeSeries === timeSeries) { - var set = this.seriesSet.splice(i, 1); - this.seriesSet.push(set[0]); - break; - } - } - }; - - /** - * Instructs the SmoothieChart to start rendering to the provided canvas, with specified delay. - * - * @param canvas the target canvas element - * @param delayMillis an amount of time to wait before a data point is shown. This can prevent the end of the series - * from appearing on screen, with new values flashing into view, at the expense of some latency. - */ - SmoothieChart.prototype.streamTo = function(canvas, delayMillis) { - this.canvas = canvas; - this.delay = delayMillis; - this.start(); - }; - - SmoothieChart.prototype.getTooltipEl = function () { - // Use a single tooltip element across all chart instances - var el = SmoothieChart.tooltipEl; - if (!el) { - el = SmoothieChart.tooltipEl = document.createElement('div'); - el.className = 'smoothie-chart-tooltip'; - el.style.position = 'absolute'; - el.style.display = 'none'; - document.body.appendChild(el); - } - return el; - }; - - SmoothieChart.prototype.updateTooltip = function () { - var el = this.getTooltipEl(); - - if (!this.mouseover || !this.options.tooltip) { - el.style.display = 'none'; - return; - } - - var time = this.lastRenderTimeMillis - (this.delay || 0); - - // Round time down to pixel granularity, so motion appears smoother. - time -= time % this.options.millisPerPixel; - - // x pixel to time - var t = this.options.scrollBackwards - ? time - this.mouseX * this.options.millisPerPixel - : time - (this.canvas.offsetWidth - this.mouseX) * this.options.millisPerPixel; - - var data = []; - - // For each data set... - for (var d = 0; d < this.seriesSet.length; d++) { - var timeSeries = this.seriesSet[d].timeSeries, - // find datapoint closest to time 't' - closeIdx = Util.binarySearch(timeSeries.data, t); - - if (closeIdx > 0 && closeIdx < timeSeries.data.length) { - data.push({ series: this.seriesSet[d], index: closeIdx, value: timeSeries.data[closeIdx][1] }); - } - } - - if (data.length) { - el.innerHTML = this.options.tooltipFormatter.call(this, t, data); - el.style.display = 'block'; - } else { - el.style.display = 'none'; - } - }; - - SmoothieChart.prototype.mousemove = function (evt) { - this.mouseover = true; - this.mouseX = evt.offsetX; - this.mouseY = evt.offsetY; - this.mousePageX = evt.pageX; - this.mousePageY = evt.pageY; - - var el = this.getTooltipEl(); - el.style.top = Math.round(this.mousePageY) + 'px'; - el.style.left = Math.round(this.mousePageX) + 'px'; - this.updateTooltip(); - }; - - SmoothieChart.prototype.mouseout = function () { - this.mouseover = false; - this.mouseX = this.mouseY = -1; - if (SmoothieChart.tooltipEl) - SmoothieChart.tooltipEl.style.display = 'none'; - }; - - /** - * Make sure the canvas has the optimal resolution for the device's pixel ratio. - */ - SmoothieChart.prototype.resize = function () { - var dpr = !this.options.enableDpiScaling || !window ? 1 : window.devicePixelRatio, - width, height; - if (this.options.responsive) { - // Newer behaviour: Use the canvas's size in the layout, and set the internal - // resolution according to that size and the device pixel ratio (eg: high DPI) - width = this.canvas.offsetWidth; - height = this.canvas.offsetHeight; - - if (width !== this.lastWidth) { - this.lastWidth = width; - this.canvas.setAttribute("width", width.toString()) - //this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString()); - } - if (height !== this.lastHeight) { - this.lastHeight = height; - this.canvas.setAttribute("height", height.toString()) - //this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString()); - } - } else if (dpr !== 1) { - // Older behaviour: use the canvas's inner dimensions and scale the element's size - // according to that size and the device pixel ratio (eg: high DPI) - width = parseInt(this.canvas.getAttribute('width')); - height = parseInt(this.canvas.getAttribute('height')); - - if (!this.originalWidth || (Math.floor(this.originalWidth * dpr) !== width)) { - this.originalWidth = width; - this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString()); - this.canvas.style.width = width + 'px'; - this.canvas.getContext('2d').scale(dpr, dpr); - } - - if (!this.originalHeight || (Math.floor(this.originalHeight * dpr) !== height)) { - this.originalHeight = height; - this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString()); - this.canvas.style.height = height + 'px'; - this.canvas.getContext('2d').scale(dpr, dpr); - } - } - }; - - /** - * Starts the animation of this chart. - */ - SmoothieChart.prototype.start = function() { - if (this.frame) { - // We're already running, so just return - return; - } - - this.canvas.addEventListener('mousemove', this.mousemove); - this.canvas.addEventListener('mouseout', this.mouseout); - - // Renders a frame, and queues the next frame for later rendering - var animate = function() { - this.frame = SmoothieChart.AnimateCompatibility.requestAnimationFrame(function() { - this.render(); - animate(); - }.bind(this)); - }.bind(this); - - animate(); - }; - - /** - * Stops the animation of this chart. - */ - SmoothieChart.prototype.stop = function() { - if (this.frame) { - SmoothieChart.AnimateCompatibility.cancelAnimationFrame(this.frame); - delete this.frame; - this.canvas.removeEventListener('mousemove', this.mousemove); - this.canvas.removeEventListener('mouseout', this.mouseout); - } - }; - - SmoothieChart.prototype.updateValueRange = function() { - // Calculate the current scale of the chart, from all time series. - var chartOptions = this.options, - chartMaxValue = Number.NaN, - chartMinValue = Number.NaN; - - for (var d = 0; d < this.seriesSet.length; d++) { - // TODO(ndunn): We could calculate / track these values as they stream in. - var timeSeries = this.seriesSet[d].timeSeries; - if (!isNaN(timeSeries.maxValue)) { - chartMaxValue = !isNaN(chartMaxValue) ? Math.max(chartMaxValue, timeSeries.maxValue) : timeSeries.maxValue; - } - - if (!isNaN(timeSeries.minValue)) { - chartMinValue = !isNaN(chartMinValue) ? Math.min(chartMinValue, timeSeries.minValue) : timeSeries.minValue; - } - } - - // Scale the chartMaxValue to add padding at the top if required - if (chartOptions.maxValue != null) { - chartMaxValue = chartOptions.maxValue; - } else { - chartMaxValue *= chartOptions.maxValueScale; - } - - // Set the minimum if we've specified one - if (chartOptions.minValue != null) { - chartMinValue = chartOptions.minValue; - } else { - chartMinValue -= Math.abs(chartMinValue * chartOptions.minValueScale - chartMinValue); - } - - // If a custom range function is set, call it - if (this.options.yRangeFunction) { - var range = this.options.yRangeFunction({min: chartMinValue, max: chartMaxValue}); - chartMinValue = range.min; - chartMaxValue = range.max; - } - - if (!isNaN(chartMaxValue) && !isNaN(chartMinValue)) { - var targetValueRange = chartMaxValue - chartMinValue; - var valueRangeDiff = (targetValueRange - this.currentValueRange); - var minValueDiff = (chartMinValue - this.currentVisMinValue); - this.isAnimatingScale = Math.abs(valueRangeDiff) > 0.1 || Math.abs(minValueDiff) > 0.1; - this.currentValueRange += chartOptions.scaleSmoothing * valueRangeDiff; - this.currentVisMinValue += chartOptions.scaleSmoothing * minValueDiff; - } - - this.valueRange = { min: chartMinValue, max: chartMaxValue }; - }; - - SmoothieChart.prototype.render = function(canvas, time) { - var nowMillis = new Date().getTime(); - - // Respect any frame rate limit. - if (this.options.limitFPS > 0 && nowMillis - this.lastRenderTimeMillis < (1000/this.options.limitFPS)) - return; - - if (!this.isAnimatingScale) { - // We're not animating. We can use the last render time and the scroll speed to work out whether - // we actually need to paint anything yet. If not, we can return immediately. - - // Render at least every 1/6th of a second. The canvas may be resized, which there is - // no reliable way to detect. - var maxIdleMillis = Math.min(1000/6, this.options.millisPerPixel); - - if (nowMillis - this.lastRenderTimeMillis < maxIdleMillis) { - return; - } - } - - this.resize(); - this.updateTooltip(); - - this.lastRenderTimeMillis = nowMillis; - - canvas = canvas || this.canvas; - time = time || nowMillis - (this.delay || 0); - - // Round time down to pixel granularity, so motion appears smoother. - time -= time % this.options.millisPerPixel; - - var context = canvas.getContext('2d'), - chartOptions = this.options, - dimensions = { top: 0, left: 0, width: canvas.clientWidth, height: canvas.clientHeight }, - // Calculate the threshold time for the oldest data points. - oldestValidTime = time - (dimensions.width * chartOptions.millisPerPixel), - valueToYPixel = function(value) { - var offset = value - this.currentVisMinValue; - return this.currentValueRange === 0 - ? dimensions.height - : dimensions.height - (Math.round((offset / this.currentValueRange) * dimensions.height)); - }.bind(this), - timeToXPixel = function(t) { - if(chartOptions.scrollBackwards) { - return Math.round((time - t) / chartOptions.millisPerPixel); - } - return Math.round(dimensions.width - ((time - t) / chartOptions.millisPerPixel)); - }; - - this.updateValueRange(); - - context.font = chartOptions.labels.fontSize + 'px ' + chartOptions.labels.fontFamily; - - // Save the state of the canvas context, any transformations applied in this method - // will get removed from the stack at the end of this method when .restore() is called. - context.save(); - - // Move the origin. - context.translate(dimensions.left, dimensions.top); - - // Create a clipped rectangle - anything we draw will be constrained to this rectangle. - // This prevents the occasional pixels from curves near the edges overrunning and creating - // screen cheese (that phrase should need no explanation). - context.beginPath(); - context.rect(0, 0, dimensions.width, dimensions.height); - context.clip(); - - // Clear the working area. - context.save(); - context.fillStyle = chartOptions.grid.fillStyle; - context.clearRect(0, 0, dimensions.width, dimensions.height); - context.fillRect(0, 0, dimensions.width, dimensions.height); - context.restore(); - - // Grid lines... - context.save(); - context.lineWidth = chartOptions.grid.lineWidth; - context.strokeStyle = chartOptions.grid.strokeStyle; - // Vertical (time) dividers. - if (chartOptions.grid.millisPerLine > 0) { - context.beginPath(); - for (var t = time - (time % chartOptions.grid.millisPerLine); - t >= oldestValidTime; - t -= chartOptions.grid.millisPerLine) { - var gx = timeToXPixel(t); - if (chartOptions.grid.sharpLines) { - gx -= 0.5; - } - context.moveTo(gx, 0); - context.lineTo(gx, dimensions.height); - } - context.stroke(); - context.closePath(); - } - - // Horizontal (value) dividers. - for (var v = 1; v < chartOptions.grid.verticalSections; v++) { - var gy = Math.round(v * dimensions.height / chartOptions.grid.verticalSections); - if (chartOptions.grid.sharpLines) { - gy -= 0.5; - } - context.beginPath(); - context.moveTo(0, gy); - context.lineTo(dimensions.width, gy); - context.stroke(); - context.closePath(); - } - // Bounding rectangle. - if (chartOptions.grid.borderVisible) { - context.beginPath(); - context.strokeRect(0, 0, dimensions.width, dimensions.height); - context.closePath(); - } - context.restore(); - - // Draw any horizontal lines... - if (chartOptions.horizontalLines && chartOptions.horizontalLines.length) { - for (var hl = 0; hl < chartOptions.horizontalLines.length; hl++) { - var line = chartOptions.horizontalLines[hl], - hly = Math.round(valueToYPixel(line.value)) - 0.5; - context.strokeStyle = line.color || '#ffffff'; - context.lineWidth = line.lineWidth || 1; - context.beginPath(); - context.moveTo(0, hly); - context.lineTo(dimensions.width, hly); - context.stroke(); - context.closePath(); - } - } - - // For each data set... - for (var d = 0; d < this.seriesSet.length; d++) { - context.save(); - var timeSeries = this.seriesSet[d].timeSeries, - dataSet = timeSeries.data, - seriesOptions = this.seriesSet[d].options; - - // Delete old data that's moved off the left of the chart. - timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength); - - // Set style for this dataSet. - context.lineWidth = seriesOptions.lineWidth; - context.strokeStyle = seriesOptions.strokeStyle; - // Draw the line... - context.beginPath(); - // Retain lastX, lastY for calculating the control points of bezier curves. - var firstX = 0, lastX = 0, lastY = 0; - for (var i = 0; i < dataSet.length && dataSet.length !== 1; i++) { - var x = timeToXPixel(dataSet[i][0]), - y = valueToYPixel(dataSet[i][1]); - - if (i === 0) { - firstX = x; - context.moveTo(x, y); - } else { - switch (chartOptions.interpolation) { - case "linear": - case "line": { - context.lineTo(x,y); - break; - } - case "bezier": - default: { - // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves - // - // Assuming A was the last point in the line plotted and B is the new point, - // we draw a curve with control points P and Q as below. - // - // A---P - // | - // | - // | - // Q---B - // - // Importantly, A and P are at the same y coordinate, as are B and Q. This is - // so adjacent curves appear to flow as one. - // - context.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop - Math.round((lastX + x) / 2), lastY, // controlPoint1 (P) - Math.round((lastX + x)) / 2, y, // controlPoint2 (Q) - x, y); // endPoint (B) - break; - } - case "step": { - context.lineTo(x,lastY); - context.lineTo(x,y); - break; - } - } - } - - lastX = x; lastY = y; - } - - if (dataSet.length > 1) { - if (seriesOptions.fillStyle) { - // Close up the fill region. - context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY); - context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1); - context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); - context.fillStyle = seriesOptions.fillStyle; - context.fill(); - } - - if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') { - context.stroke(); - } - context.closePath(); - } - context.restore(); - } - - if (chartOptions.tooltip && this.mouseX >= 0) { - // Draw vertical bar to show tooltip position - context.lineWidth = chartOptions.tooltipLine.lineWidth; - context.strokeStyle = chartOptions.tooltipLine.strokeStyle; - context.beginPath(); - context.moveTo(this.mouseX, 0); - context.lineTo(this.mouseX, dimensions.height); - context.closePath(); - context.stroke(); - this.updateTooltip(); - } - - // Draw the axis values on the chart. - if (!chartOptions.labels.disabled && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)) { - var maxValueString = chartOptions.yMaxFormatter(this.valueRange.max, chartOptions.labels.precision), - minValueString = chartOptions.yMinFormatter(this.valueRange.min, chartOptions.labels.precision), - maxLabelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(maxValueString).width - 5, - minLabelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(minValueString).width - 5; - context.fillStyle = chartOptions.labels.fillStyle; - context.fillText(maxValueString, maxLabelPos, chartOptions.labels.fontSize); - context.fillText(minValueString, minLabelPos, dimensions.height - 2); - } - - // Display timestamps along x-axis at the bottom of the chart. - if (chartOptions.timestampFormatter && chartOptions.grid.millisPerLine > 0) { - var textUntilX = chartOptions.scrollBackwards - ? context.measureText(minValueString).width - : dimensions.width - context.measureText(minValueString).width + 4; - for (var t = time - (time % chartOptions.grid.millisPerLine); - t >= oldestValidTime; - t -= chartOptions.grid.millisPerLine) { - var gx = timeToXPixel(t); - // Only draw the timestamp if it won't overlap with the previously drawn one. - if ((!chartOptions.scrollBackwards && gx < textUntilX) || (chartOptions.scrollBackwards && gx > textUntilX)) { - // Formats the timestamp based on user specified formatting function - // SmoothieChart.timeFormatter function above is one such formatting option - var tx = new Date(t), - ts = chartOptions.timestampFormatter(tx), - tsWidth = context.measureText(ts).width; - - textUntilX = chartOptions.scrollBackwards - ? gx + tsWidth + 2 - : gx - tsWidth - 2; - - context.fillStyle = chartOptions.labels.fillStyle; - if(chartOptions.scrollBackwards) { - context.fillText(ts, gx, dimensions.height - 2); - } else { - context.fillText(ts, gx - tsWidth, dimensions.height - 2); - } - } - } - } - - context.restore(); // See .save() above. - }; - - // Sample timestamp formatting function - SmoothieChart.timeFormatter = function(date) { - function pad2(number) { return (number < 10 ? '0' : '') + number } - return pad2(date.getHours()) + ':' + pad2(date.getMinutes()) + ':' + pad2(date.getSeconds()); - }; - - exports.TimeSeries = TimeSeries; - exports.SmoothieChart = SmoothieChart; - - })(typeof exports === 'undefined' ? this : exports); \ No newline at end of file diff --git a/webapp/public/smoothie/smoothie_compressed.js b/webapp/public/smoothie/smoothie_compressed.js index 1d99c2ee5b93..cd329dd34330 100644 --- a/webapp/public/smoothie/smoothie_compressed.js +++ b/webapp/public/smoothie/smoothie_compressed.js @@ -1,82 +1,27 @@ -// MIT License: -// -// Copyright (c) 2010-2013, Joe Walnes -// 2013-2017, Drew Noakes -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - +!function(t){ /** - * Smoothie Charts - http://smoothiecharts.org/ - * (c) 2010-2013, Joe Walnes - * 2013-2017, Drew Noakes + * @license + * MIT License: * - * v1.0: Main charting library, by Joe Walnes - * v1.1: Auto scaling of axis, by Neil Dunn - * v1.2: fps (frames per second) option, by Mathias Petterson - * v1.3: Fix for divide by zero, by Paul Nikitochkin - * v1.4: Set minimum, top-scale padding, remove timeseries, add optional timer to reset bounds, by Kelley Reynolds - * v1.5: Set default frames per second to 50... smoother. - * .start(), .stop() methods for conserving CPU, by Dmitry Vyal - * options.interpolation = 'bezier' or 'line', by Dmitry Vyal - * options.maxValue to fix scale, by Dmitry Vyal - * v1.6: minValue/maxValue will always get converted to floats, by Przemek Matylla - * v1.7: options.grid.fillStyle may be a transparent color, by Dmitry A. Shashkin - * Smooth rescaling, by Kostas Michalopoulos - * v1.8: Set max length to customize number of live points in the dataset with options.maxDataSetLength, by Krishna Narni - * v1.9: Display timestamps along the bottom, by Nick and Stev-io - * (https://groups.google.com/forum/?fromgroups#!topic/smoothie-charts/-Ywse8FCpKI%5B1-25%5D) - * Refactored by Krishna Narni, to support timestamp formatting function - * v1.10: Switch to requestAnimationFrame, removed the now obsoleted options.fps, by Gergely Imreh - * v1.11: options.grid.sharpLines option added, by @drewnoakes - * Addressed warning seen in Firefox when seriesOption.fillStyle undefined, by @drewnoakes - * v1.12: Support for horizontalLines added, by @drewnoakes - * Support for yRangeFunction callback added, by @drewnoakes - * v1.13: Fixed typo (#32), by @alnikitich - * v1.14: Timer cleared when last TimeSeries removed (#23), by @davidgaleano - * Fixed diagonal line on chart at start/end of data stream, by @drewnoakes - * v1.15: Support for npm package (#18), by @dominictarr - * Fixed broken removeTimeSeries function (#24) by @davidgaleano - * Minor performance and tidying, by @drewnoakes - * v1.16: Bug fix introduced in v1.14 relating to timer creation/clearance (#23), by @drewnoakes - * TimeSeries.append now deals with out-of-order timestamps, and can merge duplicates, by @zacwitte (#12) - * Documentation and some local variable renaming for clarity, by @drewnoakes - * v1.17: Allow control over font size (#10), by @drewnoakes - * Timestamp text won't overlap, by @drewnoakes - * v1.18: Allow control of max/min label precision, by @drewnoakes - * Added 'borderVisible' chart option, by @drewnoakes - * Allow drawing series with fill but no stroke (line), by @drewnoakes - * v1.19: Avoid unnecessary repaints, and fixed flicker in old browsers having multiple charts in document (#40), by @asbai - * v1.20: Add SmoothieChart.getTimeSeriesOptions and SmoothieChart.bringToFront functions, by @drewnoakes - * v1.21: Add 'step' interpolation mode, by @drewnoakes - * v1.22: Add support for different pixel ratios. Also add optional y limit formatters, by @copacetic - * v1.23: Fix bug introduced in v1.22 (#44), by @drewnoakes - * v1.24: Fix bug introduced in v1.23, re-adding parseFloat to y-axis formatter defaults, by @siggy_sf - * v1.25: Fix bug seen when adding a data point to TimeSeries which is older than the current data, by @Nking92 - * Draw time labels on top of series, by @comolosabia - * Add TimeSeries.clear function, by @drewnoakes - * v1.26: Add support for resizing on high device pixel ratio screens, by @copacetic - * v1.27: Fix bug introduced in v1.26 for non whole number devicePixelRatio values, by @zmbush - * v1.28: Add 'minValueScale' option, by @megawac - * Fix 'labelPos' for different size of 'minValueString' 'maxValueString', by @henryn - * v1.29: Support responsive sizing, by @drewnoakes - * v1.29.1: Include types in package, and make property optional, by @TrentHouliston - * v1.30: Fix inverted logic in devicePixelRatio support, by @scanlime - * v1.31: Support tooltips, by @Sly1024 and @drewnoakes - * v1.32: Support frame rate limit, by @dpuyosa - */!function(t){function e(t){this.options=s.extend({},e.defaultOptions,t),this.clear()}function i(t){this.options=s.extend({},i.defaultChartOptions,t),this.seriesSet=[],this.currentValueRange=1,this.currentVisMinValue=0,this.lastRenderTimeMillis=0,this.mousemove=this.mousemove.bind(this),this.mouseout=this.mouseout.bind(this)}var s={extend:function(){arguments[0]=arguments[0]||{};for(var t=1;ti;){var a=i+s>>1;ethis.maxValue&&(this.maxValue=e),e=0&&this.data[s][0]>t;)s--;-1===s?this.data.splice(0,0,[t,e]):this.data.length>0&&this.data[s][0]===t?i?(this.data[s][1]+=e,e=this.data[s][1]):this.data[s][1]=e:s=e&&this.data[i+1][0]'+this.options.yMaxFormatter(e[o].value,this.options.labels.precision)+"
    ");return a.join("
    ")},i.defaultChartOptions={millisPerPixel:20,enableDpiScaling:!0,yMinFormatter:function(t,e){return parseFloat(t).toFixed(e)},yMaxFormatter:function(t,e){return parseFloat(t).toFixed(e)},maxValueScale:1,minValueScale:1,interpolation:"bezier",scaleSmoothing:.125,maxDataSetLength:2,scrollBackwards:!1,grid:{fillStyle:"#000000",strokeStyle:"#777777",lineWidth:1,sharpLines:!1,millisPerLine:1e3,verticalSections:2,borderVisible:!0},labels:{fillStyle:"#ffffff",disabled:!1,fontSize:10,fontFamily:"monospace",precision:2},horizontalLines:[],tooltip:!1,tooltipLine:{lineWidth:1,strokeStyle:"#BBBBBB"},tooltipFormatter:i.tooltipFormatter,responsive:!1,limitFPS:0},i.AnimateCompatibility=function(){var t=function(t,e){var i=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(function(){t((new Date).getTime())},16)};return i.call(window,t,e)},e=function(t){var e=window.cancelAnimationFrame||function(t){clearTimeout(t)};return e.call(window,t)};return{requestAnimationFrame:t,cancelAnimationFrame:e}}(),i.defaultSeriesPresentationOptions={lineWidth:1,strokeStyle:"#ffffff"},i.prototype.addTimeSeries=function(t,e){this.seriesSet.push({timeSeries:t,options:s.extend({},i.defaultSeriesPresentationOptions,e)}),t.options.resetBounds&&t.options.resetBoundsInterval>0&&(t.resetBoundsTimerId=setInterval(function(){t.resetBounds()},t.options.resetBoundsInterval))},i.prototype.removeTimeSeries=function(t){for(var e=this.seriesSet.length,i=0;e>i;i++)if(this.seriesSet[i].timeSeries===t){this.seriesSet.splice(i,1);break}t.resetBoundsTimerId&&clearInterval(t.resetBoundsTimerId)},i.prototype.getTimeSeriesOptions=function(t){for(var e=this.seriesSet.length,i=0;e>i;i++)if(this.seriesSet[i].timeSeries===t)return this.seriesSet[i].options},i.prototype.bringToFront=function(t){for(var e=this.seriesSet.length,i=0;e>i;i++)if(this.seriesSet[i].timeSeries===t){var s=this.seriesSet.splice(i,1);this.seriesSet.push(s[0]);break}},i.prototype.streamTo=function(t,e){this.canvas=t,this.delay=e,this.start()},i.prototype.getTooltipEl=function(){var t=i.tooltipEl;return t||(t=i.tooltipEl=document.createElement("div"),t.className="smoothie-chart-tooltip",t.style.position="absolute",t.style.display="none",document.body.appendChild(t)),t},i.prototype.updateTooltip=function(){var t=this.getTooltipEl();if(!this.mouseover||!this.options.tooltip)return void(t.style.display="none");var e=this.lastRenderTimeMillis-(this.delay||0);e-=e%this.options.millisPerPixel;for(var i=this.options.scrollBackwards?e-this.mouseX*this.options.millisPerPixel:e-(this.canvas.offsetWidth-this.mouseX)*this.options.millisPerPixel,a=[],o=0;o0&&r.1||Math.abs(l)>.1,this.currentValueRange+=t.scaleSmoothing*r,this.currentVisMinValue+=t.scaleSmoothing*l}this.valueRange={min:i,max:e}},i.prototype.render=function(t,e){var i=(new Date).getTime();if(!(this.options.limitFPS>0&&i-this.lastRenderTimeMillis<1e3/this.options.limitFPS)){if(!this.isAnimatingScale){var s=Math.min(1e3/6,this.options.millisPerPixel);if(i-this.lastRenderTimeMillis0){a.beginPath();for(var u=e-e%o.grid.millisPerLine;u>=r;u-=o.grid.millisPerLine){var m=h(u);o.grid.sharpLines&&(m-=.5),a.moveTo(m,0),a.lineTo(m,n.height)}a.stroke(),a.closePath()}for(var d=1;d1&&(b.fillStyle&&(a.lineTo(n.width+b.lineWidth+1,T),a.lineTo(n.width+b.lineWidth+1,n.height+b.lineWidth+1),a.lineTo(x,n.height+b.lineWidth),a.fillStyle=b.fillStyle,a.fill()),b.strokeStyle&&"none"!==b.strokeStyle&&a.stroke(),a.closePath()),a.restore()}if(o.tooltip&&this.mouseX>=0&&(a.lineWidth=o.tooltipLine.lineWidth,a.strokeStyle=o.tooltipLine.strokeStyle,a.beginPath(),a.moveTo(this.mouseX,0),a.lineTo(this.mouseX,n.height),a.closePath(),a.stroke(),this.updateTooltip()),!o.labels.disabled&&!isNaN(this.valueRange.min)&&!isNaN(this.valueRange.max)){var M=o.yMaxFormatter(this.valueRange.max,o.labels.precision),F=o.yMinFormatter(this.valueRange.min,o.labels.precision),k=o.scrollBackwards?0:n.width-a.measureText(M).width-5,R=o.scrollBackwards?0:n.width-a.measureText(F).width-5;a.fillStyle=o.labels.fillStyle,a.fillText(M,k,o.labels.fontSize),a.fillText(F,R,n.height-2)}if(o.timestampFormatter&&o.grid.millisPerLine>0)for(var B=o.scrollBackwards?a.measureText(F).width:n.width-a.measureText(F).width+4,u=e-e%o.grid.millisPerLine;u>=r;u-=o.grid.millisPerLine){var m=h(u);if(!o.scrollBackwards&&B>m||o.scrollBackwards&&m>B){var L=new Date(u),W=o.timestampFormatter(L),A=a.measureText(W).width;B=o.scrollBackwards?m+A+2:m-A-2,a.fillStyle=o.labels.fillStyle,o.scrollBackwards?a.fillText(W,m,n.height-2):a.fillText(W,m-A,n.height-2)}}a.restore()}},i.timeFormatter=function(t){function e(t){return(10>t?"0":"")+t}return e(t.getHours())+":"+e(t.getMinutes())+":"+e(t.getSeconds())},t.TimeSeries=e,t.SmoothieChart=i}("undefined"==typeof exports?this:exports); \ No newline at end of file + * Copyright (c) 2010-2013, Joe Walnes + * 2013-2018, Drew Noakes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +Date.now=Date.now||function(){return(new Date).getTime()};var e={extend:function(){arguments[0]=arguments[0]||{};for(var t=1;t>1;ethis.maxValue&&(this.maxValue=e),e=0)for(var a=s;;){var n=this.data[a];if(t>=n[0]){t===n[0]?i?(n[1]+=e,e=n[1]):n[1]=e:this.data.splice(a+1,0,[t,e]);break}if(--a<0){this.data.splice(0,0,[t,e]);break}}else this.data.push([t,e]);this.maxValue=isNaN(this.maxValue)?e:Math.max(this.maxValue,e),this.minValue=isNaN(this.minValue)?e:Math.min(this.minValue,e)}},i.prototype.dropOldData=function(t,e){for(var i=0;this.data.length-i>=e&&this.data[i+1][0]0&&(t.resetBoundsTimerId=setInterval(function(){t.resetBounds()},t.options.resetBoundsInterval))},s.prototype.removeTimeSeries=function(t){for(var e=this.seriesSet.length,i=0;i0&&r=0?s:0)<=i.length-1?s:i.length-1,i&&i.length>0){var a=i[s][0];t=t>a?t:a}return t}.bind(this),e);this.render(this.canvas,i>e?i:null)}else this.render();t()}.bind(this))}.bind(this);t()}},s.prototype.stop=function(){this.frame&&(s.AnimateCompatibility.cancelAnimationFrame(this.frame),delete this.frame,this.canvas.removeEventListener("mousemove",this.mousemove),this.canvas.removeEventListener("mouseout",this.mouseout))},s.prototype.updateValueRange=function(){for(var t=this.options,e=Number.NaN,i=Number.NaN,s=0;s.1||Math.abs(r)>.1,this.currentValueRange+=t.scaleSmoothing*o,this.currentVisMinValue+=t.scaleSmoothing*r}this.valueRange={min:i,max:e}},s.prototype.render=function(t,i){var s=Date.now();if(!(this.options.limitFPS>0&&s-this.lastRenderTimeMillis<1e3/this.options.limitFPS)){if(i=(i||s)-(this.delay||0),i-=i%this.options.millisPerPixel,!this.isAnimatingScale)if(this.lastChartTimestamp===i)if(!(s-this.lastRenderTimeMillis>1e3/6))return;this.lastRenderTimeMillis=s,this.lastChartTimestamp=i,this.resize();var a=(t=t||this.canvas).getContext("2d"),n=this.options,o={top:0,left:0,width:this.clientWidth,height:this.clientHeight},r=i-o.width*n.millisPerPixel,l=function(t,i){var s=t-this.currentVisMinValue,a=0===this.currentValueRange?o.height:o.height*(1-s/this.currentValueRange);return e.pixelSnap(a,i)}.bind(this),h=function(t,s){var a=n.scrollBackwards?(i-t)/n.millisPerPixel:o.width-(i-t)/n.millisPerPixel;return e.pixelSnap(a,s)};if(this.updateValueRange(),a.font=n.labels.fontSize+"px "+n.labels.fontFamily,a.save(),a.translate(o.left,o.top),a.beginPath(),a.rect(0,0,o.width,o.height),a.clip(),a.save(),a.fillStyle=n.grid.fillStyle,a.clearRect(0,0,o.width,o.height),a.fillRect(0,0,o.width,o.height),a.restore(),a.save(),a.lineWidth=n.grid.lineWidth,a.strokeStyle=n.grid.strokeStyle,n.grid.millisPerLine>0){a.beginPath();for(var m=i-i%n.grid.millisPerLine;m>=r;m-=n.grid.millisPerLine){var d=h(m,n.grid.lineWidth);a.moveTo(d,0),a.lineTo(d,o.height)}a.stroke(),a.closePath()}for(var u=1;u=0&&(a.lineWidth=n.tooltipLine.lineWidth,a.strokeStyle=n.tooltipLine.strokeStyle,a.beginPath(),a.moveTo(this.mouseX,0),a.lineTo(this.mouseX,o.height),a.closePath(),a.stroke()),this.updateTooltip();var L=n.labels;if(!L.disabled&&!isNaN(this.valueRange.min)&&!isNaN(this.valueRange.max)){var W=n.yMaxFormatter(this.valueRange.max,L.precision),E=n.yMinFormatter(this.valueRange.min,L.precision),C=n.scrollBackwards?0:o.width-a.measureText(W).width-2,D=n.scrollBackwards?0:o.width-a.measureText(E).width-2;a.fillStyle=L.fillStyle,a.fillText(W,C,L.fontSize),a.fillText(E,D,o.height-2)}if(L.showIntermediateLabels&&!isNaN(this.valueRange.min)&&!isNaN(this.valueRange.max)&&n.grid.verticalSections>0){var z=(this.valueRange.max-this.valueRange.min)/n.grid.verticalSections,I=o.height/n.grid.verticalSections;for(u=1;u0){var O=n.scrollBackwards?a.measureText(E).width:o.width-a.measureText(E).width+4;for(m=i-i%n.grid.millisPerLine;m>=r;m-=n.grid.millisPerLine){d=h(m,0);if(!n.scrollBackwards&&dO){var q=new Date(m),Y=n.timestampFormatter(q),j=a.measureText(Y).width;O=n.scrollBackwards?d+j+2:d-j-2,a.fillStyle=n.labels.fillStyle,n.scrollBackwards?a.fillText(Y,d,o.height-2):a.fillText(Y,d-j,o.height-2)}}}if(""!==n.title.text){a.font=n.title.fontSize+"px "+n.title.fontFamily;var G=n.scrollBackwards?o.width-a.measureText(n.title.text).width-2:2;if("bottom"==n.title.verticalAlign){a.textBaseline="bottom";var J=o.height}else if("middle"==n.title.verticalAlign){a.textBaseline="middle";J=o.height/2}else{a.textBaseline="top";J=0}a.fillStyle=n.title.fillStyle,a.fillText(n.title.text,G,J)}a.restore()}},s.timeFormatter=function(t){function e(t){return(t<10?"0":"")+t}return e(t.getHours())+":"+e(t.getMinutes())+":"+e(t.getSeconds())},t.TimeSeries=i,t.SmoothieChart=s}("undefined"==typeof exports?this:exports); \ No newline at end of file diff --git a/webapp/src/marked.tsx b/webapp/src/marked.tsx index cae64d386791..49331ba1d287 100644 --- a/webapp/src/marked.tsx +++ b/webapp/src/marked.tsx @@ -665,35 +665,42 @@ export class MarkedContent extends data.Component pubinfo[param] || 'unknown macro') // create a custom renderer - let renderer = new marked.Renderer() + const renderer = new marked.Renderer() pxt.docs.setupRenderer(renderer); // always popout links - const linkRenderer = renderer.link; - renderer.link = function (href: string, title: string, text: string) { - const html = linkRenderer.call(renderer, href, title, text); + const linkRenderer = renderer.link ? renderer.link.bind(renderer) : undefined; + renderer.link = function (this: any, token: marked.Tokens.Link) { + const html = linkRenderer + ? linkRenderer(token) + : `
    ${this.parser.parseInline(token.tokens)}`; return html.replace(/^ string) | undefined; + // preemptively remove script tags, although they'll be escaped anyway // prevents ugly