Commit f5890e33 authored by Klaus Ramm's avatar Klaus Ramm

First refactor step, structogram view is usable

parent 890d2e08
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="struktogRAMM.min.css">
<script src="main.js"></script>
<title>Struktog.</title>
</head>
<body>
<!-- Header -->
<header class="container">
<div class="columns">
<div class="column col-12 navbar">
<section class="navbar-section">
<div class="text-large">
<a href="index.html" class="navbar-brand mr-2 text-dark">
<figure class="avatar" data-initial="SG"></figure>
<strong>Struktog.</strong>
</a>
</div>
</section>
<section class="navbar-section">
<p>
<span>
&#169; 2019 Klaus Ramm (
</span>
<a href="license.html">MIT License</a>
<span>
)
</span>
</p>
</section>
</div>
</div>
</header>
<div class="divider"></div>
<main>
<div class="container">
<div id="optionButtons" class="columns elementButtonColumns">
<div class="column col-6 col-md-12 pt-2">
</div>
<!-- Option button row -->
<div class="column col-auto">
<button id="ToggleSourcecode" class="btn" onclick="alterSourcecodeDisplay(this.id);" value="Quellcode" />
</div>
<div class="column col-auto elementButtonColumns">
<button class="btn" onclick="document.getElementById('IEModal').classList.add('active');">
Import / Export
</button>
</div>
</div>
<div id="editorDisplay" class="columns">
<!-- Transform to code -->
<div class="column col-3 col-md-12 col-ml-auto">
<div id='SourcecodeDisplay' class="columns d-hide">
<div class="column col-10 col-mx-auto">
<div>
<span>Übersetzen in:</span>
</div>
<div class="form-group">
<select id="SourcecodeSelect" class="form-select" onchange="startTransforming(this.value);">
<option value="--">--</option>
</select>
</div>
</div>
<div id="Sourcecode" class="column col-12 lowerPadding">
</div>
</div>
</div>
</div>
</div>
<!-- Popup modal for import and export -->
<div class="modal" id="IEModal">
<a href="#"
class="modal-overlay"
aria-label="Close"
onclick="document.getElementById('IEModal').classList.remove('active');"></a>
<div class="modal-container">
<div class="modal-header">
<a href="#"
class="btn btn-clear float-right"
aria-label="Close"
onclick="document.getElementById('IEModal').classList.remove('active');"></a>
<div class="modal-title h5">Import / Export</div>
</div>
<div class="modal-body">
<div class="content">
<div class="container">
<div class="columns">
<div class="column col-12">
<div class="columns">
<div class="col-12 lowerPadding">
<p>
JSON-Import:
</p>
<input id="ImportForm" type="file" class="form-input" \>
</div>
<div class="column col-12">
<p>
Export:
</p>
<div id="Export" class="text-center pt-1"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Licence Struktog.</title>
<style>
body {
width: 50%;
}
@media (max-width: 840px) {
body {width: 95%}
}
</style>
</head>
<body>
<p>The MIT License (MIT)</p>
<p>Copyright © 2019 Klaus Ramm</p>
<p>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
</p>
<p>
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
</p>
<p>
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</p>
</body>
</html>
This diff is collapsed.
The MIT License (MIT)
Copyright © 2019 Klaus Ramm
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This diff is collapsed.
{
"name": "drafttocode",
"version": "0.5.0",
"description": "Webprogramm zur Erstellung von Struktogrammen / PAP sowie zur Umwandlung dessen in Code (Python, PHP, Javascript)",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git@gitlab.hrz.tu-chemnitz.de:ddi-tudresden/ovk-informatik/drafttocode.git"
},
"keywords": [],
"author": "Klaus Ramm",
"license": "MIT",
"devDependencies": {
"webpack": "^4.41.1",
"webpack-cli": "^3.3.9"
}
}
/**
* Generate a random id string
*
* @ return string random generated
*/
export function guidGenerator() {
const gen = function() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
return (gen()+gen()+"-"+gen()+"-"+gen()+"-"+gen()+"-"+gen()+gen());
}
/**
* Main javascript of the Struktog. software
*
*
* @author Klaus Ramm <klaus@ramm-web.de>
* @copyright Klaus Ramm 25.06.2019
* @licence MIT License (MIT), see license.md
* @version 0.5
* @link struktog.ramm-web.de
*/
import { model } from './model/main';
import { Presenter } from './presenter/main';
import { Structogram } from './views/structogram';
// check the web storage for old data
if (typeof(Storage) !== "undefined") {
if ('tree' in localStorage) {
model.setTree(JSON.parse(localStorage.tree));
// TODO: model.displaySourcecode = JSON.parse(localStorage.displaySourcecode);
}
}
window.onload = function() {
// create presenter object
const presenter = new Presenter(model);
// TODO: this should not be necessary, but some functions depend on moveId and nextInsertElement
model.setPresenter(presenter);
// create our view objects
const structogram = new Structogram(presenter, document.getElementById("editorDisplay"));
presenter.addView(structogram);
presenter.init();
}
import {guidGenerator} from '../helpers/generator';
class Model {
constructor(tree = {'id': guidGenerator(),
'type': 'InsertNode',
'followElement': {'type': 'Placeholder'}
}) {
this.tree = tree;
this.presenter = null;
}
setPresenter(presenter) {
this.presenter = presenter;
}
getTree() {
return this.tree;
}
setTree(content) {
this.tree = content;
}
reset() {
this.tree = {'id': guidGenerator(),
'type': 'InsertNode',
'followElement': {'type': 'Placeholder'}
};
}
/**
* Find an element by id in the tree and use a function on that element
*
* @param subTree part of the tree with all children of current element
* @param alterFunction function to be executed on the found element
* @param hasRealParent indicates, if the parent element was a container node
* @param text the new text for the node
* @return subTree altered subTree object
*/
findAndAlterElement(uid, subTree, alterFunction, hasRealParent, text) {
// end the recursion
if (subTree === null || subTree.type == 'Placeholder') {
return subTree
} else {
if (subTree.id === uid) {
// call the given function
alterFunction = alterFunction.bind(this);
subTree = alterFunction(subTree, hasRealParent, text);
// reset the insert buttons, on drag and drop
if (this.presenter.getMoveId() === null) {
this.presenter.resetButtons();
}
return subTree;
} else {
switch (subTree.type) {
case 'InsertNode':
subTree.followElement = this.findAndAlterElement(uid, subTree.followElement, alterFunction, hasRealParent, text);
return subTree;
break;
case 'InputNode':
case 'OutputNode':
case 'TaskNode':
subTree.followElement = this.findAndAlterElement(uid, subTree.followElement, alterFunction, true, text);
return subTree;
break;
case 'HeadLoopNode':
case 'FootLoopNode':
case 'CountLoopNode':
subTree.child = this.findAndAlterElement(uid, subTree.child, alterFunction, false, text);
subTree.followElement = this.findAndAlterElement(uid, subTree.followElement, alterFunction, true, text);
return subTree;
break;
case 'BranchNode':
subTree.trueChild = this.findAndAlterElement(uid, subTree.trueChild, alterFunction, false, text);
subTree.falseChild = this.findAndAlterElement(uid, subTree.falseChild, alterFunction, false, text);
subTree.followElement = this.findAndAlterElement(uid, subTree.followElement, alterFunction, true, text);
return subTree;
break;
case 'CaseNode':
let nodes = [];
for (const element of subTree.cases) {
let val = this.findAndAlterElement(uid, element, alterFunction, false, text);
if (!(val === null)) {
nodes.push(val);
}
}
if (nodes.length >= 2) {
subTree.cases = nodes;
}
let valDefault = this.findAndAlterElement(uid, subTree.defaultNode, alterFunction, false, text);
if (valDefault === null) {
subTree.defaultOn = false;
} else {
subTree.defaultNode = valDefault;
}
subTree.followElement = this.findAndAlterElement(uid, subTree.followElement, alterFunction, true, text);
return subTree;
break;
case 'InsertCase':
subTree.followElement = this.findAndAlterElement(uid, subTree.followElement, alterFunction, hasRealParent, text);
return subTree;
break;
}
}
}
}
/**
* Remove the node and reconnect the follow element
*
* @param subTree part of the tree with all children of current element
* @param hasRealParent indicates if an Placeholder node has to be added
* @param text not used in this function
* @return subTree altered subTree object (without removed element)
*/
removeNode(subTree, hasRealParent, text) {
// InsertCases are just completly removed, they do not have follow elements
if (subTree.type == 'InsertCase') {
return null
}
// remove a node, but check if the parent is a container and a placeholder must be inserted
if (subTree.followElement.followElement === null && !hasRealParent) {
return {'type': 'Placeholder'}
}
// alter followElement of the node to the follow element of the next node
return subTree.followElement.followElement
}
/**
* Change the text of the current node
*
* @param subTree part of the tree with all children of current element
* @param hasRealParent not used in this function
* @param text the new text for the node
* @return subTree altered subTree object (with changed text)
*/
editElement(subTree, hasRealParent, text) {
subTree.text = text;
return subTree
}
/**
* Insert an element in the model tree and connect children
*
* @param subTree part of the tree with all children of current element
* @param hasRealParent not used in this function
* @param text not used in this function
* @return subTree altered subTree object (with newly inserted element)
*/
insertElement(subTree, hasRealParent, text) {
let element = this.presenter.getNextInsertElement();
// check for children
if (!(subTree.followElement === null || subTree.followElement.type == 'Placeholder')) {
// connect children with the element to insert
element.followElement.followElement = subTree.followElement;
}
// insert the new element
subTree.followElement = element;
return subTree
}
/**
* Switch the display of the default case
*
* @param subTree part of the tree with all children of current element
* @param hasRealParent not used in this function
* @param text not used in this function
* @return subTree altered subTree object (with changed state of default case)
*/
switchDefaultCase(subTree, hasRealParent, text) {
if (subTree.defaultOn) {
subTree.defaultOn = false;
} else {
subTree.defaultOn = true;
}
return subTree
}
/**
* Insert a new empty case element
*
* @param subTree part of the tree with all children of current element
* @param hasRealParent not used in this function
* @param text not used in this function
* @return subTree altered subTree object (with inserted case element)
*/
insertNewCase(subTree, hasRealParent, text) {
// check for max number of cases, duo to rendering issues
if (subTree.cases.length < 7) {
// add a new case
subTree.cases.push({'id': guidGenerator(),
'type': 'InsertCase',
'text': "Fall",
'followElement': {'id': guidGenerator(),
'type': 'InsertNode',
'followElement': {'type': 'Placeholder'}
}
});
}
return subTree
}
/**
* Recursive function to get a real copy of an element by his id
*
* @param id id of the element, which to find
* @param subTree part of the tree with all children of current element
* @return subTree copy of the subTree object
*/
getElementInTree(uid, subTree) {
// stop recursion if the end of a sub tree is reached
if (subTree === null || subTree.type == 'Placeholder') {
return null
} else {
if (subTree.id === uid) {
// return a real copy
return JSON.parse(JSON.stringify(subTree));
} else {
switch (subTree.type) {
case 'InsertNode':
case 'InputNode':
case 'OutputNode':
case 'TaskNode':
case 'InsertCase':
return this.getElementInTree(uid, subTree.followElement)
break;
case 'HeadLoopNode':
case 'CountLoopNode':
case 'FootLoopNode':
{
// follow children first, then the follow node
let node = this.getElementInTree(uid, subTree.child);
if (node === null) {
return this.getElementInTree(uid, subTree.followElement)
} else {
return node
}
}
break;
case 'BranchNode':
{
// follow both children first, then the follow node
let node = this.getElementInTree(uid, subTree.trueChild);
if (node === null) {
node = this.getElementInTree(uid, subTree.falseChild);
if (node === null) {
return this.getElementInTree(uid, subTree.followElement)
} else {
return node
}
} else {
return node
}
}
break;
case 'CaseNode':
{
// follow every case first
let node = null;
for (const element of subTree.cases) {
let caseNode = this.getElementInTree(uid, element);
if (caseNode != null) {
node = caseNode;
}
}
// then the default case
if (node === null) {
node = this.getElementInTree(uid, subTree.defaultNode);
if (node === null) {
// then the follow element
return this.getElementInTree(uid, subTree.followElement);
} else {
return node
}
} else {
return node;
}
}
break;
}
}
}
}
}
// create a singleton of the model object
export const model = new Model();
import {guidGenerator} from '../helpers/generator';
export class Presenter {
constructor(model) {
this.model = model;
this.insertMode = false;
this.views = [];
this.moveId = null;
this.nextInsertElement = null;
}
addView(view) {
this.views.push(view);
}
getInsertMode() {
return this.insertMode;
}
resetButtons() {
for (const view of this.views) {
view.resetButtons();
}
}
reset() {
// reset the model fields connected to inserting
this.insertMode = false;
this.nextInsertElement = null;
this.moveId = null;
}
/**
* Update the model stored in the browser store
*/
updateBrowserStore() {
// check if browser supports web storage
if (typeof(Storage) !== "undefined") {
// update the model as stringified JSON data
localStorage.tree = JSON.stringify(this.model.getTree());
//localStorage.displaySourcecode = JSON.stringify(model.displaySourcecode);
}
}
getMoveId() {
return this.moveId;
}
getNextInsertElement() {
return this.nextInsertElement;
}
renderAllViews() {
for (const view of this.views) {
view.render(this.model.getTree());
}
}
init() {
this.renderAllViews();
}
/**
* Prepare for inserting an element
*
* @param buttonId id of the selected button
*/
insertNode(id) {
switch (id) {
case 'InputButton':
this.nextInsertElement = {'id': guidGenerator(),
'type': 'InputNode',
'text': "",
'followElement': {'id': guidGenerator(),
'type': 'InsertNode',
'followElement': null
}