Skip to content

Instantly share code, notes, and snippets.

@ThomasRohde
Created November 12, 2022 07:08
Show Gist options
  • Save ThomasRohde/342b8551428ad56f6c9b2233e7fa4878 to your computer and use it in GitHub Desktop.
Save ThomasRohde/342b8551428ad56f6c9b2233e7fa4878 to your computer and use it in GitHub Desktop.
Forms in Archi - with examples. #Jarchi, #Archi, #ArchiMate
/*
Author: Thomas Klok Rohde
Description: Edit documentation markdown
History:
November 12, 2022: Created.
*/
console.show();
console.clear();
load(__DIR__ + "lib/show_form.js");
let element = $(selection).first();
let formSchema = {
type: "object",
title: " ",
format: "grid-strict",
properties: {
documentation: {
type: "string",
format: "markdown",
options: {
grid_columns: 12,
compact: true
}
},
submit: {
format: "button",
title: "Insert",
options: {
grid_columns: 1,
inputAttributes: {
class: "btn btn-primary btn-sm json-editor-btn-"
},
button: {
align: "left",
action: "submitform",
validated: true
}
}
},
cancel: {
format: "button",
title: "Cancel",
options: {
grid_columns: 1,
grid_break: true,
button: {
align: "left",
action: "cancelform"
}
}
}
}
}
const primary = shell.getDisplay().getPrimaryMonitor();
const bounds = primary.getBounds();
let documentation = {
documentation: element.documentation
};
let value;
if (bounds.width <= 1920)
value = showForm(formSchema, documentation, 800, 750);
else
value = showForm(formSchema, documentation, 1800, 1100);
if (value) {
console.log(value.documentation);
element.documentation = value.documentation;
}
/*
Author: Thomas Klok Rohde
Description: Insert chart to view.
Dependencies: https://github.com/json-editor/json-editor
History:
October 11, 2022 : Created with base set of scripts
November 11, 2022 : Updated to new form library (JSON Library)
*/
console.show();
console.clear();
load(__DIR__ + "lib/show_form.js");
let note = $(selection).filter("diagram-model-note").first();
if (note == undefined)
throw new Error("No note selected");
// Get a list of all defined properties in the model
let props = new Set();
$("*").not("folder").each(o => {
o.prop().forEach(p => {
if (p.length) props.add(p);
})
})
let properties = Array.from(props);
let elements = [];
$("element").each(e => {
elements.push(e.name);
})
let formSchema = {
type: "object",
title: " ",
format: "grid-strict",
required: ["title", "chart"],
options: {
compact: true
},
properties: {
chart: {
type: "string",
title: "Chart type to insert",
enum: [
"chartArchiDistribution",
"chartPropertyTable",
"chartTIME",
"chartTreemap",
"chartGauges",
"chartSpider"
],
default: "chartArchiDistribution",
description: "Select a chart",
options: {
infoText: "Select among the available charts to render",
grid_columns: 6,
inputAttributes: {
placeholder: "Select an available chart"
},
enum_titles: [
"Distribution of ArchiMate elements",
"Table of used properties",
"TIME chart - Tolerate, Invest, Migrate or Eliminate",
"Treemap of Capabilities",
"Gauges of select properties",
"Spider chart of select properties"
]
}
},
title: {
type: "string",
title: "Chart title",
minLength: 5,
options: {
infoText: "The chart title will in most charts be placed in the top part.",
grid_columns: 6,
grid_break: true,
inputAttributes: {
placeholder: "Enter a chart title"
}
}
},
filter: {
type: "string",
title: "Javascript filter",
options: {
infoText: "The expression will be encapsulated in a function with the parameters name, type and obj.",
grid_columns: 12,
grid_break: true,
inputAttributes: {
placeholder: "Enter javascript expression"
}
}
},
elements: {
type: "array",
uniqueItems: true,
format: "choices",
title: "List of elements to consider in the chart",
items: {
type: "string",
enum: elements
},
options: {
infoText: "Select one or more elements to include in the chart. You can search by typing the first few characters",
grid_columns: 12,
grid_break: true,
}
},
distribution: {
type: "object",
title: "Element distribution",
required: ["type"],
format: "grid-strict",
properties: {
type: {
type: "string",
title: "Chart variation",
enum: [
"bar",
"lines"
],
options: {
infoText: "Select visual appearance",
grid_columns: 4,
inputAttributes: {
placeholder: "Select visual appearance"
},
enum_titles: [
"Bar",
"Lines"
]
}
},
distributionChartIcon: {
type: "info",
title: "Example",
description: "<img src='https://plotly.github.io/static/images/zoom-pan-hover/double-click-autoscale.gif' height='150' />",
options: {
align: "left",
grid_columns: 4,
grid_break: true
}
},
},
options: {
infoText: "Select options for the element distribution chart",
grid_columns: 12,
grid_break: true,
dependencies: {
"chart": "chartArchiDistribution"
},
}
},
treemap: {
type: "object",
title: "Treemap chart",
required: ["type"],
format: "grid-strict",
properties: {
property: {
type: "string",
title: "Property",
enum: properties,
options: {
infoText: "Select the property, or leave empty, the property used to size the elements",
grid_columns: 4,
}
},
type: {
type: "string",
title: "Chart variation",
enum: [
"treemap",
"sunburst"
],
options: {
infoText: "Select visual appearance",
grid_columns: 3,
inputAttributes: {
placeholder: "Select visual appearance"
},
enum_titles: [
"Treemap",
"Sunburst"
]
}
},
level: {
type: "integer",
format: "range",
title: "Level",
minimum: 1,
maximum: 6,
options: {
infoText: "Select the maxium level of the treemap",
grid_columns: 2
}
},
treemapChartIcon: {
type: "info",
title: "Example",
description: "<img src='https://images.plot.ly/plotly-documentation/thumbnail/sunburst.gif' height='150' />",
options: {
align: "left",
grid_columns: 3,
grid_break: true
}
}
},
options: {
infoText: "Select options for the treemap chart",
grid_columns: 12,
grid_break: true,
dependencies: {
"chart": "chartTreemap"
},
}
},
spider: {
type: "object",
title: "Spider chart",
required: ["type", "properties"],
format: "grid-strict",
properties: {
type: {
type: "string",
title: "Chart variation",
enum: [
"scatterpolar",
"bar"
],
options: {
infoText: "Select visual appearance",
grid_columns: 3,
inputAttributes: {
placeholder: "Select visual appearance"
},
enum_titles: [
"Spider",
"Bar"
]
}
},
properties: {
type: "array",
title: "List of spoke properties",
uniqueItems: true,
minItems: 2,
maxItems: 7,
format: "choices",
items: {
type: "string",
enum: properties
},
options: {
infoText: "Select three or more properties to include in the chart. You can search by typing the first few characters",
grid_columns: 6
}
},
spiderChartIcon: {
type: "info",
title: "Example",
description: "<img src='https://images.plot.ly/plotly-documentation/thumbnail/radar.gif' height='150' />",
options: {
align: "left",
grid_columns: 3,
grid_break: true
}
}
},
options: {
infoText: "Select options for the spider chart",
grid_columns: 12,
dependencies: {
"chart": "chartSpider"
},
}
},
gauge: {
type: "object",
title: "Gauge",
required: ["type", "property"],
format: "grid-strict",
properties: {
property: {
type: "string",
title: "Property",
enum: properties,
options: {
infoText: "Select a property",
grid_columns: 6,
}
},
type: {
type: "string",
title: "Chart variation",
enum: [
"bullet",
"gauge"
],
options: {
infoText: "Select visual appearance",
grid_columns: 6,
grid_break: true,
inputAttributes: {
placeholder: "Select visual appearance"
},
enum_titles: [
"Bullet",
"Gauge"
]
}
},
min: {
type: "number",
title: "Minimum",
options: {
infoText: "Select minimum value",
grid_columns: 3
}
},
max: {
type: "number",
title: "Maximum",
options: {
infoText: "Select maximum value",
grid_columns: 3
}
},
gaugeChartIcon: {
type: "info",
title: "Example",
description: "<img src='https://images.plot.ly/plotly-documentation/thumbnail/indicator.jpg' height='150' />",
options: {
align: "left",
grid_columns: 3,
grid_break: true
}
}
},
options: {
infoText: "Select options for the spider chart",
grid_columns: 12,
dependencies: {
"chart": "chartGauges"
},
}
},
submit: {
format: "button",
title: "Insert",
options: {
grid_columns: 1,
inputAttributes: {
class: "btn btn-primary btn-sm json-editor-btn-"
},
button: {
align: "left",
action: "submitform",
validated: true
}
}
},
cancel: {
format: "button",
title: "Cancel",
options: {
grid_columns: 1,
grid_break: true,
button: {
align: "left",
action: "cancelform"
}
}
}
}
}
if (note.prop("Chart")) formSchema.properties.chart.readOnly = true;
const primary = shell.getDisplay().getPrimaryMonitor();
const bounds = primary.getBounds();
let value;
if (bounds.width <= 1920)
value = showForm(formSchema, JSON.parse(note.prop("Chart")), 800, 750);
else
value = showForm(formSchema, JSON.parse(note.prop("Chart")), 1800, 1100);
if (value) {
// Do some house cleaning, which the form validation can't handle
console.log(JSON.stringify(value, null, 3));
note.prop("Chart", JSON.stringify(value));
load(__DIR__ + "Refresh chart.ajs");
}
/*
Author: Thomas Klok Rohde
Description: Display form and return data
Dependencies:
https://github.com/json-editor/json-editor
https://github.com/Choices-js/Choices
https://github.com/cure53/DOMPurify
+ some CSS
History:
October 11, 2022 : Created with base set of scripts
November 11, 2022 : Updated to new form library
*/
function showForm(formSchema, defaultValue = null, width = 1200, height = 900) {
const SWT = Java.type('org.eclipse.swt.SWT');
const FillLayout = Java.type('org.eclipse.swt.layout.FillLayout');
const Shell = Java.type('org.eclipse.swt.widgets.Shell');
const Browser = Java.type('org.eclipse.swt.browser.Browser');
const ProgressAdapter = Java.extend(Java.type('org.eclipse.swt.browser.ProgressAdapter'));
const LocationAdapter = Java.extend(Java.type('org.eclipse.swt.browser.LocationAdapter'));
const CustomFunction = Java.extend(Java.type('org.eclipse.swt.browser.BrowserFunction'));
const IArchiImages = Java.type('com.archimatetool.editor.ui.IArchiImages');
const ImageFactory = Java.type('com.archimatetool.editor.ui.ImageFactory');
const Rectangle = Java.type('org.eclipse.swt.graphics.Rectangle');
const Monitor = Java.type('org.eclipse.swt.widgets.Monitor');
let display = shell.getDisplay();
let newShell = new Shell(display, SWT.MODAL | SWT.TITLE | SWT.ON_TOP);
newShell.setText("Archi form");
newShell.setLayout(new FillLayout());
let insertValue = (defaultValue) ? `startval: ${JSON.stringify(defaultValue, null, 1)},` : "";
let html = `
<!DOCTYPE HTML>
<html>
<head>
<title>radio button editor examples</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel='stylesheet' href='https://use.fontawesome.com/releases/v5.12.1/css/all.css'>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<script type="text/javascript" src="${__DIR__ + "jsoneditor.js"}"></script>
<script type="text/javascript" src="${__DIR__ + "choices.min.js"}"></script>
<script type="text/javascript" src="${__DIR__ + "purify.min.js"}"></script>
<script type="text/javascript" src="${__DIR__ + "simplemde.min.js"}"></script>
<style type="text/css">
html {
font-size: 14px;
line-height: 1.0;
}
h3 {
font-size: 16px;
}
body {
margin: 8px
}
[data-schemaid="root"] {
margin-top: -2rem;
}
.required::after {
content: " *";
color: red;
font: inherit;
}
</style>
</head>
<body>
<!-- <button id='submit' onclick='submit()' class="btn btn-primary">Submit</button> -->
<!-- <button id='cancel' onclick='cancel()' class="btn btn-secondary">Cancel</button> -->
<div id='editor_holder'></div>
<script type="text/javascript">
let schema = ${JSON.stringify(formSchema, null, 1)};
var editor = new JSONEditor(document.getElementById('editor_holder'),{
theme: "bootstrap4",
template: "handlebars",
iconlib: "fontawesome5",
disable_collapse: true,
disable_edit_json: true,
disable_properties: true,
schema: schema,
${insertValue}
show_errors: "always"
});
function submit() {
// Get the value from the editor
const errors = editor.validate();
if (errors.length) {
console.log(errors);
}
else {
insertPressedEvent(JSON.stringify(editor.getValue()));
}
}
function cancel() {
cancelPressedEvent();
}
window.JSONEditor.defaults.callbacks.button = {
submitform: function(jseditor,e) {
var res = jseditor.jsoneditor.getValue();
insertPressedEvent(JSON.stringify(res));
},
cancelform: function(jseditor,e) {
cancelPressedEvent();
},
resetform: function(jseditor,e) {
jseditor.jsoneditor.setValue({});
}
};
let insertElement = document.querySelector("button[name='Insert']");
console.log(insertElement);
</script>
</body>
</html>`;
var insertPressed = false;
var cancelPressed = false;
let browser = new Browser(newShell, SWT.NONE);
var value;
browser.addProgressListener(new ProgressAdapter({
completed: function (event) {
let fncOk = new CustomFunction(browser, "insertPressedEvent", {
function: function (args) {
value = args[0];
insertPressed = true;
}
});
let fncCancel = new CustomFunction(browser, "cancelPressedEvent", {
function: function (args) {
cancelPressed = true;
}
});
browser.addLocationListener(new LocationAdapter({
changed: function (e) {
browser.removeLocationListener(this);
fncOk.dispose();
fncCancel.dispose();
}
}));
}
}));
// Write the HTML to a temporary file, so we are allowed to execute a local script
let System = Java.type('java.lang.System');
let tmpfile = System.getProperty("java.io.tmpdir") + "form.html";
$.fs.writeFile(tmpfile, html);
browser.setUrl("file:///" + tmpfile);
// Set icon to Archi icon, in case shell has a style which displays icons
newShell.setImage(IArchiImages.ImageFactory.getImage(IArchiImages.ICON_APP));
newShell.setSize(width, height);
// Center dialog on screen
const primary = display.getPrimaryMonitor();
const bounds = primary.getBounds();
const rect = newShell.getBounds();
const x = bounds.x + (bounds.width - rect.width) / 2;
const y = bounds.y + (bounds.height - rect.height) / 2;
newShell.setLocation(x, y);
newShell.open();
while (!newShell.isDisposed() && !insertPressed && !cancelPressed) {
if (!display.readAndDispatch()) display.sleep();
}
if (insertPressed && value != undefined && value != null) {
newShell.dispose();
}
else if (cancelPressed) {
console.log('Dialog cancelled.');
newShell.dispose();
}
if (value) return JSON.parse(value);
else return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment