123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- Draw.loadPlugin(function(ui) {
- const WEE = "wee.rys.app";
- const BACKEND = "dtb.rys.app";
- const EXPECTED_BACKEND_VERSION = 6; // expected backend version
- const SPARQL_SERVER = "fuseki.rys.app"
- const SPARQL_ENDPOINT = "/SystemDesignOntology2Layers/"
- const dropVocabularyPrefix = str => str.substring(41);
- const dropDescriptionPrefix = str => str.substring(30);
- const dropArtifactPrefix = str => str.substring(41);
- const addFormalismsPrefix = str => "http://ua.be/sdo2l/vocabulary/formalisms/" + str;
- const addArtifactPrefix = str => "http://ua.be/sdo2l/description/artifacts/" + str;
- const QUERIES = {
- // Query that navigates the given link from the given source element, and returns the IRI and most-concrete-type of the target element.
- // If `reverse` is true, then incoming links are returned instead.
- getOutgoingLink: (iri, link_type, reverse=false) => `\
- PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
- PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
- PREFIX pt: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>
- SELECT DISTINCT ?element ?type
- WHERE {
- ${reverse ? `?element <${link_type}> <${iri}>` : `<${iri}> <${link_type}> ?element`}.
- ?element a ?type .
- {
- ?type rdfs:subClassOf object_diagram:Object .
- } UNION {
- ?type rdfs:subClassOf pt:Event .
- } UNION {
- ?type rdfs:subClassOf pt:Artifact .
- }
- FILTER(NOT EXISTS {
- ?more_concrete_type rdfs:subClassOf ?type .
- ?element a ?more_concrete_type .
- FILTER(?more_concrete_type != ?type)
- })
- FILTER(NOT EXISTS {
- ?more_concrete_link_type rdfs:subPropertyOf <${link_type}> .
- ${reverse ? `?element ?more_concrete_link_type <${iri}>` : `<${iri}> ?more_concrete_link_type ?element`} .
- FILTER(?more_concrete_link_type != <${link_type}>)
- })
- }`,
- // Query that gets the IRI and most-concrete-type of a given drawio cell.
- getCellStuff: (cellId) => `\
- PREFIX drawio: <http://ua.be/sdo2l/vocabulary/formalisms/drawio#>
- PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
- PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
- SELECT DISTINCT ?element ?type
- WHERE {
- ?element drawio:hasDrawioId "${cellId}" .
- ?element a ?type .
- ?type rdfs:subClassOf object_diagram:Object .
- NOT EXISTS {
- ?more_concrete_type rdfs:subClassOf ?type .
- ?element a ?more_concrete_type .
- FILTER(?more_concrete_type != ?type)
- }
- }`,
- // Query that, for a given cell IRI, gets the name of the diagram containing the cell, and the ID of the cell.
- getDiagramAndCellId: cellIri => `\
- PREFIX drawio: <http://ua.be/sdo2l/vocabulary/formalisms/drawio#>
- PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
- SELECT DISTINCT ?diagramName ?cellId
- WHERE {
- <${cellIri}> drawio:hasDrawioId ?cellId .
- <${cellIri}> object_diagram:inModel ?model .
- ?model object_diagram:hasName ?diagramName .
- }`,
- // Query that gets ALL subclass relations.
- getSubClassRelations: `
- PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
- SELECT DISTINCT ?subclass ?superclass
- WHERE {
- ?subclass rdfs:subClassOf ?superclass
- }`,
- getAllGraphs: `
- SELECT ?g (COUNT(*) as ?count) {GRAPH ?g {?s ?p ?o}} GROUP BY ?g
- `,
- getArtifactFilename: artifactIri => `\
- PREFIX pt: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>
- PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>
- PREFIX base: <http://ua.be/sdo2l/vocabulary/base/base#>
- SELECT DISTINCT ?filename ?formalismName
- WHERE {
- <${artifactIri}> pt:hasLocation ?filename .
- <${artifactIri}> pt:relatesTo ?pmArtifact .
- ?pmArtifact pm:hasType ?ftgFormalism .
- ?ftgFormalism base:hasGUID ?formalismName .
- }`,
- getModels: `\
- PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
- PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
- PREFIX cs_as: <http://ua.be/sdo2l/vocabulary/formalisms/cs_as#>
- SELECT DISTINCT ?model ?type
- WHERE {
- ?model a object_diagram:Model .
- ?model a ?type .
- ?type rdfs:subClassOf object_diagram:Model .
- FILTER(NOT EXISTS {
- ?model a cs_as:CorrespondenceModel .
- })
- FILTER(NOT EXISTS {
- ?more_concrete_type rdfs:subClassOf ?type .
- ?model a ?more_concrete_type .
- FILTER(?more_concrete_type != ?type)
- })
- }
- `,
- getProperty: (iri, propertyIri) => `\
- SELECT DISTINCT ?value
- WHERE {
- <${iri}> <${propertyIri}> ?value .
- }
- `,
- insertTraceLink: (fromIri, toIri) => `\
- PREFIX trace: <http://ua.be/sdo2l/vocabulary/formalisms/traceability_model#>
- INSERT DATA {
- GRAPH <http://ua.be/sdo2l/description/traces> {
- <${fromIri}> trace:traceLinkTo <${toIri}> .
- <${fromIri}> trace:traceLinkFrom <${toIri}> .
- }
- }
- `,
- };
- // When rebuilding OML, all graphs will be deleted from Fuseki, except for the following (which are not sourced from OML, but inserted directly as RDF triples into Fuseki)
- GRAPHS_NOT_TO_DELETE = [
- "http://ua.be/sdo2l/description/traces",
- ];
- const typeToDescription = new Map([
- ["ftg#Model", async (element, type, getProperty) => getProperty("object_diagram#hasName").then(modelName => `ftg "${modelName}"`)],
- ["xopp#Text", async (element, type, getProperty) => getProperty("xopp#hasText").then(text => `text "${text}"`)],
- ["xopp#Model", async (element, type, getProperty) => getProperty("object_diagram#hasName").then(modelName => `xournal++ model "${modelName}"`)],
- ["drawio#Cell", async (element, type, getProperty) => getProperty("drawio#hasDrawioId").then(id => `cell "${id}"`)],
- ["drawio#Vertex", async (element, type, getProperty) => getProperty("drawio#hasDrawioId").then(id => `vertex "${id}"`)],
- ["drawio#Edge", async (element, type, getProperty) => getProperty("drawio#hasDrawioId").then(id => `edge "${id}"`)],
- ["drawio#Model", async (element, type, getProperty) => getProperty("object_diagram#hasName").then(modelName => `drawio model "${modelName}"`)],
- ["pm#Model", async (element, type, getProperty) => getProperty("object_diagram#hasName").then(modelName => `process model "${modelName}"`)],
- ["pm#Activity", async (element, type, getProperty) => element],
- ["pm#Artifact", async (element, type, getProperty) => element],
- ["pm#CtrlInputPort", async (element, type, getProperty) => getProperty("pm#hasName").then(portname => `ctrl inport "${portname}"`)],
- ["pm#CtrlOutputPort", async (element, type, getProperty) => getProperty("pm#hasName").then(portname => `ctrl outport "${portname}"`)],
- ["pm#DataInputPort", async (element, type, getProperty) => getProperty("pm#hasName").then(portname => `data inport "${portname}"`)],
- ["pm#DataOutputPort", async (element, type, getProperty) => getProperty("pm#hasName").then(portname => `data outport "${portname}"`)],
- ])
- // (Hardcoded) link types exposed to the user.
- const typeToLinkType = new Map([
- ["object_diagram#Object", [
- {
- relation: "cs_as#parsedAs",
- description: `Is parsed as`,
- },
- {
- relation: "cs_as#renderedAs",
- description: `Is rendered as`,
- },
- // Outcommented, because it's not an interesting relation:
- {
- relation: "object_diagram#inModel",
- description: `Is part of`,
- },
- {
- relation: "traceability_model#traceLinkTo",
- description: `Trace-link (outgoing)`,
- },
- {
- relation: "traceability_model#traceLinkFrom",
- description: `Trace-link (incoming)`,
- },
- ]],
- ["drawio#Model", [
- {
- relation: "drawio#hasRootCell",
- description: `Has root`,
- },
- ]],
- // Outcommented, blows up:
- // ["drawio#Cell", [
- // {
- // relation: "drawio#hasChild",
- // description: `Has child`,
- // },
- // ]],
- ["pm#Activity", [
- {
- relation: "pm#isTransformation",
- description: `Is typed by`,
- },
- {
- relation: "pm#hasPort",
- description: `Has`,
- },
- ]],
- ["pm#element", [
- {
- relation: "processtraces#relatesTo",
- description: `Enacted by`,
- reverse: true,
- },
- ]],
- ["processtraces#element", [
- {
- relation: "processtraces#relatesTo",
- description: `Enactment of`,
- },
- // pt-element is not a sub-type of 'Object', so we must repeat this:
- {
- relation: "cs_as#renderedAs",
- description: `Is rendered as`,
- },
- ]],
- ["pm#Artifact", [
- {
- relation: "pm#hasType",
- description: `Is typed by`,
- },
- ]],
- ["ftg#Transformation", [
- {
- relation: "pm#occursAsActivity",
- description: `Occurs as`,
- }
- ]],
- ["ftg#Formalism", [
- {
- relation: "pm#occursAsArtifact",
- description: `Occurs as`,
- },
- ]],
- ["xopp#Model", [
- {
- relation: "xopp#hasPage",
- description: `Has page`,
- },
- ]],
- ["xopp#Page", [
- {
- relation: "xopp#hasLayer",
- description: `Has layer`,
- },
- ]],
- ["xopp#Layer", [
- {
- relation: "xopp#hasElement",
- description: `Has element`,
- },
- ]],
- ]);
- // mapping from class-IRI to array of superclass-IRIs
- const superclasses = new Map(); // map is populated as soon as the plugin is loaded
- function getQueries(type) {
- const result = [].concat(
- typeToLinkType.get(dropVocabularyPrefix(type)) || [],
- ... (superclasses.get(type) || []).map(supertype => typeToLinkType.get(dropVocabularyPrefix(supertype)) || []));
- console.log("getQueries,type=",type,"superclasses=",superclasses.get(type),"result=",result);
- return result;
- }
- function querySPARQL(query) {
- const body = new URLSearchParams();
- body.append("query", query);
- return fetch(SPARQL_SERVER+SPARQL_ENDPOINT, {
- headers: new Headers({"Content-Type": "application/x-www-form-urlencoded"}),
- method: "POST",
- body,
- })
- .then(res => res.json())
- .then(json => {
- console.log("Query:\n"+query+"\Result:",json.results.bindings);
- return json.results.bindings;
- })
- }
- function updateSPARQL(update) {
- const body = new URLSearchParams();
- body.append("update", update);
- return fetch(SPARQL_SERVER+SPARQL_ENDPOINT, {
- headers: new Headers({"Content-Type": "application/x-www-form-urlencoded"}),
- method: "POST",
- body,
- })
- .then(res => {
- console.log("Update:\n"+update+"\Result:",res);
- })
- }
- const defaultSettings = {
- dialogPosX: 100,
- dialogPosY: 100,
- dialogWidth: 240,
- dialogHeight: 400,
- };
- fetch(BACKEND+"/version")
- .then(response => response.json())
- .catch(() => 0) // parsing failed - probably backend doesn't even have a version
- .then(version => {
- if (version !== EXPECTED_BACKEND_VERSION) {
- alert("Incorrect DTDesign Python backend version.\nExpected: " + EXPECTED_BACKEND_VERSION + ", got: " + version + ".\nRefusing to load plugin. Please upgrade :)");
- return;
- }
- else {
- console.log("Backend version is ", version);
- console.log("All good.")
- }
- // Loads all 'rdfs:subClassOf' relations and stores them in a mapping.
- function loadSuperclasses() {
- return querySPARQL(QUERIES.getSubClassRelations)
- .then(results => {
- for (const {subclass, superclass} of results) {
- const superclasslist = superclasses.get(subclass.value) || (() => {
- const list = [];
- superclasses.set(subclass.value, list);
- return list;
- })();
- superclasslist.push(superclass.value);
- }
- });
- }
- loadSuperclasses();
- // Override context menu when right-clicking somewhere in the diagram:
- const oldFactoryMethod = ui.editor.graph.popupMenuHandler.factoryMethod;
- ui.editor.graph.popupMenuHandler.factoryMethod = function(menu, cell, evt) {
- const modelverseMenu = menu.addItem("Knowledge Graph...", null, null);
- const createLinkMenu = menu.addItem("Create traceability link to...", null, null);
- menu.addSeparator();
- oldFactoryMethod.apply(this, arguments);
- const entry = (element, type) => element+':'+type;
- function addMenuItem(element, type, maxRecursion, parentMenuItem, description, alreadyVisited, onClick) {
- if (alreadyVisited.has(entry(element, type))) {
- return; // don't go in circles
- }
- const shortType = dropVocabularyPrefix(type);
- const itemDescriptionGen = typeToDescription.get(shortType) || (async () => shortType);
- itemDescriptionGen(dropArtifactPrefix(element), shortType, property => {
- return querySPARQL(QUERIES.getProperty(element, addFormalismsPrefix(property)))
- .then(results => {
- if (results.length > 0) return results[0].value.value;
- else return "";
- });
- })
- .then(itemDescription => {
- const menuItemText = description + ' ' + itemDescription;
- let createdMenuItem;
- // If the menu item is a (subtype of a) drawio cell, then clicking on it will take us to that cell in drawio.
- if (onClick) {
- createdMenuItem = menu.addItem(menuItemText, null, () => onClick(element), parentMenuItem);
- }
- else if ((superclasses.get(type) || []).some(t => t.endsWith("drawio#Cell"))) {
- createdMenuItem = menu.addItem(menuItemText, null, () => goto(element), parentMenuItem);
- }
- else {
- createdMenuItem = menu.addItem(menuItemText, null, null, parentMenuItem);
- }
- if ((superclasses.get(type) || []).some(t => t.endsWith("processtraces#Artifact"))) {
- querySPARQL(QUERIES.getArtifactFilename(element))
- .then(results => {
- for (const {filename, formalismName} of results) {
- menu.addItem(`File "${filename.value}"`, null, () => (createDownloadHandler(formalismName.value))(filename.value), createdMenuItem);
- }
- })
- }
- alreadyVisited.add(entry(element, type));
- if (maxRecursion <= 0) {
- return;
- }
- addSubmenuItems(element, type, createdMenuItem, maxRecursion, alreadyVisited, onClick);
- })
- }
- function addSubmenuItems(element, type, parentMenuItem, maxRecursion, alreadyVisited, onClick) {
- const queries = getQueries(type);
- for (const {relation, description, reverse} of queries) {
- querySPARQL(QUERIES.getOutgoingLink(element, "http://ua.be/sdo2l/vocabulary/formalisms/"+relation, reverse))
- .then(results => {
- for (const {element, type} of results) {
- addMenuItem(element.value, type.value, maxRecursion-1, parentMenuItem, description, alreadyVisited, onClick);
- }
- })
- }
- }
- // Populate ModelVerse submenu asynchronously:
- if (cell && cell.vertex) {
- querySPARQL(QUERIES.getCellStuff(cell.id))
- .then(results => {
- for (const {element, type} of results) {
- const alreadyVisited = new Set([[element.value, type.value]]);
- addSubmenuItems(element.value, type.value, modelverseMenu, 3, alreadyVisited);
- querySPARQL(QUERIES.getModels)
- .then(results => {
- const alreadyVisited2 = new Set([[element.value, type.value]]);
- for (const {model, type} of results) {
- addMenuItem(model.value, type.value, 3, createLinkMenu, "Model",
- alreadyVisited2,
- targetIri => updateSPARQL(QUERIES.insertTraceLink(element.value, targetIri)));
- }
- });
- }
- })
- }
- }
- function getSetting(prop) {
- const result = localStorage.getItem("dtdesign-"+prop);
- if (result !== null) {
- return JSON.parse(result);
- }
- return defaultSettings[prop];
- }
- function setSetting(prop, value) {
- localStorage.setItem("dtdesign-"+prop, JSON.stringify(value));
- }
- const errWndDiv = document.createElement('div');
- // wndDiv.style.color = "red";
- errWndDiv.style.overflow = "auto";
- errWndDiv.style.height = "100%";
- errWndDiv.style.padding = '12px 14px 8px 14px';
- const errWnd = new mxWindow("Error Details",
- errWndDiv, 300, 300, 300, 360, true, true);
- errWnd.destroyOnClose = false;
- errWnd.setMaximizable(false);
- errWnd.setResizable(true);
- errWnd.setClosable(true);
- function getErrDetailsAnchor() {
- const anchor = document.createElement('a');
- anchor.innerText = "Details";
- anchor.href = "#";
- anchor.onclick = function() {
- errWnd.show();
- };
- return anchor;
- }
- let rebuildOMLPromise = Promise.resolve();
- function rejectIfStatusAbove400(httpResponse) {
- if (httpResponse.status >= 400) {
- return httpResponse.text().then(text => {
- return Promise.reject(text);
- });
- }
- return Promise.resolve();
- }
- function rebuildOMLForced() {
- return querySPARQL(QUERIES.getAllGraphs)
- .then(results => {
- const graphsToDelete = results
- .map(({g,count}) => g.value)
- .filter(graphIri => !GRAPHS_NOT_TO_DELETE.includes(graphIri));
- // Delete all graphs first
- return Promise.all(graphsToDelete.map(graphToDelete =>
- fetch(SPARQL_SERVER+SPARQL_ENDPOINT+"?graph="+graphToDelete, {
- method: "DELETE",
- })
- .then(rejectIfStatusAbove400)
- ))
- .then(() => {
- // Build OML and load the resulting .owl files:
- return fetch(BACKEND+"/owl_load", {
- method: "PUT",
- })
- .then(rejectIfStatusAbove400)
- });
- });
- }
- function rebuildOMLIncremental() {
- // Backend takes care of all the heavy work:
- return fetch(BACKEND+"/owl_reload_incremental", { method: "PUT", })
- .then(rejectIfStatusAbove400);
- }
- function showRebuildOMLResult(rebuildAsyncCallback) {
- // only perform one rebuild at a time -> queue rebuilds.
- rebuildOMLPromise = rebuildOMLPromise.then(() => {
- omlStatusDiv.style.color = null;
- omlStatusDiv.innerText = "Rebuilding OML...";
- return rebuildAsyncCallback()
- .then(() => {
- omlStatusDiv.innerText = "✓ Built OML";
- omlStatusDiv.style.color = "green";
- })
- .catch(errText => {
- errWndDiv.innerText = errText;
- omlStatusDiv.innerHTML = "✗ Failed to build OML ";
- omlStatusDiv.style.color = "red";
- omlStatusDiv.appendChild(getErrDetailsAnchor());
- });
- });
- }
- function uploadResponseHandler(statusDiv, refreshListCallback) {
- statusDiv.innerHTML = "Saving ...";
- statusDiv.style.color = null;
- omlStatusDiv.innerHTML = "";
- return res => {
- if (res.status >= 200 && res.status < 300) {
- errWndDiv.innerText = "No error.";
- res.json().then(parsedAs => {
- statusDiv.innerHTML = "✓ Generated OML (" + parsedAs.join(", ") + ")";
- statusDiv.style.color = "green";
- });
- refreshListCallback();
- showRebuildOMLResult(rebuildOMLIncremental);
- }
- else {
- statusDiv.innerHTML = "✗ Failed to save/parse. ";
- statusDiv.appendChild(getErrDetailsAnchor());
- res.text().then(text => {
- errWndDiv.innerText = text;
- });
- statusDiv.style.color = "red";
- }
- };
- }
- let highlight;
- // Given a cell IRI, open the diagram that contains the cell
- function goto(cellIri) {
- querySPARQL(QUERIES.getDiagramAndCellId(cellIri))
- .then(([{diagramName, cellId}]) => {
- // drop the "_drawio" at the end:
- const actualDiagramName = diagramName.value.substring(0, diagramName.value.length-7);
- loadPage(actualDiagramName)
- .then(() => {
- if (highlight) {
- highlight.destroy();
- }
- const [cell] = ui.editor.graph.getCellsById([cellId.value]);
- highlight = new mxCellHighlight(ui.editor.graph, "#eb34e8", 6);
- highlight.highlight(ui.editor.graph.view.getState(cell));
- })
- })
- }
- const wndDiv = document.createElement('div');
- wndDiv.style.userSelect = 'none';
- wndDiv.style.height = '100%';
- wndDiv.style.color = "rgb(112, 112, 112)";
- wndDiv.style.overflow = 'auto';
- wndDiv.classList.add("geFormatContainer");
- const omlDiv = document.createElement('div');
- omlDiv.classList.add('geFormatSection')
- omlDiv.style.padding = '12px 14px 8px 14px';
- wndDiv.appendChild(omlDiv);
- const omlLabel = document.createElement('div');
- omlLabel.innerText = "OML Status";
- omlLabel.style.fontWeight = 'bold';
- omlDiv.appendChild(omlLabel);
- const omlStatusDiv = document.createElement('div');
- omlStatusDiv.innerText = "Ready";
- omlDiv.appendChild(omlStatusDiv);
- const omlIncButton = mxUtils.button("Incremental rebuild (faster)", function() {
- showRebuildOMLResult(rebuildOMLIncremental);
- });
- omlDiv.appendChild(omlIncButton);
- const omlForceButton = mxUtils.button("Forced rebuild (slow)", function() {
- showRebuildOMLResult(rebuildOMLForced);
- });
- omlDiv.appendChild(omlForceButton);
- function createSavePageDiv(refreshModelsCallback) {
- const savePageDiv = document.createElement('div');
- const saveButton = mxUtils.button("Save Current Page", function() {
- const responseHandler = uploadResponseHandler(saveStatusDiv, refreshModelsCallback);
- const headers = new Headers({
- "Content-Type": "application/xml",
- });
- const serializer = new XMLSerializer();
- const xmlnode = ui.getXmlFileData(
- null, // ignore selection
- true, // only current page
- true); // uncompressed
- const diagram = xmlnode.children[0]
- const diagramName = diagram.getAttribute("name");
- const xml = serializer.serializeToString(diagram);
- return fetch(BACKEND + "/files/drawio/"+diagramName, {
- method: "PUT",
- headers,
- body: xml,
- })
- .then(responseHandler);
- });
- saveButton.style.width = "100%";
- saveButton.style.marginTop = "4px";
- saveButton.style.marginBottom = "2px";
- savePageDiv.appendChild(saveButton);
- const saveStatusDiv = document.createElement('div');
- savePageDiv.appendChild(saveStatusDiv);
- return savePageDiv;
- }
- function createUploadDiv(modelType, mimeType) {
- return function(refreshModelsCallback) {
- const uploadDiv = document.createElement('div');
- const uploadLabel = document.createElement('label');
- uploadLabel.innerText = "Upload "+modelType+":";
- uploadLabel.for = "uploadButton";
- uploadDiv.appendChild(uploadLabel);
- const uploadButton = document.createElement('input');
- uploadButton.type = "file";
- uploadButton.id = "uploadButton";
- uploadButton.style.width = "100%";
- uploadButton.style.marginTop = "4px";
- uploadButton.style.marginBottom = "2px";
- uploadButton.accept = mimeType;
- // uploadButton.multiple = true;
- uploadButton.onchange = e => {
- const responseHandler = uploadResponseHandler(statusDiv, refreshModelsCallback);
- Array.from(e.target.files).map(file => {
- fetch(BACKEND+"/files/"+modelType+"/"+file.name, {
- method: "PUT",
- body: file,
- })
- .then(responseHandler);
- });
- };
- uploadDiv.appendChild(uploadButton);
- const statusDiv = document.createElement('div');
- uploadDiv.appendChild(statusDiv);
- return uploadDiv;
- };
- }
- function createDownloadHandler(modelType) {
- return modelName => {
- const a = document.createElement('a');
- document.body.appendChild(a);
- a.setAttribute('href', BACKEND + "/files/" + modelType + "/" + modelName);
- a.setAttribute('download', modelName);
- a.setAttribute('target', "_blank"); // for browsers that don't support the 'download' attribute, tell them to open the link in a new tab
- a.click();
- document.body.removeChild(a);
- };
- }
- function createModelList(modelType, createUploadDiv, downloadButtonLabel, onDownloadClick, extraStuff) {
- const containerDiv = document.createElement('div');
- containerDiv.classList.add('geFormatSection')
- containerDiv.style.padding = '12px 14px 8px 14px';
- wndDiv.appendChild(containerDiv);
- const labelDiv = document.createElement('div');
- labelDiv.innerText = "Models — " + modelType;
- labelDiv.style.fontWeight = 'bold';
- containerDiv.appendChild(labelDiv);
- const modelListDiv = document.createElement('div');
- containerDiv.appendChild(modelListDiv);
- // Refreshes the list of models shown in the ModelVerse window
- function refreshList() {
- fetch(BACKEND + "/files/" + modelType, {
- method: "GET",
- })
- .then(res => res.json())
- .then(models => {
- if (models.length > 0) {
- modelListDiv.replaceChildren(...models.sort().map(modelName => {
- const div = document.createElement('div');
- div.style.padding = '3px 0px';
- const loadButton = mxUtils.button(downloadButtonLabel, () => onDownloadClick(modelName));
- loadButton.style.marginLeft = "12px";
- div.appendChild(document.createTextNode(modelName));
- div.appendChild(loadButton);
- const extraStuffWrapper = document.createElement('div');
- div.appendChild(extraStuffWrapper);
- function refreshExtraStuff() {
- extraStuff(modelName)
- .then(extraDiv => {
- if (extraDiv) {
- extraStuffWrapper.replaceChildren(extraDiv);
- extraDiv.appendChild(mxUtils.button("⟳", refreshExtraStuff));
- }
- });
- }
- refreshExtraStuff();
- return div;
- }));
- }
- else {
- const div = document.createElement('div');
- div.style.padding = '3px 0px';
- div.appendChild(document.createTextNode("No "+modelType+" models."));
- modelListDiv.replaceChildren(div);
- }
- });
- }
- refreshList();
- containerDiv.appendChild(createUploadDiv(refreshList));
- return refreshList;
- }
- function createPTrenderCallback(startTraceIri) {
- return () => {
- return fetch(BACKEND+"/render_pt/"+encodeURIComponent(startTraceIri))
- .then(treatResponseAsPageXml)
- .then(() => {
- refreshDrawioModelList();
- showRebuildOMLResult(rebuildOMLIncremental);
- });
- }
- }
- const extraPMstuff = modelName => {
- if (modelName.endsWith(":pm")) {
- const pmModel = addArtifactPrefix(modelName.substr(0, modelName.length-3)+"_pm#model");
- const urlEncoded = encodeURIComponent(pmModel);
- const enactButton = mxUtils.button("Start New...", ()=>{
- // open WEE in new tab:
- window.open(WEE+"/gettraces?iri="+urlEncoded, "_blank");
- });
- const shortTraceName = traceIri => dropArtifactPrefix(traceIri).split('#').findLast(str => str.length > 0);
- return Promise.all([
- fetch(WEE+"/traces/active/"+urlEncoded)
- .then(response => response.json())
- .then(enactments => {
- return enactments.map((enactment, i) => mxUtils.button(`Open "${shortTraceName(enactment.iri)}" (ongoing)`, createPTrenderCallback(enactment.iri)))
- }),
- fetch(WEE+"/traces/finished/"+urlEncoded)
- .then(response => response.json())
- .then(enactments => {
- return enactments.map((enactment, i) => mxUtils.button(`Open "${shortTraceName(enactment.iri)}" (finished)`, createPTrenderCallback(enactment.iri)))
- }),
- ])
- .then(([ongoing, finished]) => {
- const pmDiv = document.createElement('div');
- pmDiv.style.marginLeft = '8px';
- pmDiv.style.borderWidth = '1px';
- pmDiv.style.borderStyle = 'solid';
- pmDiv.appendChild(document.createTextNode("PM Enactment: "));
- pmDiv.appendChild(enactButton);
- if (ongoing.length > 0) {
- ongoing.forEach(o => pmDiv.appendChild(o));
- }
- if (finished.length > 0) {
- finished.forEach(f => pmDiv.appendChild(f));
- }
- return pmDiv;
- });
- }
- return Promise.resolve(null);
- };
- const noExtraStuff = () => Promise.resolve(null);
- const refreshDrawioModelList = createModelList("drawio", createSavePageDiv, "Open", modelName => loadPage(modelName), extraPMstuff);
- createModelList("xopp", createUploadDiv("xopp", "application/x-xopp,.xopp"), "Download", createDownloadHandler("xopp"), noExtraStuff);
- createModelList("csv", createUploadDiv("csv", "text/csv,.csv"), "Download", createDownloadHandler("csv"), noExtraStuff);
- createModelList("file", createUploadDiv("file", ""), "Download", createDownloadHandler("file"), noExtraStuff);
- // Load a model and add it as a new page to the editor
- function treatResponseAsPageXml(response) {
- return response.text()
- .then(xmltext => {
- // console.log(xmltext);
- const parser = new DOMParser();
- const doc = parser.parseFromString(xmltext, "application/xml");
- const node = doc.documentElement;
- const page = new DiagramPage(node);
- // if page with same name already exists, erase it:
- ui.pages = ui.pages.filter(pg => pg.node.getAttribute("name") != page.node.getAttribute("name"));
- ui.pages.push(page);
- ui.currentPage = page;
- ui.editor.setGraphXml(node);
- });
- }
- function loadPage(pageName) {
- return fetch(BACKEND + "/files/drawio/" + pageName)
- .then(treatResponseAsPageXml);
- }
- // make sure the plugin popup window is always within the bounds of the browser window:
- const dialogPosX = x => Math.max(0, Math.min(x, window.innerWidth - getSetting("dialogWidth")));
- const dialogPosY = y => Math.max(0, Math.min(y, window.innerHeight - getSetting("dialogHeight")));
- const wnd = new mxWindow("ModelVerse", wndDiv,
- dialogPosX(getSetting("dialogPosX")),
- dialogPosY(getSetting("dialogPosY")),
- getSetting("dialogWidth"),
- getSetting("dialogHeight"),
- true, true);
- wnd.destroyOnClose = false;
- wnd.setMaximizable(false);
- wnd.setResizable(true);
- wnd.setClosable(false);
- // remember window geometry in localstorage:
- wnd.addListener('resize', function(wnd) {
- // couldn't find a better way to get the new window size but the following:
- const parsepx = px => parseInt(px.substring(0, px.length-2));
- setSetting("dialogWidth", parsepx(wnd.div.style.width));
- setSetting("dialogHeight", parsepx(wnd.div.style.height));
- })
- wnd.addListener('move', function() {
- setSetting("dialogPosX", wnd.getX());
- setSetting("dialogPosY", wnd.getY());
- })
- wnd.show();
- window.onresize = event => {
- // make sure popup window remains within browser window bounds:
- const newX = dialogPosX(getSetting("dialogPosX"));
- const newY = dialogPosY(getSetting("dialogPosY"));
- wnd.div.style.left = newX + "px";
- wnd.div.style.top = newY + "px";
- // setSetting("dialogPosX", newX);
- // setSetting("dialogPosY", newY);
- }
- }) // end of promise that checks the backend version.
- })
|