Enrich Vensim SVG

Here we offer a tool which makes a best effort to enrich SVG diagrams exported from Vensim with hyperlinks that conform to wiki's internal link conventions.

//wiki.dbbs.co/assets/pages/js-snippet-template/esm.html HEIGHT 55

Placeholder for more notes...


Below this pagefold we reveal the code which does the enriching. We believe this transparency may help other tinkerers to experiment with enrichment for specialized SVG structures. See Enrich Arrows SVG.

Import Frame Integration Promises and setup DOM helpers.

import * as frame from "https://wiki.dbbs.co/assets/v1/frame.js" const $ = (s, el=document) => el.querySelector(s) const $$ = (s, el=document) => Array.from(el.querySelectorAll(s))

Given a URL to and SVG document, or a data:image/svg+xml URL, we fetch the SVG DOM. We also remove width and height attributes so our processed images will scale to fit wiki's narrow pages.

async function getSvg(url) { let res = await fetch(url) let string = await res.text() let dom = new DOMParser() .parseFromString(string, "image/svg+xml") let svg = dom.documentElement svg.removeAttribute("width") svg.removeAttribute("height") return svg }

Here is where we make educated guesses about the structure of the SVG. We wrap specific elements in the document with annotated anchor tags that our HTML plugin interprets as internal links.

function enrich(svg) { const uniqueParents = Array.from( $$('g text', svg) ).reduce((acc, text) => { acc.add(text.parentElement); return acc; }, new Set()); Array.from(uniqueParents) .forEach(wrapElement); }

function wrapElement(el) { let title = Array.from($$('text', el)) .map(text => text.textContent.trim()) .join(" ") let anchor = anchorFor(title) el.parentElement.appendChild(anchor) el.parentElement.removeChild(el) anchor.appendChild(el) }

function anchorFor(title) { let anchor = document.createElementNS( "http://www.w3.org/2000/svg", "a") anchor.setAttribute("class", "internal") anchor.setAttribute("data-title", title) anchor.setAttribute("href", `/${asSlug(title)}.html`) return anchor }

const asSlug = title => title .replace(/\s/g, '-') .replace(/[^A-Za-z0-9-]/g, '') .toLowerCase()

Emit the HTML form.

export async function emit(el) { el.innerHTML = ` <style>input {width: 100%; display: block;}</style> <input name="title" type="text" placeholder="page title"> <input name="source" type="text" placeholder="URL to svg file"> <button>Create</button> ` }

Bind a click handler to the form button.

export async function bind(el) { el.querySelector('button').onclick = async e => { let title = $("[name=title]").value.trim() || "Enriched SVG" let url = $("input[name=source]").value.trim() let svg = await getSvg(url) enrich(svg) frame.open({ title, story: [ {type:"paragraph", text: "Describe this diagram."}, {type:"html", text: svg.outerHTML} ] }, e.shiftKey) } }

Expose tools for debugging in the javascript console.

Object.assign(window, {debug: { frame, enrich, wrapElement, anchorFor, getSvg, $, $$ }})

There are more specialized variations of this page which better understand the structure of some SVG exports.