Typescript #2
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,4 +7,7 @@ CHANGELOG.md
|
||||
test.json
|
||||
server.py
|
||||
/workflows
|
||||
/spaß
|
||||
/spaß
|
||||
/node_modules
|
||||
*.js
|
||||
bundle.js.LICENSE.txt
|
||||
|
||||
65
editor.html
65
editor.html
@ -7,30 +7,25 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Editor</title>
|
||||
<!-- <script src="//unpkg.com/force-graph"></script> -->
|
||||
<script src="https://unpkg.com/force-graph@1.43.0/dist/force-graph.min.js"></script>
|
||||
<!-- <script src="./force-graph-master/src/force-graph.js"></script> -->
|
||||
<!--<script src="../../dist/force-graph.js"></script>-->
|
||||
<script src="https://cdn.jsdelivr.net/gh/nextapps-de/flexsearch@0.7.31/dist/flexsearch.bundle.js"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="./editor.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="editor">
|
||||
<div id="curtain" onclick="closeFileDisplay()"></div> <!--Backdrop behind the file menu-->
|
||||
<div id="curtain"></div> <!--Backdrop behind the file menu-->
|
||||
<div id="submenu-backdrop"></div>
|
||||
<div id="mainmenu" class="menu-lightmode"> <!--Horizontal main menu-->
|
||||
<div class="menuitem" onclick="openFileMenu(this)">
|
||||
<div id="file-menu-btn" class="menuitem">
|
||||
<div class="menubutton">File</div>
|
||||
<div id="filemenu" class="submenu" onclick="event.stopPropagation()">
|
||||
<div class="menutop menu-lightmode">New Workflow</div>
|
||||
<div class="menucenter menu-lightmode" onclick="openFileDisplay()">Open</div>
|
||||
<div id="open-file" class="menucenter menu-lightmode">Open</div>
|
||||
<div class="menucenter menu-lightmode">Save</div>
|
||||
<div class="menucenter menu-lightmode">Save As</div>
|
||||
<div class="menubottom menu-lightmode">Export</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuitem" onclick="openEditMenu(this)">
|
||||
<div id="edit-menu-btn" class="menuitem">
|
||||
<div class="menubutton">Edit</div>
|
||||
<div id="editmenu" class="submenu" onclick="event.stopPropagation()">
|
||||
<div class="menutop menu-lightmode">Undo</div>
|
||||
@ -39,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<div class="menubottom menu-lightmode">Run Linter</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuitem" onclick="openViewMenu(this)">
|
||||
<div id="view-menu-btn" class="menuitem">
|
||||
<div class="menubutton">View</div>
|
||||
<div class="submenu menu-lightmode" onclick="event.stopPropagation()">
|
||||
<table id="view-table">
|
||||
@ -48,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<label for="actor">Highlight actor:</label>
|
||||
</td>
|
||||
<td>
|
||||
<select name="actor" id="actor" onchange="selectActor();"></select>
|
||||
<select name="actor" id="actor"></select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -56,13 +51,13 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<label for="viewer">Highlight viewer:</label>
|
||||
</td>
|
||||
<td>
|
||||
<select name="viewer" id="viewer" onchange="selectViewer();"></select>
|
||||
<select name="viewer" id="viewer"></select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuitem" onclick="openSettingsMenu(this)">
|
||||
<div id="settings-menu-btn" class="menuitem">
|
||||
<div class="menubutton">Settings</div> <!--Main settings menu-->
|
||||
<div class="submenu menu-lightmode" onclick="event.stopPropagation()">
|
||||
<table id="settings-table">
|
||||
@ -75,7 +70,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<td>
|
||||
<label class="toggle" title="Dark Mode">
|
||||
<input type="checkbox">
|
||||
<span class="toggle-slider" onclick="toggleTheme()"></span>
|
||||
<span id="theme-toggle" class="toggle-slider"></span>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
@ -127,14 +122,14 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuitem" onclick="openAboutMenu(this)">
|
||||
<div id="about-menu-btn" class="menuitem">
|
||||
<div class="menubutton">About</div>
|
||||
<div class="submenu menu-lightmode">Visualiser & editor for Uni2work workflows</div>
|
||||
</div>
|
||||
<div class="menuitem" onclick="openSearchMenu(this)">
|
||||
<div id="search-menu-btn" class="menuitem">
|
||||
<div id="search-container">
|
||||
<input id="search-input" type="text" placeholder="Search" onclick="showSearchResults()" oninput="search(this.value)">
|
||||
<span class="search-button" onclick="searchInput.focus()">
|
||||
<input id="search-input" type="text" placeholder="Search">
|
||||
<span class="search-button">
|
||||
<svg id="search-icon" class="search-icon-lightmode" height="18" width="18" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="11" cy="7" r="6" stroke-width="2" fill="none" />
|
||||
<polyline points="6,12 2,16" style="fill:none;stroke-width:2" stroke-linecap="round" />
|
||||
@ -182,7 +177,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
Node
|
||||
</div>
|
||||
<h1 id="sideheading">Hello</h1>
|
||||
<svg class="close" height="15" width="15" xmlns="http://www.w3.org/2000/svg" onclick="deselect()">
|
||||
<svg id="close-side-panel" class="close" height="15" width="15" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,1 14,14" style="fill:none;stroke:rgb(120, 120, 120);stroke-width:2" stroke-linecap="round" />
|
||||
<polyline points="14,1 1,14" style="fill:none;stroke:rgb(120, 120, 120);stroke-width:2" stroke-linecap="round" />
|
||||
X
|
||||
@ -190,16 +185,16 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</div>
|
||||
<div id="sidecontent"></div>
|
||||
<div id="sidebuttons">
|
||||
<button type="submit">Apply</button>
|
||||
<button type="reset" onclick="deselect()">Cancel</button>
|
||||
<button type="submit" onclick="focusSelection()">Focus</button>
|
||||
<button type="submit" onclick="removeSelection()" style="position: absolute; right: 0px;">Delete</button>
|
||||
<button id="side-panel-apply" type="submit">Apply</button>
|
||||
<button id="side-panel-cancel" type="reset">Cancel</button>
|
||||
<button id="side-panel-focus" type="submit">Focus</button>
|
||||
<button id="side-panel-delete" type="submit" style="position: absolute; right: 0px;">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="filepanel" class="menu-lightmode"> <!--Pop-up panel covering the center of the screen for file interactions-->
|
||||
<div id="fileheader">
|
||||
<h2 id="fileheading">Hello</h2>
|
||||
<svg class="close" height="15" width="15" xmlns="http://www.w3.org/2000/svg" onclick="closeFileDisplay()">
|
||||
<svg id="close-file-panel" class="close" height="15" width="15" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,1 14,14" style="fill:none;stroke:rgb(120, 120, 120);stroke-width:2" stroke-linecap="round" />
|
||||
<polyline points="14,1 1,14" style="fill:none;stroke:rgb(120, 120, 120);stroke-width:2" stroke-linecap="round" />
|
||||
X
|
||||
@ -208,11 +203,11 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<div id="filecontent"></div>
|
||||
<div id="filebuttons">
|
||||
<!-- <button type="submit">Load</button> -->
|
||||
<button type="reset" onclick="closeFileDisplay()">Cancel</button>
|
||||
<button id="file-panel-cancel" type="reset">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ctmenubg" class="contextmenu"> <!--Context menu displayed after right-clicking the background-->
|
||||
<div class="menutop menu-lightmode" onclick="addState()">
|
||||
<div id="add-state" class="menutop menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="5" cy="5" r="4" stroke-width="2" fill="none" />
|
||||
</svg>
|
||||
@ -226,19 +221,19 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</div>
|
||||
</div>
|
||||
<div id="ctmenust" class="contextmenu"> <!--Context menu displayed after right-clicking a state-->
|
||||
<div class="menutop menu-lightmode" onclick="markEdgeFrom()">
|
||||
<div id="edge-from" class="menutop menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,1 9,9" style="fill:none;stroke-width:2" stroke-linecap="round" />
|
||||
</svg>
|
||||
New Edge from here
|
||||
</div>
|
||||
<div class="menucenter menu-lightmode" onclick="markEdgeTo()">
|
||||
<div id="edge-to" class="menucenter menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,9 9,1" style="fill:none;stroke-width:2" stroke-linecap="round" />
|
||||
</svg>
|
||||
New Edge to here
|
||||
</div>
|
||||
<div class="menucenter menu-lightmode" onclick="rightSelect()">
|
||||
<div class="edit-item menucenter menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,9 9,1" style="fill:none;stroke-width:4" />
|
||||
<polyline points="1,9 8,2" style="fill:none;stroke-width:2" stroke-linecap="round" />
|
||||
@ -253,7 +248,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</svg>
|
||||
Duplicate
|
||||
</div>
|
||||
<div class="menubottom menu-lightmode" onclick="removeRightSelection()">
|
||||
<div class="delete-item menubottom menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,1 9,9" style="fill:none;stroke:rgb(183, 76, 76);stroke-width:2" stroke-linecap="round" />
|
||||
<polyline points="9,1 1,9" style="fill:none;stroke:rgb(183, 76, 76);stroke-width:2" stroke-linecap="round" />
|
||||
@ -262,7 +257,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</div>
|
||||
</div>
|
||||
<div id="ctmenued" class="contextmenu"> <!--Context menu displayed after right-clicking an edge-->
|
||||
<div class="menutop menu-lightmode" onclick="rightSelect()">
|
||||
<div class="edit-item menutop menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,9 9,1" style="fill:none;stroke-width:4" />
|
||||
<polyline points="1,9 8,2" style="fill:none;stroke-width:2" stroke-linecap="round" />
|
||||
@ -277,7 +272,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</svg>
|
||||
Duplicate
|
||||
</div>
|
||||
<div class="menubottom menu-lightmode" onclick="removeRightSelection()">
|
||||
<div class="delete-item menubottom menu-lightmode">
|
||||
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="1,1 9,9" style="fill:none;stroke:rgb(183, 76, 76);stroke-width:2" stroke-linecap="round" />
|
||||
<polyline points="9,1 1,9" style="fill:none;stroke:rgb(183, 76, 76);stroke-width:2" stroke-linecap="round" />
|
||||
@ -288,7 +283,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
<div id="graph"></div> <!--The graph canvas-->
|
||||
</div>
|
||||
|
||||
<script src="./workflow.js"></script>
|
||||
<script src="./editor.js"></script>
|
||||
<script src="./keyboard.js"></script>
|
||||
<!-- <script src="./workflow.ts"></script> -->
|
||||
<script type="module" src="bundle.js"></script>
|
||||
<!-- <script src="./keyboard.ts"></script> -->
|
||||
</body>
|
||||
File diff suppressed because it is too large
Load Diff
17
flake.nix
17
flake.nix
@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
{
|
||||
description = "Develop environment for the Workflow Visualiser";
|
||||
description = "Development environment for the Workflow Visualiser";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
@ -43,17 +43,20 @@
|
||||
|
||||
devShell = {
|
||||
enable = true;
|
||||
mkShellArgs.shellHook = ''
|
||||
if command -v zsh &> /dev/null; then
|
||||
zsh && exit
|
||||
fi
|
||||
'';
|
||||
mkShellArgs = {
|
||||
shellHook = ''
|
||||
if command -v zsh &> /dev/null; then
|
||||
zsh && exit
|
||||
fi
|
||||
'';
|
||||
buildInputs = with pkgs; [ nodejs_18 reuse ];
|
||||
};
|
||||
|
||||
# Programs you want to make available in the shell.
|
||||
# Default programs can be disabled by setting to 'null'
|
||||
# tools = hp: { fourmolu = hp.fourmolu; ghcid = null; };
|
||||
|
||||
hlsCheck.enable = true;
|
||||
hlsCheck.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
27
keyboard.js
27
keyboard.js
@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
console.log(e.ctrlKey, e.key);
|
||||
if (e.key === 'Escape') {
|
||||
closeContextMenus(contextMenuEd, contextMenuSt, contextMenuBg);
|
||||
closeMenuItem();
|
||||
closeFileDisplay();
|
||||
deselect();
|
||||
rightSelection = null;
|
||||
searchInput.blur();
|
||||
} else if (!e.ctrlKey) return;
|
||||
switch (e.key) {
|
||||
case 'f':
|
||||
e.preventDefault();
|
||||
searchInput.focus();
|
||||
openSearchMenu(searchContainer.parentElement);
|
||||
break;
|
||||
case 'o':
|
||||
e.preventDefault();
|
||||
openFileDisplay();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
2618
package-lock.json
generated
Normal file
2618
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
package-lock.json.license
Normal file
3
package-lock.json.license
Normal file
@ -0,0 +1,3 @@
|
||||
SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
30
package.json
Normal file
30
package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "workflow-visualiser",
|
||||
"version": "1.0.0",
|
||||
"description": "Visualiser for Uni2work workflows",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "bash ./start.sh",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitlab.uniworx.de/uni2work/workflows/workflow-visualiser"
|
||||
},
|
||||
"author": "David Mosbach",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"commonjs": "^0.0.1",
|
||||
"flexsearch": "0.7.11",
|
||||
"force-graph": "^1.43.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/flexsearch": "^0.7.3",
|
||||
"@types/node": "^20.5.3",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"ts-loader": "^9.4.4",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
3
package.json.license
Normal file
3
package.json.license
Normal file
@ -0,0 +1,3 @@
|
||||
SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
13
start.sh
Executable file
13
start.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
|
||||
echo 'Transpiling to JS...'
|
||||
npx tsc
|
||||
echo 'Generating Webpack bundle...'
|
||||
npx webpack
|
||||
echo 'Starting server...'
|
||||
npx http-server --cors -o ./editor.html
|
||||
109
tsconfig.json
Normal file
109
tsconfig.json
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
//"lib": ["es2022"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "es6", /* Specify what module code is generated. */
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
3
tsconfig.json.license
Normal file
3
tsconfig.json.license
Normal file
@ -0,0 +1,3 @@
|
||||
SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
21
webpack.config.cjs
Normal file
21
webpack.config.cjs
Normal file
@ -0,0 +1,21 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './editor.ts',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, '.')
|
||||
},
|
||||
};
|
||||
152
workflow.js
152
workflow.js
@ -1,152 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
class Role {
|
||||
|
||||
constructor(json) {
|
||||
this.json = json;
|
||||
if (json.tag == 'payload-reference') {
|
||||
this.name = json['payload-label'];
|
||||
} else if (json.authorized) {
|
||||
this.name = json.authorized['dnf-terms'][0][0].var + ' (auth)';
|
||||
} else if (json.user) {
|
||||
this.name = json.user;
|
||||
} else if (json.tag) {
|
||||
this.name = json.tag + ' (tag)';
|
||||
} else {
|
||||
this.name = JSON.stringify(json);
|
||||
}
|
||||
}
|
||||
|
||||
format() {
|
||||
return [document.createTextNode(this.name)];
|
||||
}
|
||||
}
|
||||
|
||||
class Roles {
|
||||
constructor(json, roleName) {
|
||||
this.roleName = roleName
|
||||
this.anchor = json.anchor && new Anchor(json.anchor)
|
||||
this[roleName] = [];
|
||||
for (const role of json[roleName])
|
||||
this[roleName].push(new Role(role));
|
||||
this.comment = json.comment;
|
||||
}
|
||||
|
||||
format() {
|
||||
var r = document.createElement('h4');
|
||||
var roles = document.createTextNode('Roles');
|
||||
r.appendChild(roles);
|
||||
var rolesList = document.createElement('ul');
|
||||
this[this.roleName].forEach(r => {
|
||||
var role = document.createElement('li');
|
||||
role.appendChild(document.createTextNode(r.name));
|
||||
rolesList.appendChild(role);
|
||||
});
|
||||
var result = [];
|
||||
if (this.comment.length > 0) {
|
||||
var c = document.createElement('h4');
|
||||
c.innerText = 'Comment';
|
||||
var comment = document.createElement('p');
|
||||
comment.innerText = this.comment.join(' ');
|
||||
result.push(c, comment);
|
||||
}
|
||||
if (this.anchor) {
|
||||
var a = document.createElement('h4');
|
||||
a.appendChild(this.anchor.format());
|
||||
result.push(a);
|
||||
} else result.push(r)
|
||||
result.push(rolesList);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class Viewers extends Roles {
|
||||
constructor(json) {
|
||||
super(json, 'viewers');
|
||||
}
|
||||
}
|
||||
|
||||
class Actors extends Roles {
|
||||
constructor(json) {
|
||||
super(json, 'actors');
|
||||
}
|
||||
}
|
||||
class Anchor {
|
||||
constructor(json) {
|
||||
this.name = json.name;
|
||||
this.type = json.type;
|
||||
}
|
||||
|
||||
format() {
|
||||
return document.createTextNode(`${this.type == 'alias' ? '*' : '&'}${this.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
class Message {
|
||||
|
||||
constructor(json) {
|
||||
var content = json.content;
|
||||
this.fallback = content.fallback;
|
||||
this.fallbackLang = content['fallback-lang'];
|
||||
this.translations = content.translations;
|
||||
this.status = json.status;
|
||||
this.viewers = new Viewers(json.viewers);
|
||||
|
||||
}
|
||||
|
||||
format() {
|
||||
var v = document.createElement('h3');
|
||||
var viewers = document.createTextNode('Viewers');
|
||||
v.appendChild(viewers);
|
||||
var viewerList = this.viewers.format();
|
||||
var h = document.createElement('h3');
|
||||
var heading = document.createTextNode('Status');
|
||||
h.appendChild(heading);
|
||||
var p = document.createElement('p');
|
||||
var text = document.createTextNode(this.status);
|
||||
p.appendChild(text);
|
||||
var result = [v];
|
||||
result = result.concat(viewerList);
|
||||
result.push(h, p);
|
||||
h = document.createElement('h3');
|
||||
heading = document.createTextNode(this.fallbackLang);
|
||||
h.appendChild(heading);
|
||||
p = document.createElement('html');
|
||||
p.setAttribute('lang', this.fallbackLang);
|
||||
p.innerHTML = this.fallback;
|
||||
result.push(h, p);
|
||||
for (var t in this.translations) {
|
||||
h = document.createElement('h3');
|
||||
heading = document.createTextNode(t);
|
||||
h.appendChild(heading);
|
||||
p = document.createElement('html');
|
||||
p.setAttribute('lang', this.translations[t]);
|
||||
p.innerHTML = this.translations[t];
|
||||
result.push(h, p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class Payload {
|
||||
|
||||
constructor(json) {
|
||||
this.fields = [];
|
||||
if (json === null) return;
|
||||
for (var f in json) {
|
||||
this.fields.push(f);
|
||||
}
|
||||
}
|
||||
|
||||
format() {
|
||||
var fieldList = document.createElement('ul');
|
||||
this.fields.forEach(f => {
|
||||
var field = document.createElement('li');
|
||||
field.appendChild(document.createTextNode(f));
|
||||
fieldList.appendChild(field);
|
||||
});
|
||||
return [fieldList];
|
||||
}
|
||||
}
|
||||
398
workflow.ts
Normal file
398
workflow.ts
Normal file
@ -0,0 +1,398 @@
|
||||
// SPDX-FileCopyrightText: 2023 David Mosbach <david.mosbach@campus.lmu.de>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import { LinkObject, NodeObject } from "force-graph";
|
||||
|
||||
export type WF = {
|
||||
states : NodeFormat[],
|
||||
actions : EdgeFormat[]
|
||||
}
|
||||
|
||||
export type NodeFormat = {
|
||||
stateData: SDFormat,
|
||||
x: number,
|
||||
y: number,
|
||||
fx: number,
|
||||
fy: number,
|
||||
id: string,
|
||||
name: string,
|
||||
val: number
|
||||
}
|
||||
|
||||
export type SDFormat = {
|
||||
abbreviation: string,
|
||||
final: boolean | string,
|
||||
messages?: MessageFormat[],
|
||||
viewers?: RolesFormat,
|
||||
payload?: string[]
|
||||
}
|
||||
|
||||
export type EdgeFormat = {
|
||||
actionData: ADFormat,
|
||||
source: WFNode | string,
|
||||
target: WFNode | string,
|
||||
id: string,
|
||||
name: string,
|
||||
nodePairId : string
|
||||
}
|
||||
|
||||
export type ActionMode = 'initial' | 'manual' | 'automatic';
|
||||
|
||||
export type ADFormat = {
|
||||
messages?: MessageFormat[],
|
||||
viewers?: RolesFormat,
|
||||
actors?: RolesFormat,
|
||||
['actor Viewers']?: RolesFormat,
|
||||
mode?: ActionMode,
|
||||
form?: string[]
|
||||
}
|
||||
|
||||
export type RoleFormat = {
|
||||
tag : string | null,
|
||||
authorized : JSON | null,
|
||||
user : string | null,
|
||||
'payload-label': string | null
|
||||
}
|
||||
|
||||
export type RoleName = 'actors' | 'viewers';
|
||||
|
||||
export type RolesFormat = {
|
||||
[roleName: string]: any,
|
||||
anchor: AnchorFormat,
|
||||
comment: string[]
|
||||
}
|
||||
|
||||
export type AnchorFormat = {
|
||||
name: string,
|
||||
type: AnchorType
|
||||
} | 'NoAnchor' | null //TODO either NoAnchor or null in parser
|
||||
|
||||
export type AnchorType = 'anchor' | 'alias' | 'none'
|
||||
|
||||
export type MessageFormat = {
|
||||
content: {
|
||||
fallback: string,
|
||||
'fallback-lang': string,
|
||||
translations: JSON
|
||||
},
|
||||
status: string,
|
||||
viewers: RolesFormat
|
||||
}
|
||||
|
||||
export class Workflow {
|
||||
|
||||
states: WFNode[];
|
||||
actions: WFEdge[];
|
||||
|
||||
constructor(json: WF) {
|
||||
const stateMap: Map<string, WFNode> = new Map();
|
||||
this.states = [];
|
||||
for (const state of json.states) {
|
||||
var node = new WFNode(state);
|
||||
this.states.push(node);
|
||||
stateMap.set(node.id, node);
|
||||
}
|
||||
// Resolve ID refs to nodes
|
||||
var actions = json.actions.map(action => {
|
||||
function transformRef(ref: string | WFNode) {
|
||||
if (ref instanceof String)
|
||||
stateMap.has(<string>ref)
|
||||
&& (ref = <WFNode>stateMap.get(<string>ref))
|
||||
|| console.error('Ref to unknown state: ' + ref);
|
||||
return ref;
|
||||
}
|
||||
action.source = transformRef(action.source);
|
||||
action.target = transformRef(action.target);
|
||||
return action;
|
||||
});
|
||||
|
||||
this.actions = [];
|
||||
for (const action of actions)
|
||||
this.actions.push(new WFEdge(action));
|
||||
}
|
||||
}
|
||||
|
||||
export class WFNode implements NodeObject {
|
||||
|
||||
stateData: StateData;
|
||||
x: number;
|
||||
y: number;
|
||||
fx: number;
|
||||
fy: number;
|
||||
id: string;
|
||||
name: string;
|
||||
val: number;
|
||||
|
||||
constructor(json: NodeFormat) {
|
||||
this.stateData = new StateData(json.stateData);
|
||||
this.x = json.x;
|
||||
this.y = json.y;
|
||||
this.fx = json.fx;
|
||||
this.fy = json.fy;
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.val = json.val;
|
||||
}
|
||||
}
|
||||
|
||||
export class StateData {
|
||||
|
||||
abbreviation: string;
|
||||
messages: Message[];
|
||||
viewers: Viewers;
|
||||
payload: Payload;
|
||||
viewerNames: string[];
|
||||
final: boolean | string;
|
||||
|
||||
constructor(json: SDFormat) {
|
||||
this.abbreviation = json.abbreviation;
|
||||
this.messages = json.messages ? json.messages.map(message => { return new Message(message) }) : [];
|
||||
this.viewers = json.viewers ? new Viewers(json.viewers) : Viewers.empty();
|
||||
this.payload = new Payload(json.payload);
|
||||
this.viewerNames = [];
|
||||
this.final = json.final;
|
||||
}
|
||||
}
|
||||
|
||||
export class WFEdge implements LinkObject {
|
||||
|
||||
actionData: ActionData;
|
||||
source: WFNode;
|
||||
target: WFNode;
|
||||
id: string;
|
||||
name: string;
|
||||
nodePairId : string;
|
||||
curvature: number;
|
||||
__controlPoints?: any;
|
||||
|
||||
constructor(json: EdgeFormat) {
|
||||
this.actionData = new ActionData(json.actionData);
|
||||
this.source = <WFNode>json.source;
|
||||
this.target = <WFNode>json.target;
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.nodePairId = json.nodePairId;
|
||||
this.curvature = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class ActionData {
|
||||
|
||||
messages: Message[];
|
||||
viewers: Viewers;
|
||||
actors: Actors;
|
||||
'actor Viewers': Viewers;
|
||||
form: Payload;
|
||||
viewerNames: string[];
|
||||
actorNames: string[];
|
||||
mode: ActionMode | undefined;
|
||||
|
||||
constructor(json: ADFormat) {
|
||||
this.messages = json.messages ? json.messages.map(message => { return new Message(message) }) : [];
|
||||
this.viewers = json.viewers ? new Viewers(json.viewers) : Viewers.empty();
|
||||
this.actors = json.actors ? new Actors(json.actors) : Actors.empty();
|
||||
this['actor Viewers'] = json['actor Viewers'] ? new Viewers(json['actor Viewers']) : Viewers.empty();
|
||||
this.form = new Payload(json.form);
|
||||
this.viewerNames = [];
|
||||
this.actorNames = [];
|
||||
this.mode = json.mode ?? undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class Role {
|
||||
|
||||
json: RoleFormat;
|
||||
name: string;
|
||||
|
||||
constructor(json: RoleFormat) {
|
||||
this.json = json;
|
||||
if (json.tag == 'payload-reference') {
|
||||
this.name = <string>json['payload-label' as keyof RoleFormat];
|
||||
} else if (json.authorized) {
|
||||
this.name = (<any[]><unknown>json.authorized['dnf-terms' as keyof JSON])[0][0].var + ' (auth)'; //TODO ugly
|
||||
} else if (json.user) {
|
||||
this.name = json.user;
|
||||
} else if (json.tag) {
|
||||
this.name = json.tag + ' (tag)';
|
||||
} else {
|
||||
this.name = JSON.stringify(json);
|
||||
}
|
||||
}
|
||||
|
||||
format() {
|
||||
return [document.createTextNode(this.name)];
|
||||
}
|
||||
}
|
||||
|
||||
export class Roles {
|
||||
|
||||
roleName: RoleName;
|
||||
anchor: Anchor;
|
||||
comment: string[];
|
||||
roles: Role[];
|
||||
|
||||
constructor(json: RolesFormat, roleName: RoleName) {
|
||||
this.roleName = roleName
|
||||
this.anchor = json.anchor ? new Anchor(json.anchor) : new Anchor('NoAnchor');
|
||||
this.roles = [];
|
||||
for (const role of json[roleName as keyof RolesFormat])
|
||||
this.roles.push(new Role(role));
|
||||
this.comment = json.comment;
|
||||
}
|
||||
|
||||
length() {
|
||||
return this.roles.length;
|
||||
}
|
||||
|
||||
format() {
|
||||
var r = document.createElement('h4');
|
||||
var roles = document.createTextNode('Roles');
|
||||
r.appendChild(roles);
|
||||
var rolesList = document.createElement('ul');
|
||||
this.roles.forEach(r => {
|
||||
var role = document.createElement('li');
|
||||
role.appendChild(document.createTextNode(r.name));
|
||||
rolesList.appendChild(role);
|
||||
});
|
||||
var result: HTMLElement[] = [];
|
||||
if (this.comment.length > 0) {
|
||||
var c = document.createElement('h4');
|
||||
c.innerText = 'Comment';
|
||||
var comment = document.createElement('p');
|
||||
comment.innerText = this.comment.join(' ');
|
||||
result.push(c, comment);
|
||||
}
|
||||
if (this.anchor) {
|
||||
var a = document.createElement('h4');
|
||||
a.appendChild(this.anchor.format());
|
||||
result.push(a);
|
||||
} else result.push(r)
|
||||
result.push(rolesList);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class Viewers extends Roles {
|
||||
|
||||
static empty() {
|
||||
return new Viewers({
|
||||
viewers: [],
|
||||
anchor: 'NoAnchor',
|
||||
comment: []
|
||||
})
|
||||
}
|
||||
|
||||
constructor(json: RolesFormat) {
|
||||
super(json, 'viewers');
|
||||
}
|
||||
}
|
||||
|
||||
export class Actors extends Roles {
|
||||
|
||||
static empty() {
|
||||
return new Actors({
|
||||
actors: [],
|
||||
anchor: 'NoAnchor',
|
||||
comment: []
|
||||
})
|
||||
}
|
||||
|
||||
constructor(json: RolesFormat) {
|
||||
super(json, 'actors');
|
||||
}
|
||||
}
|
||||
export class Anchor {
|
||||
|
||||
name: string | undefined;
|
||||
type: AnchorType;
|
||||
|
||||
constructor(json: AnchorFormat) {
|
||||
if (!json || json === 'NoAnchor') {
|
||||
this.name = undefined;
|
||||
this.type = 'none';
|
||||
} else {
|
||||
this.name = json.name;
|
||||
this.type = json.type;
|
||||
}
|
||||
}
|
||||
|
||||
format() {
|
||||
return document.createTextNode(`${this.type == 'alias' ? '*' : '&'}${this.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Message {
|
||||
|
||||
fallback: string;
|
||||
fallbackLang: string;
|
||||
translations: JSON;
|
||||
status: string;
|
||||
viewers: Viewers;
|
||||
|
||||
constructor(json: MessageFormat) {
|
||||
var content = json.content;
|
||||
this.fallback = content.fallback;
|
||||
this.fallbackLang = content['fallback-lang'];
|
||||
this.translations = content.translations;
|
||||
this.status = json.status;
|
||||
this.viewers = new Viewers(json.viewers);
|
||||
|
||||
}
|
||||
|
||||
format() {
|
||||
var v = document.createElement('h3');
|
||||
var viewers = document.createTextNode('Viewers');
|
||||
v.appendChild(viewers);
|
||||
var viewerList = this.viewers.format();
|
||||
var h = document.createElement('h3');
|
||||
var heading = document.createTextNode('Status');
|
||||
h.appendChild(heading);
|
||||
var p: HTMLElement = document.createElement('p');
|
||||
var text = document.createTextNode(this.status);
|
||||
p.appendChild(text);
|
||||
var result: HTMLElement[] = [v];
|
||||
result = result.concat(viewerList);
|
||||
result.push(h, p);
|
||||
h = document.createElement('h3');
|
||||
heading = document.createTextNode(this.fallbackLang);
|
||||
h.appendChild(heading);
|
||||
p = document.createElement('html');
|
||||
p.setAttribute('lang', this.fallbackLang);
|
||||
p.innerHTML = this.fallback;
|
||||
result.push(h, p);
|
||||
for (var t in this.translations) {
|
||||
h = document.createElement('h3');
|
||||
heading = document.createTextNode(t);
|
||||
h.appendChild(heading);
|
||||
p = document.createElement('html');
|
||||
p.setAttribute('lang', <string>this.translations[t as keyof JSON]);
|
||||
p.innerHTML = <string>this.translations[t as keyof JSON];
|
||||
result.push(h, p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class Payload {
|
||||
|
||||
fields: string[]
|
||||
|
||||
constructor(json: string[] | undefined) {
|
||||
this.fields = [];
|
||||
if (json === undefined) return;
|
||||
for (var f in json) {
|
||||
this.fields.push(f);
|
||||
}
|
||||
}
|
||||
|
||||
format() {
|
||||
var fieldList = document.createElement('ul');
|
||||
this.fields.forEach(f => {
|
||||
var field = document.createElement('li');
|
||||
field.appendChild(document.createTextNode(f));
|
||||
fieldList.appendChild(field);
|
||||
});
|
||||
return [fieldList];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user