Skip to content

Instantly share code, notes, and snippets.

@ThomasRohde
Last active July 7, 2023 14:39
Show Gist options
  • Save ThomasRohde/8510e13e05681e990d826bd1bea9a84f to your computer and use it in GitHub Desktop.
Save ThomasRohde/8510e13e05681e990d826bd1bea9a84f to your computer and use it in GitHub Desktop.
/*
Author: Thomas Klok Rohde
Description: Selection sensitive menu system for curated scripts
History:
October 2, 2022 : Created with base set of scripts
*/
console.show();
console.clear();
/*
Describe the menu structure as a list of menu entries:
{
name: <name to display in menu>
file: <filename of script>
description: <text describing the script to display in the menu>
types: <list of types. For the script to be in the context menu, the selected elements must all have types included in the types list.>
selector: <function taking a single argument of type Archi element. For the script to be in the context menu, the selector function must return true for all selected elements.>
}
*/
const scripts = [
{
name: "Set Confluence publishing options",
file: "Set Confluence export options.ajs",
category: "Publishing & exporting",
description: "This script must be run prior to publishing to Confluence. The options are saved and persist over Archi sessions.",
types: []
},
{
name: "Publish template to Confluence",
file: "Export to Confluence.ajs",
category: "Publishing & exporting",
description: "Publish the selected template sketch view to Confluence. ",
types: ["sketch-model"]
},
{
name: "Publish views to Confluence",
file: "Export to Confluence.ajs",
category: "Publishing & exporting",
description: "Publish the selected views as a single Confluence page.",
types: ["archimate-diagram-model"]
},
{
name: "Export views to Gliffy file",
file: "Export to Gliffy.ajs",
category: "Publishing & exporting",
description: "Publish the selected views to Gliffy files ready for embedding in Confluence.",
types: ["archimate-diagram-model"]
},
{
name: "Set Verdana as font for all elements and relations",
file: "Set Verdana as font for all elements.ajs",
category: "Appearance",
description: "Sets the font to Verdana on all elements and relations in the selected model.",
types: []
},
{
name: "Add unshown relationships",
file: "Add unshown relationships.ajs",
category: "Modeling",
description: "Adds relationships which exist in the model but not in the selected views.",
types: ["archimate-diagram-model"]
},
{
name: "Apply treemap layout on selected views",
file: "Apply treemap layout on selected views or elements.ajs",
category: "Layout",
description: "Apply treemap layout on selected views.",
types: ["archimate-diagram-model"]
},
{
name: "Set specialization label on all elements and relations",
file: "Set specialization label on all elements and relations.ajs",
category: "Modeling",
description: "Sets a label expression on alle elements and relations with specializations. The format is similar to displayed prototypes in BiZZdesign Enterprise Studio (using << and >>)",
types: []
},
{
name: "Property bulk editor",
file: "Edit properties.ajs",
category: "Modeling",
description: "Edit properties for a selection of objects. This script will create a dialog to edit properties of a selection of Archi objects. The dialog shows a list of all properties found in one or more objects.",
types: []
},
{
name: "Delete unused elements and relations",
file: "Delete unused.ajs",
category: "Modeling",
description: "This script will delete any element or relationship not used in at least one view.",
types: []
},
{
name: "Export views to SVG",
file: "Export views to SVG.ajs",
category: "Publishing & exporting",
description: "Export selected views to SVG file format. The SVG file can be dragged onto a Miro board, or attached to a Confluence page (press ! in the editor to insert SVG attachment).",
types: ["archimate-diagram-model"]
},
{
name: "Copy view to clipboard",
file: "Copy view as CSV to clipboard.ajs",
category: "Publishing & exporting",
description: "Export selected views a custom CSV format and copies it to the clipboard. This can be pasted into any program, that can be made to interpret the data to recreate the view and the elements and relationships contained in the view.\nCurrently, due to a bug in BiZZdesign clipboard functionality, the script also places the CSV in a temporary file.",
types: ["archimate-diagram-model"]
},
{
name: "Copy view as SVG image to clipboard",
file: "Copy view as SVG image to clipboard.ajs",
category: "Publishing & exporting",
description: "Copy the seleceted view as an SVG image to the system clipboard. The SVG can be pasted into modern Microsoft Office programs.",
types: ["archimate-diagram-model", "sketch-model", "canvas-model"]
},
{
name: "Copy view as SVG string to clipboard",
file: "Copy view as SVG string to clipboard.ajs",
category: "Publishing & exporting",
description: "Copy the seleceted view as an SVG string to the system clipboard. The SVG can be pasted into a Confluence HTML macro (not recommended).",
types: ["archimate-diagram-model", "sketch-model", "canvas-model"]
},
{
name: "Copy view as SVG file to clipboard",
file: "Copy view as SVG file to clipboard.ajs",
category: "Publishing & exporting",
description: "Copy the seleceted view as an SVG file to the system clipboard via a temporary SVG file. The SVG file can be pasted into modern Microsoft Office programs, as well as other programs that support pasting of SVG files, e.g., Miro",
types: ["archimate-diagram-model", "sketch-model", "canvas-model"]
},
{
name: "Layout view",
file: "Layout view.ajs",
category: "Layout",
description: "Layout view using the Eclipse Layout Kernel (Elkjs) and a chosen algorithm. Read more about ELK here: https://www.eclipse.org/elk/",
types: ["archimate-diagram-model"]
},
{
name: "Remove all bend points",
file: "Remove all bend points.ajs",
category: "Layout",
description: "Remove bend points from relations in selection.",
types: [],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Create L-shaped relations",
file: "Create L-shaped relations.ajs",
category: "Layout",
description: "Create L-shaped relations, replacing existing relations in the selection.",
types: [],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Create S-shaped relations",
file: "Create S-shaped relations.ajs",
category: "Layout",
description: "Create S-shaped relations, replacing existing relations in the selection.",
types: [],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Create orthogonal relations",
file: "Create orthogonal relations.ajs",
category: "Layout",
description: "Create orthogonal relations, replacing existing relations in the selection.",
types: [],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Spread relations orthogonally",
file: "Spread relations orthogonally.ajs",
category: "Layout",
description: "Arrange and spread equally multiple visual relationships along the edges of an element.",
types: [],
selector: e => (e.type === "folder") ? false : true
}, {
name: "Insert chart",
file: "Insert chart.ajs",
category: "Charting",
description: "Select, configure and insert a chart into an existing diagram note in an ArchiMate view",
types: ["diagram-model-note"],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Refresh chart",
file: "Refresh chart.ajs",
category: "Charting",
description: "Refresh an existing chart with the current model data",
types: ["diagram-model-note"],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Open chart",
file: "Browse chart.ajs",
category: "Charting",
description: "Open chart in a browser tab",
types: ["diagram-model-note"],
selector: e => (e.type === "folder") ? false : true
},
{
name: "Paste view",
file: "Paste view.ajs",
category: "Import",
description: "Paste a Archi script from the clipboard and exectute. This script will typically create elements, relations and views copied from another tool.",
types: ["folder"],
selector: e => (e.name === "Views") ? true : false
}
];
if (false) {
load(__DIR__ + "lib/marked.js");
markdown = "";
scripts.sort((a, b) => { if (a.category < b.category) return -1; if (a.category > b.category) return 1; return 0; });
scripts.forEach(s => {
markdown += `## ${s.name}` + "\n\n";
markdown += `Category: ${s.category}` + "\n\n";
markdown += `${s.description}` + "\n\n";
});
console.log(marked.parse(markdown));
}
// Dialog code borrowed from https://gist.github.com/rchevallier/d155527dcc3e956848a90e51bf21d7e1
const _SWT = Java.type('org.eclipse.swt.SWT');
const _GridDataFactory = Java.type('org.eclipse.jface.layout.GridDataFactory');
const _GridLayoutFactory = Java.type('org.eclipse.jface.layout.GridLayoutFactory');
const _CompositeWidget = Java.type('org.eclipse.swt.widgets.Composite');
const _TreeWidget = Java.type('org.eclipse.swt.widgets.Tree');
const _TreeItemWidget = Java.type('org.eclipse.swt.widgets.TreeItem');
const _LabelWidget = Java.type('org.eclipse.swt.widgets.Label');
const _IMessageProvider = Java.type('org.eclipse.jface.dialogs.IMessageProvider');
const _ListWidget = Java.type('org.eclipse.swt.widgets.List');
const _TextWidget = Java.type('org.eclipse.swt.widgets.Text');
const _SelectionAdapter = Java.type('org.eclipse.swt.events.SelectionAdapter')
const _SelectDialog = Java.extend(Java.type("org.eclipse.jface.dialogs.TitleAreaDialog"));
let _selDialog = {
scriptList: scripts.filter(
function (script) {
function screen() {
if (("selector" in script) && (typeof script.selector === 'function')) {
for (let selectedElement of $(selection)) {
if (!script.selector(selectedElement)) return false;
}
}
return true;
}
// Case when no array of types is given
if ((!script.types?.length)) {
// If no selector is available, always return true
if (!("selector" in script) || (!(typeof script.selector === 'function'))) return true;
// Else use the selector to determine whether the script can be selected
return screen();
}
// Return false if a selected element does not match any of the allowed types for this script
for (let selectedElement of $(selection)) {
if (!script.types.includes(selectedElement.type)) return false;
}
// Finally, if a selector is given, let the selector determine if the script is selected
return screen();
}),
scriptNames: [],
selectedScript: "",
// widgets memorized to get value from before closing
widgets: {},
open: function () {
this.scriptList.forEach((s) => this.scriptNames.push(s.name));
return (this.dialog.open() == 0) // OK = 0, Cancel = 1, Closed = -1
},
dialog: new _SelectDialog(shell,
{
treeWidget: null,
textWidget: null,
create: function () {
Java.super(_selDialog.dialog).create();
_selDialog.dialog.setTitle("Script library");
_selDialog.dialog.setMessage("Please select a script to execute", _IMessageProvider.NONE);
},
widgetSelected: function (e) {
let script = null;
scripts.forEach((s) => { if (s.name == treeWidget.getSelection()[0].getText()) script = s });
if (script) textWidget.setText(script.description);
else textWidget.setText("");
},
createDialogArea: function (parent) {
let area = Java.super(_selDialog.dialog).createDialogArea(parent);
let container = new _CompositeWidget(area, _SWT.NONE);
_GridDataFactory.swtDefaults().align(_SWT.FILL, _SWT.BEGINNING).applyTo(container);
_GridLayoutFactory.swtDefaults().numColumns(2).margins(10, 10).spacing(10, 5).applyTo(container);
treeWidget = new _TreeWidget(container, _SWT.V_SCROLL | _SWT.BORDER);
// Currently, we cannot explicitly select GraalJS overloads, and neither can we set the parent of a TreeItem, so we have to construct the tree manually
let publishingItem = new _TreeItemWidget(treeWidget, 0);
publishingItem.setText("Publishing & exporting");
let appearanceItem = new _TreeItemWidget(treeWidget, 0);
appearanceItem.setText("Appearance");
let modelingItem = new _TreeItemWidget(treeWidget, 0);
modelingItem.setText("Modeling");
let layoutItem = new _TreeItemWidget(treeWidget, 0);
layoutItem.setText("Layout");
let chartingItem = new _TreeItemWidget(treeWidget, 0);
chartingItem.setText("Charting");
let importItem = new _TreeItemWidget(treeWidget, 0);
importItem.setText("Import");
let miscItem = new _TreeItemWidget(treeWidget, 0);
miscItem.setText("Miscelaneous");
_selDialog.scriptList.forEach(s => {
switch (s.category) {
case "Publishing & exporting":
treeItemWidget = new _TreeItemWidget(publishingItem, 0);
break;
case "Appearance":
treeItemWidget = new _TreeItemWidget(appearanceItem, 0);
break;
case "Modeling":
treeItemWidget = new _TreeItemWidget(modelingItem, 0);
break;
case "Layout":
treeItemWidget = new _TreeItemWidget(layoutItem, 0);
break;
case "Charting":
treeItemWidget = new _TreeItemWidget(chartingItem, 0);
break;
case "Import":
treeItemWidget = new _TreeItemWidget(importItem, 0);
break;
default:
treeItemWidget = new _TreeItemWidget(miscItem, 0);
}
treeItemWidget.setText(s.name);
})
_selDialog.widgets["script"] = treeWidget;
_GridDataFactory.fillDefaults().grab(true, true).hint(300, 300).applyTo(treeWidget);
textWidget = new _TextWidget(container, _SWT.MULTI | _SWT.BORDER | _SWT.V_SCROLL | _SWT.WRAP);
_GridDataFactory.fillDefaults().grab(true, true).hint(300, 300).applyTo(textWidget);
treeWidget.addSelectionListener(this);
// Filler widget that is needed for some reason
let labeWidget = new _LabelWidget(container, _SWT.NONE);
return area
},
okPressed: function () {
_selDialog.selectedScript = _selDialog.widgets.script.getSelection()[0].getText();
Java.super(_selDialog.dialog).okPressed();
}
})
}
if (_selDialog.open()) {
console.log(`Executing script: "${_selDialog.selectedScript}"`);
let script = null;
scripts.forEach((s) => { if (s.name == _selDialog.selectedScript) script = s });
if (script) load(__DIR__ + script.file);
}
@fredlegrain
Copy link

fredlegrain commented Jul 7, 2023

Many thanks Thomas for the many shares on #jArchi.

I have an error with this one, running inside Archi 4.9.3r1 :

Script%20library.ajs:248:31 Expected an operand but found .
if ((!script.types?.length)) {
^ in at line number 1

Is it because of the version or am I missing something else?
##EDIT##
jArchi was running the Nashorn ES6.
Works fine with GraalVM.
##EDIT##

Cheers,
Frédéric

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment