1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
import { parse } from "wmap-parser";
import { renderToCanvas } from "wmap-renderer-canvas";
import DOMPurify from "isomorphic-dompurify";
const internals = {
kStyleSheet: "/plugins/wmap/wmap.css",
kCanvasWidth: 1384,
kCanvasHeight: 1884,
// Returns HTML that shows a message in the wiki.
message(text) {
return `
<div class="viewer" data-item="viewer" style="width:98%">
<div style="width:80%; padding:8px; color:gray; background-color:#eee; margin:0 auto; text-align:center">
<i>${text}</i>
</div>
</div>`;
},
// Creates a wmap PNG from text.
async renderImage(source) {
const parsedText = parse(source);
const canvas = document.createElement("canvas");
canvas.width = internals.kCanvasWidth;
canvas.height = internals.kCanvasHeight;
await renderToCanvas(parsedText, canvas);
return canvas.toDataURL();
},
// Starts a download of the image.
download(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", text);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
},
// 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());
const image = document.createElement("img");
image.src = imageData;
image.alt = "Wardley map rendered from source.";
$container.appendChild(image);
},
};
/**
* Runs once for each block. Do setup for the block before rendering.
*/
const emit = function emit($item) {
const hasStyleSheet = [...document.styleSheets].some((styleSheet) =>
styleSheet.href.endsWith(internals.kStyleSheet),
);
if (!hasStyleSheet) {
console.log("Adding wmap stylesheet.");
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = internals.kStyleSheet;
link.type = "text/css";
document.getElementsByTagName("head")[0].appendChild(link);
}
return $item.append(internals.message("Loading Wardley Map"));
};
/**
* 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 {Object} item the configuration for that particular object.
*/
const bind = async function bind($item, item) {
$item.on("dblclick", () => {
return wiki.textEditor($item, item);
});
$item.on("click", "a", (event) => {
const { currentTarget } = event;
const action = currentTarget.dataset?.action;
if (!action) {
return;
}
event.stopPropagation();
event.preventDefault();
switch (action) {
case "download":
{
const slug = $item.parents(".page").attr("id");
internals.download(`${slug}.png`, item.png);
}
break;
case "zoom":
{
const shouldOpenStandalone = !!event.shiftKey;
const target = shouldOpenStandalone ? "_blank" : "wmap";
const dialog = window.open(
"/plugins/wmap/dialog/#",
target,
"popup,height=600,width=800",
);
if (dialog.location.pathname !== "/plugins/wmap/dialog/") {
dialog.addEventListener("load", () => {
internals.replaceImage(dialog, item.png);
});
} else {
internals.replaceImage(dialog, item.png);
}
}
break;
}
});
try {
let imageData = await internals.renderImage(item.text);
item.png = imageData;
$item.find(".viewer").html(`
<article class="wardley-map"><img src="${imageData}"</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>
<li><a href="#" data-action="zoom" title="Zoom"><img width="18" height="18" alt="toggle zoom" 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"><g><rect fill="none" height="24" width="24"/></g><g><g><g><path fill="grey" d="M15,3l2.3,2.3l-2.89,2.87l1.42,1.42L18.7,6.7L21,9V3H15z M3,9l2.3-2.3l2.87,2.89l1.42-1.42L6.7,5.3L9,3H3V9z M9,21 l-2.3-2.3l2.89-2.87l-1.42-1.42L5.3,17.3L3,15v6H9z M21,15l-2.3,2.3l-2.87-2.89l-1.42,1.42l2.89,2.87L15,21h6V15z"/></g></g></g></svg>'></a></li>
</menu>
</nav>
`);
} catch (err) {
console.log("Failed to parse wardley map: ", err);
$item.html(internals.message(err.message));
}
};
/**
* On load, attempt to add the emit and bind functions to window, as these
* are called by the wiki plugin manager.
*/
if (typeof window !== "undefined" && window !== null) {
if (!window.plugins.wmap) {
window.plugins.wmap = { emit, bind };
}
}
const expand = function (text) {
return DOMPurify.sanitize(text);
};
export const wmap = typeof window === "undefined" ? { expand } : undefined;
|