Difference between revisions of "MediaWiki:VisNetwork.js"

From KIproBatt Wiki
(fix subobjects)
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
$(document).ready(function() {
 
$(document).ready(function() {
 
 
     $.getScript('https://unpkg.com/vis-network/standalone/umd/vis-network.min.js').done(function() {
 
     $.getScript('https://unpkg.com/vis-network/standalone/umd/vis-network.min.js').done(function() {
    var pathId = 0;
+
        var pathId = 0;
    var newNodes = {};
+
        var newNodes = {};
    var editNodes = {};
+
        var editNodes = {};
    var editDeletedEdges = {};
+
        var editDeletedEdges = {};
    var editDeletedNodes = {};
+
        var editDeletedNodes = {};
   
+
        $(".InteractiveSemanticGraph").each(function(index) {
    $(".visNetworkGraph").each(function(index) {
+
            if ($('.InteractiveSemanticGraph').length) { //check if div element(s) exist
        if ($('.visNetworkGraph').length) { //check if div element(s) exist
+
                var input = JSON.parse(this.innerHTML);
            var input = JSON.parse(this.innerHTML);
+
                // create an array with nodes
+
                var nodes = new vis.DataSet([]);
            // create an array with nodes
+
                // create an array with edges
            var nodes = new vis.DataSet([]);
+
                var edges = new vis.DataSet([]);
            // create an array with edges
+
                //colors for the graph
            var edges = new vis.DataSet([]);
+
                var colors = [];
            //colors for the graph
+
                var oldGroups = {};
            var colors = [];
+
                var givenDiv = this;
            var oldGroups = {};
+
                givenDiv.style.position = "relative";
var givenDiv = this;
+
                givenDiv.style.display = "inline-block";
givenDiv.style.position = "relative";
 
givenDiv.style.display = "inline-block";
 
 
  
+
//Function for random colors
            function getColor() {
+
                 var h = Math.random();
                 return "hsl(" + 360 * Math.random() + ',' +
+
                var golden = 0.618033988749895;
                    (25 + 70 * Math.random()) + '%,' +
 
                    (85 + 10 * Math.random()) + '%)';
 
            }
 
  
            var h = Math.random();
+
                function randomHSL() {
            var golden = 0.618033988749895;
+
                    h += golden;
            function randomHSL() {
+
                    h %= 1;
            h += golden;
+
                    return "hsla(" + (360 * h) + "," +
            h %= 1;
+
                        "70%," +
            //~~(360 * Math.random())
+
                        "80%,1)";
                return "hsla(" + (360 * h) + "," +
+
                }
                    "70%," +
+
                for (var i = 0; i < input.properties.length; i++) {
                    "80%,1)";
+
                    colors.push(randomHSL());
            }
+
                }
  
            for (var i = 0; i < input.properties.length; i++) {
+
                 function isLabelSet(id) {
                 colors.push(randomHSL());
+
                    node = nodes.get(id);
            }
+
                    if (node === null) return false;
 
+
                    else return id;
            function isLabelSet(id) {
+
                 }
                node = nodes.get(id);
 
                if (node === null) return false;
 
                else return id;
 
            }
 
 
 
            //var colors = ['#ff7878', '#73ff77', '#e878ff', '#ffae57', '#80f8ff', '#ffff75', '#adadad', '#b482ff'];
 
 
 
            nodes.add({
 
                id: input.root,
 
                label: input.root, //todo: query display title
 
                color: '#6dbfa9'
 
            });
 
 
 
            //Creates API query Url with the given root and properties
 
            function createUrl(root, properties) {
 
                 var url = `/w/api.php?action=ask&query=[[${encodeURIComponent(root)}]]`;
 
                var propertiesVar = '';
 
                for (var i = 0; i < properties.length; i++) {
 
                    propertiesVar += '|?' + encodeURIComponent(properties[i]);
 
  
 +
                nodes.add({
 +
                    id: input.root,
 +
                    label: input.root, //todo: query display title
 +
                    color: '#6dbfa9'
 +
                });
 +
                //Creates API query Url with the given root and properties
 +
                function createUrl(root, properties) {
 +
                if (properties[0] === "-Category") properties[0] = "Category";
 +
                else if (root.startsWith("Category:")) root = ":" + root; //[[Category:X]] queries pages within this category, [[:Category:X]] the category itself
 +
                    var url = `/w/api.php?action=ask&query=[[${encodeURIComponent(root)}]]`;
 +
                    var propertiesVar = '';
 +
                    for (var i = 0; i < properties.length; i++) {
 +
                        propertiesVar += '|?' + encodeURIComponent(properties[i]) + "=" + encodeURIComponent(properties[i]); //explicit label overwrites property display title. ToDo: extrakt label in result and get corresponding printout
 +
                        propertiesVar += '|?' + encodeURIComponent(properties[i] + ".Display title of") + "=" + encodeURIComponent(properties[i] + ".Display title of"); //explicit query for display title due to slow update of the displaytitle page field
 +
                    }
 +
                    url = url + propertiesVar + '&format=json';
 +
                    return url;
 
                 }
 
                 }
 +
                //Makes an API call with the given parameters and adds the results to the nodes and edges datasets.
 +
                //With a given nodeID the edges are set to the nodeID, else they are set to the root node.
 +
                function fetchData(root, properties, nodeID, setGroup, setColor) {
 +
                    fetch(createUrl(root, properties))
 +
                        .then(response => response.json())
 +
                        .then(data => {
 +
                            if (!nodeID && root) { //first query on root node
 +
                                var rootNode = nodes.get(root);
 +
                                rootNode.url = data.query.results[root].fullurl;
 +
                                if (data.query.results[root].displaytitle) rootNode.label = data.query.results[root].displaytitle;
 +
                            }
  
                url = url + propertiesVar + '&format=json';
+
                            for (var i = 0; i < properties.length; i++) {
                return url;
 
            }
 
  
            //Makes an API call with the given parameters and adds the results to the nodes and edges datasets.
+
                                for (var j = 0; j < data.query.results[root].printouts[properties[i]].length; j++) {
            //With a given nodeID the edges are set to the nodeID, else they are set to the root node.
 
            function fetchData(root, properties, nodeID, setGroup, setColor) {
 
                fetch(createUrl(root, properties))
 
                    .then(response => response.json())
 
                    .then(data => {
 
                    if (!nodeID){ //first query on root node
 
                    var rootNode = nodes.get(root);
 
                    rootNode.url = data.query.results[root].fullurl;
 
                    if (data.query.results[root].displaytitle) rootNode.label = data.query.results[root].displaytitle;
 
                    }
 
                        for (var i = 0; i < properties.length; i++) {
 
                            for (var j = 0; j < data.query.results[root].printouts[properties[i]].length; j++) {
 
                           
 
                           
 
                            //define colors
 
                                if(!(properties[i] in legendColors) && setColor) {legendColors[properties[i]] = colors[i]; }
 
                                else {setColor = legendColors[properties[i]]; colors[i] = legendColors[properties[i]]; colors[i] = legendColors[properties[i]];}
 
                                //define id and label. use displaytitle if available. Use string representation of non-page properties
 
                                var id = "";
 
                                var label = "";
 
                                if (data.query.results[root].printouts[properties[i]][j].fulltext) id = data.query.results[root].printouts[properties[i]][j].fulltext;
 
                                else if(data.query.results[root].printouts[properties[i]][j].value) id = '' + data.query.results[root].printouts[properties[i]][j].value + ' ' + data.query.results[root].printouts[properties[i]][j].unit; 
 
                                else id = data.query.results[root].printouts[properties[i]][j].toString();
 
                                if (data.query.results[root].printouts[properties[i]][j].displaytitle) label = data.query.results[root].printouts[properties[i]][j].displaytitle;
 
                                if (label === "") label = id;
 
 
                                if (isLabelSet(id) === false) {
 
  
                                     if (setGroup && setColor) {
+
                                    //define colors
                                         nodes.add({
+
                                     if (!(properties[i] in legendColors) && setColor) {
                                            id: id,
+
                                         legendColors[properties[i]] = colors[i];
                                            label: label,
 
                                            color: setColor,
 
                                            group: setGroup[0],
 
                                            hidden: false,
 
                                            url: data.query.results[root].printouts[properties[i]][j].fullurl,
 
                                            oncontext: true,
 
                                        });
 
                                        oldGroups["" + id] = setGroup[0];
 
 
                                     } else {
 
                                     } else {
                                         nodes.add({
+
                                         setColor = legendColors[properties[i]];
                                            id: id,
+
                                        colors[i] = legendColors[properties[i]];
                                            label: label,
+
                                         colors[i] = legendColors[properties[i]];
                                            color: colors[i],
 
                                            group: properties[i],
 
                                            hidden: false,
 
                                            url: data.query.results[root].printouts[properties[i]][j].fullurl
 
                                        });
 
                                         oldGroups["" + id] = properties[i];
 
 
                                     }
 
                                     }
                                     if (nodeID) {
+
                                    //define id and label. use displaytitle if available. Use string representation of non-page properties
 +
                                    var id = "";
 +
                                    var label = "";
 +
                                    if (data.query.results[root].printouts[properties[i]][j].fulltext) id = data.query.results[root].printouts[properties[i]][j].fulltext;
 +
                                    else if (data.query.results[root].printouts[properties[i]][j].value) id = '' + data.query.results[root].printouts[properties[i]][j].value + ' ' + data.query.results[root].printouts[properties[i]][j].unit;
 +
                                    else id = data.query.results[root].printouts[properties[i]][j].toString();
 +
                                     if (data.query.results[root].printouts[properties[i]][j].displaytitle) label = data.query.results[root].printouts[properties[i]][j].displaytitle;
 +
                                    if (data.query.results[root].printouts[properties[i] + ".Display title of"][j]) label = data.query.results[root].printouts[properties[i] + ".Display title of"][j]; //explicit use property display title due to slow update of the displaytitle page field
 +
                                    if (label === "") label = id;
 +
                                    if (isLabelSet(id) === false) {
 +
                                        if (setGroup && setColor) {
 +
                                            nodes.add({
 +
                                                id: id,
 +
                                                label: label,
 +
                                                color: setColor,
 +
                                                group: setGroup[0],
 +
                                                hidden: false,
 +
                                                url: data.query.results[root].printouts[properties[i]][j].fullurl,
 +
                                                oncontext: true,
 +
                                            });
 +
                                            oldGroups["" + id] = setGroup[0];
 +
                                        } else {
 +
                                            nodes.add({
 +
                                                id: id,
 +
                                                label: label,
 +
                                                color: colors[i],
 +
                                                group: properties[i],
 +
                                                hidden: false,
 +
                                                url: data.query.results[root].printouts[properties[i]][j].fullurl
 +
                                            });
 +
                                            oldGroups["" + id] = properties[i];
 +
                                        }
 +
                                        if (nodeID) {
 +
                                            edges.add({
 +
                                                from: nodeID,
 +
                                                to: id,
 +
                                                label: properties[i],
 +
                                                color: colors[i],
 +
                                                group: properties[i]
 +
                                            });
 +
                                        } else {
 +
                                            edges.add({
 +
                                                from: input.root,
 +
                                                to: id,
 +
                                                label: properties[i],
 +
                                                color: colors[i],
 +
                                                group: properties[i]
 +
                                            });
 +
                                        }
 +
                                    } else {
 
                                         edges.add({
 
                                         edges.add({
 
                                             from: nodeID,
 
                                             from: nodeID,
                                             to: id,
+
                                             to: isLabelSet(id),
 
                                             label: properties[i],
 
                                             label: properties[i],
                                             color: colors[i],
+
                                             color: setColor,
                                            group: properties[i]
 
                                        });
 
                                    } else {
 
                                        edges.add({
 
                                            from: input.root,
 
                                            to: id,
 
                                            label: properties[i],
 
                                            color: colors[i],
 
 
                                             group: properties[i]
 
                                             group: properties[i]
 
                                         });
 
                                         });
 
                                     }
 
                                     }
                                   
 
                                } else {
 
                                    edges.add({
 
                                        from: nodeID,
 
                                        to: isLabelSet(id),
 
                                        label: properties[i],
 
                                        color: setColor,
 
                                        group: properties[i]
 
                                    });
 
 
                                 }
 
                                 }
 
                             }
 
                             }
 +
                            network.setOptions(options);
 +
                            network.body.emitter.emit('_dataChanged');
 +
                            network.redraw();
 +
                        });
 +
                }
 +
                fetchData(input.root, input.properties);
 +
                // create a network
 +
                var container = this; //document.getElementById("InteractiveSemanticGraph");
 +
                var data = {
 +
                    nodes: nodes,
 +
                    edges: edges,
 +
                };
 +
                var options = {
 +
                    width: "100%",
 +
                    height: "100%",
 +
                    interaction: {
 +
                        hover: true
 +
                    },
 +
                    manipulation: {
 +
                        enabled: true,
 +
                        editEdge: false,
 +
                        deleteNode: function(data, callback) {
 +
                            deleteSelectedNode(data, callback)
 +
                        }.bind(this),
 +
                        deleteEdge: function(data, callback) {
 +
                            deleteSelectedEdge(data, callback)
 +
                        }.bind(this),
 +
                        addNode: function(data, callback) {
 +
                            // filling in the popup DOM elements
 +
                            document.getElementById("node-operation").innerText = "Add Node";
 +
                            dragElement(document.getElementById("node-popUp"));
 +
                            editNode(data, clearNodePopUp, callback);
 +
                        },
 +
                        addEdge: function(data, callback) {
 +
                            if (data.from == data.to) {
 +
                                var r = confirm("Do you want to connect the node to itself?");
 +
                                if (r != true) {
 +
                                    callback(null);
 +
                                    return;
 +
                                }
 +
                            }
 +
                            document.getElementById("edge-operation").innerText = "Add Edge";
 +
                            dragElement(document.getElementById("edge-popUp"));
 +
                            editEdgeWithoutDrag(data, callback);
 +
                        },
 +
                    },
 +
                    edges: {
 +
                        arrows: {
 +
                            to: {
 +
                                enabled: true
 +
                            },
 +
                            //from:{enabled: true}
 
                         }
 
                         }
                        network.setOptions(options);
+
                     },
                        network.body.emitter.emit('_dataChanged');
+
                    groups: {
                        network.redraw();
+
                        useDefaultGroups: false
                       
+
                    },
                     });
+
                    physics: {
            }
+
                        stabilization: {
            fetchData(input.root, input.properties);
+
                            enabled: true,
            // create a network
+
                        },
            var container = this; //document.getElementById("visNetworkGraph");
+
                        barnesHut: {
            var data = {
+
                            gravitationalConstant: -40000,
                nodes: nodes,
+
                            centralGravity: 0,
                edges: edges,
+
                            springLength: 0,
            };
+
                            springConstant: 0.5,
            var options = {
+
                            damping: 1,
                width: "100%",
+
                             avoidOverlap: 0
                height: "100%",
 
                interaction: {
 
                    hover: true
 
                },
 
                manipulation: {
 
                    enabled: true,
 
                    editEdge: false,
 
                    deleteNode: function (data, callback) {deleteSelectedNode(data, callback)}.bind(this),
 
                    deleteEdge: function (data, callback) {deleteSelectedEdge(data, callback)}.bind(this),
 
                    addNode: function (data, callback) {
 
                   
 
        // filling in the popup DOM elements
 
        document.getElementById("node-operation").innerText = "Add Node";
 
        dragElement(document.getElementById("node-popUp"));
 
        editNode(data, clearNodePopUp, callback);
 
      },
 
      addEdge: function (data, callback) {
 
        if (data.from == data.to) {
 
          var r = confirm("Do you want to connect the node to itself?");
 
          if (r != true) {
 
            callback(null);
 
            return;
 
          }
 
        }
 
        document.getElementById("edge-operation").innerText = "Add Edge";
 
        dragElement(document.getElementById("edge-popUp"));
 
        editEdgeWithoutDrag(data, callback);
 
      },
 
          },
 
                edges: {
 
                    arrows: {
 
                        to: {
 
                             enabled: true
 
 
                         },
 
                         },
                         //from:{enabled: true}
+
                         maxVelocity: 5
                    }
 
                },
 
                groups: {
 
                    useDefaultGroups: false
 
                },
 
                physics: {
 
                    stabilization: {
 
                        enabled: true,
 
                    },
 
                    barnesHut: {
 
                        gravitationalConstant: -40000,
 
                        centralGravity: 0,
 
                        springLength: 0,
 
                        springConstant: 0.5,
 
                        damping: 1,
 
                        avoidOverlap: 0
 
 
                     },
 
                     },
                    maxVelocity: 5
 
                },
 
 
            };
 
            //Creates groups in the options and sets them all to hidden:false.
 
            for (var i = 0; i < input.properties.length; i++) {
 
                options.groups[input.properties[i]] = {
 
                    hidden: false
 
 
                 };
 
                 };
            }
+
                //Creates groups in the options and sets them all to hidden:false.
            var network = new vis.Network(container, data, options);
+
                for (var i = 0; i < input.properties.length; i++) {
           
+
                    options.groups[input.properties[i]] = {
 
+
                        hidden: false
 
+
                    };
 
+
                }
function getAllEdgesBetween(node1,node2) {
+
                var network = new vis.Network(container, data, options);
    return edges.get().filter(function (edge) {
+
        return (edge.from === node1 && edge.to === node2 )|| (edge.from === node2 && edge.to === node1);
+
//The function getAllEdgesBetween() returns all edges between two nodes
    });
+
                function getAllEdgesBetween(node1, node2) {
}
+
                    return edges.get().filter(function(edge) {
 
+
                        return (edge.from === node1 && edge.to === node2) || (edge.from === node2 && edge.to === node1);
function getAllCombs(arrays){
+
                    });
    var numberOfCombs = 1;
+
                }
    for(var i=0; i<arrays.length; i++){
 
        numberOfCombs = numberOfCombs * arrays[i].length;
 
    }
 
    var allCombs = new Array(numberOfCombs);
 
    for(var i=0; i<allCombs.length; i++){
 
        allCombs[i] = new Array(arrays.length);
 
    }
 
   
 
    for(var i=0; i<arrays.length; i++){
 
        var current = arrays[i];
 
        for(var c=0; c<numberOfCombs; c++){
 
 
            for(var j=0; j<current.length; j++){
 
                allCombs[c][i] = current[c%current.length];
 
 
                  
 
                  
            }
+
                //Cartesian Product of arrays
        }
+
                function cartesianProduct(arr) {
    }
+
    return arr.reduce(function(a,b){
+
        return a.map(function(x){
    return allCombs;
+
            return b.map(function(y){
}
+
                return x.concat([y]);
   
+
            })
function getEdgePathsForPath(path){
+
        }).reduce(function(a,b){ return a.concat(b) },[])
  var arraysOfEdgesForNodeInPath = [];
+
    }, [[]])
  for(var i=1; i<path.length; i++){
+
}
    var edgesBetween = getAllEdgesBetween(path[i-1], path[i]);
+
//Cartesian Product of given arrays
    var localedgesBetween = edgesBetween.slice();
+
                function getAllCombs(arrays) {
    arraysOfEdgesForNodeInPath.push(localedgesBetween);
+
                var allCombs = cartesianProduct(arrays);
  }
+
                    return allCombs;
  var allEdgePaths = getAllCombs(arraysOfEdgesForNodeInPath);
+
                }
return allEdgePaths;
+
//Gets Path array with nodes, returns Cartesian Product of edges
}
+
                function getEdgePathsForPath(path) {
 
+
                    var arraysOfEdgesForNodeInPath = [];
function reverseLabel(label){
+
                    for (var i = 1; i < path.length; i++) {
  if(label[0] == "-"){
+
                        var edgesBetween = getAllEdgesBetween(path[i - 1], path[i]);
    return label.substring(1);
+
                        var localedgesBetween = edgesBetween.slice();
  }
+
                       
  else{
+
                        arraysOfEdgesForNodeInPath.push(localedgesBetween);
    return "-" + label;
+
                    }
  }
+
                    var allEdgePaths = getAllCombs(arraysOfEdgesForNodeInPath);
}
+
                    return allEdgePaths;
 
+
                }
function getEdgeLabelStringsForPath(path){
+
//Given Label is reversed with "-" or "-" is removed
  var allEdgePaths = getEdgePathsForPath(path);
+
                function reverseLabel(label) {
  var allStrings = new Array(allEdgePaths.length);
+
                    if (label[0] == "-") {
  for(var i=0; i<allEdgePaths.length; i++){
+
                        return label.substring(1);
    var s = "";
+
                    } else {
    for(var j=0; j<allEdgePaths[i].length;j++){
+
                        return "-" + label;
     
+
                    }
      var edge = allEdgePaths[i][j];
+
                }
      var label = edge.label;
+
//Gets Path array with nodes, returns all possible edge paths
      var nodeId1 = path[j];
+
                function getEdgeLabelStringsForPath(path) {
      var nodeId2 = path[j+1];
+
                    var allEdgePaths = getEdgePathsForPath(path);
      if(edge.to == nodeId1 && edge.from == nodeId2){
+
                    var allStrings = new Array(allEdgePaths.length);
        label = reverseLabel(label);
+
                    for (var i = 0; i < allEdgePaths.length; i++) {
      }
+
                        var s = "";
      if(j == (allEdgePaths[i].length - 1)){
+
                        for (var j = 0; j < allEdgePaths[i].length; j++) {
        s = s + label;
+
                            var edge = allEdgePaths[i][j];
      }
+
                            var label = edge.label;
      else{
+
                            var nodeId1 = path[j];
        s = s + label + ".";
+
                            var nodeId2 = path[j + 1];
      }
+
                            if (edge.to == nodeId1 && edge.from == nodeId2) {
   
+
                                label = reverseLabel(label);
     
+
                            }
    }
+
                            if (j == (allEdgePaths[i].length - 1)) {
    allStrings[i] = s;
+
                                s = s + label;
  }
+
                            } else {
  return allStrings;
+
                                s = s + label + ".";
}
+
                            }
 
+
                        }
function getAllStringsForAllPaths(paths){
+
                        allStrings[i] = s;
  var arrayOfAllStrings = [];
+
                    }
  for(var i=0; i<paths.length;i++){
+
                    return allStrings;
    var path = paths[i];
+
                }
    var allStrings = getEdgeLabelStringsForPath(path);
+
//Gets Path arrays with nodes, returns all possible edge paths
    arrayOfAllStrings.push(allStrings);
+
                function getAllStringsForAllPaths(paths) {
  }
+
                    var arrayOfAllStrings = [];
return arrayOfAllStrings;
+
                    for (var i = 0; i < paths.length; i++) {
}
+
                        var path = paths[i];
 
+
                        var allStrings = getEdgeLabelStringsForPath(path);
 
+
                        arrayOfAllStrings.push(allStrings);
 
+
                    }
function removeItem(arr, value) {
+
                    return arrayOfAllStrings;
  var index = arr.indexOf(value);
+
                }
  if (index > -1) {
+
//Removes the given value from the given array
    arr.splice(index, 1);
+
                function removeItem(arr, value) {
  }
+
                    var index = arr.indexOf(value);
  return arr;
+
                    if (index > -1) {
}
+
                        arr.splice(index, 1);
 
+
                    }
 
+
                    return arr;
 
+
                }
 
+
//Returns all paths between startNode and endNode
function findAllPaths(startNode, endNode){
+
                function findAllPaths(startNode, endNode) {
    var visitedNodes = [];
+
                    var visitedNodes = [];
    var currentPath = [];
+
                    var currentPath = [];
    var allPaths = [];
+
                    var allPaths = [];
    dfs(startNode, endNode, currentPath, allPaths, visitedNodes);
+
                    dfs(startNode, endNode, currentPath, allPaths, visitedNodes);
    return allPaths;
+
                    return allPaths;
}
+
                }
 
+
//Algorithm to search for all paths between two nodes
function dfs(start, end, currentPath, allPaths, visitedNodes){
+
                function dfs(start, end, currentPath, allPaths, visitedNodes) {
    if(visitedNodes.includes(start)) return;
+
                    if (visitedNodes.includes(start)) return;
    visitedNodes.push(start);
+
                    visitedNodes.push(start);
    currentPath.push(start);
+
                    currentPath.push(start);
    if(start == end){
+
                    if (start == end) {
    var localCurrentPath = currentPath.slice();
+
                        var localCurrentPath = currentPath.slice();
        allPaths.push(localCurrentPath);
+
                        allPaths.push(localCurrentPath);
        removeItem(visitedNodes, start);
+
                        removeItem(visitedNodes, start);
        currentPath.pop();
+
                        currentPath.pop();
        return;
+
                        return;
    }
+
                    }
   
+
                    var neighbours = network.getConnectedNodes(start);
    var neighbours = network.getConnectedNodes(start);
+
                    for (var i = 0; i < neighbours.length; i++) {
    for(var i = 0; i < neighbours.length; i++){  
+
                        var current = neighbours[i];
        var current = neighbours[i];
+
                        dfs(current, end, currentPath, allPaths, visitedNodes);
        dfs(current, end, currentPath, allPaths, visitedNodes);
+
                    }
    }
+
                    currentPath.pop();
 
+
                    removeItem(visitedNodes, start);
    currentPath.pop();
 
    removeItem(visitedNodes, start);
 
 
 
}
 
 
 
 
 
 
 
 
 
 
 
           
 
           
 
 
 
            //This function deletes all children of a given node.
 
            function getAllReachableNodesTo(nodeId, excludeIds, reachableNodes) {
 
 
 
                if (reachableNodes.includes(nodeId) || excludeIds.includes(nodeId)) { return;}
 
                var children = network.getConnectedNodes(nodeId);
 
                reachableNodes.push(nodeId);
 
                for (var i = 0; i < children.length; i++) {
 
                    getAllReachableNodesTo(children[i], excludeIds, reachableNodes);
 
                    //if(excludeIds.includes(children[i]))continue;
 
                    //reachableNodes.push(children[i]);
 
 
                 }
 
                 }
            }
+
                //Algorithm that gets all nodes that are reachable from the given node in the graph
           
+
                function getAllReachableNodesTo(nodeId, excludeIds, reachableNodes) {
            function deleteNodesChildren(nodeId, deleteEdge) {
+
                    if (reachableNodes.includes(nodeId) || excludeIds.includes(nodeId)) {
                var excludedIds = [];
+
                        return;
                if(deleteEdge === true){
+
                    }
                console.log("deleteEdge true")
+
                    var children = network.getConnectedNodes(nodeId);
                }else{
+
                    reachableNodes.push(nodeId);
                excludedIds.push(nodeId);
+
                    for (var i = 0; i < children.length; i++) {
 +
                        getAllReachableNodesTo(children[i], excludeIds, reachableNodes);
 +
                        //if(excludeIds.includes(children[i]))continue;
 +
                        //reachableNodes.push(children[i]);
 +
                    }
 
                 }
 
                 }
                 var reachableNodesTo = [];
+
//This function deletes all children of a given node.
                getAllReachableNodesTo(input.root, excludedIds, reachableNodesTo);
+
                 function deleteNodesChildren(nodeId, deleteEdge) {
                var nodesToDelete = [];
+
                    var excludedIds = [];
                var allIds = nodes.getIds();
+
                    if (deleteEdge === true) {
 
+
                        console.log("deleteEdge true")
                for (var i = 0; i < allIds.length; i++) {
+
                    } else {
                    if (reachableNodesTo.includes(allIds[i])) continue;
+
                        excludedIds.push(nodeId);
                    if (allIds[i] == nodeId) {
+
                    }
                    deleteEdges(nodeId);
+
                    var reachableNodesTo = [];
                    continue;
+
                    getAllReachableNodesTo(input.root, excludedIds, reachableNodesTo);
 +
                    var nodesToDelete = [];
 +
                    var allIds = nodes.getIds();
 +
                    for (var i = 0; i < allIds.length; i++) {
 +
                        if (reachableNodesTo.includes(allIds[i])) continue;
 +
                        if (allIds[i] == nodeId) {
 +
                            deleteEdges(nodeId);
 +
                            continue;
 +
                        }
 +
                        nodesToDelete.push(allIds[i]);
 +
                        deleteEdges(allIds[i]);
 +
                        nodes.remove(allIds[i]);
 +
                        delete oldGroups["" + allIds[i]];
 +
                        delete objClickedProps["" + allIds[i]];
 
                     }
 
                     }
                     nodesToDelete.push(allIds[i]);
+
                     return nodesToDelete;
                    deleteEdges(allIds[i]);
 
                    nodes.remove(allIds[i]);
 
                    delete oldGroups["" + allIds[i]];
 
                    delete objClickedProps["" + allIds[i]];
 
 
                 }
 
                 }
 
+
//Deletes all edges from given node
                 return nodesToDelete;
+
                 function deleteEdges(nodeID) {
            }
+
                    var fromEdges = edges.get({
           
+
                        filter: function(item) {
function deleteEdges(nodeID){
+
                            return item.from == nodeID;
var fromEdges = edges.get({
+
                        }
                    filter: function (item) {
+
                    });
                        return item.from == nodeID;
+
                    for (var j = 0; j < fromEdges.length; j++) {
 +
                        edges.remove(fromEdges[j]);
 
                     }
 
                     }
                });
 
               
 
        for(var j = 0; j < fromEdges.length; j++){
 
        edges.remove(fromEdges[j]);
 
        }
 
}
 
var nodesClicked = [];
 
var tip = '<p><strong>Hinweis:</strong> Um sich einen Pfad zwischen zwei Knoten ausgeben zu lassen, <em>Strg</em> gedrückt halten und die gewünschten zwei Knoten mit der <em>linken Maustaste</em> anklicken. </p>'
 
this.insertAdjacentHTML('afterbegin', tip);
 
 
network.on("click", function(params) {
 
 
                if (params.nodes[0] && params.event.srcEvent.ctrlKey) {
 
               
 
                if(nodesClicked.length < 2){
 
                nodesClicked.push(params.nodes[0]);
 
                }
 
                if(nodesClicked.length == 2 && nodesClicked[0] != nodesClicked[1]){
 
                var foundPaths = findAllPaths(nodesClicked[0],nodesClicked[1]);
 
                //.querySelector('[id^="poll-"]').id;
 
                if(document.querySelectorAll('[id^="fullPath"]')){
 
                for(var i=0; i < document.querySelectorAll('[id^="fullPath"]').length; i++){
 
                document.querySelectorAll('[id^="fullPath"]')[i].remove();
 
                }
 
                }
 
                var element = '<div id="fullPath'+ pathId +'"></div>'
 
givenDiv.children[0].insertAdjacentHTML('afterend', element);
 
 
                var allStringsArray = getAllStringsForAllPaths(foundPaths);
 
               
 
                var stringDiv = givenDiv.querySelector('#fullPath' + pathId);
 
               
 
                if(foundPaths.length == 1){stringDiv.innerHTML = "<strong>Gefundener Pfad:</strong><br>"}else{stringDiv.innerHTML = "<strong>Gefundene Pfade:</strong><br>"}
 
               
 
                for(var s=0; s<foundPaths.length; s++){
 
                if(foundPaths.length == 1){var pathNumb = ""}else{var pathNumb = "<strong>" + (s+1) + ". Pfad:</strong> <br>"}
 
               
 
                stringDiv.innerHTML += pathNumb + "<strong>Knoten: </strong>";
 
  for(var t=0; t<foundPaths[s].length; t++){
 
    var currentFoundPath = foundPaths[s][t];
 
   
 
    if(t == (foundPaths[s].length - 1)){
 
        stringDiv.innerHTML = stringDiv.innerHTML + currentFoundPath + " ";
 
    }
 
    else{
 
        stringDiv.innerHTML = stringDiv.innerHTML + currentFoundPath + " - ";
 
    }
 
  }
 
  stringDiv.innerHTML += "<br>"
 
  stringDiv.innerHTML += "<strong>Kanten:</strong> "
 
  for(var t=0; t<allStringsArray[s].length; t++){
 
    var currentString = allStringsArray[s][t];
 
    var currentFoundPath = foundPaths[s][t];
 
    var stringDiv = givenDiv.querySelector('#fullPath' + pathId);
 
    stringDiv.innerHTML = stringDiv.innerHTML + currentString;
 
  }
 
  stringDiv.innerHTML += "<br>"
 
}
 
 
                nodesClicked = [];
 
 
                }
 
                if(nodesClicked[0] === nodesClicked[1] || nodesClicked.length > 2){
 
                nodesClicked = [];
 
                }
 
               
 
               
 
 
                 }
 
                 }
pathId++;
+
                var nodesClicked = [];
});
+
                var tip = '<p><strong>Hinweis:</strong> Um sich einen Pfad zwischen zwei Knoten ausgeben zu lassen, <em>Strg</em> gedrückt halten und die gewünschten zwei Knoten mit der <em>linken Maustaste</em> anklicken. </p>'
 
+
                this.insertAdjacentHTML('afterbegin', tip);
$(document).keyup(function(event) {
+
                //Ctrl and click on two nodes, puts out all possible paths between the two nodes under the tip
  if(!event.ctrlKey){
+
                network.on("click", function(params) {
  nodesClicked = [];
+
                    if (params.nodes[0] && params.event.srcEvent.ctrlKey) {
  }
+
                        if (nodesClicked.length < 2) {
});
+
                            nodesClicked.push(params.nodes[0]);
+
                        }
+
                        if (nodesClicked.length == 2 && nodesClicked[0] != nodesClicked[1]) {
+
                            var foundPaths = findAllPaths(nodesClicked[0], nodesClicked[1]);
+
                            //.querySelector('[id^="poll-"]').id;
+
                            if (document.querySelectorAll('[id^="fullPath"]')) {
 
+
                                for (var i = 0; i < document.querySelectorAll('[id^="fullPath"]').length; i++) {
            var contextCreatedProps = [];
+
                                    document.querySelectorAll('[id^="fullPath"]')[i].remove();
 
 
            network.on("doubleClick", function(params) {
 
 
                if (params.nodes[0]) {
 
               
 
                var conManNodes = network.getConnectedNodes(params.nodes[0], 'to');
 
               
 
                var onlyConManNodes = true;
 
                for(var i = 0; i < conManNodes.length;i++){
 
               
 
               
 
                if(!(nodes.get(conManNodes[i]).oncontext || nodes.get(conManNodes[i]).manually)){
 
                onlyConManNodes = false;
 
                }
 
                }
 
               
 
                //Node is expanded -> delete it and all nodes related to its expansion
 
                    if (network.getConnectedNodes(params.nodes[0]).length > 1 && onlyConManNodes == false) {
 
                        deleteNodesChildren(params.nodes[0]);
 
                        for (var i = 0; i < contextCreatedProps.length; i++) {
 
                            var noNodesInNetwork = true;
 
                            for (var j = 0; j < nodes.getIds().length; j++) {
 
                                if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
 
                                    noNodesInNetwork = false;
 
 
                                 }
 
                                 }
 
                             }
 
                             }
                             if (noNodesInNetwork === true) {
+
                             var element = '<div id="fullPath' + pathId + '"></div>'
                                givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
+
                            givenDiv.children[0].insertAdjacentHTML('afterend', element);
                                contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
+
                            var allStringsArray = getAllStringsForAllPaths(foundPaths);
                                 i--;
+
                            var stringDiv = givenDiv.querySelector('#fullPath' + pathId);
 +
                            if (foundPaths.length == 1) {
 +
                                stringDiv.innerHTML = "<strong>Gefundener Pfad:</strong><br>"
 +
                            } else {
 +
                                 stringDiv.innerHTML = "<strong>Gefundene Pfade:</strong><br>"
 
                             }
 
                             }
 +
                            for (var s = 0; s < foundPaths.length; s++) {
 +
                                if (foundPaths.length == 1) {
 +
                                    var pathNumb = ""
 +
                                } else {
 +
                                    var pathNumb = "<strong>" + (s + 1) + ". Pfad:</strong> <br>"
 +
                                }
 +
                                stringDiv.innerHTML += pathNumb + "<strong>Knoten: </strong>";
 +
                                for (var t = 0; t < foundPaths[s].length; t++) {
 +
                                    var currentFoundPath = foundPaths[s][t];
 +
                                    if (t == (foundPaths[s].length - 1)) {
 +
                                        stringDiv.innerHTML = stringDiv.innerHTML + currentFoundPath + " ";
 +
                                    } else {
 +
                                        stringDiv.innerHTML = stringDiv.innerHTML + currentFoundPath + " - ";
 +
                                    }
 +
                                }
 +
                                stringDiv.innerHTML += "<br>"
 +
                                stringDiv.innerHTML += "<strong>Kanten:</strong><br>"
 +
                                for (var t = 0; t < allStringsArray[s].length; t++) {
 +
                                    var currentString = allStringsArray[s][t];
 +
                                    var currentFoundPath = foundPaths[s][t];
 +
                                    var stringDiv = givenDiv.querySelector('#fullPath' + pathId);
 +
                                    stringDiv.innerHTML = stringDiv.innerHTML + '&#9679; ' + currentString + '<br>';
 +
                                }
 +
                                stringDiv.innerHTML += "<br>"
 +
                            }
 +
                            nodesClicked = [];
 +
                        }
 +
                        if (nodesClicked[0] === nodesClicked[1] || nodesClicked.length > 2) {
 +
                            nodesClicked = [];
 
                         }
 
                         }
                        delete objClickedProps["" + params.nodes[0]];
 
                       
 
                        //nodesArray.splice(nodesArray.indexOf(params.nodes[0]), 1);
 
                    } else {
 
                    //Node is unexpanded -> expand it
 
                        var nodeById = nodes.get(params.nodes[0]);
 
                        fetchData(nodeById.id, input.properties, params.nodes[0]);
 
                        //nodesArray.push(params.nodes[0]);
 
 
                     }
 
                     }
 
+
                    pathId++;
                 }
+
                 });
            });
+
                $(document).keyup(function(event) {
           
+
                    if (!event.ctrlKey) {
            function newGroup(node, legendGroup) {
+
                        nodesClicked = [];
 +
                    }
 +
                });
 +
                var contextCreatedProps = [];
 
                  
 
                  
                 nodes.update({
+
                 network.on("doubleClick", function(params) {
                    id: node,
+
                    if (params.nodes[0]) {
                     group: legendGroup
+
                    //Checks if all node children are created from context menu or manually, if so it creates nodes for before defined properties else it deletes all children
 +
                        var conManNodes = network.getConnectedNodes(params.nodes[0], 'to');
 +
                        var onlyConManNodes = true;
 +
                        for (var i = 0; i < conManNodes.length; i++) {
 +
                            if (!(nodes.get(conManNodes[i]).oncontext || nodes.get(conManNodes[i]).manually)) {
 +
                                onlyConManNodes = false;
 +
                            }
 +
                        }
 +
                        //Node is expanded -> delete it and all nodes related to its expansion
 +
                        if (network.getConnectedNodes(params.nodes[0]).length > 1 && onlyConManNodes == false) {
 +
                            deleteNodesChildren(params.nodes[0]);
 +
                            for (var i = 0; i < contextCreatedProps.length; i++) {
 +
                                var noNodesInNetwork = true;
 +
                                for (var j = 0; j < nodes.getIds().length; j++) {
 +
                                    if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
 +
                                        noNodesInNetwork = false;
 +
                                    }
 +
                                }
 +
                                if (noNodesInNetwork === true) {
 +
                                    givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
 +
                                    contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
 +
                                    i--;
 +
                                }
 +
                            }
 +
                            delete objClickedProps["" + params.nodes[0]];
 +
                            //nodesArray.splice(nodesArray.indexOf(params.nodes[0]), 1);
 +
                        } else {
 +
                            //Node is unexpanded -> expand it
 +
                            var nodeById = nodes.get(params.nodes[0]);
 +
                            fetchData(nodeById.id, input.properties, params.nodes[0]);
 +
                            //nodesArray.push(params.nodes[0]);
 +
                        }
 +
                     }
 
                 });
 
                 });
  
 
+
                function newGroup(node, legendGroup) {
                var connectedNodes = network.getConnectedNodes(node, 'to');
+
                    nodes.update({
 
+
                        id: node,
                for (var i = 0; i < connectedNodes.length; i++) {
+
                        group: legendGroup
                    newGroup(connectedNodes[i], legendGroup);
+
                    });
 +
                    var connectedNodes = network.getConnectedNodes(node, 'to');
 +
                    for (var i = 0; i < connectedNodes.length; i++) {
 +
                        newGroup(connectedNodes[i], legendGroup);
 +
                    }
 
                 }
 
                 }
            }
+
                //Checks, if a node has a path over visible edges to the root node.
 
+
                //If not, the nodes gets hidden
//Checks, if a node has a path over visible edges to the root node.
+
                function setNodeVisibilityByVisiblePath(nodeId, rootNodeId) {
//If not, the nodes gets hidden
+
                    if (nodeId == rootNodeId) return true; //root is always visible
function setNodeVisibilityByVisiblePath(nodeId, rootNodeId){
+
                    var node = nodes.get(nodeId);
if (nodeId == rootNodeId) return true; //root is always visible
+
                    if (node.visited) return !node.hidden //prevent circles. ToDo: Reuse results between runs
var node = nodes.get(nodeId);
+
                    node.visited = true;
if (node.visited) return !node.hidden //prevent circles. ToDo: Reuse results between runs
+
                    node.hidden = true;
node.visited = true;
+
                    var connectedEdgesIds = network.getConnectedEdges(nodeId);
node.hidden = true;
+
                    var connectedEdges = edges.get(connectedEdgesIds);
var connectedEdgesIds = network.getConnectedEdges(nodeId);
+
                    connectedEdges.forEach(function(edge) {
var connectedEdges = edges.get(connectedEdgesIds);
+
                        if (edge.hidden) return; //don't follow hidden edges
connectedEdges.forEach(function(edge) {
+
                        var connectedNodesIds = network.getConnectedNodes(edge.id);
if (edge.hidden) return; //don't follow hidden edges
+
                        var connectedNodes = nodes.get(connectedNodesIds);
                    var connectedNodesIds = network.getConnectedNodes(edge.id);
+
                        connectedNodes.forEach(function(connectedNode) {
                    var connectedNodes = nodes.get(connectedNodesIds);
+
                            if (connectedNode.id == nodeId) return; //prevent self evaluation
                    connectedNodes.forEach(function(connectedNode) {
+
                            if (setNodeVisibilityByVisiblePath(connectedNode.id, rootNodeId)) {
                    if (connectedNode.id == nodeId) return; //prevent self evaluation
+
                                node.hidden = false; //set node visible, if at least one connected node is visible
                    if (setNodeVisibilityByVisiblePath(connectedNode.id, rootNodeId)) {
+
                            }
                    node.hidden = false; //set node visible, if at least one connected node is visible
+
                        });
                    }
 
 
                     });
 
                     });
                });
+
                    node.physics = !node.hidden; //disable physics for hidden nodes
                node.physics = !node.hidden;//disable physics for hidden nodes
+
                    return !node.hidden;
                return !node.hidden;
+
                }
}
 
  
            function legendFunctionality() {
+
                function legendFunctionality() {
 
+
                    var legendGroup;
                var legendGroup;
+
                    var group;
                var group;
+
                    var nodeChildren;
                var nodeChildren;
+
                    legendGroup = this.parentNode.childNodes[1].innerHTML;
                legendGroup = this.parentNode.childNodes[1].innerHTML;
+
                    var strategy = "strategy2"
               
+
                    if (strategy == "strategy2") {
               
+
                        //A node is visible if at least one path over visible edges to the root node exists.
                var strategy = "strategy2"
+
                        options.groups[legendGroup].hidden = !options.groups[legendGroup].hidden; //toggle state
               
+
                        if (options.groups[legendGroup].hidden) this.parentNode.childNodes[1].style.background = '#FFFFFF';
                if (strategy == "strategy2"){
+
                        else this.parentNode.childNodes[1].style.background = '#DEF';
                //A node is visible if at least one path over visible edges to the root node exists.
+
                        //update all edges
                options.groups[legendGroup].hidden = !options.groups[legendGroup].hidden; //toggle state
+
                        edges.forEach(function(edge) {
                if(options.groups[legendGroup].hidden) this.parentNode.childNodes[1].style.background = '#FFFFFF';
+
                            edge.hidden = options.groups[edge.label].hidden;
                else this.parentNode.childNodes[1].style.background = '#DEF';
+
                            edge.physics = !edge.hidden;
                //update all edges
+
                        });
                edges.forEach(function(edge) {
+
                        //reset nodes
                    edge.hidden = options.groups[edge.label].hidden;
+
                        nodes.forEach(function(node) {
                    edge.physics = !edge.hidden;
+
                            node.hidden = false;
                });
+
                            node.physics = !node.hidden;
                //reset nodes
+
                            node.visited = false;
                    nodes.forEach(function(node) {
+
                        });
                    node.hidden = false;
+
                        //check each node
                    node.physics = !node.hidden;
+
                        nodes.forEach(function(node) {
                    node.visited = false;
+
                            setNodeVisibilityByVisiblePath(node.id, input.root)
                    });
+
                            //reset visited state. Todo: Reuse visited nodes between runs
                    //check each node
+
                            nodes.forEach(function(node) {
                    nodes.forEach(function(node) {
+
                                node.visited = false;
                    setNodeVisibilityByVisiblePath(node.id, input.root)
+
                            });
                    //reset visited state. Todo: Reuse visited nodes between runs
+
                        });
                    nodes.forEach(function(node) {
+
                    }
                    node.visited = false;
+
                    network.setOptions(options);
                    });
+
                    network.body.emitter.emit('_dataChanged');
 +
                    network.redraw();
 +
                    var allFalse = Object.keys(options.groups).every(function(k) {
 +
                        if (k === 'useDefaultGroups') {
 +
                            return true
 +
                        }
 +
                        return options.groups[k].hidden === false
 
                     });
 
                     });
                }
+
                     if (allFalse === true) {
               
+
                         /*oldGroups = {};*/
                network.setOptions(options);
 
                network.body.emitter.emit('_dataChanged');
 
                network.redraw();
 
 
 
                var allFalse = Object.keys(options.groups).every(function(k) {
 
                     if (k === 'useDefaultGroups') {
 
                         return true
 
 
                     }
 
                     }
                    return options.groups[k].hidden === false
+
                };
                 });
+
                var legendDiv = document.createElement("div");
                 if (allFalse === true) {
+
                this.append(legendDiv);
                     /*oldGroups = {};*/
+
                legendDiv.style.width = '100%';
 +
                legendDiv.style.position = 'relative';
 +
                legendDiv.style.display = 'inline-block';
 +
                legendDiv.id = "legendContainer";
 +
                 var legendColors = {};
 +
                 for (var i = 0; i < input.properties.length; i++) {
 +
                    legendColors[input.properties[i]] = colors[i];
 +
                    var propertyContainer = document.createElement("div");
 +
                    var propertyColor = document.createElement("div");
 +
                    var propertyName = document.createElement("div");
 +
                    propertyContainer.className = "legend-element-container";
 +
                    propertyContainer.id = input.properties[i];
 +
                    propertyColor.className = "color-container";
 +
                    propertyName.className = "name-container";
 +
                    propertyColor.style.float = "left";
 +
                    propertyName.style.float = "left";
 +
                    propertyColor.style.border = "1px solid black";
 +
                    propertyName.style.border = "1px solid black";
 +
                    propertyColor.style.background = colors[i];
 +
                    propertyColor.innerHTML = "";
 +
                    propertyName.innerHTML = input.properties[i];
 +
                    propertyColor.style.width = "30px";
 +
                    propertyColor.style.height = "30px";
 +
                    propertyName.style.height = "30px";
 +
                    propertyName.style.background = '#DEF';
 +
                     //propertyName.text-align = 'center';
 +
                    propertyContainer.paddinng = '5px 5px 5px 5px';
 +
                    propertyName.addEventListener("click", legendFunctionality);
 +
                    propertyColor.addEventListener("click", legendFunctionality);
 +
                    legendDiv.append(propertyContainer);
 +
                    propertyContainer.append(propertyColor);
 +
                    propertyContainer.append(propertyName);
 
                 }
 
                 }
            };
+
                 var ul = document.createElement("ul");
 
+
                ul.className = 'custom-menu';
            var legendDiv = document.createElement("div");
+
                document.body.append(ul);
            this.append(legendDiv);
+
                objClickedProps = {};
            legendDiv.style.width = '100%';
+
                objColors = {};
            legendDiv.style.position = 'relative';
+
                var start = 0;
            legendDiv.style.display = 'inline-block';
+
                //On a node right click it puts out all properties of the clicked node and a link to the node wiki-page
           
+
                network.on("oncontext", function(params) {
            legendDiv.id = "legendContainer";
+
                    params.event.preventDefault();
            var legendColors = {};
+
                    var timeNow = Date.now();
            for (var i = 0; i < input.properties.length; i++) {
+
                    var timeDiff = timeNow - start
                legendColors[input.properties[i]] = colors[i];
+
                    if (timeDiff > 300) {
                var propertyContainer = document.createElement("div");
+
                        start = Date.now();
                var propertyColor = document.createElement("div");
+
                        //console.log(nodes.get(network.getNodeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })));
                var propertyName = document.createElement("div");
+
                        //console.log(edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })));
 
+
                        $('.custom-menu').each(function(index) {
                propertyContainer.className = "legend-element-container";
+
                            while (this.lastElementChild) {
                propertyContainer.id = input.properties[i];
+
                                this.removeChild(this.lastElementChild);
 
+
                            }
                propertyColor.className = "color-container";
+
                        });
 
+
                        if (!(network.getEdgeAt({
                propertyName.className = "name-container";
+
                                x: params.pointer.DOM.x,
 
+
                                y: params.pointer.DOM.y
                propertyColor.style.float = "left";
+
                            }) && network.getNodeAt({
                propertyName.style.float = "left";
+
                                x: params.pointer.DOM.x,
                propertyColor.style.border = "1px solid black";
+
                                y: params.pointer.DOM.y
                propertyName.style.border = "1px solid black";
+
                            }))) {
 
+
                            if (edges.get(network.getEdgeAt({
                propertyColor.style.background = colors[i];
+
                                    x: params.pointer.DOM.x,
                propertyColor.innerHTML = "";
+
                                    y: params.pointer.DOM.y
                propertyName.innerHTML = input.properties[i];
+
                                })).from) {
 
+
                                params.event.preventDefault();
                propertyColor.style.width = "30px";
+
                                if (edges.get(network.getEdgeAt({
                propertyColor.style.height = "30px";
+
                                        x: params.pointer.DOM.x,
                propertyName.style.height = "30px";
+
                                        y: params.pointer.DOM.y
                propertyName.style.background = '#DEF';
+
                                    })).label == 'Category') {
 
+
                                    var li = document.createElement("li");
                //propertyName.text-align = 'center';
+
                                    li.innerHTML = '' + '\uD83D\uDD17' + ' ' + edges.get(network.getEdgeAt({
                propertyContainer.paddinng = '5px 5px 5px 5px';
+
                                        x: params.pointer.DOM.x,
 
+
                                        y: params.pointer.DOM.y
                propertyName.addEventListener("click", legendFunctionality);
+
                                    })).to;
                propertyColor.addEventListener("click", legendFunctionality);
+
                                    li.addEventListener("click", function NewTab() {
 
+
                                        window.open('/wiki/' + edges.get(network.getEdgeAt({
                legendDiv.append(propertyContainer);
+
                                            x: params.pointer.DOM.x,
                propertyContainer.append(propertyColor);
+
                                            y: params.pointer.DOM.y
                 propertyContainer.append(propertyName);
+
                                        })).to);
 
+
                                    });
            }
+
                                    ul.prepend(li);
 
+
                                } else {
            var ul = document.createElement("ul");
+
                                    var li = document.createElement("li");
            ul.className = 'custom-menu';
+
                                    li.innerHTML = '' + '\uD83D\uDD17' + ' ' + edges.get(network.getEdgeAt({
            document.body.append(ul);
+
                                        x: params.pointer.DOM.x,
            objClickedProps = {};
+
                                        y: params.pointer.DOM.y
            objColors = {};
+
                                    })).label;
            var start = 0;
+
                                    li.addEventListener("click", function NewTab() {
           
+
                                        window.open('/wiki/' + 'Property:' + edges.get(network.getEdgeAt({
 
+
                                            x: params.pointer.DOM.x,
            network.on("oncontext", function(params) {
+
                                            y: params.pointer.DOM.y
            params.event.preventDefault();
+
                                        })).label);
            var timeNow = Date.now();
+
                                    });
            var timeDiff = timeNow - start
+
                                    ul.prepend(li);
            if(timeDiff > 300){
 
            start = Date.now();
 
                console.log(nodes.get(network.getNodeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })));
 
               
 
//console.log(edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })));
 
               
 
                $('.custom-menu').each( function(index) {
 
                while (this.lastElementChild) {
 
                    this.removeChild(this.lastElementChild);
 
                }});
 
                if(!(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y }) && network.getNodeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y }))){
 
                if(edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })).from){
 
                params.event.preventDefault();
 
     
 
                if(edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })).label == 'Category'){
 
               
 
                var li = document.createElement("li");
 
                        li.innerHTML = '' + '\uD83D\uDD17' + ' ' + edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })).to;
 
 
 
                                li.addEventListener("click", function NewTab() {
 
                                window.open('/wiki/' + edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })).to);
 
                                });
 
                                ul.prepend(li);}else{
 
                                var li = document.createElement("li");
 
                        li.innerHTML = '' + '\uD83D\uDD17' + ' ' + edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })).label;
 
 
 
                                li.addEventListener("click", function NewTab() {
 
                                window.open('/wiki/' + 'Property:' + edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })).label);
 
                                });
 
                                ul.prepend(li);
 
 
                                 }
 
                                 }
                        $(".custom-menu").finish().toggle(100).css({
+
                                $(".custom-menu").finish().toggle(100).css({
                        top: params.event.pageY + "px",
+
                                    top: params.event.pageY + "px",
                        left: params.event.pageX + "px",
+
                                    left: params.event.pageX + "px",
                        display: "block"
+
                                    display: "block"
                    });
 
                }
 
                }
 
                if (network.getNodeAt({
 
                        x: params.pointer.DOM.x,
 
                        y: params.pointer.DOM.y
 
                    })) {
 
                    params.event.preventDefault();
 
 
const nodeID = nodes.get(network.getNodeAt({x: params.pointer.DOM.x,y: params.pointer.DOM.y})).id;
 
const subject = nodeID.split("#")[0];
 
var subObject = "";
 
if (nodeID.split("#")[1]) {
 
subObject = nodeID.split("#")[1];
 
}
 
//inverse properties are only available in html format
 
const query = `/w/api.php?action=smwbrowse&browse=subject&params={"subject":"'${encodeURIComponent(subject)}'","subobject":"${subObject}",options":{"showAll":"true"}, "ns":0, "type":"html"}&format=json`;
 
 
 
                    fetch(query)
 
                        .then(response => response.json())
 
                        .then(data => {
 
                        var selected_node =  nodes.get(network.getNodeAt({
 
                            x: params.pointer.DOM.x,
 
                            y: params.pointer.DOM.y
 
                        }));
 
                        if (selected_node.url){
 
                       
 
                        var li = document.createElement("li");
 
                        li.innerHTML = '' + '\uD83D\uDD17' + ' ' + selected_node.label;
 
 
 
                                li.addEventListener("click", function NewTab() {
 
                                window.open(selected_node.url);
 
 
                                 });
 
                                 });
                                  
+
                            }
                                 ul.prepend(li);
+
                        }
                       
+
                        if (network.getNodeAt({
                        }
+
                                x: params.pointer.DOM.x,
                        var page_properties = [];
+
                                y: params.pointer.DOM.y
                        $html = $(data.query);
+
                            })) {
$html.find("div.smwb-propvalue").each(function(){
+
                            params.event.preventDefault();
$prop = $(this).find("div.smwb-prophead a");
+
                            const nodeID = nodes.get(network.getNodeAt({
//var propName = $prop.text();
+
                                 x: params.pointer.DOM.x,
//var propName = $prop.attr('title').replace("Property:", "");
+
                                y: params.pointer.DOM.y
var propName = "";
+
                            })).id;
if ($prop.attr('title') === "Special:Categories") propName += "Category";
+
                            var subject = nodeID.split("#")[0];
else propName += $prop.attr('href').split("Property:")[1].split("&")[0];
+
                            var subObject = "";
page_properties.push(propName);
+
                            if (nodeID.split("#")[1]) {
console.log(propName);
+
                                 subObject = nodeID.split("#")[1].replace(" ", "_");
$(this).find("div.smwb-propval span.smwb-value").each(function(){
+
                            }
var value = $(this).find("a").attr("title");
+
                            var namespace_id = 0;
console.log("-> " + value);
+
                            if (subject.split(":")[1]) {
});
+
                            const namespace = subject.split(":")[0];
})
+
                            subject = subject.split(":")[1];
$html.find("div.smwb-ipropvalue").each(function(){
+
                            namespace_id = mw.config.get('wgNamespaceIds')[namespace.replaceAll(" ","_").toLowerCase()];
$prop = $(this).find("div.smwb-prophead a");
+
                            //console.log(`Namespace ${namespace}, ID ${namespace_id}`);
//var propName = $prop.text();
+
                            }
//var propName = $prop.attr('title').replace("Property:", "");
+
                           
var propName = "-";
+
                            //inverse properties are only available in html format
if ($prop.attr('title') === "Special:Categories") propName += "Category";
+
                            const query = `/w/api.php?action=smwbrowse&browse=subject&params={"subject":"${encodeURIComponent(subject)}","subobject":"${subObject}","options":{"showAll":"true"}, "ns":${namespace_id}, "type":"html"}&format=json`;
else propName += $prop.attr('href').split("Property:")[1].split("&")[0];
+
                            fetch(query)
page_properties.push(propName);
+
                                .then(response => response.json())
console.log(propName);
+
                                .then(data => {
$(this).find("div.smwb-propval span.smwb-ivalue").each(function(){
+
                                    var selected_node = nodes.get(network.getNodeAt({
var value = $(this).find("a").attr("title");
+
                                        x: params.pointer.DOM.x,
console.log("-> " + value);
+
                                        y: params.pointer.DOM.y
});
+
                                    }));
})
+
                                    if (selected_node.url) {
for (var i = 0; i < page_properties.length; i++) {
+
                                        var li = document.createElement("li");
                            if (!page_properties[i].startsWith("_")) {
+
                                        li.innerHTML = '' + '\uD83D\uDD17' + ' ' + selected_node.label;
                                var li = document.createElement("li");
+
                                        li.addEventListener("click", function NewTab() {
                                li.dataset.action = page_properties[i].replaceAll('_',' ');
+
                                            window.open(selected_node.url);
                                li.innerHTML = page_properties[i].replaceAll('_',' ');
+
                                        });
                                ul.append(li);
+
                                        ul.prepend(li);
                            }
+
                                    }
                        }
+
                                    var page_properties = [];
                       
+
                                    $html = $(data.query);
                        /* old: use json result */
+
                                    $html.find("div.smwb-propvalue").each(function() {
                        /*
+
                                        $prop = $(this).find("div.smwb-prophead a");
 +
                                        //var propName = $prop.text();
 +
                                        //var propName = $prop.attr('title').replace("Property:", "");
 +
                                        var propName = "";
 +
                                        if ($prop.attr('title') === "Special:Categories") propName += "Category";
 +
                                        else if ($prop.attr('title') === "Special:ListRedirects") return;
 +
                                        else if ($prop.attr('href')) propName += $prop.attr('href').split("Property:")[1].split("&")[0];
 +
                                        else return; //empty property
 +
                                        page_properties.push(propName);
 +
                                        //console.log(propName);
 +
                                        $(this).find("div.smwb-propval span.smwb-value").each(function() {
 +
                                            var value = $(this).find("a").attr("title");
 +
                                            //console.log("-> " + value);
 +
                                        });
 +
                                    })
 +
                                    $html.find("div.smwb-ipropvalue").each(function() {
 +
                                        $prop = $(this).find("div.smwb-prophead a");
 +
                                        //var propName = $prop.text();
 +
                                        //var propName = $prop.attr('title').replace("Property:", "");
 +
                                        var propName = "-";
 +
                                        if ($prop.attr('title') === "Special:Categories") propName += "Category";
 +
                                        else if ($prop.attr('title') === "Special:ListRedirects") return;
 +
                                        else if ($prop.attr('href')) propName += $prop.attr('href').split("Property:")[1].split("&")[0];
 +
                                        else return; //empty property
 +
                                        page_properties.push(propName);
 +
                                        //console.log(propName);
 +
                                        $(this).find("div.smwb-propval span.smwb-ivalue").each(function() {
 +
                                            var value = $(this).find("a").attr("title");
 +
                                            //console.log("-> " + value);
 +
                                        });
 +
                                    })
 +
                                    for (var i = 0; i < page_properties.length; i++) {
 +
                                        if (!page_properties[i].startsWith("_")) {
 +
                                            var li = document.createElement("li");
 +
                                            li.dataset.action = page_properties[i].replaceAll('_', ' ');
 +
                                            li.innerHTML = page_properties[i].replaceAll('_', ' ');
 +
                                            ul.append(li);
 +
                                        }
 +
                                    }
 +
                                    /* old: use json result */
 +
                                    /*
 
                         var page_properties = data.query.data; //normal page
 
                         var page_properties = data.query.data; //normal page
 
                         if (selected_node.id.includes('#')) { //subobject
 
                         if (selected_node.id.includes('#')) { //subobject
Line 828: Line 771:
 
                            }
 
                            }
 
                        }*/
 
                        }*/
 
+
                        //On left click on one of the properties it creates nodes for the clicked property and if the legend doesnt have that property as a legend entry it is created
                            $(".custom-menu li").click(function() {
+
                                    $(".custom-menu li").click(function() {
 
+
                                        var clickedProperty = [$(this).attr("data-action")]
                                var clickedProperty = [$(this).attr("data-action")]
+
                                        var clickedPropertyColor = randomHSL();
                               
+
                                        if (!(clickedProperty in legendColors)) {
                                var clickedPropertyColor = randomHSL();
+
                                            legendColors[clickedProperty] = clickedPropertyColor;
                               
+
                                        } else {
                                if(!(clickedProperty in legendColors)){legendColors[clickedProperty] = clickedPropertyColor; }else{clickedPropertyColor = legendColors[clickedProperty]; }
+
                                            clickedPropertyColor = legendColors[clickedProperty];
 
+
                                        }
                               
+
                                        if (objColors[clickedProperty]) {
 
+
                                            clickedPropertyColor = objColors[clickedProperty]
                                if (objColors[clickedProperty]) {
+
                                        } else {
                                    clickedPropertyColor = objColors[clickedProperty]
+
                                            objColors[clickedProperty] = clickedPropertyColor;
                                } else {
+
                                        }
                                    objColors[clickedProperty] = clickedPropertyColor;
+
                                        if (!objClickedProps[nodes.get(network.getNodeAt({
                                }
+
                                                x: params.pointer.DOM.x,
 
+
                                                y: params.pointer.DOM.y
 
+
                                            })).id]) {
                                if (!objClickedProps[nodes.get(network.getNodeAt({
+
                                            objClickedProps[nodes.get(network.getNodeAt({
                                        x: params.pointer.DOM.x,
+
                                                x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
+
                                                y: params.pointer.DOM.y
                                    })).id]) {
+
                                            })).id] = new Array();
                                    objClickedProps[nodes.get(network.getNodeAt({
+
                                        }
                                        x: params.pointer.DOM.x,
+
                                        if (!objClickedProps["" + nodes.get(network.getNodeAt({
                                        y: params.pointer.DOM.y
+
                                                x: params.pointer.DOM.x,
                                    })).id] = new Array();
+
                                                y: params.pointer.DOM.y
                                }
+
                                            })).id].includes(clickedProperty[0])) {
 
+
                                            fetchData(nodes.get(network.getNodeAt({
 
+
                                                x: params.pointer.DOM.x,
 
+
                                                y: params.pointer.DOM.y
                                if (!objClickedProps["" + nodes.get(network.getNodeAt({
+
                                            })).id, clickedProperty, nodes.get(network.getNodeAt({
                                        x: params.pointer.DOM.x,
+
                                                x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
+
                                                y: params.pointer.DOM.y
                                    })).id].includes(clickedProperty[0])) {
+
                                            })).id, clickedProperty, clickedPropertyColor)
                                    fetchData(nodes.get(network.getNodeAt({
+
                                            objClickedProps["" + nodes.get(network.getNodeAt({
                                        x: params.pointer.DOM.x,
+
                                                x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
+
                                                y: params.pointer.DOM.y
                                    })).id, clickedProperty, nodes.get(network.getNodeAt({
+
                                            })).id].push(clickedProperty[0]);
                                        x: params.pointer.DOM.x,
+
                                        }
                                        y: params.pointer.DOM.y
+
                                        if (!(contextCreatedProps.includes(clickedProperty[0]) || input.properties.includes(clickedProperty[0]) /*|| legendColors[clickedProperty[0]]*/ )) {
                                    })).id, clickedProperty, clickedPropertyColor)
+
                                            contextCreatedProps.push(clickedProperty[0]);
                                    objClickedProps["" + nodes.get(network.getNodeAt({
+
                                            options.groups[clickedProperty] = {
                                        x: params.pointer.DOM.x,
+
                                                hidden: false
                                        y: params.pointer.DOM.y
+
                                            };
                                    })).id].push(clickedProperty[0]);
+
                                            var propertyContainer = document.createElement("div");
                                }
+
                                            var propertyColor = document.createElement("div");
 
+
                                            var propertyName = document.createElement("div");
                                if (!(contextCreatedProps.includes(clickedProperty[0]) || input.properties.includes(clickedProperty[0]) /*|| legendColors[clickedProperty[0]]*/ )) {
+
                                            propertyContainer.className = "legend-element-container";
 
+
                                            propertyContainer.id = clickedProperty;
                                    contextCreatedProps.push(clickedProperty[0]);
+
                                            propertyColor.className = "color-container";
 
+
                                            propertyName.className = "name-container";
                                    options.groups[clickedProperty] = {
+
                                            propertyColor.style.float = "left";
                                        hidden: false
+
                                            propertyName.style.float = "left";
                                    };
+
                                            propertyColor.style.border = "1px solid black";
 
+
                                            propertyName.style.border = "1px solid black";
                                    var propertyContainer = document.createElement("div");
+
                                            propertyContainer.style = "margin-right: 5px";
                                    var propertyColor = document.createElement("div");
+
                                            propertyColor.style.background = clickedPropertyColor;
                                    var propertyName = document.createElement("div");
+
                                            propertyColor.innerHTML = "";
                                   
+
                                            propertyName.innerHTML = clickedProperty;
 
+
                                            propertyColor.style.width = "30px";
                                    propertyContainer.className = "legend-element-container";
+
                                            propertyColor.style.height = "30px";
                                    propertyContainer.id = clickedProperty;
+
                                            propertyName.style.height = "30px";
 
+
                                            propertyName.style.background = '#DEF';
                                    propertyColor.className = "color-container";
+
                                            //propertyName.text-align = 'center';
 
+
                                            propertyName.margin = 'auto 5px auto 5px';
                                    propertyName.className = "name-container";
+
                                            propertyName.addEventListener("click", legendFunctionality);
 
+
                                            propertyColor.addEventListener("click", legendFunctionality);
                                    propertyColor.style.float = "left";
+
                                            legendDiv.append(propertyContainer);
                                    propertyName.style.float = "left";
+
                                            propertyContainer.append(propertyColor);
                                    propertyColor.style.border = "1px solid black";
+
                                            propertyContainer.append(propertyName);
                                    propertyName.style.border = "1px solid black";
+
                                        }
                                    propertyContainer.style = "margin-right: 5px";
+
                                        $(".custom-menu").hide(100);
 
+
                                    });
 
+
                                 });
                                    propertyColor.style.background = clickedPropertyColor;
+
                            $(".custom-menu").finish().toggle(100).css({
                                    propertyColor.innerHTML = "";
+
                                 top: params.event.pageY + "px",
                                    propertyName.innerHTML = clickedProperty;
+
                                left: params.event.pageX + "px",
 
+
                                display: "block"
                                    propertyColor.style.width = "30px";
+
                            });
                                    propertyColor.style.height = "30px";
+
                        }
                                    propertyName.style.height = "30px";
+
                    }
                                    propertyName.style.background = '#DEF';
+
                });
 
+
                // If the document is clicked somewhere it hides the context menu
                                    //propertyName.text-align = 'center';
+
                $(document).bind("mousedown", function(e) {
                                    propertyName.margin = 'auto 5px auto 5px';
+
                    // If the clicked element is not the menu
 
+
                    if (!$(e.target).parents(".custom-menu").length > 0) {
                                    propertyName.addEventListener("click", legendFunctionality);
+
                        // Hide it
                                    propertyColor.addEventListener("click", legendFunctionality);
+
                        $(".custom-menu").hide(100);
 
+
                    }
 
+
                });
                                    legendDiv.append(propertyContainer);
+
//Add Node popup
                                    propertyContainer.append(propertyColor);
+
                function editNode(data, cancelAction, callback) {
                                    propertyContainer.append(propertyName);
+
                    var newNodeActive = true;
 
+
                    document.getElementById("node-label").value = data.label;
                                 }
+
                    document.getElementById("node-saveButton").onclick = saveNodeData.bind(
 
+
                        this,
                                 $(".custom-menu").hide(100);
+
                        data,
 +
                        callback
 +
                    );
 +
                    document.getElementById("node-cancelButton").onclick = cancelAction.bind(
 +
                        this,
 +
                        callback
 +
                    );
 +
                    //document.getElementById("node-popUp")
 +
                    $('canvas').on('click', function(e) {
 +
                        if (newNodeActive === true) {
 +
                            $("#node-popUp").css({
 +
                                top: e.pageY + "px",
 +
                                left: e.pageX + "px",
 +
                                display: "block"
 
                             });
 
                             });
                         });
