aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2026-01-07 10:01:08 +0100
committerRuben Beltran del Rio <jj@r.bdr.sh>2026-01-07 11:01:24 +0100
commitf842640e9d0d1c10d2a23fd93929d274ebd86ad1 (patch)
treec397909bd4b40be04b073316ee6a3166c326f3cc
parentf29b392c6b68b7db98f5d6921ea588b559435f35 (diff)
Improve thumbnail rendering and dialog loading, flatten CSS1.0.1
-rw-r--r--client/wmap.css55
-rw-r--r--package.json2
-rw-r--r--src/wmap.js128
3 files changed, 127 insertions, 58 deletions
diff --git a/client/wmap.css b/client/wmap.css
index 7f4cdfd..a524bcb 100644
--- a/client/wmap.css
+++ b/client/wmap.css
@@ -1,34 +1,27 @@
.wmap {
-
position: relative;
-
- .actions {
- background-color: #fff;
- opacity: 0;
- transition: all 300ms;
- position: absolute;
- bottom: 0;
- right: 0;
- padding: 0 2px;
-
- img {
- height: 18px;
- width: 18px;
- }
- }
-
- menu {
- list-style: none;
- padding: 0;
-
- li { display: inline-block; }
- }
-
- &:hover .actions {
- opacity: 1;
- }
-
- img {
- width: 100%;
- }
+}
+.wmap .actions {
+ background-color: #fff;
+ opacity: 0;
+ transition: all 300ms;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ padding: 0 2px;
+}
+.wmap .actions img {
+ height: 18px;
+ width: 18px;
+}
+.wmap menu {
+ list-style: none;
+ padding: 0;
+}
+.wmap menu li { display: inline-block; }
+.wmap:hover .actions {
+ opacity: 1;
+}
+.wmap img {
+ width: 100%;
}
diff --git a/package.json b/package.json
index fe98dc4..ab61c53 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wiki-plugin-wmap",
- "version": "1.0.0",
+ "version": "1.0.1",
"description": "Federated Wiki - Wmap Plugin",
"keywords": [
"wmap",
diff --git a/src/wmap.js b/src/wmap.js
index 2a179a7..4532368 100644
--- a/src/wmap.js
+++ b/src/wmap.js
@@ -1,11 +1,35 @@
import { parse } from "wmap-parser";
-import { renderToCanvas } from "wmap-renderer-canvas";
+import { renderToCanvas, StageType } from "wmap-renderer-canvas";
import DOMPurify from "isomorphic-dompurify";
const internals = {
kStyleSheet: "/plugins/wmap/wmap.css",
- kCanvasWidth: 1384,
- kCanvasHeight: 1884,
+ kDimensions: {
+ full: {
+ map: {
+ mapWidth: 1300,
+ mapHeight: 1000,
+ padding: 42,
+ },
+ fonts: {},
+ },
+ thumbnail: {
+ map: {
+ mapWidth: 403,
+ mapHeight: 310,
+ padding: 15,
+ vertexWidth: 8,
+ vertexHeight: 8,
+ arrowHeadSize: 3,
+ stageHeight: 50,
+ },
+ fonts: {
+ axisLabel: 8,
+ vertexLabel: 9,
+ note: 9,
+ },
+ },
+ },
// Returns HTML that shows a message in the wiki.
message(text) {
@@ -18,20 +42,35 @@ const internals = {
},
// Creates a wmap PNG from text.
- async renderImage(source) {
+ async renderImage(source, dimensions) {
const parsedText = parse(source);
const canvas = document.createElement("canvas");
- canvas.width = internals.kCanvasWidth;
- canvas.height = internals.kCanvasHeight;
+ canvas.width = dimensions.map.mapWidth + 2 * dimensions.map.padding;
+ canvas.height = dimensions.map.mapHeight + 2 * dimensions.map.padding;
- await renderToCanvas(parsedText, canvas);
+ // Further Improvements. The zoomed version could have a drop down
+ // to show the different StageTypes and allow one to see the image
+ // with the different types.
+ // This could also potentially be in the toolbar that appears
+ // on hover to also show this in the download. It requires some
+ // thought on what the right interaction model would be.
+ await renderToCanvas(parsedText, canvas, StageType.ACTIVITIES, {
+ theme: {
+ sizes: {
+ ...dimensions.map,
+ },
+ fonts: {
+ ...dimensions.fonts,
+ },
+ },
+ });
return canvas.toDataURL();
},
// Starts a download of the image.
download(filename, text) {
- var element = document.createElement("a");
+ const element = document.createElement("a");
element.setAttribute("href", text);
element.setAttribute("download", filename);
element.style.display = "none";
@@ -42,13 +81,20 @@ const internals = {
// Replaces the image in a window. Intended for use in a dialog.
replaceImage(window, imageData) {
- const $container = window.document.querySelector("main");
- $container.querySelectorAll("img").forEach((img) => img.remove());
+ try {
+ const $container = window.document.querySelector("main");
+ $container.querySelectorAll("img").forEach((img) => img.remove());
- const image = document.createElement("img");
- image.src = imageData;
- image.alt = "Wardley map rendered from source.";
- $container.appendChild(image);
+ const image = document.createElement("img");
+ image.src = imageData;
+ image.alt = "Wardley map rendered from source.";
+ $container.appendChild(image);
+ } catch (error) {
+ console.error(
+ "wmap: Could not replace image. The DOM wasn't ready!",
+ error,
+ );
+ }
},
};
@@ -57,7 +103,7 @@ const internals = {
*/
const emit = function emit($item) {
const hasStyleSheet = [...document.styleSheets].some((styleSheet) =>
- styleSheet.href.endsWith(internals.kStyleSheet),
+ styleSheet.href?.endsWith(internals.kStyleSheet),
);
if (!hasStyleSheet) {
@@ -76,7 +122,7 @@ const emit = function emit($item) {
* Bind to events on the rendered block. Called once per block.
* Convention is that double click opens the editor.
*
- * @param {JQuery} $item the HTML element representing hte block.
+ * @param {JQuery} $item the HTML element representing the block.
* @param {Object} item the configuration for that particular object.
*/
const bind = async function bind($item, item) {
@@ -84,7 +130,7 @@ const bind = async function bind($item, item) {
return wiki.textEditor($item, item);
});
- $item.on("click", "a", (event) => {
+ $item.on("click", "a", async (event) => {
const { currentTarget } = event;
const action = currentTarget.dataset?.action;
@@ -97,8 +143,12 @@ const bind = async function bind($item, item) {
switch (action) {
case "download":
{
+ let imageData = await internals.renderImage(
+ item.text,
+ internals.kDimensions.full,
+ );
const slug = $item.parents(".page").attr("id");
- internals.download(`${slug}.png`, item.png);
+ internals.download(`${slug}.png`, imageData);
}
break;
case "zoom":
@@ -110,12 +160,36 @@ const bind = async function bind($item, item) {
target,
"popup,height=600,width=800",
);
- if (dialog.location.pathname !== "/plugins/wmap/dialog/") {
- dialog.addEventListener("load", () => {
- internals.replaceImage(dialog, item.png);
- });
+
+ if (!dialog || dialog.closed) {
+ console.error("wmap: Failed to open dialog.");
+ return;
+ }
+
+ let imageData = await internals.renderImage(
+ item.text,
+ internals.kDimensions.full,
+ );
+
+ // We *MUST* check both readyState and href, because
+ // readyState will be complete when it first loads,
+ // and this will be an empty window.
+ if (
+ dialog.document.readyState === "complete" &&
+ dialog.document.location.href.includes(
+ "/plugins/wmap/dialog/",
+ )
+ ) {
+ internals.replaceImage(dialog, imageData);
} else {
- internals.replaceImage(dialog, item.png);
+ console.info("We not loaded");
+ dialog.addEventListener(
+ "load",
+ () => {
+ internals.replaceImage(dialog, imageData);
+ },
+ { once: true },
+ );
}
}
break;
@@ -123,10 +197,12 @@ const bind = async function bind($item, item) {
});
try {
- let imageData = await internals.renderImage(item.text);
- item.png = imageData;
+ let thumbnailData = await internals.renderImage(
+ item.text,
+ internals.kDimensions.thumbnail,
+ );
$item.find(".viewer").html(`
- <article class="wardley-map"><img src="${imageData}"</article>
+ <article class="wardley-map"><img src="${thumbnailData}"></article>
<nav class="actions">
<menu>
<li><a href="#" data-action="download" title="Download"><img width="18" height="18" alt="download" src='data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24" fill="grey"><g><rect fill="none" height="24" width="24"/></g><g><path d="M5,20h14v-2H5V20z M19,9h-4V3H9v6H5l7,7L19,9z"/></g></svg>'></a></li>