diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-07 10:01:08 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-07 11:01:24 +0100 |
| commit | f842640e9d0d1c10d2a23fd93929d274ebd86ad1 (patch) | |
| tree | c397909bd4b40be04b073316ee6a3166c326f3cc /src | |
| parent | f29b392c6b68b7db98f5d6921ea588b559435f35 (diff) | |
Improve thumbnail rendering and dialog loading, flatten CSS1.0.1
Diffstat (limited to 'src')
| -rw-r--r-- | src/wmap.js | 128 |
1 files changed, 102 insertions, 26 deletions
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> |