+
                         }
 
+
                         newNodeActive = false;
                    $(".custom-menu").finish().toggle(100).css({
 
                         top: params.event.pageY + "px",
 
                        left: params.event.pageX + "px",
 
                        display: "block"
 
 
                     });
 
                     });
 
                 }
 
                 }
            }
+
            });
+
                function clearNodePopUp() {
            // If the document is clicked somewhere
+
                    document.getElementById("node-saveButton").onclick = null;
            $(document).bind("mousedown", function(e) {
+
                    document.getElementById("node-cancelButton").onclick = null;
 +
                    document.getElementById("node-popUp").style.display = "none";
 +
                }
  
                 // If the clicked element is not the menu
+
                 function cancelNodeEdit(callback) {
                 if (!$(e.target).parents(".custom-menu").length > 0) {
+
                    clearNodePopUp();
 
+
                    callback(null);
                     // Hide it
+
                }
                     $(".custom-menu").hide(100);
+
//saveNodeData to the graph
 +
                 function saveNodeData(data, callback) {
 +
                    data.label = document.getElementById("node-label").value;
 +
                    data.id = document.getElementById("node-label").value;
 +
                    data.hidden = false;
 +
                     data.physics = false;
 +
                     document.getElementById("node-label").value = "";
 +
                    clearNodePopUp();
 +
                    callback(data);
 
                 }
 
                 }
            });
