mirror of
https://github.com/glassflame/glassflame.github.io.git
synced 2024-11-22 16:14:26 +00:00
Complete refactoring?
This commit is contained in:
parent
610c849264
commit
803b61f434
12 changed files with 252 additions and 116 deletions
39
index.html
39
index.html
|
@ -24,12 +24,15 @@
|
||||||
--color-blue: #53DFDD;
|
--color-blue: #53DFDD;
|
||||||
--color-purple: #A882FF;
|
--color-purple: #A882FF;
|
||||||
|
|
||||||
|
--color-node: var(--color-gray);
|
||||||
|
|
||||||
--edge-width: 2px;
|
--edge-width: 2px;
|
||||||
--node-group-border-width: 2px;
|
--node-group-border-width: 2px;
|
||||||
--node-file-border-width: 2px;
|
--node-file-border-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
margin: 0;
|
||||||
padding: 128px;
|
padding: 128px;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
@ -37,11 +40,18 @@
|
||||||
background-color: var(--color-background);
|
background-color: var(--color-background);
|
||||||
color: var(--color-foreground);
|
color: var(--color-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg line {
|
||||||
|
stroke: var(--color-node);
|
||||||
|
stroke-width: var(--edge-width);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- Templates -->
|
<!-- Templates -->
|
||||||
<template id="template-vault">
|
<template id="template-vault">
|
||||||
<style>
|
<style>
|
||||||
|
.vault {
|
||||||
|
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="vault">
|
<div class="vault">
|
||||||
<slot name="vault-child"></slot>
|
<slot name="vault-child"></slot>
|
||||||
|
@ -57,19 +67,6 @@
|
||||||
width: max-content;
|
width: max-content;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas * {
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.canvas *:first-child {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.canvas *:not(:first-child) {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<div class="canvas">
|
<div class="canvas">
|
||||||
<slot name="canvas-edges">{Canvas edges}</slot>
|
<slot name="canvas-edges">{Canvas edges}</slot>
|
||||||
|
@ -159,18 +156,19 @@
|
||||||
</template>
|
</template>
|
||||||
<template id="template-edge">
|
<template id="template-edge">
|
||||||
<style>
|
<style>
|
||||||
.edge svg line {
|
.edge {
|
||||||
stroke: var(--color-node);
|
|
||||||
stroke-width: var(--edge-width);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="edge">
|
<div class="canvas-item edge">
|
||||||
<slot name="edge-svg">{Edge SVG}</slot>
|
<slot name="edge-svg">{Edge SVG}</slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template id="template-markdown">
|
<template id="template-markdown">
|
||||||
<style>
|
<style>
|
||||||
.markdown
|
.markdown {
|
||||||
|
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="markdown">
|
<div class="markdown">
|
||||||
<slot name="markdown-document">{Markdown text}</slot>
|
<slot name="markdown-document">{Markdown text}</slot>
|
||||||
|
@ -203,9 +201,8 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<a class="wikilink"><slot name="wikilink-anchor">{Wikilink text}</slot></a>
|
<a class="wikilink"></a>
|
||||||
</template>
|
</template>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body is="x-browse"></body>
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
|
@ -1,11 +0,0 @@
|
||||||
export function configFromURL(location) {
|
|
||||||
const queryString = new URLSearchParams(location.search)
|
|
||||||
const vref = queryString.get("vref")
|
|
||||||
const wref = queryString.get("wref")
|
|
||||||
|
|
||||||
return {vref, wref}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function configFromWindow() {
|
|
||||||
return configFromURL(window.location)
|
|
||||||
}
|
|
64
src/elements/browse.mjs
Normal file
64
src/elements/browse.mjs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* The body element for the pages viewer, handling most low-level things.
|
||||||
|
*/
|
||||||
|
export class BrowseElement extends HTMLBodyElement {
|
||||||
|
/**
|
||||||
|
* Parameters to be used to display *things*.
|
||||||
|
* @type {{vault: string, path: string, highlight: string}}
|
||||||
|
*/
|
||||||
|
parameters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculate the value of {@link parameters} using the current {@link window.location}.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
recalculateParameters() {
|
||||||
|
const location = window.location
|
||||||
|
const params = new URLSearchParams(location.search)
|
||||||
|
const vault = params.get("vault")
|
||||||
|
const path = params.get("path")
|
||||||
|
const highlight = location.hash.replace(/^#/, "")
|
||||||
|
this.parameters = {vault, path, highlight}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a landing page
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vault element, describing to its descendants how to handle various situations.
|
||||||
|
* @type {VaultElement}
|
||||||
|
*/
|
||||||
|
vaultElement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The display element showing the contents of the specified file.
|
||||||
|
* @type {DisplayElement}
|
||||||
|
*/
|
||||||
|
rootDisplayElement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreate all contents of this element to match the current value of {@link parameters}.
|
||||||
|
*/
|
||||||
|
recreateContents() {
|
||||||
|
if(this.vaultElement) {
|
||||||
|
this.vaultElement.remove()
|
||||||
|
this.vaultElement = null
|
||||||
|
this.rootDisplayElement = null
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vaultElement = document.createElement("x-vault")
|
||||||
|
this.vaultElement.base = this.parameters.vault
|
||||||
|
this.vaultElement.cooldownMs = 200
|
||||||
|
|
||||||
|
this.rootDisplayElement = document.createElement("x-display")
|
||||||
|
this.rootDisplayElement.path = this.parameters.path
|
||||||
|
this.rootDisplayElement.slot = "vault-child"
|
||||||
|
|
||||||
|
this.vaultElement.appendChild(this.rootDisplayElement)
|
||||||
|
this.appendChild(this.vaultElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.recalculateParameters()
|
||||||
|
this.recreateContents()
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,10 +40,10 @@ export class CanvasElement extends CustomElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the values of {@link document} and {@link parsedDocument} from the `document` attribute of the element.
|
* Update the values of {@link document} and {@link parsedDocument} from the `document` attribute of the element.
|
||||||
* @throws SyntaxError If `contents` is not valid JSON.
|
* @throws SyntaxError If `document` is not valid JSON.
|
||||||
*/
|
*/
|
||||||
recalculateContents() {
|
reparseDocument() {
|
||||||
this.#document = this.getAttribute("contents")
|
this.#document = this.getAttribute("document")
|
||||||
this.#parsedDocument = JSON.parse(this.#document)
|
this.#parsedDocument = JSON.parse(this.#document)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,31 +155,28 @@ export class CanvasElement extends CustomElement {
|
||||||
this.nodesContainer.slot = this.constructor.NODES_SLOT_NAME
|
this.nodesContainer.slot = this.constructor.NODES_SLOT_NAME
|
||||||
|
|
||||||
for(const node of this.parsedDocument["nodes"]) {
|
for(const node of this.parsedDocument["nodes"]) {
|
||||||
let {id, type, color, x, y, width, height} = node
|
const {id, type, color, x, y, width, height} = node
|
||||||
x, y, width, height = Number(x), Number(y), Number(width), Number(height)
|
const [nodeX, nodeY, nodeWidth, nodeHeight] = [Number(x), Number(y), Number(width), Number(height)]
|
||||||
|
|
||||||
const element = document.createElement(`${this.constructor.NODE_ELEMENT_NAME_PREFIX}${type}`)
|
const element = document.createElement(`${this.constructor.NODE_ELEMENT_NAME_PREFIX}${type}`)
|
||||||
|
element.x = nodeX - this.minX.x
|
||||||
element.setAttribute("id", `node-${id}`)
|
element.y = nodeY - this.minY.y
|
||||||
element.setAttribute("x", `${x - this.minX.x}`)
|
element.width = nodeWidth
|
||||||
element.setAttribute("y", `${y - this.minY.y}`)
|
element.height = nodeHeight
|
||||||
element.setAttribute("width", `${width}`)
|
if(color) element.obsidianColor = color
|
||||||
element.setAttribute("height", `${height}`)
|
|
||||||
if(color) element.setAttribute("color", color)
|
|
||||||
|
|
||||||
this.nodeElementsById[id] = element
|
this.nodeElementsById[id] = element
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case "text":
|
case "text":
|
||||||
const {text} = node
|
const {text} = node
|
||||||
element.setAttribute("text", text)
|
element.setAttribute("document", text)
|
||||||
break
|
break
|
||||||
|
|
||||||
case "file":
|
case "file":
|
||||||
const {file} = node
|
const {file} = node
|
||||||
const {name} = fileDetails(file)
|
const {name} = fileDetails(file)
|
||||||
element.setAttribute("file", file)
|
element.setAttribute("path", file)
|
||||||
element.setAttribute("file-name", name)
|
|
||||||
this.nodeElementsByPath[file] = element
|
this.nodeElementsByPath[file] = element
|
||||||
this.nodeElementsByName[name] = element
|
this.nodeElementsByName[name] = element
|
||||||
break
|
break
|
||||||
|
@ -199,6 +196,7 @@ export class CanvasElement extends CustomElement {
|
||||||
|
|
||||||
this.nodesContainer.style["width"] = `${this.maxX.x + this.maxX.width - this.minX.x}px`
|
this.nodesContainer.style["width"] = `${this.maxX.x + this.maxX.width - this.minX.x}px`
|
||||||
this.nodesContainer.style["height"] = `${this.maxY.y + this.maxY.height - this.minY.y}px`
|
this.nodesContainer.style["height"] = `${this.maxY.y + this.maxY.height - this.minY.y}px`
|
||||||
|
this.nodesContainer.style["position"] = "relative"
|
||||||
|
|
||||||
this.appendChild(this.nodesContainer)
|
this.appendChild(this.nodesContainer)
|
||||||
}
|
}
|
||||||
|
@ -219,7 +217,7 @@ export class CanvasElement extends CustomElement {
|
||||||
* Name of the slot where the edge container should be placed.
|
* Name of the slot where the edge container should be placed.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
static EDGES_SLOT_NAME = "canvas-nodes"
|
static EDGES_SLOT_NAME = "canvas-edges"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefix to the name of the element to create for each edge.
|
* Prefix to the name of the element to create for each edge.
|
||||||
|
@ -258,13 +256,14 @@ export class CanvasElement extends CustomElement {
|
||||||
|
|
||||||
this.edgesContainer.style["width"] = `${this.maxX.x + this.maxX.width - this.minX.x}px`
|
this.edgesContainer.style["width"] = `${this.maxX.x + this.maxX.width - this.minX.x}px`
|
||||||
this.edgesContainer.style["height"] = `${this.maxY.y + this.maxY.height - this.minY.y}px`
|
this.edgesContainer.style["height"] = `${this.maxY.y + this.maxY.height - this.minY.y}px`
|
||||||
|
this.edgesContainer.style["position"] = "absolute"
|
||||||
|
|
||||||
this.appendChild(this.edgesContainer)
|
this.appendChild(this.edgesContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnect() {
|
onConnect() {
|
||||||
super.onConnect()
|
super.onConnect()
|
||||||
this.recalculateContents()
|
this.reparseDocument()
|
||||||
this.recalculateMinMax()
|
this.recalculateMinMax()
|
||||||
this.recreateNodes()
|
this.recreateNodes()
|
||||||
this.recreateEdges()
|
this.recreateEdges()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { CustomElement } from "../base.mjs";
|
import { CustomElement } from "../base.mjs";
|
||||||
|
import {MalformedError} from "../../utils/errors.mjs";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +13,9 @@ export class CanvasItemElement extends CustomElement {
|
||||||
get x() {
|
get x() {
|
||||||
return Number(this.getAttribute("x"))
|
return Number(this.getAttribute("x"))
|
||||||
}
|
}
|
||||||
|
set x(value) {
|
||||||
|
this.setAttribute("x", value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {number} The Y coordinate of the top left vertex of this element.
|
* @returns {number} The Y coordinate of the top left vertex of this element.
|
||||||
|
@ -19,6 +23,9 @@ export class CanvasItemElement extends CustomElement {
|
||||||
get y() {
|
get y() {
|
||||||
return Number(this.getAttribute("y"))
|
return Number(this.getAttribute("y"))
|
||||||
}
|
}
|
||||||
|
set y(value) {
|
||||||
|
this.setAttribute("y", value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {number} The horizontal width of this element.
|
* @returns {number} The horizontal width of this element.
|
||||||
|
@ -26,6 +33,9 @@ export class CanvasItemElement extends CustomElement {
|
||||||
get width() {
|
get width() {
|
||||||
return Number(this.getAttribute("width"))
|
return Number(this.getAttribute("width"))
|
||||||
}
|
}
|
||||||
|
set width(value) {
|
||||||
|
this.setAttribute("width", value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {number} The vertical height of this element.
|
* @returns {number} The vertical height of this element.
|
||||||
|
@ -33,6 +43,9 @@ export class CanvasItemElement extends CustomElement {
|
||||||
get height() {
|
get height() {
|
||||||
return Number(this.getAttribute("height"))
|
return Number(this.getAttribute("height"))
|
||||||
}
|
}
|
||||||
|
set height(value) {
|
||||||
|
this.setAttribute("height", value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color of this element, as stored in Obsidian Canvas files.
|
* The color of this element, as stored in Obsidian Canvas files.
|
||||||
|
@ -48,6 +61,14 @@ export class CanvasItemElement extends CustomElement {
|
||||||
|
|
||||||
return color // Hex color specified
|
return color // Hex color specified
|
||||||
}
|
}
|
||||||
|
set obsidianColor(value) {
|
||||||
|
if(value === null) {
|
||||||
|
this.removeAttribute("color")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setAttribute("color", value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an Obsidian Canvas color, return its corresponding CSS color.
|
* Given an Obsidian Canvas color, return its corresponding CSS color.
|
||||||
|
@ -58,18 +79,23 @@ export class CanvasItemElement extends CustomElement {
|
||||||
if(color === null || color === "") {
|
if(color === null || color === "") {
|
||||||
return "var(--color-gray)"
|
return "var(--color-gray)"
|
||||||
}
|
}
|
||||||
else if(color.match(/^#[0-9A-F]{3}$|^#[0-9A-F]{6}$/i)) {
|
else if(typeof color === "string") {
|
||||||
|
if(color.match(/^#[0-9A-F]{3}$|^#[0-9A-F]{6}$/i)) {
|
||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
throw new MalformedError("String obisidianColor is not an hex code.")
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return {
|
return {
|
||||||
"0": "var(--color-gray)",
|
0: "var(--color-gray)",
|
||||||
"1": "var(--color-red)",
|
1: "var(--color-red)",
|
||||||
"2": "var(--color-orange)",
|
2: "var(--color-orange)",
|
||||||
"3": "var(--color-yellow)",
|
3: "var(--color-yellow)",
|
||||||
"4": "var(--color-green)",
|
4: "var(--color-green)",
|
||||||
"5": "var(--color-blue)",
|
5: "var(--color-blue)",
|
||||||
"6": "var(--color-purple)",
|
6: "var(--color-purple)",
|
||||||
}[color]
|
}[color]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +106,14 @@ export class CanvasItemElement extends CustomElement {
|
||||||
get cssColor() {
|
get cssColor() {
|
||||||
return this.constructor.obsidianColorToCssColor(this.obsidianColor)
|
return this.constructor.obsidianColorToCssColor(this.obsidianColor)
|
||||||
}
|
}
|
||||||
|
set cssColor(value) {
|
||||||
|
if(value === null) {
|
||||||
|
this.removeAttribute("color")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setAttribute("color", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CSS selector of the element in the template representing the canvas item.
|
* The CSS selector of the element in the template representing the canvas item.
|
||||||
|
@ -108,11 +142,11 @@ export class CanvasItemElement extends CustomElement {
|
||||||
resetCanvasItemCssProperties() {
|
resetCanvasItemCssProperties() {
|
||||||
this.canvasItemElement.style.setProperty("box-sizing", "border-box")
|
this.canvasItemElement.style.setProperty("box-sizing", "border-box")
|
||||||
this.canvasItemElement.style.setProperty("position", "absolute")
|
this.canvasItemElement.style.setProperty("position", "absolute")
|
||||||
this.canvasItemElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
this.canvasItemElement.style.setProperty("left", `${this.x}px`)
|
||||||
this.canvasItemElement.style.setProperty("top", `${this.getAttribute("y")}px`)
|
this.canvasItemElement.style.setProperty("top", `${this.y}px`)
|
||||||
this.canvasItemElement.style.setProperty("width", `${this.getAttribute("width")}px`)
|
this.canvasItemElement.style.setProperty("width", `${this.width}px`)
|
||||||
this.canvasItemElement.style.setProperty("height", `${this.getAttribute("height")}px`)
|
this.canvasItemElement.style.setProperty("height", `${this.height}px`)
|
||||||
this.canvasItemElement.style.setProperty("--color-node", this.constructor.obsidianColorToCssColor(this.getAttribute("color")))
|
this.canvasItemElement.style.setProperty("--color-node", this.cssColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnect() {
|
onConnect() {
|
||||||
|
|
|
@ -76,8 +76,7 @@ export class NodeFileElement extends NodeElement {
|
||||||
|
|
||||||
this.contentsElement = document.createElement("x-display")
|
this.contentsElement = document.createElement("x-display")
|
||||||
this.contentsElement.slot = this.constructor.CONTENTS_ELEMENT_SLOT
|
this.contentsElement.slot = this.constructor.CONTENTS_ELEMENT_SLOT
|
||||||
this.contentsElement.setAttribute("vault", findFirstAncestor(this, DisplayElement).vault) // TODO: Add a vault attribute to DisplayElement
|
this.contentsElement.path = this.pathRelativeToVault
|
||||||
this.contentsElement.setAttribute("path", this.pathRelativeToVault) // TODO: Add a path attribute to DisplayElement
|
|
||||||
this.appendChild(this.contentsElement)
|
this.appendChild(this.contentsElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,11 @@ export class DisplayElement extends CustomElement {
|
||||||
* Get the path or name of the file this node points to.
|
* Get the path or name of the file this node points to.
|
||||||
* @returns {string} The value in question.
|
* @returns {string} The value in question.
|
||||||
*/
|
*/
|
||||||
get target() {
|
get path() {
|
||||||
return this.getAttribute("target")
|
return this.getAttribute("path")
|
||||||
|
}
|
||||||
|
set path(value) {
|
||||||
|
return this.setAttribute("path", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,7 +87,7 @@ export class DisplayElement extends CustomElement {
|
||||||
this.contentsElement = null
|
this.contentsElement = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const {extension} = fileDetails(this.target)
|
const {extension} = fileDetails(this.path)
|
||||||
|
|
||||||
switch(extension) {
|
switch(extension) {
|
||||||
case "md":
|
case "md":
|
||||||
|
@ -100,30 +103,30 @@ export class DisplayElement extends CustomElement {
|
||||||
|
|
||||||
this.contentsElement.setAttribute("document", this.document)
|
this.contentsElement.setAttribute("document", this.document)
|
||||||
this.contentsElement.slot = this.constructor.CONTAINER_ELEMENT_SLOT
|
this.contentsElement.slot = this.constructor.CONTAINER_ELEMENT_SLOT
|
||||||
this.appendChild(this.loadingElement)
|
this.appendChild(this.contentsElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plaintext contents of the {@link target} document.
|
* The plaintext contents of the {@link path} document.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
document
|
document
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload the {@link target} {@link document}.
|
* Reload the {@link path} {@link document}.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async reloadDocument() {
|
async reloadDocument() {
|
||||||
const response = await this.vault.fetchCooldown(this.target)
|
const response = await this.vault.fetchCooldown(this.path)
|
||||||
// TODO: Add a check that the request was successful
|
// TODO: Add a check that the request was successful
|
||||||
this.document = await response.text()
|
this.document = await response.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnect() {
|
async onConnect() {
|
||||||
super.onConnect()
|
super.onConnect()
|
||||||
this.recalculateVault()
|
this.recalculateVault()
|
||||||
this.recreateLoadingElement()
|
this.recreateLoadingElement()
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
await this.reloadDocument()
|
||||||
this.reloadDocument().then(this.recreateContentsElement)
|
this.recreateContentsElement()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,3 +2,4 @@ export {CanvasElement, NodeFileElement, NodeGroupElement, NodeTextElement, EdgeE
|
||||||
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement} from "./markdown/index.mjs"
|
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement} from "./markdown/index.mjs"
|
||||||
export {DisplayElement} from "./display.mjs"
|
export {DisplayElement} from "./display.mjs"
|
||||||
export {VaultElement} from "./vault.mjs"
|
export {VaultElement} from "./vault.mjs"
|
||||||
|
export {BrowseElement} from "./browse.mjs"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { CustomElement } from "../base.mjs";
|
||||||
*/
|
*/
|
||||||
export class WikilinkElement extends CustomElement {
|
export class WikilinkElement extends CustomElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return document.getElementById("template-hashtag")
|
return document.getElementById("template-wikilink")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,8 +22,20 @@ export class WikilinkElement extends CustomElement {
|
||||||
* @returns {string} The text in question.
|
* @returns {string} The text in question.
|
||||||
*/
|
*/
|
||||||
get text() {
|
get text() {
|
||||||
return this.getAttribute("text") ?? this.target
|
// TODO: Dirty hack to hide "undefined"
|
||||||
|
const text = this.getAttribute("text")
|
||||||
|
// noinspection EqualityComparisonWithCoercionJS
|
||||||
|
if(text == "undefined") {
|
||||||
|
return this.target
|
||||||
}
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CSS selector of the anchor element.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static ANCHOR_SELECTOR = "a.wikilink"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The element displaying the wikilink.
|
* The element displaying the wikilink.
|
||||||
|
@ -33,30 +45,21 @@ export class WikilinkElement extends CustomElement {
|
||||||
anchorElement
|
anchorElement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the slot where {@link anchorElement} should be placed in.
|
Update the value of the {@link canvasItemElement} by querying the current {@link instance} with {@link ANCHOR_SELECTOR}.
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
static ANCHOR_ELEMENT_SLOT = "wikilink-anchor"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreate {@link anchorElement} with the current value of {@link target} and {@link text}.
|
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
recreateTagElement() {
|
recalculateAnchorElement() {
|
||||||
if(this.anchorElement) {
|
this.anchorElement = this.instance.querySelector(this.constructor.ANCHOR_SELECTOR)
|
||||||
this.anchorElement.remove()
|
|
||||||
this.anchorElement = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.anchorElement = document.createElement("a")
|
resetAnchorElementProperties() {
|
||||||
this.anchorElement.slot = this.constructor.ANCHOR_ELEMENT_SLOT
|
this.anchorElement.href = this.target
|
||||||
this.anchorElement.href = "#" // TODO: Add href behaviour to the anchor.
|
|
||||||
this.anchorElement.innerText = this.text
|
this.anchorElement.innerText = this.text
|
||||||
this.appendChild(this.anchorElement)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnect() {
|
onConnect() {
|
||||||
super.onConnect()
|
super.onConnect()
|
||||||
this.recreateTagElement()
|
this.recalculateAnchorElement()
|
||||||
|
this.resetAnchorElementProperties()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ export class VaultElement extends CustomElement {
|
||||||
get base() {
|
get base() {
|
||||||
return this.getAttribute("base")
|
return this.getAttribute("base")
|
||||||
}
|
}
|
||||||
|
set base(value) {
|
||||||
|
this.setAttribute("base", value)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link fetch} the file at the given path ignoring cooldowns.
|
* {@link fetch} the file at the given path ignoring cooldowns.
|
||||||
|
@ -32,33 +35,59 @@ export class VaultElement extends CustomElement {
|
||||||
* Cooldown between two {@link fetchCooldown} requests in milliseconds, as obtained from the `cooldown` parameter.
|
* Cooldown between two {@link fetchCooldown} requests in milliseconds, as obtained from the `cooldown` parameter.
|
||||||
*/
|
*/
|
||||||
get cooldownMs() {
|
get cooldownMs() {
|
||||||
return Number(this.getAttribute("cooldown"))
|
return Number(this.getAttribute("cooldown") ?? 5000)
|
||||||
|
}
|
||||||
|
set cooldownMs(value) {
|
||||||
|
this.setAttribute("cooldown", value.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue containing the `resolve` functions necessary to make the calls to {@link fetchCooldown} proceed beyond the waiting phase.
|
* FIFO queue of {@link fetch} promises to be awaited, with a {@link cooldownMs} pause between each of them.
|
||||||
* To be called by the preceding {@link fetchCooldown} if possible.
|
* @type {((v: undefined) => void)[]}
|
||||||
* @type {((v: unknown) => void)[]}
|
|
||||||
*/
|
*/
|
||||||
#fetchQueue = []
|
#fetchQueue = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise that can be called to resume {@link #fetchQueueScheduler} if {@link #fetchQueue} is no longer empty.
|
||||||
|
* @type {((v: undefined) => void)|null}
|
||||||
|
*/
|
||||||
|
#somethingInFetchQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Promise<void>} A Promise that will wait for this caller's turn in the {@link #fetchQueue}.
|
* @returns {Promise<void>} A Promise that will wait for this caller's turn in the {@link #fetchQueue}.
|
||||||
*/
|
*/
|
||||||
fetchQueueTurn() {
|
async fetchQueueTurn() {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.#fetchQueue.push(resolve)
|
this.#fetchQueue.push(resolve)
|
||||||
|
console.debug("[Fetch queue] Waiting for my turn...")
|
||||||
|
if(this.#somethingInFetchQueue !== null) {
|
||||||
|
console.debug("[Fetch queue] Asking scheduler to resume...")
|
||||||
|
this.#somethingInFetchQueue(undefined)
|
||||||
|
this.#somethingInFetchQueue = null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A promise that will advance the fetch queue after {@link cooldownMs}.
|
* Resolves promises in the {@link #fetchQueue} with a cooldown.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async #scheduleNextFetchQueueTurn() {
|
async #fetchQueueScheduler() {
|
||||||
|
while(this.isConnected) {
|
||||||
|
console.debug("[Fetch scheduler] Scheduler running one iteration...")
|
||||||
|
if(this.#fetchQueue.length === 0) {
|
||||||
|
const somethingInFetchQueue = new Promise(resolve => {
|
||||||
|
this.#somethingInFetchQueue = resolve
|
||||||
|
console.debug("[Fetch scheduler] Nothing to do, waiting...")
|
||||||
|
})
|
||||||
|
await somethingInFetchQueue
|
||||||
|
}
|
||||||
|
const promise = this.#fetchQueue.shift()
|
||||||
|
console.debug("[Fetch scheduler] Advancing...")
|
||||||
|
promise()
|
||||||
|
console.debug("[Fetch scheduler] Cooling down for:", this.cooldownMs)
|
||||||
await sleep(this.cooldownMs)
|
await sleep(this.cooldownMs)
|
||||||
const resolve = this.#fetchQueue.shift()
|
}
|
||||||
resolve()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,20 +96,19 @@ export class VaultElement extends CustomElement {
|
||||||
* @returns {Promise<Response>} The resulting HTTP response.
|
* @returns {Promise<Response>} The resulting HTTP response.
|
||||||
*/
|
*/
|
||||||
async fetchCooldown(path) {
|
async fetchCooldown(path) {
|
||||||
// Sit waiting in queue
|
// Await for this request's turn in the fetchQueue
|
||||||
if(this.#fetchQueue.length > 0) {
|
|
||||||
await this.fetchQueueTurn()
|
await this.fetchQueueTurn()
|
||||||
}
|
|
||||||
// Perform the request
|
// Perform the request
|
||||||
const result = await this.fetchImmediately(path)
|
const result = await this.fetchImmediately(path)
|
||||||
// Start the next item in queue
|
// Start the next item in queue
|
||||||
// noinspection ES6MissingAwait
|
|
||||||
this.#scheduleNextFetchQueueTurn()
|
|
||||||
// Return the request's result
|
// Return the request's result
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnect() {
|
onConnect() {
|
||||||
super.onConnect()
|
super.onConnect()
|
||||||
|
|
||||||
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
|
this.#fetchQueueScheduler()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,26 @@
|
||||||
import { CanvasElement, HashtagElement, NodeFileElement, WikilinkElement, DisplayElement, EdgeElement, NodeGroupElement, NodeTextElement, FrontMatterElement, MarkdownElement } from "./elements/index.mjs";
|
import {
|
||||||
|
CanvasElement,
|
||||||
|
HashtagElement,
|
||||||
|
NodeFileElement,
|
||||||
|
WikilinkElement,
|
||||||
|
DisplayElement,
|
||||||
|
EdgeElement,
|
||||||
|
NodeGroupElement,
|
||||||
|
NodeTextElement,
|
||||||
|
FrontMatterElement,
|
||||||
|
MarkdownElement,
|
||||||
|
VaultElement, BrowseElement
|
||||||
|
} from "./elements/index.mjs";
|
||||||
|
|
||||||
|
customElements.define("x-vault", VaultElement)
|
||||||
|
customElements.define("x-display", DisplayElement)
|
||||||
|
customElements.define("x-canvas", CanvasElement)
|
||||||
|
customElements.define("x-node-group", NodeGroupElement)
|
||||||
customElements.define("x-node-file", NodeFileElement)
|
customElements.define("x-node-file", NodeFileElement)
|
||||||
customElements.define("x-node-text", NodeTextElement)
|
customElements.define("x-node-text", NodeTextElement)
|
||||||
customElements.define("x-node-group", NodeGroupElement)
|
customElements.define("x-edge", EdgeElement)
|
||||||
customElements.define("x-markdown", MarkdownElement)
|
customElements.define("x-markdown", MarkdownElement)
|
||||||
customElements.define("x-frontmatter", FrontMatterElement)
|
customElements.define("x-frontmatter", FrontMatterElement)
|
||||||
customElements.define("x-wikilink", WikilinkElement)
|
|
||||||
customElements.define("x-hashtag", HashtagElement)
|
customElements.define("x-hashtag", HashtagElement)
|
||||||
customElements.define("x-canvas", CanvasElement)
|
customElements.define("x-wikilink", WikilinkElement)
|
||||||
customElements.define("x-display", DisplayElement)
|
customElements.define("x-browse", BrowseElement, {extends: "body"})
|
||||||
customElements.define("x-edge", EdgeElement)
|
|
||||||
|
|
|
@ -21,3 +21,8 @@ export class FetchError extends Error {
|
||||||
this.response = response
|
this.response = response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file is maliciously malformed.
|
||||||
|
*/
|
||||||
|
export class MalformedError extends Error {}
|
Loading…
Reference in a new issue