+
//addEdge popup
         
+
                function editEdgeWithoutDrag(data, callback) {
function editNode(data, cancelAction, callback) {
+
                    var newEdgeActive = true;
var newNodeActive = true;
+
                    // filling in the popup DOM elements
  document.getElementById("node-label").value = data.label;
+
                    document.getElementById("edge-label").value = data.label;
  document.getElementById("node-saveButton").onclick = saveNodeData.bind(
+
                   
    this,
+
                    document.getElementById("edge-saveButton").onclick = saveEdgeData.bind(
    data,
+
                        this,
    callback
+
                        data,
  );
+
                        callback
  document.getElementById("node-cancelButton").onclick = cancelAction.bind(
+
                    );
    this,
+
                    document.getElementById("edge-cancelButton").onclick = cancelEdgeEdit.bind(
    callback
+
                        this,
  );
+
                        callback
  //document.getElementById("node-popUp")
+
                    );
 
+
                    $('canvas').on('click', function(e) {
 
+
                        if (newEdgeActive === true) {
  $('canvas').on('click', function(e) {
+
                            $("#edge-popUp").css({
  if(newNodeActive === true){
+
                                top: e.pageY + "px",
    $("#node-popUp").css({
+
                                left: e.pageX + "px",
                        top: e.pageY + "px",
+
                                display: "block"
                        left: e.pageX + "px",
+
                            });
                        display: "block"
+
                        }
                        });}
 
                        newNodeActive = false;
 
                       
 
});
 
 
 
}
 
 
 
// Callback passed as parameter is ignored
 
function clearNodePopUp() {
 
  document.getElementById("node-saveButton").onclick = null;
 
  document.getElementById("node-cancelButton").onclick = null;
 
  document.getElementById("node-popUp").style.display = "none";
 
}
 
 
 
function cancelNodeEdit(callback) {
 
  clearNodePopUp();
 
  callback(null);
 
}
 
 
 
function saveNodeData(data, callback) {
 
  data.label = document.getElementById("node-label").value;
 
  data.id = document.getElementById("node-label").value;
 
  data.hidden = false;
 
  data.physics = false;
 
  document.getElementById("node-label").value = "";
 
  clearNodePopUp();
 
  callback(data);
 
}
 
 
 
function editEdgeWithoutDrag(data, callback) {
 
var newEdgeActive = true;
 
  // filling in the popup DOM elements
 
  document.getElementById("edge-label").value = data.label;
 
    /*if(data.from === "H.1"){
 
  console.log("here");
 
  return;}*/
 
  document.getElementById("edge-saveButton").onclick = saveEdgeData.bind(
 
    this,
 
    data,
 
    callback
 
  );
 
  document.getElementById("edge-cancelButton").onclick = cancelEdgeEdit.bind(
 
    this,
 
    callback
 
  );
 
 
 
  $('canvas').on('click', function(e) {
 
  if(newEdgeActive === true){
 
    $("#edge-popUp").css({
 
                        top: e.pageY + "px",
 
                        left: e.pageX + "px",
 
                        display: "block"
 
                        });}
 
 
                         newEdgeActive = false;
 
                         newEdgeActive = false;
});
+
                    });
  //document.getElementById("edge-popUp").style.display = "block";
+
                    //document.getElementById("edge-popUp").style.display = "block";
}
+
                }
  
function clearEdgePopUp() {
+
                function clearEdgePopUp() {
  document.getElementById("edge-saveButton").onclick = null;
+
                    document.getElementById("edge-saveButton").onclick = null;
  document.getElementById("edge-cancelButton").onclick = null;
+
                    document.getElementById("edge-cancelButton").onclick = null;
  document.getElementById("edge-popUp").style.display = "none";
+
                    document.getElementById("edge-popUp").style.display = "none";
}
+
                }
  
function cancelEdgeEdit(callback) {
+
                function cancelEdgeEdit(callback) {
  clearEdgePopUp();
+
                    clearEdgePopUp();
  callback(null);
+
                    callback(null);
}
+
                }
  
function isLabelReversed(label){
+
                function isLabelReversed(label) {
  if(label[0] == "-"){
+
                    if (label[0] == "-") {
    return true;
+
                        return true;
  }
+
                    } else {
  else{
+
                        return false;
    return false;
+
                    }
  }
+
                }
}
+
                var pageBool;
 
+
                //Checks if page exists in the wiki
 
+
                async function pageExists(id) {
 
+
                    await fetch('/w/api.php?action=parse&page=' + id + '&prop=wikitext&format=json')
 
 
 
 
 
 
var pageBool;
 
async function pageExists(id){
 
 
await fetch('/w/api.php?action=parse&page='+ id +'&prop=wikitext&format=json')
 
 
                         .then(response => response.json())
 
                         .then(response => response.json())
 
                         .then(data => {
 
                         .then(data => {
                       
+
                            if (data.error) {
                        if(data.error){
+
                                pageBool = false;
                        pageBool = false;
+
                            } else {
                        }else{
+
                                pageBool = true;
                        pageBool = true;
+
                            }
                        }
 
                       
 
 
                         })
 
                         })
return pageBool;
+
                    return pageBool;
}
+
                }
 
+
                var wikiText = "";
var wikiText = "";
+
                var semantic = "";
var semantic = "";
+
                //Splits Wikitext and Semantic/Element or Semantic/Link
async function editWikiText(node){
+
                async function editWikiText(node) {
await fetch('/w/api.php?action=parse&page='+ node +'&prop=wikitext&format=json')
+
                    await fetch('/w/api.php?action=parse&page=' + node + '&prop=wikitext&format=json')
 
                         .then(response => response.json())
 
                         .then(response => response.json())
 
                         .then(data => {
 
                         .then(data => {
                        wikiText = data.parse.wikitext['*'];
+
                            wikiText = data.parse.wikitext['*'];
                        semantic = "";
+
                            semantic = "";
if(wikiText.search(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0){
+
                            if (wikiText.search(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0) {
//var edgeStringFound = wikiText.search(re) >= 0;
+
                                //var edgeStringFound = wikiText.search(re) >= 0;
const found = wikiText.match(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\})/g);
+
                                const found = wikiText.match(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\})/g);
+
                                var newWikiText = wikiText;
var newWikiText = wikiText;
+
                                for (var i = 0; i < found.length; i++) {
for(var i=0; i<found.length;i++){
+
                                    if (i == found.length - 1) {
if(i == found.length-1){
+
                                        semantic += found[i];
semantic += found[i];
+
                                        newWikiText = newWikiText.replace(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\}[\r\n]*\}[\r\n]*\})/g, "");
newWikiText = newWikiText.replace(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\}[\r\n]*\}[\r\n]*\})/g, "");
+
                                        newWikiText = newWikiText.replace(/(\{\{Semantic\/Link[^}]*[\r\n]*\}[\r\n]*\})/g, "");
}else{
+
                                    } else {
semantic += found[i];
+
                                        semantic += found[i];
newWikiText = newWikiText.replace(found[i], "");
+
                                        newWikiText = newWikiText.replace(found[i], "");
}
+
                                     }
}
 
wikiText = newWikiText;
 
 
 
 
 
 
 
}
 
});
 
return [semantic, wikiText];
 
}
 
 
 
 
 
 
 
async function saveEdgeData(data, callback) {
 
  if (typeof data.to === "object") data.to = data.to.id;
 
  if (typeof data.from === "object") data.from = data.from.id;
 
 
 
  data.label = document.getElementById("edge-label").value;
 
  options.groups[data.label] = {hidden: false};
 
  var toNode = nodes.get(data.to);
 
  var fromNode = nodes.get(data.from);
 
  fromNode.physics = true;
 
  toNode.physics = true;
 
  delete fromNode.x;
 
  delete fromNode.y;
 
  delete toNode.x;
 
  delete toNode.y;
 
  if(!toNode.group){toNode.group = data.label}
 
  if(!fromNode.group){fromNode.group = data.label}
 
  if(legendColors[data.label]){data.color = legendColors[data.label];}else{data.color = randomHSL(); }
 
  if(!toNode.color){toNode.color = data.color; toNode.manually = true;}
 
  if(!fromNode.color){fromNode.color = data.color; fromNode.manually = true;}
 
 
 
  if(!(contextCreatedProps.includes(data.label) || input.properties.includes(data.label))){
 
  contextCreatedProps.push(data.label);
 
var propertyContainer = document.createElement("div");
 
    var propertyColor = document.createElement("div");
 
    var propertyName = document.createElement("div");
 
   
 
 
 
    propertyContainer.className = "legend-element-container";
 
    propertyContainer.id = data.label;
 
 
 
    propertyColor.className = "color-container";
 
 
 
    propertyName.className = "name-container";
 
 
 
    propertyColor.style.float = "left";
 
    propertyName.style.float = "left";
 
    propertyColor.style.border = "1px solid black";
 
    propertyName.style.border = "1px solid black";
 
    propertyContainer.style = "margin-right: 5px";
 
 
 
 
 
    propertyColor.style.background = data.color;
 
    propertyColor.innerHTML = "";
 
    propertyName.innerHTML = data.label;
 
 
 
    propertyColor.style.width = "30px";
 
    propertyColor.style.height = "30px";
 
    propertyName.style.height = "30px";
 
    propertyName.style.background = '#DEF';
 
 
 
    //propertyName.text-align = 'center';
 
    propertyName.margin = 'auto 5px auto 5px';
 
 
 
    propertyName.addEventListener("click", legendFunctionality);
 
    propertyColor.addEventListener("click", legendFunctionality);
 
 
 
 
 
    legendDiv.append(propertyContainer);
 
    propertyContainer.append(propertyColor);
 
    propertyContainer.append(propertyName);
 
  legendColors[data.label] = data.color;
 
  }
 
  if(isLabelReversed(data.label)){
 
  if(await pageExists(fromNode.id) === false){
 
  if(!(newNodes[fromNode.id])){
 
  newNodes[fromNode.id] = '' + '{{Semantic/Element' +
 
'|label=' + fromNode.label +
 
'|description=test' +
 
'|relations=';
 
  }
 
 
 
  }
 
 
 
  if(await pageExists(toNode.id) === true){
 
  var splitWikiText = await editWikiText(toNode.id);
 
  if(editNodes[toNode.id]){
 
  editNodes[toNode.id] += '' + '{{Semantic/Link'+
 
'|property=' + reverseLabel(data.label) +
 
'|value=' + fromNode.id +
 
'}}' + '';
 
 
 
  }else{
 
  if(splitWikiText[0]){
 
  editNodes[toNode.id] = splitWikiText[1] + splitWikiText[0] + '{{Semantic/Link'+
 
'|property=' + reverseLabel(data.label) +
 
'|value=' + fromNode.id +
 
'}}' + '';
 
  }else{
 
  editNodes[toNode.id] = splitWikiText[1] + '{{Semantic/Element' +
 
'|label=' + toNode.label +
 
'|description=test' +
 
'|relations='+
 
  '{{Semantic/Link'+
 
'|property=' + reverseLabel(data.label) +
 
'|value=' + fromNode.id +
 
'}}' + '';
 
  }
 
  }
 
  }else{
 
  if(newNodes[toNode.id]){
 
  newNodes[toNode.id] += '' + '{{Semantic/Link'+
 
'|property=' + reverseLabel(data.label) +
 
'|value=' + fromNode.id +
 
'}}' + '';
 
  }else{
 
  newNodes[toNode.id] = '' + '{{Semantic/Element' +
 
'|label=' + toNode.label +
 
'|description=test' +
 
'|relations={{Semantic/Link'+
 
'|property=' + reverseLabel(data.label) +
 
'|value=' + fromNode.id +
 
'}}'+
 
'';
 
  }
 
  }
 
 
 
  }else{
 
 
 
  if(await pageExists(toNode.id) === false){
 
  if(!(newNodes[toNode.id])){
 
  newNodes[toNode.id] = '' + '{{Semantic/Element' +
 
'|label=' + toNode.label +
 
'|description=test' +
 
'|relations=';
 
  }
 
  }
 
  if(await pageExists(fromNode.id) === true){
 
  var splitWikiText = await editWikiText(fromNode.id);
 
  if(editNodes[fromNode.id]){
 
  editNodes[fromNode.id] += '' + '{{Semantic/Link'+
 
'|property=' + data.label +
 
'|value=' + toNode.id +
 
'}}' + '';
 
  }else{
 
  if(splitWikiText[0]){
 
  editNodes[fromNode.id] = splitWikiText[1] +  splitWikiText[0] + '{{Semantic/Link'+
 
'|property=' + data.label +
 
'|value=' + toNode.id +
 
'}}' + '';
 
  }else{
 
  editNodes[fromNode.id] = splitWikiText[1] + '{{Semantic/Element' +
 
'|label=' + fromNode.label +
 
'|description=test' +
 
'|relations='+
 
  '{{Semantic/Link'+
 
'|property=' + data.label +
 
'|value=' + toNode.id +
 
'}}' + '';
 
  }
 
  }
 
  }else{
 
  if(newNodes[fromNode.id]){
 
  newNodes[fromNode.id] += '' + '{{Semantic/Link'+
 
'|property=' + data.label +
 
'|value=' + toNode.id +
 
'}}' + '';
 
 
 
  }else{
 
  newNodes[fromNode.id] = '' + '{{Semantic/Element' +
 
'|label=' + fromNode.label +
 
'|description=test' +
 
'|relations={{Semantic/Link'+
 
'|property=' + data.label +
 
'|value=' + toNode.id +
 
'}}'+
 
'';
 
 
  }
 
  }
 
 
 
  }
 
 
 
  //console.log(toNode);
 
  //console.log(fromNode);
 
 
 
console.log(editNodes);
 
console.log(newNodes);
 
 
 
  clearEdgePopUp();
 
  callback(data);
 
  network.setOptions(options);
 
  network.body.emitter.emit('_dataChanged');
 
  network.redraw();
 
}
 
    var saveBtn= document.createElement("button");
 
            saveBtn.addEventListener("click", saveGraphChanges);
 
            saveBtn.innerHTML = "Speichern";
 
            saveBtn.style.width = "auto";
 
            saveBtn.style.height = "auto";
 
 
 
givenDiv.appendChild(saveBtn);
 
function saveGraphChanges() {
 
var alertString = "";
 
OO.ui.confirm( 'Änderungen übernehmen?' ).done( async function ( confirmed ) {
 
    if ( confirmed ) {
 
for (const [key, value] of Object.entries(newNodes)) {
 
  var params = {
 
action: 'edit',
 
title: '' + key,
 
appendtext: '' + value + '}}',
 
format: 'json'
 
},
 
api = new mw.Api();
 
 
await api.postWithToken( 'csrf', params ).done( function ( data ) {
 
console.log( data );
 
alertString += "Seite " + key + " erstellt!\r\n"
 
} );
 
}
 
 
for (const [key, value] of Object.entries(editNodes)) {
 
  var params = {
 
action: 'edit',
 
title: '' + key,
 
text: '' + value + '}}',
 
format: 'json'
 
},
 
api = new mw.Api();
 
 
await api.postWithToken( 'csrf', params ).done( function ( data ) {
 
console.log( data );
 
alertString += "Seite " + key + " bearbeitet!\r\n"
 
} );
 
}
 
 
for (const [key, value] of Object.entries(editDeletedEdges)) {
 
  var params = {
 
action: 'edit',
 
title: '' + key,
 
text: '' + value,
 
format: 'json'
 
},
 
api = new mw.Api();
 
 
await api.postWithToken( 'csrf', params ).done( function ( data ) {
 
console.log( data );
 
alertString += "Auf der Seite " + key + " wurde ein Attribut gelöscht!\r\n"
 
} );
 
}
 
 
for (const [key, value] of Object.entries(editDeletedNodes)) {
 
  var params = {
 
action: 'delete',
 
title: '' + key,
 
format: 'json'
 
},
 
api = new mw.Api();
 
await api.postWithToken( 'csrf', params ).done( function ( data ) {
 
console.log( data );
 
alertString += "Seite " + key + " wurde gelöscht!\r\n"
 
} );
 
}
 
console.log( alertString );
 
// Example: Customize the displayed actions at the time the window is opened.
 
var messageDialog = new OO.ui.MessageDialog();
 
 
// Create and append a window manager.
 
var windowManager = new OO.ui.WindowManager();
 
$( 'body' ).append( windowManager.$element );
 
 
// Add the dialog to the window manager.
 
windowManager.addWindows( [ messageDialog ] );
 
 
// Configure the message dialog when it is opened with the window manager's openWindow() method.
 
 
windowManager.openWindow( messageDialog, {
 
  title: 'Folgende Änderugnen wurden übernommen:',
 
  message: '' + alertString,
 
  verbose: true,
 
  actions: [
 
    {
 
      action: 'accept',
 
      label: 'Okay',
 
      flags: 'primary'
 
    }
 
  ]
 
});
 
/*OO.ui.alert( "" + alertString ).done( function () {
 
    console.log( alertString );
 
} );*/
 
 
    } else {
 
       
 
    }
 
   
 
} );
 
 
 
 
}
 
 
 
 
 
 
 
 
function deleteSelectedNode(data, callback){
 
console.log(contextCreatedProps);
 
deleteNodesChildren(data.nodes[0]);
 
nodes.remove(data.nodes[0]);
 
                        for (var i = 0; i < contextCreatedProps.length; i++) {
 
                            var noNodesInNetwork = true;
 
                            for (var j = 0; j < nodes.getIds().length; j++) {
 
                                if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
 
                               
 
                                     noNodesInNetwork = false;
 
 
                                 }
 
                                 }
 +
                                wikiText = newWikiText;
 
                             }
 
                             }
                            if (noNodesInNetwork === true) {
+
                        });
                           
+
                    return [semantic, wikiText];
                                givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
+
                }
                                contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
+
                //Save button on create edge popup
                                 i--;
+
                async function saveEdgeData(data, callback) {
 +
                //Sets various options to the nodes that the edge gets connected
 +
                    if (typeof data.to === "object") data.to = data.to.id;
 +
                    if (typeof data.from === "object") data.from = data.from.id;
 +
                    data.label = document.getElementById("edge-label").value;
 +
                    options.groups[data.label] = {
 +
                        hidden: false
 +
                    };
 +
                    var toNode = nodes.get(data.to);
 +
                    var fromNode = nodes.get(data.from);
 +
                    fromNode.physics = true;
 +
                    toNode.physics = true;
 +
                    delete fromNode.x;
 +
                    delete fromNode.y;
 +
                    delete toNode.x;
 +
                    delete toNode.y;
 +
                    if (!toNode.group) {
 +
                        toNode.group = data.label
 +
                    }
 +
                    if (!fromNode.group) {
 +
                        fromNode.group = data.label
 +
                    }
 +
                    if (legendColors[data.label]) {
 +
                        data.color = legendColors[data.label];
 +
                    } else {
 +
                        data.color = randomHSL();
 +
                    }
 +
                    if (!toNode.color) {
 +
                        toNode.color = data.color;
 +
                        toNode.manually = true;
 +
                    }
 +
                    if (!fromNode.color) {
 +
                        fromNode.color = data.color;
 +
                        fromNode.manually = true;
 +
                    }
 +
                    //If the given property is not set in the legend, a legend entry is created
 +
                    if (!(contextCreatedProps.includes(data.label) || input.properties.includes(data.label))) {
 +
                        contextCreatedProps.push(data.label);
 +
                        var propertyContainer = document.createElement("div");
 +
                        var propertyColor = document.createElement("div");
 +
                        var propertyName = document.createElement("div");
 +
                        propertyContainer.className = "legend-element-container";
 +
                        propertyContainer.id = data.label;
 +
                        propertyColor.className = "color-container";
 +
                        propertyName.className = "name-container";
 +
                        propertyColor.style.float = "left";
 +
                        propertyName.style.float = "left";
 +
                        propertyColor.style.border = "1px solid black";
 +
                        propertyName.style.border = "1px solid black";
 +
                        propertyContainer.style = "margin-right: 5px";
 +
                        propertyColor.style.background = data.color;
 +
                        propertyColor.innerHTML = "";
 +
                        propertyName.innerHTML = data.label;
 +
                        propertyColor.style.width = "30px";
 +
                        propertyColor.style.height = "30px";
 +
                        propertyName.style.height = "30px";
 +
                        propertyName.style.background = '#DEF';
 +
                        //propertyName.text-align = 'center';
 +
                        propertyName.margin = 'auto 5px auto 5px';
 +
                        propertyName.addEventListener("click", legendFunctionality);
 +
                        propertyColor.addEventListener("click", legendFunctionality);
 +
                        legendDiv.append(propertyContainer);
 +
                        propertyContainer.append(propertyColor);
 +
                        propertyContainer.append(propertyName);
 +
                        legendColors[data.label] = data.color;
 +
                    }
 +
                    //Creates new wikitext that will be saved after the save button is clicked
 +
                    if (isLabelReversed(data.label)) {
 +
                        if (await pageExists(fromNode.id) === false) {
 +
                            if (!(newNodes[fromNode.id])) {
 +
                                 newNodes[fromNode.id] = '' + '{{Semantic/Element' +
 +
                                    '|label=' + fromNode.label +
 +
                                    '|description=test' +
 +
                                    '|relations=';
 
                             }
 
                             }
 
                         }
 
                         }
 
+
                         if (await pageExists(toNode.id) === true) {
                          
+
                            var splitWikiText = await editWikiText(toNode.id);
    delete oldGroups["" + data.nodes[0]];
+
                            if (editNodes[toNode.id]) {
                       
+
                                editNodes[toNode.id] += '' + '{{Semantic/Link' +
                        delete objClickedProps["" + data.nodes[0]];
+
                                    '|property=' + reverseLabel(data.label) +
                        callback();
+
                                    '|value=' + fromNode.id +
                        document.querySelector('.vis-delete').remove();
+
                                    '}}' + '';
editDeletedNodes[""+data.nodes[0]] = "";
+
                            } else {
delete newNodes[""+data.nodes[0]];
+
                                if (splitWikiText[0].search(/(\{\{Semantic\/Element[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0) {
delete editNodes[""+data.nodes[0]];
+
                                    editNodes[toNode.id] = splitWikiText[1] + splitWikiText[0] + '{{Semantic/Link' +
console.log(editDeletedNodes);
+
                                        '|property=' + reverseLabel(data.label) +
 
+
                                        '|value=' + fromNode.id +
}
+
                                        '}}' + '';
 
+
                                } else {
async function deleteSelectedEdge(data, callback){
+
                                    editNodes[toNode.id] = splitWikiText[1] + '{{Semantic/Element' +
var edgeToNode = edges.get(data.edges[0]).to;
+
                                        '|label=' + toNode.label +
var edgeFromNode = edges.get(data.edges[0]).from;
+
                                        '|description=test' +
var edgeLabel = edges.get(data.edges[0]).label;
+
                                        '|relations=' +
console.log(edgeLabel);
+
                                        '{{Semantic/Link' +
edges.remove(data.edges[0]);
+
                                        '|property=' + reverseLabel(data.label) +
deleteNodesChildren(edgeToNode, true);
+
                                        '|value=' + fromNode.id +
deleteNodesChildren(edgeFromNode, true);
+
                                        '}}' + '' + splitWikiText[0];
 
for (var i = 0; i < contextCreatedProps.length; i++) {
 
                            var noNodesInNetwork = true;
 
                            for (var j = 0; j < nodes.getIds().length; j++) {
 
                                if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
 
                               
 
                                    noNodesInNetwork = false;
 
 
                                 }
 
                                 }
 
                             }
 
                             }
                             if (noNodesInNetwork === true) {
+
                        } else {
                           
+
                             if (newNodes[toNode.id]) {
                                 givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
+
                                 newNodes[toNode.id] += '' + '{{Semantic/Link' +
                                 contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
+
                                    '|property=' + reverseLabel(data.label) +
                                i--;
+
                                    '|value=' + fromNode.id +
 +
                                    '}}' + '';
 +
                            } else {
 +
                                 newNodes[toNode.id] = '' + '{{Semantic/Element' +
 +
                                    '|label=' + toNode.label +
 +
                                    '|description=test' +
 +
                                    '|relations={{Semantic/Link' +
 +
                                    '|property=' + reverseLabel(data.label) +
 +
                                    '|value=' + fromNode.id +
 +
                                    '}}' +
 +
                                    '';
 
                             }
 
                             }
 
                         }
 
                         }
+
                    } else {
if(edgeLabel[0] == "-"){
+
                        if (await pageExists(toNode.id) === false) {
if(await pageExists(edgeToNode) === true){
+
                            if (!(newNodes[toNode.id])) {
await fetch('/w/api.php?action=parse&page='+ edgeToNode +'&prop=wikitext&format=json')
+
                                newNodes[toNode.id] = '' + '{{Semantic/Element' +
                        .then(response => response.json())
+
                                    '|label=' + toNode.label +
                        .then(data => {
+
                                    '|description=test' +
                        var wikiText = data.parse.wikitext['*'];
+
                                    '|relations=';
console.log(wikiText);
+
                            }
 
                        var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=`+reverseLabel(edgeLabel)+`[\\r\\n]*\\|[\\r\\n]*value=`+edgeFromNode+`[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`
 
//var edgeString = '(\\{\\{Semantic\/Element[^}]*[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*[\\r\\n]*\\}[\\r\\n]*\\})'
 
var re = new RegExp(edgeString,"g");
 
                       
 
                        var edgeStringFound = wikiText.search(re) >= 0;
 
                       
 
                        if(edgeStringFound){
 
                        if(editDeletedEdges[""+ edgeToNode]){
 
                        var newWikiText = editDeletedEdges[""+ edgeToNode].replace(re, "");
 
                        editDeletedEdges[""+ edgeToNode] = newWikiText;
 
                        }else{
 
                        var newWikiText = wikiText.replace(re, "");
 
                        console.log(newWikiText)
 
                        editDeletedEdges[""+ edgeToNode] = newWikiText;
 
                        }
 
   
 
 
                         }
 
                         }
                          
+
                         if (await pageExists(fromNode.id) === true) {
                        if(newNodes[""+edgeToNode]){
+
                            var splitWikiText = await editWikiText(fromNode.id);
                       
+
                            if (editNodes[fromNode.id]) {
                        var newWikiText = newNodes[""+edgeToNode].replace(re, "");
+
                                editNodes[fromNode.id] += '' + '{{Semantic/Link' +
                        newNodes[""+edgeToNode] = newWikiText;
+
                                    '|property=' + data.label +
 
+
                                    '|value=' + toNode.id +
 +
                                    '}}' + '';
 +
                            } else {
 +
                                if (splitWikiText[0].search(/(\{\{Semantic\/Element[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0) {
 +
                                    editNodes[fromNode.id] = splitWikiText[1] + splitWikiText[0] + '{{Semantic/Link' +
 +
                                        '|property=' + data.label +
 +
                                        '|value=' + toNode.id +
 +
                                        '}}' + '';
 +
                                } else {
 +
                                    editNodes[fromNode.id] = splitWikiText[1] + '{{Semantic/Element' +
 +
                                        '|label=' + fromNode.label +
 +
                                        '|description=test' +
 +
                                        '|relations=' +
 +
                                        '{{Semantic/Link' +
 +
                                        '|property=' + data.label +
 +
                                        '|value=' + toNode.id +
 +
                                        '}}' + '' + splitWikiText[0];
 +
                                }
 +
                            }
 +
                        } else {
 +
                            if (newNodes[fromNode.id]) {
 +
                                newNodes[fromNode.id] += '' + '{{Semantic/Link' +
 +
                                    '|property=' + data.label +
 +
                                    '|value=' + toNode.id +
 +
                                    '}}' + '';
 +
                            } else {
 +
                                newNodes[fromNode.id] = '' + '{{Semantic/Element' +
 +
                                    '|label=' + fromNode.label +
 +
                                    '|description=test' +
 +
                                    '|relations={{Semantic/Link' +
 +
                                    '|property=' + data.label +
 +
                                    '|value=' + toNode.id +
 +
                                    '}}' +
 +
                                    '';
 +
                            }
 
                         }
 
                         }
                       
+
                    }
                         if(editNodes[""+edgeToNode]){
+
                    //console.log(toNode);
                       
+
                    //console.log(fromNode);
                        var newWikiText = editNodes[""+edgeToNode].replace(re, "");
+
                    //console.log(editNodes);
                        editNodes[""+edgeToNode] = newWikiText;
+
                    //console.log(newNodes);
                       
+
                    clearEdgePopUp();
 +
                    callback(data);
 +
                    network.setOptions(options);
 +
                    network.body.emitter.emit('_dataChanged');
 +
                    network.redraw();
 +
                }
 +
                //save button
 +
                var saveBtn = document.createElement("button");
 +
                saveBtn.addEventListener("click", saveGraphChanges);
 +
                saveBtn.innerHTML = "Speichern";
 +
                saveBtn.style.width = "auto";
 +
                saveBtn.style.height = "auto";
 +
                givenDiv.appendChild(saveBtn);
 +
 +
//Called on save button click. Creates new wiki pages or edits them with the created wiki text.
 +
                function saveGraphChanges() {
 +
                    var alertString = "";
 +
                    OO.ui.confirm('Änderungen übernehmen?').done(async function(confirmed) {
 +
                         if (confirmed) {
 +
                            for (const [key, value] of Object.entries(newNodes)) {
 +
                                var params = {
 +
                                        action: 'edit',
 +
                                        title: '' + key,
 +
                                        appendtext: '' + value + '}}',
 +
                                        format: 'json'
 +
                                    },
 +
                                    api = new mw.Api();
 +
                                await api.postWithToken('csrf', params).done(function(data) {
 +
                                    console.log(data);
 +
                                    alertString += "Seite " + key + " erstellt!\r\n"
 +
                                });
 +
                            }
 +
                            for (const [key, value] of Object.entries(editNodes)) {
 +
                                var params = {
 +
                                        action: 'edit',
 +
                                        title: '' + key,
 +
                                        text: '' + value + '}}',
 +
                                        format: 'json'
 +
                                    },
 +
                                    api = new mw.Api();
 +
                                await api.postWithToken('csrf', params).done(function(data) {
 +
                                    console.log(data);
 +
                                    alertString += "Seite " + key + " bearbeitet!\r\n"
 +
                                });
 +
                            }
 +
                            for (const [key, value] of Object.entries(editDeletedEdges)) {
 +
                                var params = {
 +
                                        action: 'edit',
 +
                                        title: '' + key,
 +
                                        text: '' + value,
 +
                                        format: 'json'
 +
                                    },
 +
                                    api = new mw.Api();
 +
                                await api.postWithToken('csrf', params).done(function(data) {
 +
                                    console.log(data);
 +
                                    alertString += "Auf der Seite " + key + " wurde ein Attribut gelöscht!\r\n"
 +
                                });
 +
                            }
 +
                            for (const [key, value] of Object.entries(editDeletedNodes)) {
 +
                                var params = {
 +
                                        action: 'delete',
 +
                                        title: '' + key,
 +
                                        format: 'json'
 +
                                    },
 +
                                    api = new mw.Api();
 +
                                await api.postWithToken('csrf', params).done(function(data) {
 +
                                    console.log(data);
 +
                                    alertString += "Seite " + key + " wurde gelöscht!\r\n"
 +
                                });
 +
                            }
 +
                            // Example: Customize the displayed actions at the time the window is opened.
 +
                            var messageDialog = new OO.ui.MessageDialog();
 +
                            // Create and append a window manager.
 +
                            var windowManager = new OO.ui.WindowManager();
 +
                            $('body').append(windowManager.$element);
 +
                            // Add the dialog to the window manager.
 +
                            windowManager.addWindows([messageDialog]);
 +
                            // Configure the message dialog when it is opened with the window manager's openWindow() method.
 +
                            windowManager.openWindow(messageDialog, {
 +
                                title: 'Folgende Änderugnen wurden übernommen:',
 +
                                message: '' + alertString,
 +
                                verbose: true,
 +
                                actions: [{
 +
                                    action: 'accept',
 +
                                    label: 'Okay',
 +
                                    flags: 'primary'
 +
                                }]
 +
                            });
 +
                            /*OO.ui.alert( "" + alertString ).done( function () {
 +
                                console.log( alertString );
 +
                            } );*/
 +
                        } else {}
 +
                    });
 +
                }
 +
//Deletes node in manipulation mode and the wiki page.
 +
                function deleteSelectedNode(data, callback) {
 +
                    deleteNodesChildren(data.nodes[0]);
 +
                    nodes.remove(data.nodes[0]);
 +
                    for (var i = 0; i < contextCreatedProps.length; i++) {
 +
                        var noNodesInNetwork = true;
 +
                        for (var j = 0; j < nodes.getIds().length; j++) {
 +
                            if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
 +
                                noNodesInNetwork = false;
 +
                            }
 
                         }
 
                         }
 
+
                         if (noNodesInNetwork === true) {
                         });}else{
+
                            givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
                        if(network.getConnectedNodes(edgeToNode).length == 0){
+
                            contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
                        delete newNodes[""+edgeToNode];
+
                            i--;
                        }else{
 
                        var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=`+reverseLabel(edgeLabel)+`[\\r\\n]*\\|[\\r\\n]*value=`+edgeFromNode+`[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
 
 
 
                        var re = new RegExp(edgeString,"g");
 
                        var wikiText = newNodes[""+edgeToNode];
 
                        var newWikiText = wikiText.replace(re,"");
 
                        newNodes[""+edgeToNode] = newWikiText;
 
                        }
 
 
                         }
 
                         }
}else{
+
                    }
if(await pageExists(edgeFromNode) === true){
+
                    delete oldGroups["" + data.nodes[0]];
await fetch('/w/api.php?action=parse&page='+ edgeFromNode +'&prop=wikitext&format=json')
+
                    delete objClickedProps["" + data.nodes[0]];
                        .then(response => response.json())
+
                    callback();
                        .then(data => {
+
                    document.querySelector('.vis-delete').remove();
                        var wikiText = data.parse.wikitext['*'];
+
                    editDeletedNodes["" + data.nodes[0]] = "";
console.log(wikiText);
+
                    delete newNodes["" + data.nodes[0]];
+
                    delete editNodes["" + data.nodes[0]];
                        var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=`+edgeLabel+`[\\r\\n]*\\|[\\r\\n]*value=`+edgeToNode+`[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
+
                }
//var edgeString = '(\\{\\{Semantic\/Element[^}]*[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*[\\r\\n]*\\}[\\r\\n]*\\})'
+
                //Deletes edge in manipulation mode and deletes the property from the node wikipages
var re = new RegExp(edgeString,"g");
+
                async function deleteSelectedEdge(data, callback) {
                       
+
                    var edgeToNode = edges.get(data.edges[0]).to;
                        var edgeStringFound = wikiText.search(re) >= 0;
+
                    var edgeFromNode = edges.get(data.edges[0]).from;
                       
+
                    var edgeLabel = edges.get(data.edges[0]).label;
                        if(edgeStringFound){
+
                    edges.remove(data.edges[0]);
                        if(editDeletedEdges[""+ edgeFromNode]){
+
                    deleteNodesChildren(edgeToNode, true);
                        var newWikiText = editDeletedEdges[""+ edgeFromNode].replace(re, "");
+
                    deleteNodesChildren(edgeFromNode, true);
                        editDeletedEdges[""+ edgeFromNode] = newWikiText;
+
                    for (var i = 0; i < contextCreatedProps.length; i++) {
                        }else{
+
                        var noNodesInNetwork = true;
                        var newWikiText = wikiText.replace(re, "");
+
                        for (var j = 0; j < nodes.getIds().length; j++) {
                        console.log(newWikiText)
+
                            if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
                        editDeletedEdges[""+ edgeFromNode] = newWikiText;
+
                                noNodesInNetwork = false;
                        }
+
                            }
   
 
 
                         }
 
                         }
                       
+
                         if (noNodesInNetwork === true) {
                         if(newNodes[""+edgeFromNode]){
+
                            givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
                       
+
                            contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
                        var newWikiText = newNodes[""+edgeFromNode].replace(re, "");
+
                            i--;
                        newNodes[""+edgeFromNode] = newWikiText;
 
 
 
 
                         }
 
                         }
                          
+
                    }
                        if(editNodes[""+edgeFromNode]){
+
                    if (edgeLabel[0] == "-") {
                       
+
                         if (await pageExists(edgeToNode) === true) {
                        var newWikiText = editNodes[""+edgeFromNode].replace(re, "");
+
                            await fetch('/w/api.php?action=parse&page=' + edgeToNode + '&prop=wikitext&format=json')
                        editNodes[""+edgeFromNode] = newWikiText;
+
                                .then(response => response.json())
                       
+
                                .then(data => {
 +
                                    var wikiText = data.parse.wikitext['*'];
 +
                                    var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + reverseLabel(edgeLabel) + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeFromNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`
 +
                                    //var edgeString = '(\\{\\{Semantic\/Element[^}]*[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*[\\r\\n]*\\}[\\r\\n]*\\})'
 +
                                    var re = new RegExp(edgeString, "g");
 +
                                    var edgeStringFound = wikiText.search(re) >= 0;
 +
                                    if (edgeStringFound) {
 +
                                        if (editDeletedEdges["" + edgeToNode]) {
 +
                                            var newWikiText = editDeletedEdges["" + edgeToNode].replace(re, "");
 +
                                            editDeletedEdges["" + edgeToNode] = newWikiText;
 +
                                        } else {
 +
                                            var newWikiText = wikiText.replace(re, "");
 +
                                            editDeletedEdges["" + edgeToNode] = newWikiText;
 +
                                        }
 +
                                    }
 +
                                    if (newNodes["" + edgeToNode]) {
 +
                                        var newWikiText = newNodes["" + edgeToNode].replace(re, "");
 +
                                        newNodes["" + edgeToNode] = newWikiText;
 +
                                    }
 +
                                    if (editNodes["" + edgeToNode]) {
 +
                                        var newWikiText = editNodes["" + edgeToNode].replace(re, "");
 +
                                        editNodes["" + edgeToNode] = newWikiText;
 +
                                    }
 +
                                });
 +
                        } else {
 +
                            if (network.getConnectedNodes(edgeToNode).length == 0) {
 +
                                delete newNodes["" + edgeToNode];
 +
                            } else {
 +
                                var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + reverseLabel(edgeLabel) + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeFromNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
 +
                                var re = new RegExp(edgeString, "g");
 +
                                var wikiText = newNodes["" + edgeToNode];
 +
                                var newWikiText = wikiText.replace(re, "");
 +
                                newNodes["" + edgeToNode] = newWikiText;
 +
                            }
 
                         }
 
                         }
 
+
                    } else {
                       
+
                        if (await pageExists(edgeFromNode) === true) {
                        });}else{
+
                            await fetch('/w/api.php?action=parse&page=' + edgeFromNode + '&prop=wikitext&format=json')
                        if(network.getConnectedNodes(edgeFromNode).length == 0){
+
                                .then(response => response.json())
                        delete newNodes[""+edgeFromNode];
+
                                .then(data => {
                        }else{
+
                                    var wikiText = data.parse.wikitext['*'];
                        var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=`+edgeLabel+`[\\r\\n]*\\|[\\r\\n]*value=`+edgeToNode+`[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
+
                                    var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + edgeLabel + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeToNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
 
+
                                    //var edgeString = '(\\{\\{Semantic\/Element[^}]*[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*[\\r\\n]*\\}[\\r\\n]*\\})'
                        var re = new RegExp(edgeString,"g");
+
                                    var re = new RegExp(edgeString, "g");
                        var wikiText = newNodes[""+edgeFromNode];
+
                                    var edgeStringFound = wikiText.search(re) >= 0;
                        var newWikiText = wikiText.replace(re,"");
+
                                    if (edgeStringFound) {
                        newNodes[""+edgeFromNode] = newWikiText;
+
                                        if (editDeletedEdges["" + edgeFromNode]) {
                        }
+
                                            var newWikiText = editDeletedEdges["" + edgeFromNode].replace(re, "");
                        console.log(newNodes);
+
                                            editDeletedEdges["" + edgeFromNode] = newWikiText;
 +
                                        } else {
 +
                                            var newWikiText = wikiText.replace(re, "");
 +
                                            editDeletedEdges["" + edgeFromNode] = newWikiText;
 +
                                        }
 +
                                    }
 +
                                    if (newNodes["" + edgeFromNode]) {
 +
                                        var newWikiText = newNodes["" + edgeFromNode].replace(re, "");
 +
                                        newNodes["" + edgeFromNode] = newWikiText;
 +
                                    }
 +
                                    if (editNodes["" + edgeFromNode]) {
 +
                                        var newWikiText = editNodes["" + edgeFromNode].replace(re, "");
 +
                                        editNodes["" + edgeFromNode] = newWikiText;
 +
                                    }
 +
                                });
 +
                        } else {
 +
                            if (network.getConnectedNodes(edgeFromNode).length == 0) {
 +
                                delete newNodes["" + edgeFromNode];
 +
                            } else {
 +
                                var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + edgeLabel + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeToNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
 +
                                var re = new RegExp(edgeString, "g");
 +
                                var wikiText = newNodes["" + edgeFromNode];
 +
                                var newWikiText = wikiText.replace(re, "");
 +
                                newNodes["" + edgeFromNode] = newWikiText;
 +
                            }
 
                         }
 
                         }
                       
+
                    }
}
+
                    //nodes.remove(edges.get(data.edges[0]).to);
+
                    callback(data);
+
                    document.querySelector('.vis-delete').remove();
+
                }
+
                //HTML for the manipulation popups
console.log(editDeletedEdges);
+
                var editHtml = '' +
//nodes.remove(edges.get(data.edges[0]).to);
+
                    '<div id="node-popUp">' +
+
                    '  <span id="node-operation" style="cursor: move;">node</span> <br />' +
callback(data);
+
                    '  <table style="margin: auto">' +
document.querySelector('.vis-delete').remove();
+
                    '    <tbody>' +
console.log(objClickedProps);
+
                    '      <tr>' +
    console.log(oldGroups);
+
                    '        <td>label</td>' +
}
+
                    '        <td><input id="node-label" value="" /></td>' +
 +
                    '      </tr>' +
 +
                    '    </tbody>' +
 +
                    '  </table>' +
 +
                    '  <input type="button" value="save" id="node-saveButton" />' +
 +
                    '  <input type="button" value="cancel" id="node-cancelButton" />' +
 +
                    '</div>' +
 +
                    '' +
 +
                    '<div id="edge-popUp">' +
 +
                    '  <span id="edge-operation" style="cursor: move;">edge</span> <br />' +
 +
                    '  <table style="margin: auto">' +
 +
                    '    <tbody>' +
 +
                    '      <tr>' +
 +
                    '        <td>label</td>' +
 +
                    '        <td><input id="edge-label" value="" /></td>' +
 +
                    '      </tr>' +
 +
                    '    </tbody>' +
 +
                    '  </table>' +
 +
                    '  <input type="button" value="save" id="edge-saveButton" />' +
 +
                    '  <input type="button" value="cancel" id="edge-cancelButton" />' +
 +
                    '</div>' +
 +
                    '';
 +
                var editHtmlDiv = document.createElement("div");
 +
                editHtmlDiv.innerHTML = editHtml;
 +
                document.body.appendChild(editHtmlDiv);
 +
                //dragElement(document.getElementById("node-popUp"));
 +
                //dragElement(document.getElementById("edge-popUp"));
 +
               
 +
                //function to make the manipulation popups draggable
 +
                function dragElement(elmnt) {
 +
                    var pos1 = 0,
 +
                        pos2 = 0,
 +
                        pos3 = 0,
 +
                        pos4 = 0;
 +
                    if (document.getElementById(elmnt.id)) {
 +
                        // if present, the header is where you move the DIV from:
 +
                        document.getElementById("node-operation").onmousedown = dragMouseDown;
 +
                        document.getElementById("edge-operation").onmousedown = dragMouseDown;
 +
                    } else {
 +
                        // otherwise, move the DIV from anywhere inside the DIV:
 +
                        elmnt.onmousedown = dragMouseDown;
 +
                    }
  
var editHtml = '' +
+
                    function dragMouseDown(e) {
 +
                        e = e || window.event;
 +
                        e.preventDefault();
 +
                        // get the mouse cursor position at startup:
 +
                        pos3 = e.clientX;
 +
                        pos4 = e.clientY;
 +
                        document.onmouseup = closeDragElement;
 +
                        // call a function whenever the cursor moves:
 +
                        document.onmousemove = elementDrag;
 +
                    }
  
'<div id="node-popUp">' +
+
                    function elementDrag(e) {
'  <span id="node-operation" style="cursor: move;">node</span> <br />' +
+
                        e = e || window.event;
'  <table style="margin: auto">' +
+
                        e.preventDefault();
'    <tbody>' +
+
                        // calculate the new cursor position:
'      <tr>' +
+
                        pos1 = pos3 - e.clientX;
'        <td>label</td>' +
+
                        pos2 = pos4 - e.clientY;
'        <td><input id="node-label" value="" /></td>' +
+
                        pos3 = e.clientX;
'      </tr>' +
+
                        pos4 = e.clientY;
'    </tbody>' +
+
                        // set the element's new position:
'  </table>' +
+
                        elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
'  <input type="button" value="save" id="node-saveButton" />' +
+
                        elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
'  <input type="button" value="cancel" id="node-cancelButton" />' +
+
                    }
'</div>' +
 
'' +
 
'<div id="edge-popUp">' +
 
'  <span id="edge-operation" style="cursor: move;">edge</span> <br />' +
 
'  <table style="margin: auto">' +  
 
'    <tbody>' +
 
'      <tr>' +
 
'        <td>label</td>' +
 
'        <td><input id="edge-label" value="" /></td>' +
 
'      </tr>' +
 
'    </tbody>' +
 
'  </table>' +
 
'  <input type="button" value="save" id="edge-saveButton" />' +  
 
'  <input type="button" value="cancel" id="edge-cancelButton" />' +
 
'</div>' +
 
'';
 
  
 
+
                    function closeDragElement() {
 
+
                        // stop moving when mouse button is released:
 
+
                        document.onmouseup = null;
var editHtmlDiv = document.createElement("div");
+
                        document.onmousemove = null;
editHtmlDiv.innerHTML = editHtml;
+
                    }
document.body.appendChild(editHtmlDiv);
+
                }
 
+
            }
 
+
         });
 
 
 
 
 
 
 
 
 
 
//dragElement(document.getElementById("node-popUp"));
 
//dragElement(document.getElementById("edge-popUp"));
 
 
 
function dragElement(elmnt) {
 
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
 
  if (document.getElementById(elmnt.id)) {
 
 
 
    // if present, the header is where you move the DIV from:
 
    document.getElementById("node-operation").onmousedown = dragMouseDown;
 
    document.getElementById("edge-operation").onmousedown = dragMouseDown;
 
  } else {
 
    // otherwise, move the DIV from anywhere inside the DIV:
 
    elmnt.onmousedown = dragMouseDown;
 
  }
 
 
 
  function dragMouseDown(e) {
 
    e = e || window.event;
 
    e.preventDefault();
 
    // get the mouse cursor position at startup:
 
    pos3 = e.clientX;
 
    pos4 = e.clientY;
 
    document.onmouseup = closeDragElement;
 
    // call a function whenever the cursor moves:
 
    document.onmousemove = elementDrag;
 
  }
 
 
 
  function elementDrag(e) {
 
    e = e || window.event;
 
    e.preventDefault();
 
    // calculate the new cursor position:
 
    pos1 = pos3 - e.clientX;
 
    pos2 = pos4 - e.clientY;
 
    pos3 = e.clientX;
 
    pos4 = e.clientY;
 
    // set the element's new position:
 
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
 
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
 
  }
 
 
 
  function closeDragElement() {
 
    // stop moving when mouse button is released:
 
    document.onmouseup = null;
 
    document.onmousemove = null;
 
  }
 
}
 
 
 
 
 
         }
 
    });
 
 
     });
 
     });
 
 
});
 
});

Latest revision as of 05:57, 17 May 2022

$(document).ready(function() {
    $.getScript('https://unpkg.com/vis-network/standalone/umd/vis-network.min.js').done(function() {
        var pathId = 0;
        var newNodes = {};
        var editNodes = {};
        var editDeletedEdges = {};
        var editDeletedNodes = {};
        $(".InteractiveSemanticGraph").each(function(index) {
            if ($('.InteractiveSemanticGraph').length) { //check if div element(s) exist
                var input = JSON.parse(this.innerHTML);
                // create an array with nodes
                var nodes = new vis.DataSet([]);
                // create an array with edges
                var edges = new vis.DataSet([]);
                //colors for the graph
                var colors = [];
                var oldGroups = {};
                var givenDiv = this;
                givenDiv.style.position = "relative";
                givenDiv.style.display = "inline-block";

				//Function for random colors
                var h = Math.random();
                var golden = 0.618033988749895;

                function randomHSL() {
                    h += golden;
                    h %= 1;
                    return "hsla(" + (360 * h) + "," +
                        "70%," +
                        "80%,1)";
                }
                for (var i = 0; i < input.properties.length; i++) {
                    colors.push(randomHSL());
                }

                function isLabelSet(id) {
                    node = nodes.get(id);
                    if (node === null) return false;
                    else return id;
                }

                nodes.add({
                    id: input.root,
                    label: input.root, //todo: query display title
                    color: '#6dbfa9'
                });
                //Creates API query Url with the given root and properties
                function createUrl(root, properties) {
                	if (properties[0] === "-Category") properties[0] = "Category";
                	else if (root.startsWith("Category:")) root = ":" + root; //[[Category:X]] queries pages within this category, [[:Category:X]] the category itself
                    var url = `/w/api.php?action=ask&query=[[${encodeURIComponent(root)}]]`;
                    var propertiesVar = '';
                    for (var i = 0; i < properties.length; i++) {
                        propertiesVar += '|?' + encodeURIComponent(properties[i]) + "=" + encodeURIComponent(properties[i]); //explicit label overwrites property display title. ToDo: extrakt label in result and get corresponding printout
                        propertiesVar += '|?' + encodeURIComponent(properties[i] + ".Display title of") + "=" + encodeURIComponent(properties[i] + ".Display title of"); //explicit query for display title due to slow update of the displaytitle page field 
                    }
                    url = url + propertiesVar + '&format=json';
                    return url;
                }
                //Makes an API call with the given parameters and adds the results to the nodes and edges datasets.
                //With a given nodeID the edges are set to the nodeID, else they are set to the root node.
                function fetchData(root, properties, nodeID, setGroup, setColor) {
                    fetch(createUrl(root, properties))
                        .then(response => response.json())
                        .then(data => {
                            if (!nodeID && root) { //first query on root node
                                var rootNode = nodes.get(root);
                                rootNode.url = data.query.results[root].fullurl;
                                if (data.query.results[root].displaytitle) rootNode.label = data.query.results[root].displaytitle;
                            }

                            for (var i = 0; i < properties.length; i++) {

                                for (var j = 0; j < data.query.results[root].printouts[properties[i]].length; j++) {

                                    //define colors
                                    if (!(properties[i] in legendColors) && setColor) {
                                        legendColors[properties[i]] = colors[i];
                                    } else {
                                        setColor = legendColors[properties[i]];
                                        colors[i] = legendColors[properties[i]];
                                        colors[i] = legendColors[properties[i]];
                                    }
                                    //define id and label. use displaytitle if available. Use string representation of non-page properties
                                    var id = "";
                                    var label = "";
                                    if (data.query.results[root].printouts[properties[i]][j].fulltext) id = data.query.results[root].printouts[properties[i]][j].fulltext;
                                    else if (data.query.results[root].printouts[properties[i]][j].value) id = '' + data.query.results[root].printouts[properties[i]][j].value + ' ' + data.query.results[root].printouts[properties[i]][j].unit;
                                    else id = data.query.results[root].printouts[properties[i]][j].toString();
                                    if (data.query.results[root].printouts[properties[i]][j].displaytitle) label = data.query.results[root].printouts[properties[i]][j].displaytitle;
                                    if (data.query.results[root].printouts[properties[i] + ".Display title of"][j]) label = data.query.results[root].printouts[properties[i] + ".Display title of"][j]; //explicit use property display title due to slow update of the displaytitle page field
                                    if (label === "") label = id;
                                    if (isLabelSet(id) === false) {
                                        if (setGroup && setColor) {
                                            nodes.add({
                                                id: id,
                                                label: label,
                                                color: setColor,
                                                group: setGroup[0],
                                                hidden: false,
                                                url: data.query.results[root].printouts[properties[i]][j].fullurl,
                                                oncontext: true,
                                            });
                                            oldGroups["" + id] = setGroup[0];
                                        } else {
                                            nodes.add({
                                                id: id,
                                                label: label,
                                                color: colors[i],
                                                group: properties[i],
                                                hidden: false,
                                                url: data.query.results[root].printouts[properties[i]][j].fullurl
                                            });
                                            oldGroups["" + id] = properties[i];
                                        }
                                        if (nodeID) {
                                            edges.add({
                                                from: nodeID,
                                                to: id,
                                                label: properties[i],
                                                color: colors[i],
                                                group: properties[i]
                                            });
                                        } else {
                                            edges.add({
                                                from: input.root,
                                                to: id,
                                                label: properties[i],
                                                color: colors[i],
                                                group: properties[i]
                                            });
                                        }
                                    } else {
                                        edges.add({
                                            from: nodeID,
                                            to: isLabelSet(id),
                                            label: properties[i],
                                            color: setColor,
                                            group: properties[i]
                                        });
                                    }
                                }
                            }
                            network.setOptions(options);
                            network.body.emitter.emit('_dataChanged');
                            network.redraw();
                        });
                }
                fetchData(input.root, input.properties);
                // create a network
                var container = this; //document.getElementById("InteractiveSemanticGraph");
                var data = {
                    nodes: nodes,
                    edges: edges,
                };
                var options = {
                    width: "100%",
                    height: "100%",
                    interaction: {
                        hover: true
                    },
                    manipulation: {
                        enabled: true,
                        editEdge: false,
                        deleteNode: function(data, callback) {
                            deleteSelectedNode(data, callback)
                        }.bind(this),
                        deleteEdge: function(data, callback) {
                            deleteSelectedEdge(data, callback)
                        }.bind(this),
                        addNode: function(data, callback) {
                            // filling in the popup DOM elements
                            document.getElementById("node-operation").innerText = "Add Node";
                            dragElement(document.getElementById("node-popUp"));
                            editNode(data, clearNodePopUp, callback);
                        },
                        addEdge: function(data, callback) {
                            if (data.from == data.to) {
                                var r = confirm("Do you want to connect the node to itself?");
                                if (r != true) {
                                    callback(null);
                                    return;
                                }
                            }
                            document.getElementById("edge-operation").innerText = "Add Edge";
                            dragElement(document.getElementById("edge-popUp"));
                            editEdgeWithoutDrag(data, callback);
                        },
                    },
                    edges: {
                        arrows: {
                            to: {
                                enabled: true
                            },
                            //from:{enabled: true}
                        }
                    },
                    groups: {
                        useDefaultGroups: false
                    },
                    physics: {
                        stabilization: {
                            enabled: true,
                        },
                        barnesHut: {
                            gravitationalConstant: -40000,
                            centralGravity: 0,
                            springLength: 0,
                            springConstant: 0.5,
                            damping: 1,
                            avoidOverlap: 0
                        },
                        maxVelocity: 5
                    },
                };
                //Creates groups in the options and sets them all to hidden:false.
                for (var i = 0; i < input.properties.length; i++) {
                    options.groups[input.properties[i]] = {
                        hidden: false
                    };
                }
                var network = new vis.Network(container, data, options);
				
				//The function getAllEdgesBetween() returns all edges between two nodes
                function getAllEdgesBetween(node1, node2) {
                    return edges.get().filter(function(edge) {
                        return (edge.from === node1 && edge.to === node2) || (edge.from === node2 && edge.to === node1);
                    });
                }
                
                //Cartesian Product of arrays
                function cartesianProduct(arr) {
				    return arr.reduce(function(a,b){
				        return a.map(function(x){
				            return b.map(function(y){
				                return x.concat([y]);
				            })
				        }).reduce(function(a,b){ return a.concat(b) },[])
				    }, [[]])
				}
				//Cartesian Product of given arrays
                function getAllCombs(arrays) {
                	var allCombs = cartesianProduct(arrays);
                    return allCombs;
                }
				//Gets Path array with nodes, returns Cartesian Product  of edges
                function getEdgePathsForPath(path) {
                    var arraysOfEdgesForNodeInPath = [];
                    for (var i = 1; i < path.length; i++) {
                        var edgesBetween = getAllEdgesBetween(path[i - 1], path[i]);
                        var localedgesBetween = edgesBetween.slice();
                        
                        arraysOfEdgesForNodeInPath.push(localedgesBetween);
                    }
                    var allEdgePaths = getAllCombs(arraysOfEdgesForNodeInPath);
                    return allEdgePaths;
                }
				//Given Label is reversed with "-" or "-" is removed
                function reverseLabel(label) {
                    if (label[0] == "-") {
                        return label.substring(1);
                    } else {
                        return "-" + label;
                    }
                }
				//Gets Path array with nodes, returns all possible edge paths
                function getEdgeLabelStringsForPath(path) {
                    var allEdgePaths = getEdgePathsForPath(path);
                    var allStrings = new Array(allEdgePaths.length);
                    for (var i = 0; i < allEdgePaths.length; i++) {
                        var s = "";
                        for (var j = 0; j < allEdgePaths[i].length; j++) {
                            var edge = allEdgePaths[i][j];
                            var label = edge.label;
                            var nodeId1 = path[j];
                            var nodeId2 = path[j + 1];
                            if (edge.to == nodeId1 && edge.from == nodeId2) {
                                label = reverseLabel(label);
                            }
                            if (j == (allEdgePaths[i].length - 1)) {
                                s = s + label;
                            } else {
                                s = s + label + ".";
                            }
                        }
                        allStrings[i] = s;
                    }
                    return allStrings;
                }
				//Gets Path arrays with nodes, returns all possible edge paths
                function getAllStringsForAllPaths(paths) {
                    var arrayOfAllStrings = [];
                    for (var i = 0; i < paths.length; i++) {
                        var path = paths[i];
                        var allStrings = getEdgeLabelStringsForPath(path);
                        arrayOfAllStrings.push(allStrings);
                    }
                    return arrayOfAllStrings;
                }
				//Removes the given value from the given array
                function removeItem(arr, value) {
                    var index = arr.indexOf(value);
                    if (index > -1) {
                        arr.splice(index, 1);
                    }
                    return arr;
                }
				//Returns all paths between startNode and endNode
                function findAllPaths(startNode, endNode) {
                    var visitedNodes = [];
                    var currentPath = [];
                    var allPaths = [];
                    dfs(startNode, endNode, currentPath, allPaths, visitedNodes);
                    return allPaths;
                }
				//Algorithm to search for all paths between two nodes
                function dfs(start, end, currentPath, allPaths, visitedNodes) {
                    if (visitedNodes.includes(start)) return;
                    visitedNodes.push(start);
                    currentPath.push(start);
                    if (start == end) {
                        var localCurrentPath = currentPath.slice();
                        allPaths.push(localCurrentPath);
                        removeItem(visitedNodes, start);
                        currentPath.pop();
                        return;
                    }
                    var neighbours = network.getConnectedNodes(start);
                    for (var i = 0; i < neighbours.length; i++) {
                        var current = neighbours[i];
                        dfs(current, end, currentPath, allPaths, visitedNodes);
                    }
                    currentPath.pop();
                    removeItem(visitedNodes, start);
                }
                //Algorithm that gets all nodes that are reachable from the given node in the graph 
                function getAllReachableNodesTo(nodeId, excludeIds, reachableNodes) {
                    if (reachableNodes.includes(nodeId) || excludeIds.includes(nodeId)) {
                        return;
                    }
                    var children = network.getConnectedNodes(nodeId);
                    reachableNodes.push(nodeId);
                    for (var i = 0; i < children.length; i++) {
                        getAllReachableNodesTo(children[i], excludeIds, reachableNodes);
                        //if(excludeIds.includes(children[i]))continue;
                        //reachableNodes.push(children[i]);
                    }
                }
				//This function deletes all children of a given node.
                function deleteNodesChildren(nodeId, deleteEdge) {
                    var excludedIds = [];
                    if (deleteEdge === true) {
                        console.log("deleteEdge true")
                    } else {
                        excludedIds.push(nodeId);
                    }
                    var reachableNodesTo = [];
                    getAllReachableNodesTo(input.root, excludedIds, reachableNodesTo);
                    var nodesToDelete = [];
                    var allIds = nodes.getIds();
                    for (var i = 0; i < allIds.length; i++) {
                        if (reachableNodesTo.includes(allIds[i])) continue;
                        if (allIds[i] == nodeId) {
                            deleteEdges(nodeId);
                            continue;
                        }
                        nodesToDelete.push(allIds[i]);
                        deleteEdges(allIds[i]);
                        nodes.remove(allIds[i]);
                        delete oldGroups["" + allIds[i]];
                        delete objClickedProps["" + allIds[i]];
                    }
                    return nodesToDelete;
                }
				//Deletes all edges from given node
                function deleteEdges(nodeID) {
                    var fromEdges = edges.get({
                        filter: function(item) {
                            return item.from == nodeID;
                        }
                    });
                    for (var j = 0; j < fromEdges.length; j++) {
                        edges.remove(fromEdges[j]);
                    }
                }
                var nodesClicked = [];
                var tip = '<p><strong>Hinweis:</strong> Um sich einen Pfad zwischen zwei Knoten ausgeben zu lassen, <em>Strg</em> gedrückt halten und die gewünschten zwei Knoten mit der <em>linken Maustaste</em> anklicken. </p>'
                this.insertAdjacentHTML('afterbegin', tip);
                //Ctrl and click on two nodes, puts out all possible paths between the two nodes under the tip
                network.on("click", function(params) {
                    if (params.nodes[0] && params.event.srcEvent.ctrlKey) {
                        if (nodesClicked.length < 2) {
                            nodesClicked.push(params.nodes[0]);
                        }
                        if (nodesClicked.length == 2 && nodesClicked[0] != nodesClicked[1]) {
                            var foundPaths = findAllPaths(nodesClicked[0], nodesClicked[1]);
                            //.querySelector('[id^="poll-"]').id;
                            if (document.querySelectorAll('[id^="fullPath"]')) {
                                for (var i = 0; i < document.querySelectorAll('[id^="fullPath"]').length; i++) {
                                    document.querySelectorAll('[id^="fullPath"]')[i].remove();
                                }
                            }
                            var element = '<div id="fullPath' + pathId + '"></div>'
                            givenDiv.children[0].insertAdjacentHTML('afterend', element);
                            var allStringsArray = getAllStringsForAllPaths(foundPaths);
                            var stringDiv = givenDiv.querySelector('#fullPath' + pathId);
                            if (foundPaths.length == 1) {
                                stringDiv.innerHTML = "<strong>Gefundener Pfad:</strong><br>"
                            } else {
                                stringDiv.innerHTML = "<strong>Gefundene Pfade:</strong><br>"
                            }
                            for (var s = 0; s < foundPaths.length; s++) {
                                if (foundPaths.length == 1) {
                                    var pathNumb = ""
                                } else {
                                    var pathNumb = "<strong>" + (s + 1) + ". Pfad:</strong> <br>"
                                }
                                stringDiv.innerHTML += pathNumb + "<strong>Knoten: </strong>";
                                for (var t = 0; t < foundPaths[s].length; t++) {
                                    var currentFoundPath = foundPaths[s][t];
                                    if (t == (foundPaths[s].length - 1)) {
                                        stringDiv.innerHTML = stringDiv.innerHTML + currentFoundPath + " ";
                                    } else {
                                        stringDiv.innerHTML = stringDiv.innerHTML + currentFoundPath + " - ";
                                    }
                                }
                                stringDiv.innerHTML += "<br>"
                                stringDiv.innerHTML += "<strong>Kanten:</strong><br>"
                                for (var t = 0; t < allStringsArray[s].length; t++) {
                                    var currentString = allStringsArray[s][t];
                                    var currentFoundPath = foundPaths[s][t];
                                    var stringDiv = givenDiv.querySelector('#fullPath' + pathId);
                                    stringDiv.innerHTML = stringDiv.innerHTML + '&#9679; ' + currentString + '<br>';
                                }
                                stringDiv.innerHTML += "<br>"
                            }
                            nodesClicked = [];
                        }
                        if (nodesClicked[0] === nodesClicked[1] || nodesClicked.length > 2) {
                            nodesClicked = [];
                        }
                    }
                    pathId++;
                });
                $(document).keyup(function(event) {
                    if (!event.ctrlKey) {
                        nodesClicked = [];
                    }
                });
                var contextCreatedProps = [];
                
                network.on("doubleClick", function(params) {
                    if (params.nodes[0]) {
                    	//Checks if all node children are created from context menu or manually, if so it creates nodes for before defined properties else it deletes all children
                        var conManNodes = network.getConnectedNodes(params.nodes[0], 'to');
                        var onlyConManNodes = true;
                        for (var i = 0; i < conManNodes.length; i++) {
                            if (!(nodes.get(conManNodes[i]).oncontext || nodes.get(conManNodes[i]).manually)) {
                                onlyConManNodes = false;
                            }
                        }
                        //Node is expanded -> delete it and all nodes related to its expansion
                        if (network.getConnectedNodes(params.nodes[0]).length > 1 && onlyConManNodes == false) {
                            deleteNodesChildren(params.nodes[0]);
                            for (var i = 0; i < contextCreatedProps.length; i++) {
                                var noNodesInNetwork = true;
                                for (var j = 0; j < nodes.getIds().length; j++) {
                                    if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
                                        noNodesInNetwork = false;
                                    }
                                }
                                if (noNodesInNetwork === true) {
                                    givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
                                    contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
                                    i--;
                                }
                            }
                            delete objClickedProps["" + params.nodes[0]];
                            //nodesArray.splice(nodesArray.indexOf(params.nodes[0]), 1);
                        } else {
                            //Node is unexpanded -> expand it
                            var nodeById = nodes.get(params.nodes[0]);
                            fetchData(nodeById.id, input.properties, params.nodes[0]);
                            //nodesArray.push(params.nodes[0]);
                        }
                    }
                });

                function newGroup(node, legendGroup) {
                    nodes.update({
                        id: node,
                        group: legendGroup
                    });
                    var connectedNodes = network.getConnectedNodes(node, 'to');
                    for (var i = 0; i < connectedNodes.length; i++) {
                        newGroup(connectedNodes[i], legendGroup);
                    }
                }
                //Checks, if a node has a path over visible edges to the root node.
                //If not, the nodes gets hidden
                function setNodeVisibilityByVisiblePath(nodeId, rootNodeId) {
                    if (nodeId == rootNodeId) return true; //root is always visible
                    var node = nodes.get(nodeId);
                    if (node.visited) return !node.hidden //prevent circles. ToDo: Reuse results between runs
                    node.visited = true;
                    node.hidden = true;
                    var connectedEdgesIds = network.getConnectedEdges(nodeId);
                    var connectedEdges = edges.get(connectedEdgesIds);
                    connectedEdges.forEach(function(edge) {
                        if (edge.hidden) return; //don't follow hidden edges
                        var connectedNodesIds = network.getConnectedNodes(edge.id);
                        var connectedNodes = nodes.get(connectedNodesIds);
                        connectedNodes.forEach(function(connectedNode) {
                            if (connectedNode.id == nodeId) return; //prevent self evaluation
                            if (setNodeVisibilityByVisiblePath(connectedNode.id, rootNodeId)) {
                                node.hidden = false; //set node visible, if at least one connected node is visible
                            }
                        });
                    });
                    node.physics = !node.hidden; //disable physics for hidden nodes
                    return !node.hidden;
                }

                function legendFunctionality() {
                    var legendGroup;
                    var group;
                    var nodeChildren;
                    legendGroup = this.parentNode.childNodes[1].innerHTML;
                    var strategy = "strategy2"
                    if (strategy == "strategy2") {
                        //A node is visible if at least one path over visible edges to the root node exists.
                        options.groups[legendGroup].hidden = !options.groups[legendGroup].hidden; //toggle state
                        if (options.groups[legendGroup].hidden) this.parentNode.childNodes[1].style.background = '#FFFFFF';
                        else this.parentNode.childNodes[1].style.background = '#DEF';
                        //update all edges
                        edges.forEach(function(edge) {
                            edge.hidden = options.groups[edge.label].hidden;
                            edge.physics = !edge.hidden;
                        });
                        //reset nodes
                        nodes.forEach(function(node) {
                            node.hidden = false;
                            node.physics = !node.hidden;
                            node.visited = false;
                        });
                        //check each node
                        nodes.forEach(function(node) {
                            setNodeVisibilityByVisiblePath(node.id, input.root)
                            //reset visited state. Todo: Reuse visited nodes between runs
                            nodes.forEach(function(node) {
                                node.visited = false;
                            });
                        });
                    }
                    network.setOptions(options);
                    network.body.emitter.emit('_dataChanged');
                    network.redraw();
                    var allFalse = Object.keys(options.groups).every(function(k) {
                        if (k === 'useDefaultGroups') {
                            return true
                        }
                        return options.groups[k].hidden === false
                    });
                    if (allFalse === true) {
                        /*oldGroups = {};*/
                    }
                };
                var legendDiv = document.createElement("div");
                this.append(legendDiv);
                legendDiv.style.width = '100%';
                legendDiv.style.position = 'relative';
                legendDiv.style.display = 'inline-block';
                legendDiv.id = "legendContainer";
                var legendColors = {};
                for (var i = 0; i < input.properties.length; i++) {
                    legendColors[input.properties[i]] = colors[i];
                    var propertyContainer = document.createElement("div");
                    var propertyColor = document.createElement("div");
                    var propertyName = document.createElement("div");
                    propertyContainer.className = "legend-element-container";
                    propertyContainer.id = input.properties[i];
                    propertyColor.className = "color-container";
                    propertyName.className = "name-container";
                    propertyColor.style.float = "left";
                    propertyName.style.float = "left";
                    propertyColor.style.border = "1px solid black";
                    propertyName.style.border = "1px solid black";
                    propertyColor.style.background = colors[i];
                    propertyColor.innerHTML = "";
                    propertyName.innerHTML = input.properties[i];
                    propertyColor.style.width = "30px";
                    propertyColor.style.height = "30px";
                    propertyName.style.height = "30px";
                    propertyName.style.background = '#DEF';
                    //propertyName.text-align = 'center';
                    propertyContainer.paddinng = '5px 5px 5px 5px';
                    propertyName.addEventListener("click", legendFunctionality);
                    propertyColor.addEventListener("click", legendFunctionality);
                    legendDiv.append(propertyContainer);
                    propertyContainer.append(propertyColor);
                    propertyContainer.append(propertyName);
                }
                var ul = document.createElement("ul");
                ul.className = 'custom-menu';
                document.body.append(ul);
                objClickedProps = {};
                objColors = {};
                var start = 0;
                //On a node right click it puts out all properties of the clicked node and a link to the node wiki-page
                network.on("oncontext", function(params) {
                    params.event.preventDefault();
                    var timeNow = Date.now();
                    var timeDiff = timeNow - start
                    if (timeDiff > 300) {
                        start = Date.now();
                        //console.log(nodes.get(network.getNodeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })));
                        //console.log(edges.get(network.getEdgeAt({ x: params.pointer.DOM.x, y: params.pointer.DOM.y })));
                        $('.custom-menu').each(function(index) {
                            while (this.lastElementChild) {
                                this.removeChild(this.lastElementChild);
                            }
                        });
                        if (!(network.getEdgeAt({
                                x: params.pointer.DOM.x,
                                y: params.pointer.DOM.y
                            }) && network.getNodeAt({
                                x: params.pointer.DOM.x,
                                y: params.pointer.DOM.y
                            }))) {
                            if (edges.get(network.getEdgeAt({
                                    x: params.pointer.DOM.x,
                                    y: params.pointer.DOM.y
                                })).from) {
                                params.event.preventDefault();
                                if (edges.get(network.getEdgeAt({
                                        x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
                                    })).label == 'Category') {
                                    var li = document.createElement("li");
                                    li.innerHTML = '' + '\uD83D\uDD17' + ' ' + edges.get(network.getEdgeAt({
                                        x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
                                    })).to;
                                    li.addEventListener("click", function NewTab() {
                                        window.open('/wiki/' + edges.get(network.getEdgeAt({
                                            x: params.pointer.DOM.x,
                                            y: params.pointer.DOM.y
                                        })).to);
                                    });
                                    ul.prepend(li);
                                } else {
                                    var li = document.createElement("li");
                                    li.innerHTML = '' + '\uD83D\uDD17' + ' ' + edges.get(network.getEdgeAt({
                                        x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
                                    })).label;
                                    li.addEventListener("click", function NewTab() {
                                        window.open('/wiki/' + 'Property:' + edges.get(network.getEdgeAt({
                                            x: params.pointer.DOM.x,
                                            y: params.pointer.DOM.y
                                        })).label);
                                    });
                                    ul.prepend(li);
                                }
                                $(".custom-menu").finish().toggle(100).css({
                                    top: params.event.pageY + "px",
                                    left: params.event.pageX + "px",
                                    display: "block"
                                });
                            }
                        }
                        if (network.getNodeAt({
                                x: params.pointer.DOM.x,
                                y: params.pointer.DOM.y
                            })) {
                            params.event.preventDefault();
                            const nodeID = nodes.get(network.getNodeAt({
                                x: params.pointer.DOM.x,
                                y: params.pointer.DOM.y
                            })).id;
                            var subject = nodeID.split("#")[0];
                            var subObject = "";
                            if (nodeID.split("#")[1]) {
                                subObject = nodeID.split("#")[1].replace(" ", "_");
                            }
                            var namespace_id = 0;
                            if (subject.split(":")[1]) {
                            	const namespace = subject.split(":")[0];
                            	subject = subject.split(":")[1];
                            	namespace_id = mw.config.get('wgNamespaceIds')[namespace.replaceAll(" ","_").toLowerCase()];
                            	//console.log(`Namespace ${namespace}, ID ${namespace_id}`);
                            }
                            
                            //inverse properties are only available in html format
                            const query = `/w/api.php?action=smwbrowse&browse=subject&params={"subject":"${encodeURIComponent(subject)}","subobject":"${subObject}","options":{"showAll":"true"}, "ns":${namespace_id}, "type":"html"}&format=json`;
                            fetch(query)
                                .then(response => response.json())
                                .then(data => {
                                    var selected_node = nodes.get(network.getNodeAt({
                                        x: params.pointer.DOM.x,
                                        y: params.pointer.DOM.y
                                    }));
                                    if (selected_node.url) {
                                        var li = document.createElement("li");
                                        li.innerHTML = '' + '\uD83D\uDD17' + ' ' + selected_node.label;
                                        li.addEventListener("click", function NewTab() {
                                            window.open(selected_node.url);
                                        });
                                        ul.prepend(li);
                                    }
                                    var page_properties = [];
                                    $html = $(data.query);
                                    $html.find("div.smwb-propvalue").each(function() {
                                        $prop = $(this).find("div.smwb-prophead a");
                                        //var propName = $prop.text();
                                        //var propName = $prop.attr('title').replace("Property:", "");
                                        var propName = "";
                                        if ($prop.attr('title') === "Special:Categories") propName += "Category";
                                        else if ($prop.attr('title') === "Special:ListRedirects") return;
                                        else if ($prop.attr('href')) propName += $prop.attr('href').split("Property:")[1].split("&")[0];
                                        else return; //empty property
                                        page_properties.push(propName);
                                        //console.log(propName);
                                        $(this).find("div.smwb-propval span.smwb-value").each(function() {
                                            var value = $(this).find("a").attr("title");
                                            //console.log("-> " + value);
                                        });
                                    })
                                    $html.find("div.smwb-ipropvalue").each(function() {
                                        $prop = $(this).find("div.smwb-prophead a");
                                        //var propName = $prop.text();
                                        //var propName = $prop.attr('title').replace("Property:", "");
                                        var propName = "-";
                                        if ($prop.attr('title') === "Special:Categories") propName += "Category";
                                        else if ($prop.attr('title') === "Special:ListRedirects") return;
                                        else if ($prop.attr('href')) propName += $prop.attr('href').split("Property:")[1].split("&")[0];
                                        else return; //empty property
                                        page_properties.push(propName);
                                        //console.log(propName);
                                        $(this).find("div.smwb-propval span.smwb-ivalue").each(function() {
                                            var value = $(this).find("a").attr("title");
                                            //console.log("-> " + value);
                                        });
                                    })
                                    for (var i = 0; i < page_properties.length; i++) {
                                        if (!page_properties[i].startsWith("_")) {
                                            var li = document.createElement("li");
                                            li.dataset.action = page_properties[i].replaceAll('_', ' ');
                                            li.innerHTML = page_properties[i].replaceAll('_', ' ');
                                            ul.append(li);
                                        }
                                    }
                                    /* old: use json result */
                                    /*
                        	var page_properties = data.query.data; //normal page
                        	if (selected_node.id.includes('#')) { //subobject
                        		for (var i = 0; i < data.query.sobj.length; i++) { 
                        			if (data.query.sobj[i].subject.endsWith(selected_node.id.split('#').pop().replace(' ',''))){
                        				page_properties = data.query.sobj[i].data
                        				break;
                        			}
                        		}
                        	}
	                        for (var i = 0; i < page_properties.length; i++) {
	                            if (!page_properties[i].property.startsWith("_")) {
	                                 var li = document.createElement("li");
	                                 li.dataset.action = page_properties[i].property.replaceAll('_',' ');
	                                 li.innerHTML = page_properties[i].property.replaceAll('_',' ');
	                                 ul.append(li);
	                            }
	                        }*/
	                        		//On left click on one of the properties it creates nodes for the clicked property and if the legend doesnt have that property as a legend entry it is created
                                    $(".custom-menu li").click(function() {
                                        var clickedProperty = [$(this).attr("data-action")]
                                        var clickedPropertyColor = randomHSL();
                                        if (!(clickedProperty in legendColors)) {
                                            legendColors[clickedProperty] = clickedPropertyColor;
                                        } else {
                                            clickedPropertyColor = legendColors[clickedProperty];
                                        }
                                        if (objColors[clickedProperty]) {
                                            clickedPropertyColor = objColors[clickedProperty]
                                        } else {
                                            objColors[clickedProperty] = clickedPropertyColor;
                                        }
                                        if (!objClickedProps[nodes.get(network.getNodeAt({
                                                x: params.pointer.DOM.x,
                                                y: params.pointer.DOM.y
                                            })).id]) {
                                            objClickedProps[nodes.get(network.getNodeAt({
                                                x: params.pointer.DOM.x,
                                                y: params.pointer.DOM.y
                                            })).id] = new Array();
                                        }
                                        if (!objClickedProps["" + nodes.get(network.getNodeAt({
                                                x: params.pointer.DOM.x,
                                                y: params.pointer.DOM.y
                                            })).id].includes(clickedProperty[0])) {
                                            fetchData(nodes.get(network.getNodeAt({
                                                x: params.pointer.DOM.x,
                                                y: params.pointer.DOM.y
                                            })).id, clickedProperty, nodes.get(network.getNodeAt({
                                                x: params.pointer.DOM.x,
                                                y: params.pointer.DOM.y
                                            })).id, clickedProperty, clickedPropertyColor)
                                            objClickedProps["" + nodes.get(network.getNodeAt({
                                                x: params.pointer.DOM.x,
                                                y: params.pointer.DOM.y
                                            })).id].push(clickedProperty[0]);
                                        }
                                        if (!(contextCreatedProps.includes(clickedProperty[0]) || input.properties.includes(clickedProperty[0]) /*|| legendColors[clickedProperty[0]]*/ )) {
                                            contextCreatedProps.push(clickedProperty[0]);
                                            options.groups[clickedProperty] = {
                                                hidden: false
                                            };
                                            var propertyContainer = document.createElement("div");
                                            var propertyColor = document.createElement("div");
                                            var propertyName = document.createElement("div");
                                            propertyContainer.className = "legend-element-container";
                                            propertyContainer.id = clickedProperty;
                                            propertyColor.className = "color-container";
                                            propertyName.className = "name-container";
                                            propertyColor.style.float = "left";
                                            propertyName.style.float = "left";
                                            propertyColor.style.border = "1px solid black";
                                            propertyName.style.border = "1px solid black";
                                            propertyContainer.style = "margin-right: 5px";
                                            propertyColor.style.background = clickedPropertyColor;
                                            propertyColor.innerHTML = "";
                                            propertyName.innerHTML = clickedProperty;
                                            propertyColor.style.width = "30px";
                                            propertyColor.style.height = "30px";
                                            propertyName.style.height = "30px";
                                            propertyName.style.background = '#DEF';
                                            //propertyName.text-align = 'center';
                                            propertyName.margin = 'auto 5px auto 5px';
                                            propertyName.addEventListener("click", legendFunctionality);
                                            propertyColor.addEventListener("click", legendFunctionality);
                                            legendDiv.append(propertyContainer);
                                            propertyContainer.append(propertyColor);
                                            propertyContainer.append(propertyName);
                                        }
                                        $(".custom-menu").hide(100);
                                    });
                                });
                            $(".custom-menu").finish().toggle(100).css({
                                top: params.event.pageY + "px",
                                left: params.event.pageX + "px",
                                display: "block"
                            });
                        }
                    }
                });
                // If the document is clicked somewhere it hides the context menu
                $(document).bind("mousedown", function(e) {
                    // If the clicked element is not the menu
                    if (!$(e.target).parents(".custom-menu").length > 0) {
                        // Hide it
                        $(".custom-menu").hide(100);
                    }
                });
				//Add Node popup
                function editNode(data, cancelAction, callback) {
                    var newNodeActive = true;
                    document.getElementById("node-label").value = data.label;
                    document.getElementById("node-saveButton").onclick = saveNodeData.bind(
                        this,
                        data,
                        callback
                    );
                    document.getElementById("node-cancelButton").onclick = cancelAction.bind(
                        this,
                        callback
                    );
                    //document.getElementById("node-popUp")
                    $('canvas').on('click', function(e) {
                        if (newNodeActive === true) {
                            $("#node-popUp").css({
                                top: e.pageY + "px",
                                left: e.pageX + "px",
                                display: "block"
                            });
                        }
                        newNodeActive = false;
                    });
                }
				
                function clearNodePopUp() {
                    document.getElementById("node-saveButton").onclick = null;
                    document.getElementById("node-cancelButton").onclick = null;
                    document.getElementById("node-popUp").style.display = "none";
                }

                function cancelNodeEdit(callback) {
                    clearNodePopUp();
                    callback(null);
                }
				//saveNodeData to the graph
                function saveNodeData(data, callback) {
                    data.label = document.getElementById("node-label").value;
                    data.id = document.getElementById("node-label").value;
                    data.hidden = false;
                    data.physics = false;
                    document.getElementById("node-label").value = "";
                    clearNodePopUp();
                    callback(data);
                }
				//addEdge popup
                function editEdgeWithoutDrag(data, callback) {
                    var newEdgeActive = true;
                    // filling in the popup DOM elements
                    document.getElementById("edge-label").value = data.label;
                    
                    document.getElementById("edge-saveButton").onclick = saveEdgeData.bind(
                        this,
                        data,
                        callback
                    );
                    document.getElementById("edge-cancelButton").onclick = cancelEdgeEdit.bind(
                        this,
                        callback
                    );
                    $('canvas').on('click', function(e) {
                        if (newEdgeActive === true) {
                            $("#edge-popUp").css({
                                top: e.pageY + "px",
                                left: e.pageX + "px",
                                display: "block"
                            });
                        }
                        newEdgeActive = false;
                    });
                    //document.getElementById("edge-popUp").style.display = "block";
                }

                function clearEdgePopUp() {
                    document.getElementById("edge-saveButton").onclick = null;
                    document.getElementById("edge-cancelButton").onclick = null;
                    document.getElementById("edge-popUp").style.display = "none";
                }

                function cancelEdgeEdit(callback) {
                    clearEdgePopUp();
                    callback(null);
                }

                function isLabelReversed(label) {
                    if (label[0] == "-") {
                        return true;
                    } else {
                        return false;
                    }
                }
                var pageBool;
                //Checks if page exists in the wiki
                async function pageExists(id) {
                    await fetch('/w/api.php?action=parse&page=' + id + '&prop=wikitext&format=json')
                        .then(response => response.json())
                        .then(data => {
                            if (data.error) {
                                pageBool = false;
                            } else {
                                pageBool = true;
                            }
                        })
                    return pageBool;
                }
                var wikiText = "";
                var semantic = "";
                //Splits Wikitext and Semantic/Element or Semantic/Link
                async function editWikiText(node) {
                    await fetch('/w/api.php?action=parse&page=' + node + '&prop=wikitext&format=json')
                        .then(response => response.json())
                        .then(data => {
                            wikiText = data.parse.wikitext['*'];
                            semantic = "";
                            if (wikiText.search(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0) {
                                //var edgeStringFound = wikiText.search(re) >= 0;
                                const found = wikiText.match(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\})/g);
                                var newWikiText = wikiText;
                                for (var i = 0; i < found.length; i++) {
                                    if (i == found.length - 1) {
                                        semantic += found[i];
                                        newWikiText = newWikiText.replace(/(\{\{Semantic\/[^}]*[\r\n]*\}[\r\n]*\}[\r\n]*\}[\r\n]*\})/g, "");
                                        newWikiText = newWikiText.replace(/(\{\{Semantic\/Link[^}]*[\r\n]*\}[\r\n]*\})/g, "");
                                    } else {
                                        semantic += found[i];
                                        newWikiText = newWikiText.replace(found[i], "");
                                    }
                                }
                                wikiText = newWikiText;
                            }
                        });
                    return [semantic, wikiText];
                }
                //Save button on create edge popup
                async function saveEdgeData(data, callback) {
                	//Sets various options to the nodes that the edge gets connected
                    if (typeof data.to === "object") data.to = data.to.id;
                    if (typeof data.from === "object") data.from = data.from.id;
                    data.label = document.getElementById("edge-label").value;
                    options.groups[data.label] = {
                        hidden: false
                    };
                    var toNode = nodes.get(data.to);
                    var fromNode = nodes.get(data.from);
                    fromNode.physics = true;
                    toNode.physics = true;
                    delete fromNode.x;
                    delete fromNode.y;
                    delete toNode.x;
                    delete toNode.y;
                    if (!toNode.group) {
                        toNode.group = data.label
                    }
                    if (!fromNode.group) {
                        fromNode.group = data.label
                    }
                    if (legendColors[data.label]) {
                        data.color = legendColors[data.label];
                    } else {
                        data.color = randomHSL();
                    }
                    if (!toNode.color) {
                        toNode.color = data.color;
                        toNode.manually = true;
                    }
                    if (!fromNode.color) {
                        fromNode.color = data.color;
                        fromNode.manually = true;
                    }
                    //If the given property is not set in the legend, a legend entry is created
                    if (!(contextCreatedProps.includes(data.label) || input.properties.includes(data.label))) {
                        contextCreatedProps.push(data.label);
                        var propertyContainer = document.createElement("div");
                        var propertyColor = document.createElement("div");
                        var propertyName = document.createElement("div");
                        propertyContainer.className = "legend-element-container";
                        propertyContainer.id = data.label;
                        propertyColor.className = "color-container";
                        propertyName.className = "name-container";
                        propertyColor.style.float = "left";
                        propertyName.style.float = "left";
                        propertyColor.style.border = "1px solid black";
                        propertyName.style.border = "1px solid black";
                        propertyContainer.style = "margin-right: 5px";
                        propertyColor.style.background = data.color;
                        propertyColor.innerHTML = "";
                        propertyName.innerHTML = data.label;
                        propertyColor.style.width = "30px";
                        propertyColor.style.height = "30px";
                        propertyName.style.height = "30px";
                        propertyName.style.background = '#DEF';
                        //propertyName.text-align = 'center';
                        propertyName.margin = 'auto 5px auto 5px';
                        propertyName.addEventListener("click", legendFunctionality);
                        propertyColor.addEventListener("click", legendFunctionality);
                        legendDiv.append(propertyContainer);
                        propertyContainer.append(propertyColor);
                        propertyContainer.append(propertyName);
                        legendColors[data.label] = data.color;
                    }
                    //Creates new wikitext that will be saved after the save button is clicked
                    if (isLabelReversed(data.label)) {
                        if (await pageExists(fromNode.id) === false) {
                            if (!(newNodes[fromNode.id])) {
                                newNodes[fromNode.id] = '' + '{{Semantic/Element' +
                                    '|label=' + fromNode.label +
                                    '|description=test' +
                                    '|relations=';
                            }
                        }
                        if (await pageExists(toNode.id) === true) {
                            var splitWikiText = await editWikiText(toNode.id);
                            if (editNodes[toNode.id]) {
                                editNodes[toNode.id] += '' + '{{Semantic/Link' +
                                    '|property=' + reverseLabel(data.label) +
                                    '|value=' + fromNode.id +
                                    '}}' + '';
                            } else {
                                if (splitWikiText[0].search(/(\{\{Semantic\/Element[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0) {
                                    editNodes[toNode.id] = splitWikiText[1] + splitWikiText[0] + '{{Semantic/Link' +
                                        '|property=' + reverseLabel(data.label) +
                                        '|value=' + fromNode.id +
                                        '}}' + '';
                                } else {
                                    editNodes[toNode.id] = splitWikiText[1] + '{{Semantic/Element' +
                                        '|label=' + toNode.label +
                                        '|description=test' +
                                        '|relations=' +
                                        '{{Semantic/Link' +
                                        '|property=' + reverseLabel(data.label) +
                                        '|value=' + fromNode.id +
                                        '}}' + '' + splitWikiText[0];
                                }
                            }
                        } else {
                            if (newNodes[toNode.id]) {
                                newNodes[toNode.id] += '' + '{{Semantic/Link' +
                                    '|property=' + reverseLabel(data.label) +
                                    '|value=' + fromNode.id +
                                    '}}' + '';
                            } else {
                                newNodes[toNode.id] = '' + '{{Semantic/Element' +
                                    '|label=' + toNode.label +
                                    '|description=test' +
                                    '|relations={{Semantic/Link' +
                                    '|property=' + reverseLabel(data.label) +
                                    '|value=' + fromNode.id +
                                    '}}' +
                                    '';
                            }
                        }
                    } else {
                        if (await pageExists(toNode.id) === false) {
                            if (!(newNodes[toNode.id])) {
                                newNodes[toNode.id] = '' + '{{Semantic/Element' +
                                    '|label=' + toNode.label +
                                    '|description=test' +
                                    '|relations=';
                            }
                        }
                        if (await pageExists(fromNode.id) === true) {
                            var splitWikiText = await editWikiText(fromNode.id);
                            if (editNodes[fromNode.id]) {
                                editNodes[fromNode.id] += '' + '{{Semantic/Link' +
                                    '|property=' + data.label +
                                    '|value=' + toNode.id +
                                    '}}' + '';
                            } else {
                                if (splitWikiText[0].search(/(\{\{Semantic\/Element[^}]*[\r\n]*\}[\r\n]*\})/g) >= 0) {
                                    editNodes[fromNode.id] = splitWikiText[1] + splitWikiText[0] + '{{Semantic/Link' +
                                        '|property=' + data.label +
                                        '|value=' + toNode.id +
                                        '}}' + '';
                                } else {
                                    editNodes[fromNode.id] = splitWikiText[1] + '{{Semantic/Element' +
                                        '|label=' + fromNode.label +
                                        '|description=test' +
                                        '|relations=' +
                                        '{{Semantic/Link' +
                                        '|property=' + data.label +
                                        '|value=' + toNode.id +
                                        '}}' + '' + splitWikiText[0];
                                }
                            }
                        } else {
                            if (newNodes[fromNode.id]) {
                                newNodes[fromNode.id] += '' + '{{Semantic/Link' +
                                    '|property=' + data.label +
                                    '|value=' + toNode.id +
                                    '}}' + '';
                            } else {
                                newNodes[fromNode.id] = '' + '{{Semantic/Element' +
                                    '|label=' + fromNode.label +
                                    '|description=test' +
                                    '|relations={{Semantic/Link' +
                                    '|property=' + data.label +
                                    '|value=' + toNode.id +
                                    '}}' +
                                    '';
                            }
                        }
                    }
                    //console.log(toNode);
                    //console.log(fromNode);
                    //console.log(editNodes);
                    //console.log(newNodes);
                    clearEdgePopUp();
                    callback(data);
                    network.setOptions(options);
                    network.body.emitter.emit('_dataChanged');
                    network.redraw();
                }
                //save button
                var saveBtn = document.createElement("button");
                saveBtn.addEventListener("click", saveGraphChanges);
                saveBtn.innerHTML = "Speichern";
                saveBtn.style.width = "auto";
                saveBtn.style.height = "auto";
                givenDiv.appendChild(saveBtn);
				
				//Called on save button click. Creates new wiki pages or edits them with the created wiki text.
                function saveGraphChanges() {
                    var alertString = "";
                    OO.ui.confirm('Änderungen übernehmen?').done(async function(confirmed) {
                        if (confirmed) {
                            for (const [key, value] of Object.entries(newNodes)) {
                                var params = {
                                        action: 'edit',
                                        title: '' + key,
                                        appendtext: '' + value + '}}',
                                        format: 'json'
                                    },
                                    api = new mw.Api();
                                await api.postWithToken('csrf', params).done(function(data) {
                                    console.log(data);
                                    alertString += "Seite " + key + " erstellt!\r\n"
                                });
                            }
                            for (const [key, value] of Object.entries(editNodes)) {
                                var params = {
                                        action: 'edit',
                                        title: '' + key,
                                        text: '' + value + '}}',
                                        format: 'json'
                                    },
                                    api = new mw.Api();
                                await api.postWithToken('csrf', params).done(function(data) {
                                    console.log(data);
                                    alertString += "Seite " + key + " bearbeitet!\r\n"
                                });
                            }
                            for (const [key, value] of Object.entries(editDeletedEdges)) {
                                var params = {
                                        action: 'edit',
                                        title: '' + key,
                                        text: '' + value,
                                        format: 'json'
                                    },
                                    api = new mw.Api();
                                await api.postWithToken('csrf', params).done(function(data) {
                                    console.log(data);
                                    alertString += "Auf der Seite " + key + " wurde ein Attribut gelöscht!\r\n"
                                });
                            }
                            for (const [key, value] of Object.entries(editDeletedNodes)) {
                                var params = {
                                        action: 'delete',
                                        title: '' + key,
                                        format: 'json'
                                    },
                                    api = new mw.Api();
                                await api.postWithToken('csrf', params).done(function(data) {
                                    console.log(data);
                                    alertString += "Seite " + key + " wurde gelöscht!\r\n"
                                });
                            }
                            // Example: Customize the displayed actions at the time the window is opened.
                            var messageDialog = new OO.ui.MessageDialog();
                            // Create and append a window manager.
                            var windowManager = new OO.ui.WindowManager();
                            $('body').append(windowManager.$element);
                            // Add the dialog to the window manager.
                            windowManager.addWindows([messageDialog]);
                            // Configure the message dialog when it is opened with the window manager's openWindow() method.
                            windowManager.openWindow(messageDialog, {
                                title: 'Folgende Änderugnen wurden übernommen:',
                                message: '' + alertString,
                                verbose: true,
                                actions: [{
                                    action: 'accept',
                                    label: 'Okay',
                                    flags: 'primary'
                                }]
                            });
                            /*OO.ui.alert( "" + alertString ).done( function () {
                                console.log( alertString );
                            } );*/
                        } else {}
                    });
                }
				//Deletes node in manipulation mode and the wiki page.
                function deleteSelectedNode(data, callback) {
                    deleteNodesChildren(data.nodes[0]);
                    nodes.remove(data.nodes[0]);
                    for (var i = 0; i < contextCreatedProps.length; i++) {
                        var noNodesInNetwork = true;
                        for (var j = 0; j < nodes.getIds().length; j++) {
                            if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
                                noNodesInNetwork = false;
                            }
                        }
                        if (noNodesInNetwork === true) {
                            givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
                            contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
                            i--;
                        }
                    }
                    delete oldGroups["" + data.nodes[0]];
                    delete objClickedProps["" + data.nodes[0]];
                    callback();
                    document.querySelector('.vis-delete').remove();
                    editDeletedNodes["" + data.nodes[0]] = "";
                    delete newNodes["" + data.nodes[0]];
                    delete editNodes["" + data.nodes[0]];
                }
                //Deletes edge in manipulation mode and deletes the property from the node wikipages
                async function deleteSelectedEdge(data, callback) {
                    var edgeToNode = edges.get(data.edges[0]).to;
                    var edgeFromNode = edges.get(data.edges[0]).from;
                    var edgeLabel = edges.get(data.edges[0]).label;
                    edges.remove(data.edges[0]);
                    deleteNodesChildren(edgeToNode, true);
                    deleteNodesChildren(edgeFromNode, true);
                    for (var i = 0; i < contextCreatedProps.length; i++) {
                        var noNodesInNetwork = true;
                        for (var j = 0; j < nodes.getIds().length; j++) {
                            if (contextCreatedProps[i] == nodes.get(nodes.getIds()[j]).group) {
                                noNodesInNetwork = false;
                            }
                        }
                        if (noNodesInNetwork === true) {
                            givenDiv.querySelector('#' + contextCreatedProps[i]).remove();
                            contextCreatedProps.splice(contextCreatedProps.indexOf(contextCreatedProps[i]), 1);
                            i--;
                        }
                    }
                    if (edgeLabel[0] == "-") {
                        if (await pageExists(edgeToNode) === true) {
                            await fetch('/w/api.php?action=parse&page=' + edgeToNode + '&prop=wikitext&format=json')
                                .then(response => response.json())
                                .then(data => {
                                    var wikiText = data.parse.wikitext['*'];
                                    var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + reverseLabel(edgeLabel) + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeFromNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`
                                    //var edgeString = '(\\{\\{Semantic\/Element[^}]*[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*[\\r\\n]*\\}[\\r\\n]*\\})'
                                    var re = new RegExp(edgeString, "g");
                                    var edgeStringFound = wikiText.search(re) >= 0;
                                    if (edgeStringFound) {
                                        if (editDeletedEdges["" + edgeToNode]) {
                                            var newWikiText = editDeletedEdges["" + edgeToNode].replace(re, "");
                                            editDeletedEdges["" + edgeToNode] = newWikiText;
                                        } else {
                                            var newWikiText = wikiText.replace(re, "");
                                            editDeletedEdges["" + edgeToNode] = newWikiText;
                                        }
                                    }
                                    if (newNodes["" + edgeToNode]) {
                                        var newWikiText = newNodes["" + edgeToNode].replace(re, "");
                                        newNodes["" + edgeToNode] = newWikiText;
                                    }
                                    if (editNodes["" + edgeToNode]) {
                                        var newWikiText = editNodes["" + edgeToNode].replace(re, "");
                                        editNodes["" + edgeToNode] = newWikiText;
                                    }
                                });
                        } else {
                            if (network.getConnectedNodes(edgeToNode).length == 0) {
                                delete newNodes["" + edgeToNode];
                            } else {
                                var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + reverseLabel(edgeLabel) + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeFromNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
                                var re = new RegExp(edgeString, "g");
                                var wikiText = newNodes["" + edgeToNode];
                                var newWikiText = wikiText.replace(re, "");
                                newNodes["" + edgeToNode] = newWikiText;
                            }
                        }
                    } else {
                        if (await pageExists(edgeFromNode) === true) {
                            await fetch('/w/api.php?action=parse&page=' + edgeFromNode + '&prop=wikitext&format=json')
                                .then(response => response.json())
                                .then(data => {
                                    var wikiText = data.parse.wikitext['*'];
                                    var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + edgeLabel + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeToNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
                                    //var edgeString = '(\\{\\{Semantic\/Element[^}]*[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*[\\r\\n]*\\}[\\r\\n]*\\})'
                                    var re = new RegExp(edgeString, "g");
                                    var edgeStringFound = wikiText.search(re) >= 0;
                                    if (edgeStringFound) {
                                        if (editDeletedEdges["" + edgeFromNode]) {
                                            var newWikiText = editDeletedEdges["" + edgeFromNode].replace(re, "");
                                            editDeletedEdges["" + edgeFromNode] = newWikiText;
                                        } else {
                                            var newWikiText = wikiText.replace(re, "");
                                            editDeletedEdges["" + edgeFromNode] = newWikiText;
                                        }
                                    }
                                    if (newNodes["" + edgeFromNode]) {
                                        var newWikiText = newNodes["" + edgeFromNode].replace(re, "");
                                        newNodes["" + edgeFromNode] = newWikiText;
                                    }
                                    if (editNodes["" + edgeFromNode]) {
                                        var newWikiText = editNodes["" + edgeFromNode].replace(re, "");
                                        editNodes["" + edgeFromNode] = newWikiText;
                                    }
                                });
                        } else {
                            if (network.getConnectedNodes(edgeFromNode).length == 0) {
                                delete newNodes["" + edgeFromNode];
                            } else {
                                var edgeString = `(\{\{Semantic\/Link[\\r\\n]*\\|[\\r\\n]*property=` + edgeLabel + `[\\r\\n]*\\|[\\r\\n]*value=` + edgeToNode + `[\\r\\n]*\\}[\\r\\n]*\\}[\\r\\n]*)`;
                                var re = new RegExp(edgeString, "g");
                                var wikiText = newNodes["" + edgeFromNode];
                                var newWikiText = wikiText.replace(re, "");
                                newNodes["" + edgeFromNode] = newWikiText;
                            }
                        }
                    }
                    //nodes.remove(edges.get(data.edges[0]).to);
                    callback(data);
                    document.querySelector('.vis-delete').remove();
                }
                //HTML for the manipulation popups
                var editHtml = '' +
                    '<div id="node-popUp">' +
                    '  <span id="node-operation" style="cursor: move;">node</span> <br />' +
                    '  <table style="margin: auto">' +
                    '    <tbody>' +
                    '      <tr>' +
                    '        <td>label</td>' +
                    '        <td><input id="node-label" value="" /></td>' +
                    '      </tr>' +
                    '    </tbody>' +
                    '  </table>' +
                    '  <input type="button" value="save" id="node-saveButton" />' +
                    '  <input type="button" value="cancel" id="node-cancelButton" />' +
                    '</div>' +
                    '' +
                    '<div id="edge-popUp">' +
                    '  <span id="edge-operation" style="cursor: move;">edge</span> <br />' +
                    '  <table style="margin: auto">' +
                    '    <tbody>' +
                    '      <tr>' +
                    '        <td>label</td>' +
                    '        <td><input id="edge-label" value="" /></td>' +
                    '      </tr>' +
                    '    </tbody>' +
                    '  </table>' +
                    '  <input type="button" value="save" id="edge-saveButton" />' +
                    '  <input type="button" value="cancel" id="edge-cancelButton" />' +
                    '</div>' +
                    '';
                var editHtmlDiv = document.createElement("div");
                editHtmlDiv.innerHTML = editHtml;
                document.body.appendChild(editHtmlDiv);
                //dragElement(document.getElementById("node-popUp"));
                //dragElement(document.getElementById("edge-popUp"));
                
                //function to make the manipulation popups draggable
                function dragElement(elmnt) {
                    var pos1 = 0,
                        pos2 = 0,
                        pos3 = 0,
                        pos4 = 0;
                    if (document.getElementById(elmnt.id)) {
                        // if present, the header is where you move the DIV from:
                        document.getElementById("node-operation").onmousedown = dragMouseDown;
                        document.getElementById("edge-operation").onmousedown = dragMouseDown;
                    } else {
                        // otherwise, move the DIV from anywhere inside the DIV:
                        elmnt.onmousedown = dragMouseDown;
                    }

                    function dragMouseDown(e) {
                        e = e || window.event;
                        e.preventDefault();
                        // get the mouse cursor position at startup:
                        pos3 = e.clientX;
                        pos4 = e.clientY;
                        document.onmouseup = closeDragElement;
                        // call a function whenever the cursor moves:
                        document.onmousemove = elementDrag;
                    }

                    function elementDrag(e) {
                        e = e || window.event;
                        e.preventDefault();
                        // calculate the new cursor position:
                        pos1 = pos3 - e.clientX;
                        pos2 = pos4 - e.clientY;
                        pos3 = e.clientX;
                        pos4 = e.clientY;
                        // set the element's new position:
                        elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
                        elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
                    }

                    function closeDragElement() {
                        // stop moving when mouse button is released:
                        document.onmouseup = null;
                        document.onmousemove = null;
                    }
                }
            }
        });
    });
});