},
"targets": 5
},{
- // "width": "10%",
+ "width": "20%",
"render": function (data, type, row) {
- return '<div class="btn-group">\n' +
- ' <button type="button" class="btn btn-default"\n' +
- ' onclick="javascript:showInstanceDetails(\''+instance_type+'\', \''+row["_id"]+'\')"\n' +
- ' data-toggle="tooltip" data-placement="top" data-container="body" title="Show Info">\n' +
- ' <i class="fa fa-info"></i>\n' +
- ' </button>\n' +
- ' <button type="button" class="btn btn-default"\n' +
- ' onclick="javascript:deleteNs(\''+ row["short-name"] +'\', \''+row["_id"]+'\')"\n' +
- ' data-toggle="tooltip" data-placement="top" data-container="body" title="Delete"><i\n' +
- ' class="far fa-trash-alt"></i></button>\n' +
- ' <button type="button" class="btn btn-default dropdown-toggle"\n' +
- ' data-toggle="dropdown" aria-expanded="false">Actions\n' +
- ' <span class="fa fa-caret-down"></span></button>\n' +
- ' <ul class="dropdown-menu">\n' +
- ' <li><a href="#"\n' +
- ' onclick="javascript:performAction(\''+ row["short-name"] +'\', \''+row["_id"]+'\')">\n' +
- ' <i class="fa fa-magic"></i> Exec NS Primitive</a></li>\n' +
- ' <li>\n' +
- ' <a href="/instances/ns/' +row["_id"] +'/operation">\n' +
- ' <i class="fa fa-list"></i> Active operations</a></li>\n' +
- ' <li class="divider"></li>\n' +
- ' <li><a href="#"\n' +
- ' onclick="javascript:newAlarmNs(\''+ row["short-name"] +'\', \''+row["_id"]+'\')">\n' +
- ' <i class="far fa-bell"></i> New Alarm</a></li>\n' +
- ' <li><a href="#"\n' +
- ' onclick="javascript:exportMetricNs(\''+ row["short-name"] +'\', \''+row["_id"]+ '\')">\n' +
- ' <i class="far fa-chart-bar"></i> Export metric</a></li>\n' +
- ' <li class="divider"></li>\n' +
- ' <li>\n' +
- ' <a href="javascript:deleteNs(\''+ row["short-name"] +'\', \''+row["_id"]+'\', true)">\n' +
- ' <i class="far fa-trash-alt" style="color:red" ></i> Force delete</a></li>\n' +
- ' </ul>\n' +
+ var template = '<div class="btn-group">' +
+ ' <button type="button" class="btn btn-default"' +
+ ' onclick="javascript:showInstanceDetails(\''+instance_type+'\', \''+row["_id"]+'\')"' +
+ ' data-toggle="tooltip" data-placement="top" data-container="body" title="Show Info">' +
+ ' <i class="fa fa-info"></i>';
+ if (row["operational-status"] === "running") {
+ template += ' <button type="button" class="btn btn-default"' +
+ ' onclick="javascript:showTopology(\'' + instance_type + '\', \'' + row["_id"] + '\')"' +
+ ' data-toggle="tooltip" data-placement="top" data-container="body" title="Show Graph">' +
+ ' <i class="fa fa-sitemap"></i>' +
+ ' </button>';
+ }else{
+ template += ' <button type="button" disabled class="btn btn-default"' +
+ ' onclick="javascript:showTopology(\'' + instance_type + '\', \'' + row["_id"] + '\')"' +
+ ' data-toggle="tooltip" data-placement="top" data-container="body" title="Show Graph">' +
+ ' <i class="fa fa-sitemap"></i>' +
+ ' </button>';
+ }
+ template += ' <button type="button" class="btn btn-default"' +
+ ' onclick="javascript:deleteNs(\''+ row["short-name"] +'\', \''+row["_id"]+'\')"' +
+ ' data-toggle="tooltip" data-placement="top" data-container="body" title="Delete"><i' +
+ ' class="far fa-trash-alt"></i></button>' +
+ ' <button type="button" class="btn btn-default dropdown-toggle"' +
+ ' data-toggle="dropdown" aria-expanded="false">Actions' +
+ ' <span class="fa fa-caret-down"></span></button>' +
+ ' <ul class="dropdown-menu">' +
+ ' <li><a href="#"' +
+ ' onclick="javascript:performAction(\''+ row["short-name"] +'\', \''+row["_id"]+'\')">' +
+ ' <i class="fa fa-magic"></i> Exec NS Primitive</a></li>' +
+ ' <li>' +
+ ' <a href="/instances/ns/' +row["_id"] +'/operation">' +
+ ' <i class="fa fa-list"></i> Active operations</a></li>' +
+ ' <li class="divider"></li>' +
+ ' <li><a href="#"' +
+ ' onclick="javascript:newAlarmNs(\''+ row["short-name"] +'\', \''+row["_id"]+'\')">' +
+ ' <i class="far fa-bell"></i> New Alarm</a></li>' +
+ ' <li><a href="#"' +
+ ' onclick="javascript:exportMetricNs(\''+ row["short-name"] +'\', \''+row["_id"]+ '\')">' +
+ ' <i class="far fa-chart-bar"></i> Export metric</a></li>' +
+ ' <li class="divider"></li>' +
+ ' <li>' +
+ ' <a href="javascript:deleteNs(\''+ row["short-name"] +'\', \''+row["_id"]+'\', true)">' +
+ ' <i class="far fa-trash-alt" style="color:red" ></i> Force delete</a></li>' +
+ ' </ul>' +
'</div>';
+ return template;
},
"targets": 5
},
},
{
"render": function (data, type, row) {
- return '<div class="btn-group">\n' +
- ' <button type="button" class="btn btn-default"\n' +
- ' onclick="javascript:showInstanceDetails(\''+instance_type+'\', \''+row["_id"]+'\')"\n' +
- ' data-toggle="tooltip" data-placement="top" data-container="body" title="Show Info">\n' +
- ' <i class="fa fa-info"></i>\n' +
- ' </button>\n' +
+ return '<div class="btn-group">' +
+ ' <button type="button" class="btn btn-default"' +
+ ' onclick="javascript:showInstanceDetails(\''+instance_type+'\', \''+row["_id"]+'\')"' +
+ ' data-toggle="tooltip" data-placement="top" data-container="body" title="Show Info">' +
+ ' <i class="fa fa-info"></i>' +
+ ' </button>' +
' </div>';
},
"targets": 5
{% block head_block %}
{{ block.super }}
- <link rel="stylesheet" href="/static/bower_components/select2/dist/css/select2.min.css">
- <link rel="stylesheet" href="/static/css/lwuitable.css">
- <!-- Topology3D core CSS -->
- <link rel="stylesheet" href="/static/topology3D/css/graph_editor_d3js.css">
- <link rel="stylesheet" href="/static/topology3D/css/d3-context-menu.css">
+<!-- TopologyComposer D3 -->
+ <link rel="stylesheet" href="/static/TopologyComposer/css/composer.css">
+ <link rel="stylesheet" href="/static/TopologyComposer/css/d3-context-menu.css">
+
{% endblock %}
{% block title_header_big %}
{% block content_body %}
{{ block.super }}
{% csrf_token %}
-
- <div class="row" >
- <div class="col-md-12">
- {% block topology_toolbar %}
- {{ block.super }}
- {% include 'topology_toolbar_instance.html' %}
- {% endblock %}
- <div id="graph_ed_container" style="width: 100%; height:100%; background-color: white; border: 2px #3c8dbc solid;">
+ <div class="container-fluid">
+ <div id="main" class="row">
+ <div id="graph_editor_container" class="col">
+ <div class="btn-group left-tool-bar-monitoring">
+ <button id="topology_force" type="button" class="btn btn btn-default" onclick="handleForce(this)"
+ data-toggle="button" aria-pressed="false" autocomplete="off"><i
+ class="fas fa-thumbtack"></i></button>
+ <button id="reset_filter" type="button" class="btn btn btn-default" onclick="resetFilters(this)"><i
+ class="fa fa-backward"></i></button>
+ </div>
+ <div id="legenda">
+ </div>
+ </div>
+ <div id="side">
</div>
</div>
</div>
-
-
{% endblock %}
{% block resource_block %}
<script>
var topology_data = {};//{{topology_data|safe }};
</script>
- <!-- Utility JS -->
- <script src="/static/bower_components/select2/dist/js/select2.js"></script>
-
<!-- d3.js -->
- <script src="https://d3js.org/d3.v4.js"></script>
-<!--
- <script src="/static/bower_components/d3/d3.js"></script>-->
-
- <!-- topology3D -->
- <script src="/static/topology3D/js/d3-context-menu.js"></script>
- <script src="/static/topology3D/js/event.js"></script>
- <script src="/static/topology3D/js/graph_editor.js"></script>
- <script src="/static/topology3D/js/graph_request.js"></script>
- <script src="/static/topology3D/js/model_graph_editor.js"></script>
+ <script src="/static/bower_components/d3/d3.js"></script>
+
+ <!-- TopologyComposer D3 -->
+ <script src="/static/TopologyComposer/js/event.js"></script>
+ <script src="/static/TopologyComposer/js/settings.js"></script>
+ <script src="/static/TopologyComposer/js/graph_editor.js"></script>
+ <script src="/static/TopologyComposer/js/model_graph_editor.js"></script>
+ <script src="/static/TopologyComposer/js/d3-context-menu.js"></script>
<script src="/static/src/osm_gui_properties.js"></script>
import json
import logging
from lib.osm.osmclient.clientv2 import Client
+from lib.osm.osm_rdcl_parser import OsmParser
import authosm.utils as osmutils
from sf_t3d.decorators import login_required
print result
return __response_handler(request, result, 'instances:list', to_redirect=True, type='ns')
-
+@login_required
def show_topology(request, instance_id=None, type=None):
user = osmutils.get_user(request)
project_id = user.project_id
raw_content_types = request.META.get('HTTP_ACCEPT', '*/*').split(',')
if 'application/json' in raw_content_types:
- result = {'vertices': [
- {"info": {"type": "vnf", "property": {"custom_label": ""},
- "group": []}, "id": "ping"},
- {"info": {"type": "vnf", "property": {"custom_label": ""},
- "group": []}, "id": "pong"},
- {"info": {"type": "vdu", "property": {"custom_label": ""},
- "group": ['pong']}, "id": "pong/ubuntu"},
- {"info": {"type": "vdu", "property": {"custom_label": ""},
- "group": ['ping']}, "id": "ping/ubuntu"},
- {"info": {"type": "cp", "property": {"custom_label": ""},
- "group": ['ping']}, "id": "ping/cp0"},
- {"info": {"type": "cp", "property": {"custom_label": ""},
- "group": ['ping']}, "id": "ping/cp1"},
- {"info": {"type": "cp", "property": {"custom_label": ""},
- "group": ['pong']}, "id": "pong/cp0"},
- {"info": {"type": "cp", "property": {"custom_label": ""},
- "group": ['pong']}, "id": "pong/cp1"},
- {"info": {"type": "ns_vl", "property": {"custom_label": ""},
- "group": []}, "id": "mgmt_vl"},
- ],
- 'edges': [
- # {"source": "ping", "group": [], "target": "ping/cp0", "view": "Data"},
- {"source": "pong/ubuntu", "group": ['pong'], "target": "pong/cp0", "view": "vnf"},
- {"source": "ping/ubuntu", "group": ['ping'], "target": "ping/cp0", "view": "vnf"},
- {"source": "pong/ubuntu", "group": ['pong'], "target": "pong/cp1", "view": "vnf"},
- {"source": "ping/ubuntu", "group": ['ping'], "target": "ping/cp1", "view": "vnf"},
- {"source": "pong", "group": [], "target": "mgmt_vl", "view": "ns"},
- {"source": "ping", "group": [], "target": "mgmt_vl", "view": "ns"},
- ], 'graph_parameters': [],
- 'model': {
- "layer": {
-
- "ns": {
- "nodes": {
- "vnf": {
- "addable": {
- "callback": "addNode"
- },
- "removable": {
- "callback": "removeNode"
- },
- "expands": "vnf"
- },
- "ns_vl": {
- "addable": {
- "callback": "addNode"
- },
- "removable": {
- "callback": "removeNode"
- }
- },
-
- },
- "allowed_edges": {
- "ns_vl": {
- "destination": {
- "vnf": {
- "callback": "addLink",
- "direct_edge": False,
- "removable": {
- "callback": "removeLink"
- }
- }
- }
- },
- "vnf": {
- "destination": {
- "ns_vl": {
- "callback": "addLink",
- "direct_edge": False,
- "removable": {
- "callback": "removeLink"
- }
- },
-
- }
- }
-
- }
- },
- "vnf": {
- "nodes": {
- "vdu": {
- "addable": {
- "callback": "addNode"
- },
- "removable": {
- "callback": "removeNode"
- }
- },
- "cp": {
- "addable": {
- "callback": "addNode"
- },
- "removable": {
- "callback": "removeNode"
- }
- },
-
- },
- "allowed_edges": {
- "vdu": {
- "destination": {
- "cp": {
- "callback": "addLink",
- "direct_edge": False,
- "removable": {
- "callback": "removeLink"
- }
- }
- }
- },
- "cp": {
- "destination": {
- "vdu": {
- "callback": "addLink",
- "direct_edge": False,
- "removable": {
- "callback": "removeLink"
- }
- }
- }
- }
- }
- },
- "name": "OSM",
- "version": 1,
- "nodes": {
- "vnf": {
- "label": "vnf"
- },
- "ns_vl": {
- "label": "vl"
- },
- "cp": {
- "label": "cp"
- },
- "vdu": {
- "label": "vdu"
- }
- },
- "description": "osm"
- }
- }}
+ client = Client()
+ nsr_object = {'nsr': {}, 'vnfr': {}, 'vnfd': {}}
+ if type == 'ns':
+
+ nsr_resp = client.ns_get(user.get_token(), instance_id)
+ nsr_object['nsr'] = nsr_resp['data']
+ if 'constituent-vnfr-ref' in nsr_object['nsr'] :
+ for vnfr_id in nsr_object['nsr']['constituent-vnfr-ref']:
+ vnfr_resp = client.vnf_get(user.get_token(), vnfr_id)
+ vnfr = vnfr_resp['data']
+ nsr_object['vnfr'][vnfr['id']] = vnfr
+ if vnfr['vnfd-id'] not in nsr_object['vnfd']:
+ vnfd_resp = client.vnfd_get(user.get_token(), vnfr['vnfd-id'])
+ nsr_object['vnfd'][vnfr['vnfd-id']] = vnfd_resp['vnfd:vnfd-catalog']['vnfd'][0]
+
+
+
+ test = OsmParser()
+ #print nsr_object
+
+ result = test.nsr_to_graph(nsr_object)
return __response_handler(request, result)
else:
result = {'type': type, 'project_id': project_id, 'instance_id': instance_id}
# limitations under the License.
#
-import json
-import pyaml
-import yaml
-from lib.util import Util
-from lib.parser import Parser
+
import logging
-import traceback
-import glob
-import os
+# from lib.rdcl_graph import RdclGraph
+import copy
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger('OsmParser')
-class OsmParser(Parser):
- """Parser methods for osm project type
+class RdclGraph(object):
+ """ Operates on the graph representation used for the GUI graph views """
+ node_ids = []
+ node_t3d_base = {
+ 'info': {
+ 'property': {
+ 'custom_label': '',
+ },
+ 'type': '',
+ 'group': []
+ }
+ }
+
+ def __init__(self):
+ pass
+
+ def add_link(self, source, target, view, group, graph_object, optional={}):
+ if (source is None or source not in self.node_ids) or (target is None or target not in self.node_ids):
+ return
+ edge_obj = {
+ 'source': source,
+ 'target': target,
+ 'view': view,
+ 'group': [group],
+ }
+
+ edge_obj.update(optional)
+ if edge_obj not in graph_object['edges']:
+ graph_object['edges'].append(edge_obj)
+
+ def add_node(self, id, type, group, positions, graph_object, optional={}):
+ if id is None:
+ return
+ node = next((x for x in graph_object['vertices'] if x['id'] == id), None)
+ if node is not None:
+ node['info']['group'].append(group)
+ else:
+ node = copy.deepcopy(self.node_t3d_base)
+ node['id'] = id
+ node['info']['type'] = type
+ if group is not None:
+ node['info']['group'].append(group)
+ if positions and id in positions['vertices'] and 'x' in positions['vertices'][id] and 'y' in \
+ positions['vertices'][id]:
+ node['fx'] = positions['vertices'][id]['x']
+ node['fy'] = positions['vertices'][id]['y']
+ node['info'].update(optional)
+ graph_object['vertices'].append(node)
+ self.node_ids.append(id)
+
+ def is_directed_edge(self, source_type=None, target_type=None, layer=None, model={}):
+ if source_type is None or target_type is None or layer is None:
+ return None
+ if layer in model['layer'] and 'allowed_edges' in model['layer'][layer]:
+ if source_type in model['layer'][layer]['allowed_edges'] and target_type in \
+ model['layer'][layer]['allowed_edges'][source_type]['destination']:
+ edge_pro = model['layer'][layer]['allowed_edges'][source_type]['destination'][target_type]
+ return edge_pro['direct_edge'] if 'direct_edge' in edge_pro else False
+
+ return None
+
+
+class OsmParser(RdclGraph):
+ """ Operates on the graph representation used for the GUI graph views """
+
+ def nsr_to_graph(self, nsr_full):
+
+ graph = {'vertices': [], 'edges': [], 'model': {
+ "layer": {
+
+ "nsr": {
+ "nodes": {
+ "vnfr": {
+ "addable": {},
+ "removable": {},
+ "expands": "vnfr"
+ },
+ "ns_vl": {
+ "addable": {},
+ "removable": {}
+ },
+ "ns_cp": {
+ "addable": {},
+ "removable": {}
+ },
+
+ },
+ "allowed_edges": {
+ "ns_vl": {
+ "destination": {
+ "vnfr": {
+ "callback": "addLink",
+ "direct_edge": False,
+ "removable": {}
+ }
+ }
+ },
+ "vnfr": {
+ "destination": {
+ "ns_vl": {
+ "callback": "addLink",
+ "direct_edge": False,
+ "removable": {}
+ },
+
+ }
+ }
+
+ }
+ },
+
+ "vnfr": {
+ "nodes": {
+ "vdur": {},
+ "cp": {},
+ "int_cp": {},
+ "vnf_vl": {}
+
+ },
+ "allowed_edges": {
+ "vdur": {
+ "destination": {
+ "cp": {
+ "direct_edge": False,
+ },
+ "int_cp": {
+ "direct_edge": False,
+ },
+ "vnf_vl": {
+ "direct_edge": False,
+ }
+ }
+ },
+ "cp": {
+ "destination": {
+ "vdur": {
+ "direct_edge": False,
+ }
+ }
+ },
+ "int_cp": {
+ "destination": {
+ "vdur": {
+ "direct_edge": False,
+ },
+ "vnf_vl": {
+ "direct_edge": False,
+ }
+ }
+ },
+ "vnf_vl": {
+ "destination": {
+ "vdur": {
+ "direct_edge": False
+ }
+ }
+ }
+ }
+ },
+ "name": "OSM",
+ "version": 1,
+ "description": "osm"
+ }
+ }, 'graph_parameters': {'view': {'nsr': {}, 'vnfr': {}}}}
+
+ nsr = nsr_full['nsr']
+
+ graph['graph_parameters']['view']['nsr'] = {}
+ nsr_graph_param = graph['graph_parameters']['view']['nsr']
+ nsr_graph_param['id'] = nsr['_id'] if '_id' in nsr else None
+ nsr_graph_param['nsdId'] = nsr['nsdId'] if 'nsdId' in nsr else None
+ nsr_graph_param['name-ref'] = nsr['name-ref'] if 'name-ref' in nsr else None
+ nsr_graph_param['operational-status'] = nsr['operational-status'] if 'operational-status' in nsr else None
+ nsr_graph_param['config-status'] = nsr['config-status'] if 'config-status' in nsr else None
+ nsr_graph_param['detailed-status'] = nsr['detailed-status'] if 'detailed-status' in nsr else None
+ nsr_graph_param['create-time'] = nsr['create-time'] if 'create-time' in nsr else None
+ nsr_graph_param['instantiate_params'] = nsr['instantiate_params'] if 'instantiate_params' in nsr else None
+
+ map_vnf_index_to_id = {}
+ for vnfr_id in nsr['constituent-vnfr-ref']:
+ current_vnfr = nsr_full['vnfr'][vnfr_id]
+
+ graph['graph_parameters']['view']['vnfr'][vnfr_id] = {}
+ vnfr_graph_param = graph['graph_parameters']['view']['vnfr'][vnfr_id]
+ vnfr_graph_param['id'] = vnfr_id
+ vnfr_graph_param['vnfd-id'] = current_vnfr['vnfd-id']
+ vnfr_graph_param['vnfd-ref'] = current_vnfr['vnfd-ref']
+ vnfr_graph_param['member-vnf-index-ref'] = current_vnfr['member-vnf-index-ref']
+ vnfr_graph_param['vim-account-id'] = current_vnfr['vim-account-id']
+ vnfr_graph_param['created-time'] = current_vnfr['created-time']
+
+ vnfr_label = current_vnfr['vnfd-ref'] + ':' + current_vnfr['member-vnf-index-ref']
+ map_vnf_index_to_id[current_vnfr['member-vnf-index-ref']] = vnfr_id
+ self.add_node(vnfr_id, 'vnfr', None, None, graph,
+ {'property': {'custom_label': vnfr_label}, 'osm': current_vnfr})
+
+ for cp in current_vnfr['connection-point']:
+ if cp['id']:
+ cp_id = vnfr_label + ':' + cp['id']
+ self.add_node(cp_id, 'cp', vnfr_id, None, graph, {'osm': cp})
+
+ for vdur in current_vnfr['vdur']:
+ vdur_id = vnfr_label + ':' + vdur['vdu-id-ref']
+ self.add_node(vdur_id, 'vdur', vnfr_id, None, graph, {'osm': vdur})
+ if current_vnfr['vnfd-id'] in nsr_full['vnfd']:
+ for vdu in nsr_full['vnfd'][current_vnfr['vnfd-id']]['vdu']:
+ if vdu['id'] == vdur['vdu-id-ref']:
+ if 'internal-connection-point' in vdu:
+ for int_cp in vdu['internal-connection-point']:
+ cp_id = vnfr_label + ':' + int_cp['id']
+ self.add_node(cp_id, 'int_cp', vnfr_id, None, graph, {'osm': int_cp})
+ for interface in vdu['interface']:
+ if interface['type'] == "EXTERNAL":
+ cp_id = vnfr_label + ':' + interface['external-connection-point-ref']
+ self.add_link(cp_id, vdur_id, 'vnfr', vnfr_id, graph)
+ elif interface['type'] == "INTERNAL":
+ cp_id = vnfr_label + ':' + interface['internal-connection-point-ref']
+ self.add_link(cp_id, vdur_id, 'vnfr', vnfr_id, graph)
+
+ if current_vnfr['vnfd-id'] in nsr_full['vnfd'] and 'internal-vld' in nsr_full['vnfd'][
+ current_vnfr['vnfd-id']]:
+ for vnfd_vld in nsr_full['vnfd'][current_vnfr['vnfd-id']]['internal-vld']:
+ vld_id = vnfr_label + ':' + vnfd_vld['id']
+ self.add_node(vld_id, 'vnf_vl', vnfr_id, None, graph, {'osm': vnfd_vld})
+ if vnfd_vld['internal-connection-point']:
+ for int_cp in vnfd_vld['internal-connection-point']:
+ int_cp_id = vnfr_label + ':' + int_cp['id-ref']
+ self.add_link(vld_id, int_cp_id, 'vnfr', vnfr_id, graph)
+
+ for ns_vld in nsr['nsd']['vld']:
+ self.add_node(ns_vld['id'], 'ns_vl', None, None, graph,
+ {'property': {'custom_label': ns_vld['name']}, 'osm': ns_vld})
+ for cp_ref in ns_vld['vnfd-connection-point-ref']:
+ self.add_link(map_vnf_index_to_id[str(cp_ref['member-vnf-index-ref'])], ns_vld['id'], 'nsr', None,
+ graph)
+
+ return graph
+
- """
+if __name__ == '__main__':
+ parser = OsmParser()
+ print parser.nsr_to_graph({})
var descr_list_url = '{% url "projects:list_descriptors" descriptor_type=descriptor_type%}';
var vim_list_url = '{% url "vims:list" %}';
var new_desc_url ="{% url 'projects:new_descriptor' descriptor_type=descriptor_type %}";
- var descriptor_type = '{{ descriptor_type }}';
+ var descriptor_type = '{{ descriptor_type | safe }}';
var project_id = '{{ project_id }}';
var table;
}
},
"error": function (hxr, error, thrown) {
- console.log(hxr)
- console.log(thrown)
- console.log(error);
+
}
},
--- /dev/null
+
+.node_path {
+ opacity: 1;
+ stroke: #2F3550;
+ stroke-width: 1;
+}
+
+.node_selected {
+ opacity: 1 !important;
+ stroke: #2F3550 !important;
+ stroke-width: 3 !important;
+}
+
+.node_text {
+ font-family: Lucida Console;
+ text-anchor: middle;
+ user-select: none;
+}
+
+.hidden_circle {
+ opacity: 0;
+ stroke: #FF0000 !important;
+ stroke-width: 2 !important;
+}
+
+.hidden_circle:hover {
+ opacity: 1;
+ stroke: #FF0000 !important;
+ stroke-width: 2;
+}
+
+.node_path:hover {
+ opacity: 1;
+ stroke: #2F3550;
+ stroke-width: 3;
+}
+
+.node_path:hover text {
+ opacity: 0.4;
+
+}
+
+.link {
+ fill: none;
+}
+
+.link path {
+ stroke-width: 2;
+ stroke: lightgray;
+}
+
+.matted {
+ opacity: 0.3;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+.left-tool-bar-monitoring {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ z-index: 1;
+ text-align: center;
+ font-size: 12px;
+
+ cursor: default;
+ padding: 0 6px;
+ line-height: 22px;
+ position: absolute;
+
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.container-fluid {
+ min-height: 100vh !important;
+ background-color: white;
+}
+
+#main {
+ height: 100%;
+ display: flex;
+}
+
+#graph_editor_container {
+ position: relative;
+ min-height: 100vh;
+ border-right: 1px solid #AFAFAF;
+ flex: 1 1 auto;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+#legenda {
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ border: 1px solid #cdcdcd;
+ border-radius: 2px;
+ padding: 15px;
+}
+
+#legenda > .node {
+ margin-top: 10px;
+ font-size: 13px;
+ display: flex;
+ align-items: center;
+}
+
+#legenda > .node:first-child {
+ margin-top: 0;
+}
+
+#legenda > .node > .icon {
+ width: 20px;
+ height: 20px;
+ border-radius: 1px;
+ margin-right: 10px;
+}
+
+#side {
+ flex: 0 0 auto;
+ max-width: 500px;
+ min-width: 300px;
+ padding: 20px 30px;
+ display: flex;
+ flex-direction: column;
+}
+
+.section {
+ font-size: 16px;
+ line-height: 40px;
+ letter-spacing: 0.4px;
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 10px;
+ margin-top: 20px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.section > .status {
+ display: flex;
+ align-items: center;
+ font-size: 12px
+}
+
+.section > .status > .indicator {
+ width: 8px;
+ height: 8px;
+ background-color: red;
+ border-radius: 50%;
+ display: block;
+ margin-right: 7px;
+}
+
+.section > .status.active > .indicator {
+ background-color: green;
+}
+
+.children td {
+ padding: 5px 7px;
+ font-size: 13px;
+ line-height: 15px;
+}
+
+.children td:first-child {
+ padding-left: 0;
+ font-weight: 500;
+ text-align: right
+}
\ No newline at end of file
--- /dev/null
+.d3-context-menu {
+ position: absolute;
+ display: none;
+ background-color: #f2f2f2;
+ border-radius: 4px;
+
+ font-family: Arial, sans-serif;
+ font-size: 14px;
+ min-width: 150px;
+ border: 1px solid #d4d4d4;
+
+ z-index:1200;
+}
+
+.d3-context-menu ul {
+ list-style-type: none;
+ margin: 4px 0px;
+ padding: 0px;
+ cursor: default;
+}
+
+.d3-context-menu ul li {
+ padding: 4px 16px;
+
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Chrome/Safari/Opera */
+ -khtml-user-select: none; /* Konqueror */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none;
+}
+
+.d3-context-menu ul li:hover {
+ background-color: #4677f8;
+ color: #fefefe;
+}
+
+/*
+ Header
+*/
+
+.d3-context-menu ul li.is-header,
+.d3-context-menu ul li.is-header:hover {
+ background-color: #f2f2f2;
+ color: #444;
+ font-weight: bold;
+ font-style: italic;
+}
+
+/*
+ Disabled
+*/
+
+.d3-context-menu ul li.is-disabled,
+.d3-context-menu ul li.is-disabled:hover {
+ background-color: #f2f2f2;
+ color: #888;
+ cursor: not-allowed;
+}
+
+/*
+ Divider
+*/
+
+.d3-context-menu ul li.is-divider {
+ padding: 0px 0px;
+}
+
+.d3-context-menu ul li.is-divider:hover {
+ background-color: #f2f2f2;
+}
+
+.d3-context-menu ul hr {
+ border: 0;
+ height: 0;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.3);
+}
--- /dev/null
+(function (root, factory) {
+ if (typeof module === 'object' && module.exports) {
+ module.exports = function (d3) {
+ d3.contextMenu = factory(d3);
+ return d3.contextMenu;
+ };
+ } else if (typeof define === 'function' && define.amd) {
+ try {
+ var d3 = require('d3');
+ } catch (e) {
+ d3 = root.d3;
+ }
+
+ d3.contextMenu = factory(d3);
+ define([], function () {
+ return d3.contextMenu;
+ });
+ } else if (root.d3) {
+ root.d3.contextMenu = factory(root.d3);
+ }
+}(this,
+ function (d3) {
+ return function (menu, opts) {
+ var openCallback,
+ closeCallback;
+
+ if (typeof opts === 'function') {
+ openCallback = opts;
+ } else {
+ opts = opts || {};
+ openCallback = opts.onOpen;
+ closeCallback = opts.onClose;
+ }
+
+ // create the div element that will hold the context menu
+ d3.selectAll('.d3-context-menu').data([1])
+ .enter()
+ .append('div')
+ .attr('class', 'd3-context-menu');
+
+ // close menu
+ d3.select('body').on('click.d3-context-menu', function () {
+ d3.select('.d3-context-menu').style('display', 'none');
+ if (closeCallback) {
+ closeCallback();
+ }
+ });
+
+ // this gets executed when a contextmenu event occurs
+ return function (data, index) {
+ var elm = this;
+ d3.selectAll('.d3-context-menu').html('');
+ var list = d3.selectAll('.d3-context-menu')
+ .on('contextmenu', function (d) {
+ d3.select('.d3-context-menu').style('display', 'none');
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+ })
+ .append('ul');
+ list.selectAll('li').data(typeof menu === 'function' ? menu(data) : menu).enter()
+ .filter(function (d) {
+ if(opts.type_object == 'node'){
+ if (opts.edit_mode || opts.edit_mode == d.edit_mode ) {
+ if ((d.nodes == undefined || d.nodes.length == 0) ||
+ (d.nodes != undefined && d.nodes.length > 0 && d.nodes.indexOf(data.info.type) > -1))
+ return true
+ }
+ }
+ if(opts.type_object == 'link'){
+ if (opts.edit_mode == d.edit_mode) {
+
+ return true
+ }
+ }
+ return false;
+ })
+ .append('li')
+ .attr('class', function (d) {
+ var ret = '';
+ if (d.divider) {
+ ret += ' is-divider';
+ }
+ if (d.disabled) {
+ ret += ' is-disabled';
+ }
+ if (!d.action) {
+ ret += ' is-header';
+ }
+ //if()
+ return ret;
+ })
+ .html(function (d) {
+ if (d.divider) {
+ return '<hr>';
+ }
+ if (!d.title) {
+ console.error('No title attribute set. Check the spelling of your options.');
+ }
+ return (typeof d.title === 'string') ? d.title : d.title(data);
+ })
+ .on('click', function (d, i) {
+ if (d.disabled) return; // do nothing if disabled
+ if (!d.action) return; // headers have no "action"
+ d.action(elm, data, index);
+ d3.select('.d3-context-menu').style('display', 'none');
+
+ if (closeCallback) {
+ closeCallback();
+ }
+ });
+
+ // the openCallback allows an action to fire before the menu is displayed
+ // an example usage would be closing a tooltip
+ if (openCallback) {
+ if (openCallback(data, index) === false) {
+ return;
+ }
+ }
+
+ // display context menu
+ d3.select('.d3-context-menu')
+ .style('left', (d3.event.pageX - 2) + 'px')
+ .style('top', (d3.event.pageY - 2) + 'px')
+ .style('display', 'block');
+
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+ };
+ };
+ }
+));
\ No newline at end of file
--- /dev/null
+/*
+ Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+ Copyright 2018 EveryUP srl
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+if (typeof TCD3 === 'undefined') {
+ var TCD3 = {};
+}
+
+TCD3.Event = (function () {
+ 'use strict';
+
+ function Event () {
+ this._listeners = {};
+ }
+
+ Event.prototype.addL = function (type, listener) {
+ if (typeof this._listeners[type] === "undefined"){
+ this._listeners[type] = [];
+ }
+ this._listeners[type].push(listener);
+ };
+
+ Event.prototype.fire = function (event, args) {
+ if (typeof event === "string"){
+ event = { type: event };
+ }
+ if (!event.target){
+ event.target = this;
+ }
+
+ if (!event.type){
+ throw new Error("Event object missing 'type' property.");
+ }
+
+ if (this._listeners[event.type] instanceof Array){
+ var listeners = this._listeners[event.type];
+ for (var i=0, len=listeners.length; i < len; i++){
+ listeners[i].call(this, event, args);
+ }
+ }
+ };
+
+ /*Event.prototype.addListener = function (type, listener) {
+ if (this._listeners[type] instanceof Array){
+ var listeners = this._listeners[type];
+ for (var i=0, len=listeners.length; i < len; i++){
+ if (listeners[i] === listener){
+ listeners.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ };*/
+
+ return Event;
+}());
+
+if (typeof module === 'object') {
+ module.exports = TCD3.Event;
+}
--- /dev/null
+/*
+ Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+ Copyright 2018 EveryUP srl
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+if (typeof TCD3 === 'undefined') {
+ var TCD3 = {};
+}
+
+TCD3.GraphEditor = (function () {
+ 'use strict';
+
+ var DEBUG = true;
+ var SHIFT_BUTTON = 16;
+ var CANC_BUTTON = 46;
+ var nominal_text_size = 14;
+ var nominal_stroke = 1.5;
+ var EventHandler = TCD3.Event;
+
+
+ /**
+ * Constructor
+ */
+ function GraphEditor(args) {
+ log("Constructor");
+ this.eventHandler = new EventHandler();
+ this.lastKeyDown = -1;
+ this._selected_node = undefined;
+ this._selected_link = undefined;
+ this._edit_mode = true;
+ this.filter_parameters = {
+ node: {
+ type: [],
+ group: [],
+ },
+ link: {
+ group: [],
+ view: [],
+ }
+ };
+ this.current_view_id = '';
+ // graph data initailization
+ this.d3_graph = {
+ nodes: [],
+ links: [],
+ graph_parameters: {}
+
+ };
+
+
+ }
+
+
+ GraphEditor.prototype.init = function (args) {
+ args = args || {};
+ var self = this;
+ this.width = args.width || 1500;
+ this.height = args.height || 1500;
+ this.forceSimulationActive = false;
+
+ var min_zoom = 0.1;
+ var max_zoom = 7;
+ this._setupBehaviorsOnEvents();
+ this._setupFiltersBehaviors(args);
+
+ this.type_property = {
+ "unrecognized": {
+ "shape": d3.symbolCircle,
+ "color": "#fff",
+ "node_label_color": "#000",
+ "size": 15
+ },
+ };
+
+ this.type_property_link = {
+ "unrecognized": {
+ "color": "lightgray",
+ },
+ };
+
+ this.force = d3.forceSimulation()
+ .force("charge", d3.forceManyBody())
+ .force("collide", d3.forceCollide().radius(80))
+ // .force("link", d3.forceLink().distance(80).iterations(1).id(function (d) {
+ .force("link", d3.forceLink().distance(100).id(function (d) {
+ return d.id;
+ }))
+ .force("center", d3.forceCenter(this.width / 2, this.height / 2));
+
+ var zoom = d3.zoom().scaleExtent([min_zoom, max_zoom]);
+
+ var size = d3.scalePow().exponent(2)
+ .domain([1, 100])
+ .range([8, 24]);
+
+ this.svg = d3.select("#graph_editor_container").append("svg")
+ .attr("id", "graph_svg")
+ .attr("preserveAspectRatio", "xMinYMid")
+ .attr("width", this.width)
+ .attr("height", this.height);
+
+ //End Arrow style
+ this.defs = this.svg.append("svg:defs");
+
+ this.defs.selectAll("marker")
+ .data(["unrecognized"]) // Different link/path types can be defined here
+ .enter().append("svg:marker") // This section adds in the arrows
+ .attr("id", String)
+ .attr("viewBox", "-5 -5 10 10")
+ .attr("refX", 13) //must be smarter way to calculate shift
+ .attr("refY", 0)
+ .attr("markerUnits", "userSpaceOnUse")
+ .attr("markerWidth", 12)
+ .attr("markerHeight", 12)
+ .attr("orient", "auto")
+ .append("path")
+ .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
+ .attr('fill', this.type_property_link['unrecognized']['color']);
+
+ d3.select(window)
+ .on('keydown', function () {
+ log('keydown ' + d3.event.keyCode);
+ //d3.event.preventDefault();
+ if (self.lastKeyDown !== -1) return;
+ self.lastKeyDown = d3.event.keyCode;
+ if (self.lastKeyDown === CANC_BUTTON && self._selected_node !== undefined) {
+ self.removeNode(self._selected_node, null, showAlert);
+ } else if (self.lastKeyDown === CANC_BUTTON && self._selected_link !== undefined) {
+ self.removeLink(self._selected_link, null, showAlert);
+ }
+
+ })
+ .on('keyup', function () {
+ log('keyup' + self.lastKeyDown);
+ self.lastKeyDown = -1;
+ });
+ var popup = this.svg.append("g")
+ .attr("id", "popup")
+ .attr("class", "popup")
+ .attr("opacity", "0")
+ .attr("transform", "translate(1 1)")
+ .call(d3.drag()
+ .on("start", dragstarted)
+ .on("drag", dragged)
+ .on("end", dragended));
+
+ function dragstarted(d) {
+ //d3.select(this).raise().classed("active", true);
+ }
+
+ function dragged(d) {
+ //console.log(JSON.stringify(d))
+ d3.select(this).attr("transform", function () {
+ return "translate(" + d3.event.x + "," + d3.event.y + ")";
+
+ })
+ }
+
+ function dragended(d) {
+ //d3.select(this).classed("active", false);
+ }
+
+ var chart = $("#graph_svg");
+ this.aspect = chart.width() / chart.height();
+ this.container = $("#graph_editor_container");
+ $(window).on("resize", function () {
+
+ self.width = self.container.width();
+ self.height = self.container.height();
+ chart.attr("width", self.container.width());
+ chart.attr("height", self.container.height());
+ }).trigger("resize");
+
+ }
+
+
+ GraphEditor.prototype.get_d3_symbol =
+ function (myString) {
+
+ switch (myString) {
+ case "circle":
+ return d3.symbolCircle;
+ case "square":
+ return d3.symbolSquare;
+ case "diamond":
+ return d3.symbolDiamond;
+ case "triangle":
+ return d3.symbolTriangle;
+ case "star":
+ return d3.symbolStar;
+ case "cross":
+ return d3.symbolCross;
+ default:
+ // if the string is not recognized
+ return d3.symbolCross;
+ }
+
+ };
+
+ GraphEditor.prototype.get_name_from_d3_symbol =
+ function (mySymbol) {
+ switch (mySymbol) {
+ case d3.symbolCircle:
+ return "circle";
+ case d3.symbolSquare:
+ return "square";
+ case d3.symbolDiamond:
+ return "diamond";
+ case d3.symbolTriangle:
+ return "triangle";
+ case d3.symbolStar:
+ return "star";
+ case d3.symbolCross:
+ return "cross";
+ default:
+ // if the string is not recognized
+ return "unknown";
+ //return d3.symbolCircleUnknown;
+ }
+
+ };
+
+ /**
+ * Start or Stop force layout
+ * @param {boolean} Required. Value true: start, false: stop
+ * @returns {boolean}
+ */
+ GraphEditor.prototype.handleForce = function (start) {
+ if (start)
+ this.force.stop();
+ this.forceSimulationActive = start;
+ this.node.each(function (d) {
+ d.fx = (start) ? null : d.x;
+ d.fy = (start) ? null : d.y;
+ });
+
+ if (start)
+ this.force.restart();
+
+ this.eventHandler.fire("force_status_changed_on", start);
+ };
+
+ /**
+ * Handle the parameters of basic filters: node type, view, group
+ * @param {Object} Required.
+ *
+ */
+ GraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
+ this.filter_parameters = (filtersParams !== undefined) ? filtersParams : this.filter_parameters;
+ this.current_view_id = (this.filter_parameters !== undefined && this.filter_parameters.link.view[0] !== undefined) ? this.filter_parameters.link.view[0] : this.current_view_id
+ this.cleanAll();
+ this.refresh();
+ this.startForce();
+ this.force.restart();
+ this._deselectAllNodes();
+ this.handleForce(this.forceSimulationActive);
+ if (!notFireEvent)
+ this.eventHandler.fire("filters_changed", filtersParams);
+
+ };
+
+ /**
+ * Add a new node to the graph.
+ * @param {Object} Required. An object that specifies tha data of the new node.
+ * @returns {boolean}
+ */
+ GraphEditor.prototype.addNode = function (args) {
+ if (args.id && args.info && args.info.type) {
+ args.fixed = true;
+ this.force.stop();
+ this.cleanAll();
+ this.d3_graph.nodes.push(args);
+ this.refresh();
+ this.startForce();
+ this.force.restart();
+ this.handleForce(this.forceSimulationActive);
+ return true;
+ }
+
+ return false;
+
+ };
+
+ /**
+ * Update the data properties of the node
+ * @param {Object} Required. An object that specifies tha data of the node.
+ * @returns {boolean}
+ */
+ GraphEditor.prototype.updateDataNode = function (args) {
+
+ };
+
+ /**
+ * Remove a node from graph and related links.
+ * @param {String} Required. Id of node to remove.
+ * @returns {boolean}
+ */
+ GraphEditor.prototype.removeNode = function (node) {
+ if (node != undefined) {
+ var node_id = node.id;
+ this.d3_graph['nodes'].forEach(function (n, index, object) {
+ if (n.id === node_id) {
+ object.splice(index, 1);
+ }
+ });
+
+ var self = this;
+ var links_to_remove = [];
+ this.d3_graph['links'].forEach(function (l, index, object) {
+ if (node_id === l.source.id || node_id === l.target.id) {
+ links_to_remove.push(index);
+ }
+ });
+ var links_removed = 0;
+ links_to_remove.forEach(function (l_index) {
+ self.d3_graph['links'].splice(l_index - links_removed, 1);
+ links_removed++;
+ });
+ this.cleanAll();
+ this.refresh();
+ this.startForce();
+ this.force.restart();
+
+ return true;
+ }
+ return false;
+ };
+
+
+ /**
+ * Add a new link to graph.
+ * @param {Object} Required. An object that specifies tha data of the new Link.
+ * @returns {boolean}
+ */
+ GraphEditor.prototype.addLink = function (link) {
+ console.log("addLink" + JSON.stringify(link));
+ if (link.source && link.target) {
+ this.force.stop();
+ this.cleanAll();
+ this.d3_graph.links.push(link);
+ this.refresh();
+ this.startForce();
+ this.force.restart();
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * Remove a link from graph.
+ * @param {String} Required. The identifier of link to remove.
+ * @returns {boolean}
+ */
+ GraphEditor.prototype.removeLink = function (link_id) {
+ var self = this;
+ if (link_id !== 'undefined') {
+ this.d3_graph['links'].forEach(function (l, index, object) {
+ if (link_id === l.index) {
+ object.splice(index, 1);
+
+ self.cleanAll();
+ self.refresh();
+ self.startForce();
+ self.force.restart();
+ return true;
+ }
+
+ });
+ }
+
+ return false;
+ };
+
+
+ /**
+ * Force a refresh of GraphView
+ * @returns {}
+ */
+ GraphEditor.prototype.refresh = function () {
+
+ //log(data)
+ var self = this;
+
+ this.link = this.svg
+ .selectAll()
+ .data(self.d3_graph.links
+ .filter(this.link_filter_cb)
+ )
+ .enter().append("g")
+ .attr("class", "link cleanable")
+ .append("path")
+ .attr("class", "link")
+ .attr("class", "cleanable")
+ .style("stroke-width", nominal_stroke)
+ .style("stroke", function (d) {
+ return self._link_property_by_type((d.type_link) ? d.type_link : "unrecognized", "color");
+ })
+ .attr("marker-end", function (d) {
+ if (!d.directed_edge)
+ return '';
+
+ var marker_url = (d.type_link) ? d.type_link : "unrecognized"
+ return (d.directed_edge ? "url(#" + marker_url + ")" : '');
+ });
+
+ this.nodeContainer = this.svg
+ .selectAll()
+ .data(self.d3_graph.nodes
+ .filter(this.node_filter_cb))
+ .enter()
+ .append("g")
+ // .attr("class", "nodosdads")
+ .attr("class", "node cleanable");
+
+ this.svg.selectAll('.node')
+ .data(self.d3_graph.nodes
+ .filter(this.node_filter_cb))
+
+ .filter(function (d) {
+ return (d.info.type === undefined) || (self._node_property_by_type(d.info.type, 'image', d) === undefined)
+ })
+
+ .append("svg:path")
+ .attr("d", d3.symbol()
+ .size(function (d) {
+ return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d), 2) / 4;
+ })
+ .type(function (d) {
+ // console.log(d.info.type, 'shape', self.current_view_id)
+ return (self._node_property_by_type(d.info.type, 'shape', d));
+ })
+ )
+ .style("fill", function (d) {
+ return self._node_property_by_type(d.info.type, 'color', d);
+ })
+ .attr("transform", function () {
+ return "rotate(-45)";
+
+ })
+ .attr("stroke-width", 2.4)
+
+ .attr("class", "node_path")
+ .attr("id", function (d) {
+ return "path_" + d.id;
+ })
+
+ .call(d3.drag()
+ .on("start", dragstarted)
+ .on("drag", dragged)
+ .on("end", dragended));
+
+ var figure_node = this.svg.selectAll('.node')
+ .data(self.d3_graph.nodes
+ .filter(this.node_filter_cb))
+
+ .filter(function (d) {
+ return self._node_property_by_type(d.info.type, 'image', d) != undefined
+ });
+
+ figure_node.append("svg:image")
+ .attr("xlink:href", function (d) {
+ return self._node_property_by_type(d.info.type, 'image', d)
+ })
+ .attr("x", function (d) {
+ return -self._node_property_by_type(d.info.type, 'size', d) / 2
+ })
+ .attr("y", function (d) {
+ return -self._node_property_by_type(d.info.type, 'size', d) / 2
+ })
+ .attr("width", function (d) {
+ return self._node_property_by_type(d.info.type, 'size', d)
+ })
+ .attr("height", function (d) {
+ return self._node_property_by_type(d.info.type, 'size', d)
+ })
+ .style("stroke", "black")
+ .style("stroke-width", "1px")
+
+ .attr("class", "node_path")
+ .attr("id", function (d) {
+ return "path_" + d.id;
+ })
+ .call(d3.drag()
+ .on("start", dragstarted)
+ .on("drag", dragged)
+ .on("end", dragended));
+
+ figure_node.append("svg:path")
+ .attr("d", d3.symbol()
+ .size(function (d) {
+ return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d) + 7, 2) / 4;
+ })
+ .type(function (d) {
+ return (self.get_d3_symbol('circle'));
+ })
+ )
+ .style("fill", 'transparent')
+ .attr("transform", function () {
+ return "rotate(-45)";
+
+ })
+ .attr("stroke-width", 2.4)
+
+ .attr("class", "hidden_circle")
+ .attr("id", function (d) {
+ return "path_" + d.id;
+ })
+
+ .call(d3.drag()
+ .on("start", dragstarted)
+ .on("drag", dragged)
+ .on("end", dragended));
+
+
+ this.node = this.svg.selectAll('.node')
+ .data(self.d3_graph.nodes
+ .filter(this.node_filter_cb)).selectAll("image, path, circle");
+
+
+ this.node.on("contextmenu", self.behavioursOnEvents.nodes["contextmenu"])
+ .on("mouseover", self.behavioursOnEvents.nodes["mouseover"])
+ .on("mouseout", self.behavioursOnEvents.nodes["mouseout"])
+ .on('click', self.behavioursOnEvents.nodes["click"])
+ .on('dblclick', self.behavioursOnEvents.nodes["dblclick"]);
+
+ this.link
+ .on("contextmenu", self.behavioursOnEvents.links["contextmenu"])
+ .on("mouseover", self.behavioursOnEvents.links["mouseover"])
+ .on('click', self.behavioursOnEvents.links["click"])
+ .on("mouseout", self.behavioursOnEvents.links["mouseout"]);
+
+
+ this.text = this.svg.selectAll(".node")
+ .data(self.d3_graph.nodes
+ .filter(this.node_filter_cb))
+ .append("svg:text")
+ .attr("class", "node_text cleanable")
+ .attr("dy", function (d) {
+ return "-5";
+ })
+ .attr("pointer-events", "none")
+ .style("font-size", nominal_text_size + "px")
+ .style("font-family", "'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif")
+ .style("fill", function (d) {
+ return self._node_property_by_type(d.info.type, 'node_label_color', d);
+ })
+ //.style("text-anchor", "middle")
+ .text(function (d) {
+ if(d.info && d.info.property.custom_label && d.info.property.custom_label !==''){
+ return d.info.property.custom_label
+ } else
+ return d.id;
+ });
+
+
+ function dragstarted(d) {
+ d.draggednode = true;
+ if (!d3.event.active) self.force.alphaTarget(0.3).restart();
+ d.fx = d.x;
+ d.fy = d.y;
+
+ }
+
+ function dragged(d) {
+ d.fx = d3.event.x;
+ d.fy = d3.event.y;
+ }
+
+ function dragended(d) {
+ d.draggednode = false;
+ if (!d3.event.active) self.force.alphaTarget(0);
+ if (self.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ self.force.stop();
+ self.forceSimulationActive = false;
+ }
+ }
+
+
+ };
+
+ /**
+ * Start force layout on Graph.
+ *
+ */
+ GraphEditor.prototype.startForce = function () {
+ this.force.stop();
+ var self = this;
+ this.force
+ .nodes(this.d3_graph.nodes)
+ .on("tick", ticked);
+
+
+ this.force
+ .force("link")
+ .links(this.d3_graph.links);
+
+ function ticked() {
+ self.node.attr("cx", function (d) {
+ return d.x = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.width - self._node_property_by_type(d.info.type, 'size', d), d.x));
+ })
+ .attr("cy", function (d) {
+ return d.y = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.height - self._node_property_by_type(d.info.type, 'size', d), d.y));
+ });
+
+ self.link.attr("d", function (d) {
+ var dx = d.target.x - d.source.x,
+ dy = d.target.y - d.source.y,
+ dr = Math.sqrt(dx * dx + dy * dy);
+ return "M" + d.source.x + "," + d.source.y + "," + d.target.x + "," + d.target.y;
+ });
+
+ self.node.attr("transform", function (d) {
+ return "translate(" + d.x + "," + d.y + ")";
+ });
+ self.text.attr("transform", function (d) {
+ var label_pos_y = d.y + self._node_property_by_type(d.info.type, 'size', d)/2 +nominal_text_size;
+ return "translate(" + d.x + "," + label_pos_y + ")";
+ });
+ }
+
+
+ };
+
+ /**
+ * This method attaches an event handler.
+ * @param {String} Required. A String that specifies the name of the event.
+ * @param {Function} Required. Specifies the function to run when the event occurs.
+ * @returns {}
+ */
+ GraphEditor.prototype.addListener = function (event_name, cb) {
+ this.eventHandler.addL(event_name, cb);
+ };
+
+ /**
+ * This method removes an event handler that has been attached with the addListener() method.
+ * @param {String} Required. A String that specifies the name of the event to remove.
+ * @param {Function} Required. Specifies the function to remove.
+ * @returns {}
+ */
+ GraphEditor.prototype.removeListener = function (event_name, cb) {
+
+ };
+
+
+ GraphEditor.prototype.setNodeClass = function (class_name, filter_cb) {
+ log("setNodeClass");
+ var self = this;
+ this.svg.selectAll('.node').classed(class_name, false);
+ this.svg.selectAll('.node')
+ .classed(class_name, filter_cb);
+ };
+
+ GraphEditor.prototype.setLinkClass = function (class_name, filter_cb) {
+ log("setLinkClass");
+ var self = this;
+ this.svg.selectAll('.link').classed(class_name, false);
+ this.svg.selectAll('.link')
+ .classed(class_name, filter_cb);
+ };
+
+ GraphEditor.prototype.showNodeInfo = function (args) {
+ this.addLinesToPopup(args['node_info'], "Info about node selected")
+ this.handlePopupVisibility(true, 'right')
+ };
+
+ GraphEditor.prototype.addLinesToPopup = function (data, title) {
+ var self = this;
+ var index = 1;
+ var translate_y = 0;
+ var width_popup = 400;
+ var height_popup = 0;
+
+ d3.selectAll(".popupcleanable").remove(); // clean
+
+ var popupbg = d3.select(".popup").append("rect")
+ .attr("id", "popupbg")
+ .attr("class", "popup bg popupcleanable cleanable")
+ .attr("width", "400")
+ .attr("height", "0")
+ .attr("rx", 10) // set the x corner curve radius
+ .attr("ry", 10); // set the y corner curve radius
+
+
+ d3.select(".popup").append("svg:path")
+ .attr("d", d3.symbol()
+ .size(function (d) {
+ return 80
+ })
+ .type(function (d) {
+ console.log("popup")
+ return (self.get_d3_symbol());
+ })
+ )
+ .style("fill", 'red')
+ .attr("transform", function () {
+ return "translate(380,15) rotate(-45)";
+
+ })
+ .attr("stroke-width", 2.4)
+ .attr("id", "close_popup")
+ .attr("class", "popupcleanable cleanable")
+ .on("click", function (d) {
+ self.handlePopupVisibility(false);
+ });
+
+ d3.select(".popup").append("text")
+ .attr("class", "popup title popupcleanable cleanable")
+ .attr("x", "10")
+ .attr("y", "20")
+ .text(title);
+
+ for (var i in data) {
+ //console.log(i, data, data[i])
+ //var typeofvalue = typeof data[i];
+ var record = data[i];
+ index = this._addRecordToPopup(i, record, index)
+
+ }
+
+ };
+
+ GraphEditor.prototype._addRecordToPopup = function (key, record, index, tab) {
+ //console.log("_addRecordToPopup", key, record, index)
+ var translate_y = 23 * index;
+ var summary = d3.select(".popup").append("g")
+ .attr("class", "popup summary d popupcleanable cleanable")
+ .attr("transform", "translate(10 " + translate_y + ")");
+ if (Object.prototype.toString.call(record) !== '[object Array]') { //is a record simple key:value
+ //console.log(key, record)
+ var summary_g = summary.append("g");
+ summary_g.append("rect")
+ .attr("class", "popup summary bg popupcleanable cleanable")
+ .attr("width", "380")
+ .attr("height", "20");
+
+ summary_g.append("text")
+ .attr("class", "popup summary popupcleanable cleanable")
+ .attr("x", (tab) ? tab : 10)
+ .attr("y", "17")
+ .attr("width", "100")
+ .text(function (d) {
+ return key.toUpperCase() + ":";
+ });
+
+ summary_g.append("text")
+ .attr("class", "popup summary popupcleanable cleanable")
+ .attr("x", "370")
+ .attr("y", "17")
+ .attr("text-anchor", "end")
+ .text(function (d) {
+ return record
+ });
+ }
+ else {//is a record simple complex: have a list of sub record key:value
+ //index ++;
+ this._addRecordToPopup(key, "", index)
+ for (var r in record) {
+ //console.log(i, r, record, record[r])
+ for (var k in record[r]) {
+ //console.log(i, r, k, record[r][k])
+ var curr_key = k;
+ var recordValue = record[r][k]
+
+ index++;
+ this._addRecordToPopup(curr_key, recordValue, index, 20)
+ }
+ }
+
+ }
+
+ translate_y = 30 * index++;
+ d3.select('#popupbg').attr("height", translate_y);
+ return index;
+ };
+
+
+ /**
+ * Remove all the graph objects from the view
+ */
+ GraphEditor.prototype.cleanAll = function () {
+ this.svg.selectAll('.cleanable').remove();
+ };
+
+ /**
+ * Internal functions
+ */
+
+ GraphEditor.prototype._node_property_by_type = function (type, property, node) {
+ //console.log(type, property, layer, group)
+ var unrecognized = function (ui_prop, property) {
+ return ui_prop['unrecognized'][property]
+ };
+
+ //type recognized
+ if (this.type_property[type]) {
+
+ if (this.type_property[type]['property']) {
+ var filt_property = this.type_property[type]['property']
+ return this.type_property[type][node.info[filt_property]][property]
+ } else { // type without property spec
+
+ return this.type_property[type][property]
+
+ }
+
+ } else { //type unrecognized
+ return unrecognized(this.type_property, property)
+ }
+
+ };
+
+ GraphEditor.prototype._link_property_by_type = function (type, property) {
+ //log(type + "-" + property)
+ if (this.type_property_link[type] != undefined && this.type_property_link[type][property] != undefined) {
+ //if(property == "shape")
+ // log("dentro" + this.type_property[type][property])
+ return this.type_property_link[type][property];
+ } else {
+ return this.type_property_link['unrecognized'][property];
+ }
+
+ }
+
+
+ /**
+ *
+ *
+ *
+ */
+ GraphEditor.prototype._setupFiltersBehaviors = function (args) {
+
+ var self = this;
+
+ this.node_filter_cb = args.node_filter_cb || function (d) {
+
+ var cond_view = true,
+ cond_group = true;
+ //log(d.info.type + " " + self.filter_parameters.node.type + " group: " + self.filter_parameters.node.group + "- " + d.info.group)
+ // check filter by node type
+ if (self.filter_parameters.node.type.length > 0) {
+
+ if (self.filter_parameters.node.type.indexOf(d.info.type) < 0)
+ cond_view = false;
+ }
+
+ // check filter by group
+ if (self.filter_parameters.node.group.length > 0) {
+ self.filter_parameters.node.group.forEach(function (group) {
+ if (d.info.group.indexOf(group) < 0)
+ cond_group = false;
+ });
+
+
+ }
+
+
+ return cond_view && cond_group;
+ };
+
+ this.link_filter_cb = args.link_filter_cb || function (d) {
+ var cond_view = true,
+ cond_group = true;
+
+ // check filter by view
+ if (self.filter_parameters.link.view.length > 0) {
+ self.filter_parameters.link.view.forEach(function (view) {
+ if (d.view.indexOf(view) < 0)
+ cond_view = false;
+ });
+ }
+
+ // check filter by group
+ if (self.filter_parameters.link.group.length > 0) {
+ self.filter_parameters.link.group.forEach(function (group) {
+ if (d.group.indexOf(group) < 0)
+ cond_group = false;
+ });
+ }
+ return cond_view && cond_group;
+ };
+
+ };
+
+ /**
+ *
+ *
+ */
+ GraphEditor.prototype._setupBehaviorsOnEvents = function () {
+ log("_setupBehaviorsOnEvents");
+ var self = this;
+ this.behavioursOnEvents = {
+ 'nodes': {
+ 'click': function (d) {
+ d3.event.preventDefault();
+ log('click', d);
+ if (self.lastKeyDown === SHIFT_BUTTON && self._selected_node !== undefined) {
+ var source_id = self._selected_node.id;
+ var target_id = d.id;
+ log("--" + JSON.stringify(self.filter_parameters.link.view));
+ var new_link = {
+ source: source_id,
+ target: target_id,
+ view: self.filter_parameters.link.view[0],
+ group: self.filter_parameters.link.group[0],
+ };
+ self.addLink(new_link);
+ self._deselectAllNodes();
+ } else {
+ self._selectNodeExclusive(this, d);
+ }
+
+ },
+ 'mouseover': function (d) {
+
+ },
+ 'mouseout': function (d) {
+ },
+ 'dblclick': function (d) {
+ d3.event.preventDefault();
+ log('dblclick');
+ },
+ 'contextmenu': function (d, i) {
+ d3.event.preventDefault();
+ log("contextmenu node");
+ self.eventHandler.fire("right_click_node", d);
+ }
+ },
+ 'links': {
+ 'click': function (event) {
+
+ },
+ 'dblclick': function (event) {
+
+ }
+ }
+ };
+ };
+
+ /**
+ * Deselect previously selected nodes
+ *
+ */
+ GraphEditor.prototype._deselectAllNodes = function () {
+ log("_deselectAllNodes");
+ this.node.classed("node_selected", false);
+ this._selected_node = undefined;
+ };
+
+ GraphEditor.prototype._deselectAllLinks = function () {
+ log("_deselectAllLinks");
+ this.link.classed("link_selected", false).style('stroke-width', 2);
+ this._selected_link = undefined;
+ };
+ /**
+ * Select node in exclusive mode
+ * @param {Object} Required. Element selected on click event
+ */
+ GraphEditor.prototype._selectNodeExclusive = function (node_instance, node_id) {
+ log("_selectNodeExclusive ");
+ var activeClass = "node_selected";
+ var alreadyIsActive = d3.select(node_instance).classed(activeClass);
+ this._deselectAllNodes();
+ this._deselectAllLinks();
+ d3.select(node_instance).classed(activeClass, !alreadyIsActive);
+ this._selected_node = (alreadyIsActive) ? undefined : node_instance.__data__;
+ if(this._selected_node){
+ this.eventHandler.fire("node:selected", this._selected_node)
+ } else {
+ this.eventHandler.fire("node:deselected", this._selected_node)
+ }
+ };
+
+ /**
+ * Select node in exclusive mode
+ * @param {Object} Required. Element selected on click event
+ */
+ GraphEditor.prototype._selectLinkExclusive = function (link_instance, link_id) {
+ log("_selectLinkExclusive ");
+ var activeClass = "link_selected";
+ var alreadyIsActive = d3.select(link_instance).classed(activeClass);
+ this._deselectAllNodes();
+ this._deselectAllLinks();
+ d3.select(link_instance).classed(activeClass, !alreadyIsActive);
+ d3.select(link_instance).style('stroke-width', 4)
+ this._selected_link = link_instance.__data__;
+ };
+
+ /**
+ * Callback to resize SVG element on window resize
+ */
+ GraphEditor.prototype.resizeSvg = function (width, height) {
+ log("resizeSvg");
+ //log(event);
+ this.width = width || this.width;
+ this.height = height || this.height;
+ this.svg.attr('width', width);
+ this.svg.attr('height', height);
+
+ }
+
+ GraphEditor.prototype.handlePopupVisibility = function (visible, side) {
+ var opacity = (visible) ? 1 : 0;
+
+ var translate_op = (side === "left") ? "translate(50 50)" : "translate(" + (this.width - 450).toString() + " 50)";
+
+ if (!visible) {
+ d3.selectAll(".popupcleanable").remove();
+ d3.select(".popup")
+ .attr("transform", "translate(-1 -1)");
+ } else {
+ d3.select(".popup")
+ .attr("transform", translate_op);
+ }
+ d3.select(".popup").attr("opacity", opacity);
+ };
+
+ GraphEditor.prototype.refreshGraphParameters = function (graphParameters) {
+ this.eventHandler.fire("refresh_graph_parameters", graphParameters);
+ };
+
+ /**
+ * Log utility
+ */
+ function log(text) {
+ if (DEBUG)
+ console.log("::GraphEditor::", text);
+ }
+
+
+ return GraphEditor;
+
+
+}(this));
+
+if (typeof module === 'object') {
+ module.exports = TCD3.GraphEditor;
+}
--- /dev/null
+/*
+ Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+ Copyright 2018 EveryUP srl
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+if (typeof TCD3 === 'undefined') {
+ var TCD3 = {};
+}
+
+TCD3.ModelGraphEditor = (function () {
+ 'use strict';
+
+ var DEBUG = true;
+ var SHIFT_BUTTON = 16;
+ var IMAGE_PATH = "/static/assets/img/";
+
+
+ ModelGraphEditor.prototype = new TCD3.GraphEditor();
+ ModelGraphEditor.prototype.constructor = ModelGraphEditor;
+ ModelGraphEditor.prototype.parent = TCD3.GraphEditor.prototype;
+
+ /**
+ * Constructor
+ */
+ function ModelGraphEditor(args) {
+
+ log("Constructor");
+
+ }
+
+
+ ModelGraphEditor.prototype.init = function (args) {
+ this.parent.init.call(this, args);
+ var self = this;
+ this.desc_id = args.desc_id || undefined; //TODO remove it
+
+ this.type_property = {};
+ this.type_property["unrecognized"] = args.gui_properties["nodes"]["default"];
+
+ this._edit_mode = args.edit_mode || false;
+
+ Object.keys(args.gui_properties["nodes"]).forEach(function (key, index) {
+
+ this.type_property[key] = args.gui_properties["nodes"][key];
+
+ this.type_property[key]["shape"] = this.parent.get_d3_symbol(this.type_property[key]["shape"]);
+ if (this.type_property[key]["image"] !== undefined) {
+ this.type_property[key]["image"] = IMAGE_PATH + this.type_property[key]["image"];
+ }
+
+ }, this);
+
+ if (args.gui_properties["edges"]) {
+ this.type_property_link = args.gui_properties["edges"];
+ var link_types = ['unrecognized'].concat(Object.keys(self.type_property_link))
+ this.defs.selectAll("marker")
+ .data(link_types)
+ .enter()
+ .append("svg:marker") // This section adds in the arrows
+ .attr("id", function (d) {
+ return d;
+ })
+ .attr("viewBox", "-5 -5 10 10")
+ .attr("refX", 13) /*must be smarter way to calculate shift*/
+ .attr("refY", 0)
+ .attr("markerUnits", "userSpaceOnUse")
+ .attr("markerWidth", 12)
+ .attr("markerHeight", 12)
+ .attr("orient", "auto")
+ .append("path")
+ .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
+ .attr('fill', function (d) {
+ return self.type_property_link[d].color;
+ });
+ }
+
+ this.customBehavioursOnEvents = args.behaviorsOnEvents || undefined;
+
+
+ var data_url = args.data_url || undefined;
+ if (!args.graph_data && args.data_url) {
+ d3.json(data_url, function (error, data) {
+ //console.log(JSON.stringify(data))
+ self.d3_graph.nodes = data.vertices;
+ self.d3_graph.links = data.edges;
+ self.d3_graph.graph_parameters = data.graph_parameters;
+ self.model = data.model;
+ self.refreshGraphParameters(self.d3_graph.graph_parameters);
+ self.refresh();
+ self.startForce();
+ //if(args.filter_base != undefined)
+
+ setTimeout(function () {
+ self.handleForce(true);
+ self.handleFiltersParams(args.filter_base);
+ }, 500);
+
+ });
+ } else {
+ this.updateData(args)
+ }
+ }
+
+ /**
+ * Update data of the graph.
+ * @param {Object} Required. An object that specifies tha data of the new node.
+ * @returns {boolean}
+ */
+ ModelGraphEditor.prototype.updateData = function (args) {
+ console.log("updateData")
+ this.d3_graph.nodes = args.graph_data.vertices;
+ this.d3_graph.links = args.graph_data.edges;
+ this.d3_graph.graph_parameters = args.graph_parameters;
+ this.model = args.model;
+ this.refreshGraphParameters(this.d3_graph.graph_parameters);
+ this.refresh();
+ this.startForce();
+ //if(args.filter_base != undefined)
+
+ //if(args.filter_base){
+ var self = this;
+ setTimeout(function () {
+ self.handleForce(true);
+ self.handleFiltersParams(args.filter_base);
+ }, 500);
+ //}
+ }
+
+ /**
+ * Add a new node to the graph.
+ * @param {Object} Required. An object that specifies tha data of the new node.
+ * @returns {boolean}
+ */
+ ModelGraphEditor.prototype.addNode = function (node, success, error) {
+ var self = this;
+ var current_layer = self.getCurrentView();
+ var node_type = node.info.type;
+
+ if (self.model.layer[current_layer] && self.model.layer[current_layer].nodes[node_type] && self.model.layer[current_layer].nodes[node_type].addable) {
+ if (self.model.layer[current_layer].nodes[node_type].addable.callback) {
+ var c = self.model.callback[self.model.layer[current_layer].nodes[node_type].addable.callback].class;
+ var controller = new TCD3[c]();
+ controller[self.model.layer[current_layer].nodes[node_type].addable.callback](self, node, function () {
+ self.parent.addNode.call(self, node);
+ success && success();
+ }, error);
+
+ } else {
+
+ log('addNode: callback undefined in model spec.');
+ error && error("You can't add a " + node.info.type + ", callback undefined.");
+ }
+ } else {
+ //FIXME Error handling????
+ log("You can't add a " + node.info.type + " in a current layer " + current_layer);
+ error && error("You can't add a " + node.info.type + " in a current layer " + current_layer);
+ }
+ };
+
+
+ /**
+ * Update the data properties of the node
+ * @param {Object} Required. An object that specifies tha data of the node.
+ * @returns {boolean}
+ */
+ ModelGraphEditor.prototype.updateDataNode = function (args) {
+ //FIXME updating a node properties need commit to server side!
+ this.parent.updateDataNode.call(this, args);
+ };
+
+ /**
+ * Remove a node from graph and related links.
+ * @param {String} Required. Id of node to remove.
+ * @returns {boolean}
+ */
+ ModelGraphEditor.prototype.removeNode = function (node, success, error) {
+ console.log('removeNode', JSON.stringify(node))
+ var self = this;
+ var current_layer = self.getCurrentView();
+ var node_type = node.info.type;
+ if (node.info.desc_id == undefined) {
+ node.info.desc_id = self.desc_id;
+ }
+ if (self.model.layer[current_layer] && self.model.layer[current_layer].nodes[node_type] && self.model.layer[current_layer].nodes[node_type].removable) {
+ if (self.model.layer[current_layer].nodes[node_type].removable.callback) {
+ var c = self.model.callback[self.model.layer[current_layer].nodes[node_type].removable.callback].class;
+ var controller = new TCD3[c]();
+ controller[self.model.layer[current_layer].nodes[node_type].removable.callback](self, node, function () {
+ self.parent.removeNode.call(self, node);
+ success && success();
+ }, error);
+ } else {
+
+ log('removeNode: callback undefined in model spec.');
+ error && error("You can't remove a " + node.info.type + ", callback undefined.");
+ }
+ } else {
+ //FIXME we need to manage alert in a different way: FAILBACK
+ log("You can't remove a " + node.info.type);
+ error && error("You can't remove a " + node.info.type);
+ }
+ };
+
+ /**
+ * Add a new link to graph.
+ * @param {Object} Required. An object that specifies tha data of the new Link.
+ * @returns {boolean}
+ */
+ ModelGraphEditor.prototype.addLink = function (s, d, success, error) {
+ var self = this;
+ var source_id = s.id;
+ var target_id = d.id;
+ var source_type = s.info.type;
+ var destination_type = d.info.type;
+ var link = {
+ source: s,
+ target: d,
+ view: this.filter_parameters.link.view[0],
+ group: this.filter_parameters.link.group,
+ desc_id: this.desc_id
+ };
+ log("addLink: " + JSON.stringify(link))
+ var current_layer = self.getCurrentView()
+ if (self.model.layer[current_layer].allowed_edges && self.model.layer[current_layer].allowed_edges[source_type] && self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type]) {
+
+ if (self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].callback) {
+ var callback = self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].callback;
+ console.log(callback, self.model.callback)
+ var direct_edge = 'direct_edge' in self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type] ? self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type]['direct_edge'] : false;
+ link.directed_edge = direct_edge;
+ var c = self.model.callback[callback].class;
+ var controller = new TCD3[c]();
+ controller[callback](self, link, function () {
+ self._deselectAllNodes();
+ self.parent.addLink.call(self, link);
+ if (success)
+ success();
+ }, error);
+ } else {
+ log('addLink: callback undefined in model spec.');
+ error && error("You can't add a link, callback undefined.");
+ }
+
+ } else {
+ //FIXME we need to manage alert in a different way: FAILBACK
+ log("You can't link a " + source_type + " with a " + destination_type);
+
+ error && error("You can't link a " + source_type + " with a " + destination_type);
+ }
+ };
+
+ /**
+ * Remove a link from graph.
+ * @param {String} Required. The identifier of link to remove.
+ * @returns {boolean}
+ */
+ ModelGraphEditor.prototype.removeLink = function (link, success, error) {
+ var self = this;
+ var s = link.source;
+ var d = link.target;
+ var source_type = s.info.type;
+ var destination_type = d.info.type;
+ var current_layer = self.getCurrentView()
+ if (self.model.layer[current_layer].allowed_edges && self.model.layer[current_layer].allowed_edges[source_type] && self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type] &&
+ self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].removable
+ ) {
+ if (self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].removable.callback) {
+ var callback = self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].removable.callback;
+ var c = self.model.callback[callback].class;
+ var controller = new TCD3[c]();
+ controller[callback](self, link, function () {
+ self._deselectAllNodes();
+ self._deselectAllLinks();
+ self.parent.removeLink.call(self, link.index);
+ success && success();
+ }, error);
+ } else {
+ log('removeLink: callback undefined in model spec.');
+ error && error("You can't remove a link, callback undefined.");
+ }
+
+ } else {
+ //FIXME we need to manage alert in a different way: FAILBACK
+ log("You can't delete the link");
+ error && error("You can't delete the link");
+ }
+
+
+ };
+
+
+ ModelGraphEditor.prototype.savePositions = function (data) {
+ var vertices = {};
+ this.node.each(function (d) {
+ vertices[d.id] = {};
+ vertices[d.id]['x'] = d.x;
+ vertices[d.id]['y'] = d.y;
+ });
+ new TCD3.GraphRequests().savePositions({
+ 'vertices': vertices
+ });
+
+ };
+
+ /**
+ * Internal functions
+ */
+
+ ModelGraphEditor.prototype._setupBehaviorsOnEvents = function (layer) {
+
+ var self = this;
+ var contextMenuLinksAction = [{
+ title: 'Delete Link',
+ action: function (elm, link, i) {
+ self.removeLink(link, null, showAlert);
+ },
+ edit_mode: true
+ }];
+ var contextMenuNodesAction = [{
+ title: 'Edit',
+ action: function (elm, d, i) {
+ if (d.info.type != undefined) {
+ self.eventHandler.fire("edit_descriptor", self.project_id, d);
+ }
+ },
+ nodes: [],
+ edit_mode: true
+ },
+ {
+ title: 'Delete',
+ action: function (elm, d, i) {
+ self.removeNode(d, null, showAlert);
+ },
+ edit_mode: true
+ }
+
+ ];
+ if (this.customBehavioursOnEvents) {
+ contextMenuNodesAction = contextMenuNodesAction.concat(this.customBehavioursOnEvents['behaviors'].nodes);
+ }
+
+
+ if (self.model && self.model.layer && self.model.layer[layer] && self.model.layer[layer].action && self.model.layer[layer].action.node) {
+ for (var i in self.model.layer[layer].action.node) {
+ var action = self.model.layer[layer].action.node[i]
+ contextMenuNodesAction.push({
+ title: action.title,
+ action: function (elm, d, i) {
+ var callback = action.callback;
+ var c = self.model.callback[callback].class;
+ var controller = new TCD3[c]();
+ var args = {
+ elm: elm,
+ d: d,
+ i: i
+ };
+
+ controller[callback](self, args);
+ },
+ edit_mode: (action.edit_mode !== undefined) ? action.edit_mode : undefined
+ });
+ }
+ }
+
+ this.behavioursOnEvents = {
+ 'nodes': {
+ 'click': function (d) {
+
+ d3.event.preventDefault();
+
+ if (self._edit_mode && self.lastKeyDown === SHIFT_BUTTON && self._selected_node !== undefined) {
+ self.addLink(self._selected_node, d, null, showAlert);
+ } else {
+ self._selectNodeExclusive(this, d);
+ }
+
+ },
+ 'mouseover': function (d) {
+ self.link.style('stroke-width', function (l) {
+ if (d === l.source || d === l.target)
+ return 4;
+ else
+ return 2;
+ });
+ },
+ 'mouseout': function (d) {
+ self.link.style('stroke-width', 2);
+ },
+ 'contextmenu': d3.contextMenu(contextMenuNodesAction, {
+ 'edit_mode': self._edit_mode,
+ 'layer': layer,
+ 'type_object': 'node'
+ })
+ },
+ 'links': {
+ 'click': function (d) {
+ self._selectLinkExclusive(this, d);
+
+ },
+ 'dblclick': function (event) {
+
+ },
+ 'mouseover': function (d) {
+ d3.select(this).style('stroke-width', 4);
+ },
+ 'mouseout': function (d) {
+ if (d !== self._selected_link)
+ d3.select(this).style('stroke-width', 2);
+ },
+ 'contextmenu': d3.contextMenu(contextMenuLinksAction, {
+ 'edit_mode': self._edit_mode,
+ 'layer': layer,
+ 'type_object': 'link'
+ })
+ }
+ }
+ };
+
+ ModelGraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
+
+ this.parent.handleFiltersParams.call(this, filtersParams, notFireEvent);
+ if (filtersParams && filtersParams.link && filtersParams.link.view)
+ this._setupBehaviorsOnEvents(filtersParams.link.view[0]);
+ };
+
+ ModelGraphEditor.prototype.getAvailableNodes = function () {
+ log('getAvailableNodes');
+ log(this.model);
+ if (this.model && this.model.layer[this.getCurrentView()] !== undefined)
+ return this.model.layer[this.getCurrentView()].nodes;
+ return [];
+ };
+
+
+ ModelGraphEditor.prototype.getTypeProperty = function () {
+ return this.type_property;
+ };
+
+ ModelGraphEditor.prototype.getCurrentGroup = function () {
+ return this.filter_parameters.node.group[0];
+
+ };
+
+ ModelGraphEditor.prototype.getCurrentView = function () {
+ return this.filter_parameters.link.view[0];
+ };
+ ModelGraphEditor.prototype.getCurrentFilters = function () {
+ return this.filter_parameters;
+ };
+
+ ModelGraphEditor.prototype.getGraphParams = function () {
+ return this.d3_graph.graph_parameters;
+ };
+
+ /**
+ * Log utility
+ */
+ function log(text) {
+ if (DEBUG)
+ console.log("::ModelGraphEditor::", text);
+ }
+
+
+ return ModelGraphEditor;
+
+
+}(this));
+
+if (typeof module === 'object') {
+ module.exports = TCD3.ModelGraphEditor;
+}
--- /dev/null
+/*
+ Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+ Copyright 2018 EveryUP srl
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+if (typeof TCD3 === 'undefined') {
+ var TCD3 = {};
+}
+
+
+TCD3.settings = (function () {
+ 'use strict';
+
+
+ return Event;
+}());
+
+if (typeof module === 'object') {
+ module.exports = TCD3.Event;
+}
$formGroup.remove();
};
+function showTopology(type, instance_id) {
+ var url = '/instances/'+type+'/'+instance_id+'/topology';
+ window.location = url;
+}
+
function showInstanceDetails(type, instance_id) {
var url_info = '/instances/'+type+'/'+instance_id;
var dialog = bootbox.dialog({
//GraphEditor instance
-var graph_editor = new dreamer.ModelGraphEditor();
-var selected_vnffgId = null;
-var show_all = null;
-
-// Enable Drop Action on the Graph
-initDropOnGraph();
+var graph_editor = new TCD3.ModelGraphEditor();
var type_view = {
- "ns": ["vnf", "ns_vl"],
- "vnf": ["vdu", "cp"]
+ "nsr": ["vnfr", "ns_vl"],
+ "vnfr": ["vdur", "cp", "vnf_vl"]
};
var params = {
- node: {
- type: type_view['ns'],
- group: []
- },
- link: {
- group: [],
- view: ['ns']
- }
- };
-$(document).ready(function() {
+ node: {
+ type: type_view['nsr'],
+ group: []
+ },
+ link: {
+ group: [],
+ view: ['nsr']
+ }
+};
+
+$(document).ready(function () {
graph_editor.addListener("filters_changed", changeFilter);
- graph_editor.addListener("refresh_graph_parameters", refreshGraphParameters);
+ graph_editor.addListener("node:selected", refreshElementInfo);
+ graph_editor.addListener("node:deselected", refreshElementInfo);
- console.log(osm_gui_properties)
// graph_editor initialization
graph_editor.init({
- width: $('#graph_ed_container').width(),
- height: $('#graph_ed_container').height(),
+ width: $('#graph_editor_container').width(),
+ height: $('#graph_editor_container').height(),
data_url: window.location.href,
- desc_id: getUrlParameter('id'),
+ //desc_id: getUrlParameter('id'),
gui_properties: osm_gui_properties,
edit_mode: false,
- behaviorsOnEvents:{
+ behaviorsOnEvents: {
viewBased: false,
behaviors: buildBehaviorsOnEvents()
}
});
- // this will filter in the different views, excluding the node types that are not listed in params
graph_editor.handleFiltersParams(params);
});
-var filters = function(e, params) {
- graph_editor.handleFiltersParams(params);
- $('#' + e).nextAll('li').remove();
-}
-
-function initDropOnGraph() {
-
- var dropZone = document.getElementById('graph_ed_container');
- dropZone.ondrop = function(e) {
- var group = graph_editor.getCurrentGroup()
- e.preventDefault();
- var elemet_id = e.dataTransfer.getData("text/plain");
- var nodetype = $('#'+elemet_id).attr('type-name');
- console.log(nodetype);
- if (nodetype) {
- var type_name = graph_editor.getTypeProperty()[nodetype].name;
- $('#div_chose_id').show();
- $('#input_choose_node_id').val(nodetype + "_" + generateUID());
- $('#modal_chooser_title_add_node').text('Add ' + type_name);
- $('#save_choose_node_id').off('click').on('click', function() {
- var name = $('#input_choose_node_id').val();
- var node_information = {
- 'id': name,
- 'info': {
- 'type': nodetype,
- 'group': [group],
- 'desc_id': getUrlParameter('id'),
- },
- 'x': e.layerX,
- 'y': e.layerY
- }
- console.log(JSON.stringify(node_information))
- graph_editor.addNode(node_information, function() {
- $('#modal_choose_node_id').modal('hide');
- }, function(error){
- showAlert(error)
- });
- });
- $('#modal_choose_node_id').modal('show');
-
-
- }
-
- }
-
- dropZone.ondragover = function(ev) {
- console.log("ondragover");
- return false;
- }
-
- dropZone.ondragleave = function() {
- console.log("ondragleave");
- return false;
- }
-}
function handleForce(el) {
- if (el.id == "topology_play") {
- $("#topology_pause").removeClass('active');
- $("#topology_play").addClass('active');
- } else {
- $("#topology_pause").addClass('active');
- $("#topology_play").removeClass('active');
- }
-
- graph_editor.handleForce((el.id == "topology_play") ? true : false);
-
+ graph_editor.handleForce((el.getAttribute('aria-pressed') === "true"));
}
function changeFilter(e, c) {
-
- console.log("changeFilter", JSON.stringify(c));
- //$("#title_header").text("OSHI Graph Editor");
- //updateNodeDraggable({type_property: type_property, nodes_layer: graph_editor.getAvailableNodes()})
- if(c)
- new dreamer.GraphRequests().getAvailableNodes({layer: c.link.view[0]}, buildPalette, showAlert);
-
-}
-
-function refreshGraphParameters(e, graphParameters) {
- var self = $(this);
- if (graphParameters == null) return;
-
+ if (c && c.link && c.link.view[0]) {
+ updateLegend(c.link.view[0]);
+ }
+ layerDetails(graph_editor.getCurrentFilters())
}
-function resetFilters(){
+function resetFilters() {
graph_editor.handleFiltersParams(params);
}
function buildBehaviorsOnEvents() {
var self = this;
- var contextmenuNodesAction = [{
- title: 'Show info',
- action: function (elm, d, i) {
- // console.log('Show NodeInfo', elm, d, i);
- var nodeData = {
- "node": {
- "id": d.id
- }
- };
- },
- edit_mode: false
-
- },
+ var contextmenuNodesAction = [
{
title: 'Explore',
action: function (elm, c_node, i) {
- if (c_node.info.type != undefined) {
+ if (c_node.info.type !== undefined) {
var current_layer_nodes = Object.keys(graph_editor.model.layer[graph_editor.getCurrentView()].nodes);
if (current_layer_nodes.indexOf(c_node.info.type) >= 0) {
if (graph_editor.model.layer[graph_editor.getCurrentView()].nodes[c_node.info.type].expands) {
},
edit_mode: false
}];
- var behavioursOnEvents = {
- 'nodes': contextmenuNodesAction
+ return {
+ 'nodes': contextmenuNodesAction
};
- return behavioursOnEvents;
+}
+
+function refreshElementInfo(event, element) {
+ if (event.type === 'node:selected') {
+ switch (element.info.type) {
+ case 'vnfr':
+ vnfrDetails(element.info.osm);
+ break;
+ case 'vdur':
+ vdurDetails(element.info.osm);
+ break;
+ case 'int_cp':
+ case 'cp':
+ cpDetails(element.info.osm);
+ break;
+ case 'vnf_vl':
+ case 'ns_vl':
+ vlDetails(element.info.osm);
+ break;
+ }
+ }
+ else if (event.type === 'node:deselected') {
+ layerDetails(graph_editor.getCurrentFilters())
+ }
+}
+
+function layerDetails(filters) {
+ var side = $('#side');
+ var graph_parameters = graph_editor.getGraphParams();
+ var layer_template = '';
+ if(graph_parameters['view'] && filters.link.view.length >0 && filters.link.view[0]){
+ if(filters.link.view[0] === 'nsr') {
+ layer_template = getMainSection('NS View');
+ layer_template += getChildrenTable(graph_parameters['view']['nsr']);
+ }
+ else if(filters.link.view[0] === 'vnfr') {
+ layer_template = getMainSection('VNF View');
+ var vnfr_id = filters.link.group[0];
+ layer_template += getChildrenTable(graph_parameters['view']['vnfr'][vnfr_id]);
+ }
+ }
+
+ side.empty();
+ side.append(layer_template)
+}
+
+function updateLegend(view) {
+ var legend = $('#legenda');
+ var nodes = type_view[view];
+ var legend_template = '';
+ var nodes_properties = osm_gui_properties['nodes'];
+ for (var n in nodes){
+ var node = nodes[n];
+ if(nodes_properties[node]){
+ legend_template += '<div class="node">' +
+ '<div class="icon" style="background-color:' + nodes_properties[node].color +'"></div>' +
+ '<div class="name">' +nodes_properties[node].name + '</div></div>';
+ }
+ }
+
+ legend.empty();
+ legend.append(legend_template)
+
+}
+
+var map = {
+ 'ip-address': 'IP', 'vnfd-id': 'Vnfd Id', 'vnfd-ref': 'Vnfd Ref', 'vim-account-id': 'Vim Id',
+ 'member-vnf-index-ref': 'Member index', 'created-time': 'Created', 'id': 'Id', 'mgmt-network': 'Mgmt network',
+ 'name': 'Name', 'type': 'Type', 'vim-network-name': 'Vim network name', 'connection-point-id': 'Cp Id',
+ 'vdu-id-ref': 'Vdu Id', 'nsr-id-ref': 'Nsr Id'
+};
+
+function vnfrDetails(vnfr) {
+ var side = $('#side');
+ var vnfr_template = getMainSection('VNFR');
+
+ vnfr_template += getChildrenTable(vnfr);
+ side.empty();
+ side.append(vnfr_template)
+}
+
+function vdurDetails(vdur) {
+ var side = $('#side');
+ var vdur_template = getMainSectionWithStatus('VDUR', vdur['status'] === 'ACTIVE');
+ vdur_template += getChildrenTable(vdur);
+
+ if (vdur['interfaces'].length > 0) {
+ vdur_template += getSubSection('Interfaces:');
+ vdur_template += '<table class="children">';
+
+ for (var i = 0; i < vdur['interfaces'].length; ++i) {
+ var interface = vdur['interfaces'][i];
+ var interface_template = '<tr><td>' + interface['name'] + '</td>'
+ + '<td>IP:' + interface['ip-address'] + '</td>'
+ + '<td>MAC:' + interface['mac-address'] + '</td>';
+ vdur_template += interface_template;
+ }
+ vdur_template += '</table>';
+ }
+
+ side.empty();
+ side.append(vdur_template)
+}
+
+function cpDetails(cp) {
+ var side = $('#side');
+ var cp_template = getMainSection('Connection Point');
+
+ cp_template += getChildrenTable(cp);
+ side.empty();
+ side.append(cp_template);
+}
+
+function vlDetails(vl) {
+ var side = $('#side');
+ var vl_template = getMainSection('Virtual Link');
+
+ vl_template += getChildrenTable(vl);
+ side.empty();
+ side.append(vl_template);
+}
+
+
+function getMainSection(title) {
+ return '<div class="section"><span style="font-weight: 500;">' + title + '</span></div>';
+}
+
+function getSubSection(title) {
+ return '<div class="section"><span>' + title + '</span></div>';
+}
+
+function getMainSectionWithStatus(title, status) {
+ var template = '<div class="section"><span style="font-weight: 500;">' + title + '</span>';
+ if (status)
+ template += '<div class="status active"><span class="indicator"></span> ACTIVE</div>';
+ else
+ template += '<div class="status"><span class="indicator"></span>NO ACTIVE</div>';
+ template += '</div>';
+ return template;
+}
+
+function getChildrenTable(data) {
+ var template = '<table class="children">';
+
+ for (var key in data) {
+ if (typeof data[key] === 'string') {
+ var key_map = (map[key]) ? map[key] : key;
+ template += '<tr><td>' + key_map + '</td><td>' + data[key] + '</td></tr>';
+ }
+ }
+ template += '</table>';
+ return template;
}
\ No newline at end of file
var osm_gui_properties = {
- "v1": {
- "default": {
- "shape": "cross",
- "color": "#42f44e",
- "label_color": "black",
- "size": 15
- },
"nodes": {
- "ns_vl": {
- "image": "vl-80.png",
- // "shape": "triangle",
- "color": "#196B90",
- "size": 20,
- "name": "VL"
- },
- "cp": {
- "image": "cp-80.png",
- // "shape": "circle",
- "color": "#F27220",
- "size": 20,
- "name": "CP"
+ "default": {
+ "shape": "square",
+ "color": "#36c2f3",
+ "label_color": "black",
+ "default_node_label_color": "#000",
+ "size": 15
},
+
"vnf": {
- "image": "vnf-100.png",
- // "shape": "square",
+ //"image": "vnf-100.png",
+ "shape": "square",
"color": "#54A698",
"size": 35,
+ "width": 40,
+ "height": 20,
"name": "VNF"
},
- "vnf_vl": {
- "shape": "triangle",
- //"color": "#5FC9DB",
- "color": "#196B90",
- "size": 11,
- "name": "IntVL"
- },
-
"vdu": {
"shape": "square",
//"color": "#50A7CC",
"color": "#54A698",
"size": 30,
+ "width": 40,
+ "height": 20,
"name": "VDU"
- }
+ },
+ "cp": {
+ //"image": "cp-80.png",
+ "shape": "square",
+ "color": "#c6b63f",
+ "size": 40,
+ "name": "CP"
+ },
+ "int_cp": {
+ //"image": "cp-80.png",
+ "shape": "square",
+ "color": "#e3bfad",
+ "size": 40,
+ "name": "CP"
+ },
+ "ns_vl": {
+ "shape": "triangle",
+ "color": "#5ba56e",
+ "size": 30,
+ "name": "VL"
+ },
+ "vnf_vl": {
+ "shape": "triangle",
+ "color": "#f8a800",
+ "size": 30,
+ "name": "IntVL"
+ },
+ "vdur": {
+ "shape": "square",
+ "color": "#cf1c24",
+ "size": 60,
+ "name": "VDUR"
+ },
+ "vnfr": {
+ "shape": "square",
+ "color": "#605ca8",
+ "size": 60,
+ "name": "VNFR"
+ },
+
},
"graphs": null
-
- }
-}
\ No newline at end of file
+ };
\ No newline at end of file
function buildPalette(args) {
$("#paletteContainer").empty();
var type_property = graph_editor.getTypeProperty();
+
if (args.length > 0) {
- args.forEach(function (category) {
+ $('#paletteContainer').append('<div id="palette-header">Legenda</div>');
+ args.forEach(function (category) {
var category_id = "category_" + category.category_name.replace(/[\s.*+?^${}()\/|[\]\\]/g, "_");//.replace(/\s/g, '');
var content_id = "palette-content-" + category.category_name.replace(/[\s.*+?^${}()\/|[\]\\]/g, "_");//.replace(/\s/g, '');
- $("#paletteContainer").append('<div id="' + category_id + '" class="palette-category" ><div class="palette-header" onClick="handlePaletteCat(this);" category_id="' + category_id + '"> ' +
- '<i class="fa fa-chevron-down "></i>' +
- '<span> ' + category.category_name + '</span>' +
- '</div>' +
+ $("#paletteContainer").append('<div id="' + category_id + '" class="palette-category" >' +
'<div id="' + content_id + '" class="palette-content">' +
-
'</div>' +
'</div>');
+
category.types.forEach(function (type) {
console.log(graph_editor.get_name_from_d3_symbol(d3.symbolCircle))
var type_id = type.id.replace(/[\s.*+?^${}()|[\]\\]/g, "_");
'<div class="palette-node-icon-container">' +
palette_node_icon +
'</div>' +
- '</div>'
+ '</div>';
$("#" + content_id).append(html_to_append);
});
}
togglePaletteSpinner(true);
-
}
function handlePaletteCat(item) {
console.log("handlePaletteContainer")
- var category_id = $(item).attr("category_id")
+ var category_id = $(item).attr("category_id");
$('#' + category_id).toggleClass("palette-close");
}
function showAlert(msg) {
// modal_alert_text
- var alert_msg = ""
- if (typeof msg == "string")
- alert_msg = msg
+ var alert_msg = "";
+ if (typeof msg === "string")
+ alert_msg = msg;
else
- alert_msg = JSON.stringify(msg)
+ alert_msg = JSON.stringify(msg);
$('#modal_alert_text').text(alert_msg);
$('#modal_alert').modal('show');
}
switch (mySymbol) {
case d3.symbolCircle:
return '<div class="palette-node-icon"> <div class="palette-node-circle" style="background:' + args.color + ';"></div></div>';
- break;
case d3.symbolSquare:
return '<div class="palette-node-icon"> <div class="palette-node-square" style="background:' + args.color + ';"></div></div>';
- break;
case d3.symbolDiamond:
return '<div class="palette-node-icon" style="background-color:' + args.color + '"></div>';
- ;
- break;
+
case d3.symbolTriangle:
return '<div class="palette-node-icon"> <div class="palette-node-triangle" style="border-color: transparent transparent ' + args.color + ' transparent;"></div></div>';
- break;
case d3.symbolStar:
return '<div class="palette-node-icon" style="background-color:' + args.color + '"></div>';
- ;
- break;
case d3.symbolCross:
return '<div class="palette-node-icon" style="background-color:' + args.color + '"></div>';
- ;
- break;
default:
// if the string is not recognized
return "unknown";
String.format = function (format) {
var args = Array.prototype.slice.call(arguments, 1);
return format.replace(/{(\d+)}/g, function (match, number) {
- return typeof args[number] != 'undefined' ?
+ return typeof args[number] !== 'undefined' ?
args[number] :
match;
});
+++ /dev/null
-.d3-context-menu {
- position: absolute;
- display: none;
- background-color: #f2f2f2;
- border-radius: 4px;
-
- font-family: Arial, sans-serif;
- font-size: 14px;
- min-width: 150px;
- border: 1px solid #d4d4d4;
-
- z-index:1200;
-}
-
-.d3-context-menu ul {
- list-style-type: none;
- margin: 4px 0px;
- padding: 0px;
- cursor: default;
-}
-
-.d3-context-menu ul li {
- padding: 4px 16px;
-
- -webkit-touch-callout: none; /* iOS Safari */
- -webkit-user-select: none; /* Chrome/Safari/Opera */
- -khtml-user-select: none; /* Konqueror */
- -moz-user-select: none; /* Firefox */
- -ms-user-select: none; /* Internet Explorer/Edge */
- user-select: none;
-}
-
-.d3-context-menu ul li:hover {
- background-color: #4677f8;
- color: #fefefe;
-}
-
-/*
- Header
-*/
-
-.d3-context-menu ul li.is-header,
-.d3-context-menu ul li.is-header:hover {
- background-color: #f2f2f2;
- color: #444;
- font-weight: bold;
- font-style: italic;
-}
-
-/*
- Disabled
-*/
-
-.d3-context-menu ul li.is-disabled,
-.d3-context-menu ul li.is-disabled:hover {
- background-color: #f2f2f2;
- color: #888;
- cursor: not-allowed;
-}
-
-/*
- Divider
-*/
-
-.d3-context-menu ul li.is-divider {
- padding: 0px 0px;
-}
-
-.d3-context-menu ul li.is-divider:hover {
- background-color: #f2f2f2;
-}
-
-.d3-context-menu ul hr {
- border: 0;
- height: 0;
- border-top: 1px solid rgba(0, 0, 0, 0.1);
- border-bottom: 1px solid rgba(255, 255, 255, 0.3);
-}
\ No newline at end of file
+++ /dev/null
-
-.node_path {
- opacity: 1;
- stroke: #2F3550;
- stroke-width: 1;
-}
-
-.node_selected {
- opacity: 1 !important;
- stroke: #2F3550 !important;
- stroke-width: 3 !important;
-}
-
-.hidden_circle {
- opacity: 0;
- stroke: #FF0000 !important;
- stroke-width: 2 !important;
-}
-
-.hidden_circle:hover {
- opacity: 1;
- stroke: #FF0000 !important;
- stroke-width: 2;
-}
-
-.node_path:hover {
- opacity: 1;
- stroke: #2F3550;
- stroke-width: 3;
-}
-
-.node_path:hover text {
- opacity: 0.4;
-
-}
-
-.link {
- fill: none;
-}
-
-.link path {
- stroke-width: 2;
-}
-
-.matted {
- opacity: 0.3;
-}
-
-.invisible {
- visibility: hidden;
-}
-
-.drag_box {
- padding: 0 40px 0 0;
- margin-bottom: 0px !important;
- padding-top: 0px;
- top: 14px;
- /* right: 25px !important; */
- /* width: 15%; */
- position: absolute;
- z-index: 1;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- right: 0;
- cursor: move;
-}
-
-.selector_box {
- padding: 0 40px 0 0;
- margin-bottom: 0px !important;
- padding-top: 0px;
- top: 70px;
- /* right: 25px !important; */
- /* width: 15%; */
- position: absolute;
- z-index: 1;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- right: 0;
- cursor: move;
-}
-
-.drag_button {
- cursor: move !important;
- /*border-style: dashed;*/
- border-width: thin;
- border-color: black;
- color: white;
- border-radius: 50% !important;
- height: 50px !important;
- width: 50px !important;
- margin-right: 5px;
-}
-
-.drag_button p {
- font-family: "Lucida Console", Monaco, monospace;;
- font-size: 0.6em;
- font-weight: bold;
- text-decoration: none;
- color: black;
- text-align: center;
- padding-top: 50%;
- padding-bottom: 50%;
- width: 100%;
-}
-
-.left-tool-bar {
- top: 14px;
- cursor: default;
- line-height: 22px;
- position: absolute;
-
- padding: 0px 6px;
-
- /* top: 7px; */
- /* right: 6px; */
- z-index: 1;
- text-align: center;
- font-size: 12px;
- /*color: #777;
- /* border-radius: 20px; */
-
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-.left-tool-bar-monitoring {
-
- z-index: 1;
- text-align: center;
- font-size: 12px;
-
- top: 64px;
- cursor: default;
- padding: 0 6px;
- line-height: 22px;
- position: absolute;
-
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-.vnffg-box {
- top: 80px;
- left: 100px;
- cursor: default;
- position: absolute;
- width: 130px;
-
- padding-top: 0px;
- padding-bottom: 0px;
-
- /* top: 7px; */
- /* right: 6px; */
- z-index: 1;
- text-align: center;
- font-size: 12px;
- /*color: #777;
- /* border-radius: 20px; */
-
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-textarea {
- font-family: "Lucida Console", Monaco, monospace;
- height: 350px !important;
- resize: none;
-}
-
-.form-group {
- margin-bottom: 0px !important;
- margin-top: 0px !important;
-}
-
-.modal-body {
- overflow-y: auto;
-}
-
-[draggable=true] {
- -khtml-user-drag: element;
- -webkit-user-drag: element;
- -khtml-user-select: none;
- -webkit-user-select: none;
-}
-
-.help-key {
- border: 1px solid #ddd;
- padding: 4px;
- border-radius: 3px;
- background: #f6f6f6;
- font-family: Courier, monospace;
- box-shadow: #999 1px 1px 1px;
-}
-
-/*
-Palette section
-*/
-#palette {
- position: absolute;
- top: 20px;
- bottom: 20px;
- right: 22px;
- background: #f3f3f3;
- width: 170px;
- text-align: center;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- border: 1px solid #bbb;
- box-sizing: border-box;
- padding: 0 0 0 0;
-}
-
-.palette-status-indicator {
- top: 50%;
- position: relative;
- justify-content: center;
-}
-
-.palette-status-hidden > .palette-status-indicator {
- top: 50%;
- position: relative;
- justify-content: center;
- display: none;
-}
-
-.palette-container {
- #display: none;
- position: absolute;
- top: 0px;
- right: 0;
- bottom: 25px;
- left: 0;
- padding: 0;
- overflow-y: auto;
- box-sizing: border-box;
-}
-
-.palette-category {
-
- border-bottom: 1px solid #ccc;
-}
-
-.palette-hide {
- display: none;
-}
-
-.palette-header {
- position: relative;
- background: #f3f3f3;
- cursor: pointer;
- text-align: left;
- padding: 1px;
- font-weight: bold;
- font-size: 16px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
-}
-
-.palette-content {
-
- background: #fff;
- padding: 3px;
-}
-
-.palette-close > .palette-content {
- background: #fff;
- padding: 3px;
- display: none;
-}
-
-.palette-close > .palette-header > i.fa.fa-chevron-down {
-
- filter: none;
- -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
- -webkit-transform: rotate(-90deg);
- -ms-transform: rotate(-90deg);
- transform: rotate(-90deg);
-}
-
-.palette-node {
- cursor: move;
- margin: 3px auto;
- height: 35px;
- border-radius: 5px;
- border: 1px solid #999;
- background: #ddd no-repeat 5% 50%;
- width: 80%;
- background-size: contain;
- position: relative;
-}
-
-.palette-node-label {
- font-size: 13px;
- font-weight: bold;
- padding-left: 30px;
- margin-left: auto;
- margin-right: auto;
- position: relative;
- top: 50%;
- transform: translateY(-50%);
-}
-
-.palette-node-icon-container {
- position: absolute;
- text-align: center;
- top: 0;
- bottom: 0;
- left: 0;
- width: 30px;
- border-right: 1px solid rgba(0, 0, 0, 0.1);
- background-color: rgba(0, 0, 0, 0.05);
-}
-
-.palette-node-icon {
- /* display: inline-block;*/
- width: 30px;
- height: 100%;
- background-position: 50% 50%;
- background-size: contain;
- background-repeat: no-repeat;
-}
-
-.palette-node-circle {
- display: inline-block;
- margin-top: 2px;
- width: 28px;
- height: 28px;
- -webkit-border-radius: 14px;
- -moz-border-radius: 14px;
- border-radius: 14px;
- background: red;
-}
-
-.palette-node-square {
- display: inline-block;
- margin-top: 4px;
- padding-top: 1px;
- width: 26px;
- height: 26px;
- background: red;
-}
-
-.palette-node-triangle {
- padding-top: 1px;
- width: 0px;
- height: 0px;
- border-left: 15px solid transparent;
- border-right: 15px solid transparent;
- border-bottom: 30px solid #2f2f2f;
-}
-
-/*
-END Palette section
-*/
-
-/*
-Popup section
-*/
-.popup.bg {
- fill: black;
- opacity: 0.21;
- border-radius: 3px;
-}
-
-.popup.summary.bg {
- fill: white;
- opacity: 1;
-}
-
-.popup.summary.counter {
- overflow: hidden;
- text-overflow: ellipsis;
- font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-}
-
-.popup.summary.countervalue {
- overflow: hidden;
- text-overflow: ellipsis;
- font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-}
-
-.popup.title {
- fill: black;
- font-size: 16px;
- font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-}
-
-/*
-Popup section
-*/
\ No newline at end of file
+++ /dev/null
-(function (root, factory) {
- if (typeof module === 'object' && module.exports) {
- module.exports = function (d3) {
- d3.contextMenu = factory(d3);
- return d3.contextMenu;
- };
- } else if (typeof define === 'function' && define.amd) {
- try {
- var d3 = require('d3');
- } catch (e) {
- d3 = root.d3;
- }
-
- d3.contextMenu = factory(d3);
- define([], function () {
- return d3.contextMenu;
- });
- } else if (root.d3) {
- root.d3.contextMenu = factory(root.d3);
- }
-}(this,
- function (d3) {
- return function (menu, opts) {
- var openCallback,
- closeCallback;
-
- if (typeof opts === 'function') {
- openCallback = opts;
- } else {
- opts = opts || {};
- openCallback = opts.onOpen;
- closeCallback = opts.onClose;
- }
-
- // create the div element that will hold the context menu
- d3.selectAll('.d3-context-menu').data([1])
- .enter()
- .append('div')
- .attr('class', 'd3-context-menu');
-
- // close menu
- d3.select('body').on('click.d3-context-menu', function () {
- d3.select('.d3-context-menu').style('display', 'none');
- if (closeCallback) {
- closeCallback();
- }
- });
-
- // this gets executed when a contextmenu event occurs
- return function (data, index) {
- var elm = this;
- d3.selectAll('.d3-context-menu').html('');
- var list = d3.selectAll('.d3-context-menu')
- .on('contextmenu', function (d) {
- d3.select('.d3-context-menu').style('display', 'none');
- d3.event.preventDefault();
- d3.event.stopPropagation();
- })
- .append('ul');
- list.selectAll('li').data(typeof menu === 'function' ? menu(data) : menu).enter()
- .filter(function (d) {
- if(opts.type_object == 'node'){
- if (opts.edit_mode || opts.edit_mode == d.edit_mode ) {
- if ((d.nodes == undefined || d.nodes.length == 0) ||
- (d.nodes != undefined && d.nodes.length > 0 && d.nodes.indexOf(data.info.type) > -1))
- return true
- }
- }
- if(opts.type_object == 'link'){
- if (opts.edit_mode == d.edit_mode) {
-
- return true
- }
- }
- return false;
- })
- .append('li')
- .attr('class', function (d) {
- var ret = '';
- if (d.divider) {
- ret += ' is-divider';
- }
- if (d.disabled) {
- ret += ' is-disabled';
- }
- if (!d.action) {
- ret += ' is-header';
- }
- //if()
- return ret;
- })
- .html(function (d) {
- if (d.divider) {
- return '<hr>';
- }
- if (!d.title) {
- console.error('No title attribute set. Check the spelling of your options.');
- }
- return (typeof d.title === 'string') ? d.title : d.title(data);
- })
- .on('click', function (d, i) {
- if (d.disabled) return; // do nothing if disabled
- if (!d.action) return; // headers have no "action"
- d.action(elm, data, index);
- d3.select('.d3-context-menu').style('display', 'none');
-
- if (closeCallback) {
- closeCallback();
- }
- });
-
- // the openCallback allows an action to fire before the menu is displayed
- // an example usage would be closing a tooltip
- if (openCallback) {
- if (openCallback(data, index) === false) {
- return;
- }
- }
-
- // display context menu
- d3.select('.d3-context-menu')
- .style('left', (d3.event.pageX - 2) + 'px')
- .style('top', (d3.event.pageY - 2) + 'px')
- .style('display', 'block');
-
- d3.event.preventDefault();
- d3.event.stopPropagation();
- };
- };
- }
-));
\ No newline at end of file
+++ /dev/null
-/*
- Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-if (typeof dreamer === 'undefined') {
- var dreamer = {};
-}
-
-dreamer.Event = (function (global) {
- 'use strict';
-
- function Event () {
- this._listeners = {};
- }
-
- Event.prototype.addL = function (type, listener) {
- if (typeof this._listeners[type] == "undefined"){
- this._listeners[type] = [];
- }
- this._listeners[type].push(listener);
- };
-
- Event.prototype.fire = function (event, args) {
- if (typeof event == "string"){
- event = { type: event };
- }
- if (!event.target){
- event.target = this;
- }
-
- if (!event.type){ //falsy
- throw new Error("Event object missing 'type' property.");
- }
-
- if (this._listeners[event.type] instanceof Array){
- var listeners = this._listeners[event.type];
- for (var i=0, len=listeners.length; i < len; i++){
- listeners[i].call(this, event, args);
- }
- }
- };
-
- Event.prototype.addListener = function (type, listener) {
- if (this._listeners[type] instanceof Array){
- var listeners = this._listeners[type];
- for (var i=0, len=listeners.length; i < len; i++){
- if (listeners[i] === listener){
- listeners.splice(i, 1);
- break;
- }
- }
- }
-
- };
-
- return Event;
-}());
-
-if (typeof module === 'object') {
- module.exports = dreamer.Event;
-}
+++ /dev/null
-/*
- Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-if (typeof dreamer === 'undefined') {
- var dreamer = {};
-}
-var level = {}
-
-dreamer.GraphEditor = (function (global) {
- 'use strict';
-
- var DEBUG = true;
- var SHIFT_BUTTON = 16;
- var CANC_BUTTON = 46;
- var default_link_color = "#888";
- var nominal_text_size = 15;
- var nominal_stroke = 1.5;
- var EventHandler = dreamer.Event;
- // var IMAGE_PATH = "/static/assets/img/";
-
-
-
- /**
- * Constructor
- */
- function GraphEditor(args) {
- log("Constructor");
- this.eventHandler = new EventHandler();
- this.lastKeyDown = -1;
- this._selected_node = undefined;
- this._selected_link = undefined;
- this._edit_mode = true;
- this.filter_parameters = {
- node: {
- type: [],
- group: [],
- },
- link: {
- group: [],
- view: [],
- }
- };
- this.current_view_id = '';
- // graph data initailization
- this.d3_graph = {
- nodes: [],
- links: [],
- graph_parameters: {}
-
- };
-
-
- }
-
-
-
- GraphEditor.prototype.init = function (args) {
- args = args || {}
- var self = this;
- this.width = 550//args.width || 500;
- this.height = 550// args.height || 500;
- this.forceSimulationActive = false;
-
- //FixMe
- this.width = this.width - this.width * 0.007;
- this.height = this.height - this.height * 0.07;
-
- //console.log("this.width", this.width, "this.height", this.height);
- var min_zoom = 0.1;
- var max_zoom = 7;
- this._setupBehaviorsOnEvents();
- this._setupFiltersBehaviors(args);
-
- this.type_property = {
- "unrecognized": {
- "shape": d3.symbolCircle,
- "color": "white",
- "node_label_color": "black",
- "size": 15
- },
- };
-
- this.type_property_link = {
- "unrecognized": {
- "color": "#888",
- //"color": "red",
- },
- };
-
- this.force = d3.forceSimulation()
- .force("collide", d3.forceCollide().radius(40))
- .force("link", d3.forceLink().distance(80).iterations(1).id(function (d) {
- return d.id;
- }))
- .force("center", d3.forceCenter(this.width / 2, this.height / 2));
-
- var zoom = d3.zoom().scaleExtent([min_zoom, max_zoom])
-
- var size = d3.scalePow().exponent(2)
- .domain([1, 100])
- .range([8, 24]);
-
- this.svg = d3.select("#graph_ed_container").append("svg")
- .attr("id", "graph_svg")
- .attr("perserveAspectRatio", "xMinYMid")
- .attr("width", this.width)
- .attr("height", this.height);
-
- //End Arrow style
- this.defs = this.svg.append("svg:defs");
-
- this.defs.selectAll("marker")
- .data(["unrecognized"]) // Different link/path types can be defined here
- .enter().append("svg:marker") // This section adds in the arrows
- .attr("id", String)
- .attr("viewBox", "-5 -5 10 10")
- .attr("refX", 13) //must be smarter way to calculate shift
- .attr("refY", 0)
- .attr("markerUnits", "userSpaceOnUse")
- .attr("markerWidth", 12)
- .attr("markerHeight", 12)
- .attr("orient", "auto")
- .append("path")
- .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
- .attr('fill', this.type_property_link['unrecognized']['color']);
-
- d3.select(window)
- .on('keydown', function () {
- log('keydown ' + d3.event.keyCode);
- //d3.event.preventDefault();
- if (self.lastKeyDown !== -1) return;
- self.lastKeyDown = d3.event.keyCode;
- if (self.lastKeyDown === CANC_BUTTON && self._selected_node != undefined) {
- self.removeNode(self._selected_node, null, showAlert);
- } else if (self.lastKeyDown === CANC_BUTTON && self._selected_link != undefined) {
- self.removeLink(self._selected_link, null, showAlert);
- }
-
- })
- .on('keyup', function () {
- log('keyup' + self.lastKeyDown);
- self.lastKeyDown = -1;
- });
- var popup = this.svg.append("g")
- .attr("id", "popup")
- .attr("class", "popup")
- .attr("opacity", "0")
- .attr("transform", "translate(1 1)")
- .call(d3.drag()
- .on("start", dragstarted)
- .on("drag", dragged)
- .on("end", dragended));
-
- function dragstarted(d) {
- //d3.select(this).raise().classed("active", true);
- }
-
- function dragged(d) {
- //console.log(JSON.stringify(d))
- d3.select(this).attr("transform", function () {
- return "translate("+d3.event.x+","+d3.event.y+")";
-
- })
- }
-
- function dragended(d) {
- //d3.select(this).classed("active", false);
- }
-
- var chart = $("#graph_svg");
- this.aspect = chart.width() / chart.height();
- this.container = chart.parent();
- $(window).on("resize", function() {
-
- var palette_width = ($("#palette").length > 0) ? $("#palette").width() : 0;
- var working_width = self.container.width() - palette_width;
- self.width = (working_width < 0) ? 0 : working_width;
- self.height = self.container.height();
- chart.attr("width", self.width);
- chart.attr("height", self.height);
- }).trigger("resize");
-
- }
-
-
- GraphEditor.prototype.get_d3_symbol =
- function (myString) {
-
- switch (myString) {
- case "circle":
- return d3.symbolCircle;
- break;
- case "square":
- return d3.symbolSquare;
- break;
- case "diamond":
- return d3.symbolDiamond;
- break;
- case "triangle":
- return d3.symbolTriangle;
- break;
- case "star":
- return d3.symbolStar;
- break;
- case "cross":
- return d3.symbolCross;
- break;
- default:
- // if the string is not recognized
- return d3.symbolCross;
- //return d3.symbolCircleUnknown;
- }
-
- }
-
- GraphEditor.prototype.get_name_from_d3_symbol =
- function (mySymbol) {
- switch (mySymbol) {
- case d3.symbolCircle:
- return "circle";
- break;
- case d3.symbolSquare:
- return "square";
- break;
- case d3.symbolDiamond:
- return "diamond";
- break;
- case d3.symbolTriangle:
- return "triangle";
- break;
- case d3.symbolStar:
- return "star";
- break;
- case d3.symbolCross:
- return "cross";
- break;
- default:
- // if the string is not recognized
- return "unknown";
- //return d3.symbolCircleUnknown;
- }
-
- }
-
- /**
- * Start or Stop force layout
- * @param {boolean} Required. Value true: start, false: stop
- * @returns {boolean}
- */
- GraphEditor.prototype.handleForce = function (start) {
- if (start)
- this.force.stop();
- this.forceSimulationActive = start;
- this.node.each(function (d) {
- d.fx = (start) ? null : d.x;
- d.fy = (start) ? null : d.y;
- });
-
- if (start)
- this.force.restart();
-
- this.eventHandler.fire("force_status_changed_on", start);
- };
-
- /**
- * Handle the parameters of basic filters: node type, view, group
- * @param {Object} Required.
- *
- */
- GraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
- console.log("handleFiltersParams", filtersParams)
- this.filter_parameters = (filtersParams != undefined) ? filtersParams : this.filter_parameters;
- this.current_view_id = (this.filter_parameters != undefined && this.filter_parameters.link.view[0] != undefined) ? this.filter_parameters.link.view[0] : this.current_view_id
- this.cleanAll();
- this.refresh();
- this.startForce();
- this.force.restart();
- this._deselectAllNodes();
- this.handleForce(this.forceSimulationActive);
- if (!notFireEvent)
- this.eventHandler.fire("filters_changed", filtersParams);
-
- };
-
- /**
- * Add a new node to the graph.
- * @param {Object} Required. An object that specifies tha data of the new node.
- * @returns {boolean}
- */
- GraphEditor.prototype.addNode = function (args) {
- if (args.id && args.info && args.info.type) {
- args.fixed = true;
- this.force.stop();
- this.cleanAll();
- this.d3_graph.nodes.push(args);
- this.refresh();
- this.startForce();
- this.force.restart();
- this.handleForce(this.forceSimulationActive);
- return true;
- }
-
- return false;
-
- };
-
- /**
- * Update the data properties of the node
- * @param {Object} Required. An object that specifies tha data of the node.
- * @returns {boolean}
- */
- GraphEditor.prototype.updateDataNode = function (args) {
-
- };
-
- /**
- * Remove a node from graph and related links.
- * @param {String} Required. Id of node to remove.
- * @returns {boolean}
- */
- GraphEditor.prototype.removeNode = function (node) {
- if (node != undefined) {
- var node_id = node.id;
- this.d3_graph['nodes'].forEach(function (n, index, object) {
- if (n.id == node_id) {
- object.splice(index, 1);
-
- }
-
- });
- //TODO trovare una metodo piu efficace
- var self = this;
- var links_to_remove = [];
- this.d3_graph['links'].forEach(function (l, index, object) {
- if (node_id === l.source.id || node_id === l.target.id) {
- links_to_remove.push(index);
- }
-
- });
- var links_removed = 0;
- links_to_remove.forEach(function (l_index) {
- self.d3_graph['links'].splice(l_index - links_removed, 1);
- links_removed++;
- });
- this.cleanAll();
- this.refresh();
- this.startForce();
- this.force.restart();
-
- return true;
- }
- return false;
- };
-
-
- /**
- * Add a new link to graph.
- * @param {Object} Required. An object that specifies tha data of the new Link.
- * @returns {boolean}
- */
- GraphEditor.prototype.addLink = function (link) {
- console.log("addLink" + JSON.stringify(link))
- if (link.source && link.target) {
- this.force.stop();
- this.cleanAll();
- this.d3_graph.links.push(link);
- this.refresh();
- this.startForce();
- this.force.restart();
- return true;
- }
-
- return false;
- };
-
- /**
- * Remove a link from graph.
- * @param {String} Required. The identifier of link to remove.
- * @returns {boolean}
- */
- GraphEditor.prototype.removeLink = function (link_id) {
- var self = this;
- if (link_id !== 'undefined') {
- this.d3_graph['links'].forEach(function (l, index, object) {
- if (link_id === l.index) {
- object.splice(index, 1);
-
- self.cleanAll();
- self.refresh();
- self.startForce();
- self.force.restart();
- return true;
- }
-
- });
- }
-
- return false;
- };
-
-
- /**
- * Force a refresh of GraphView
- * @returns {}
- */
- GraphEditor.prototype.refresh = function () {
-
- //log(data)
- var self = this;
-
- this.link = this.svg
- .selectAll()
- .data(self.d3_graph.links
- .filter(this.link_filter_cb)
- )
- .enter().append("g")
- .attr("class", "link cleanable")
- .append("path")
- .attr("class", "link")
- .attr("class", "cleanable")
- .style("stroke-width", nominal_stroke)
- .style("stroke", function (d) {
- return self._link_property_by_type((d.type_link) ? d.type_link : "unrecognized", "color");
- })
- .attr("marker-end", function (d) {
- if (!d.directed_edge)
- return '';
-
- var marker_url = (d.type_link) ? d.type_link : "unrecognized"
- return (d.directed_edge ? "url(#" + marker_url + ")" : '');
- });
-
- this.nodeContainer = this.svg
- .selectAll()
- .data(self.d3_graph.nodes
- .filter(this.node_filter_cb))
- .enter()
- .append("g")
- // .attr("class", "nodosdads")
- .attr("class", "node cleanable");
-
- this.svg.selectAll('.node')
- .data(self.d3_graph.nodes
- .filter(this.node_filter_cb))
-
- .filter(function (d) {
- return (d.info.type == undefined) || (self._node_property_by_type(d.info.type, 'image', d) == undefined)
- })
-
- .append("svg:path")
- .attr("d", d3.symbol()
- .size(function (d) {
- return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d), 2) / 4;
- })
- .type(function (d) {
- // console.log(d.info.type, 'shape', self.current_view_id)
- return (self._node_property_by_type(d.info.type, 'shape', d));
- })
- )
- .style("fill", function (d) {
- return self._node_property_by_type(d.info.type, 'color', d);
- })
- .attr("transform", function () {
- return "rotate(-45)";
-
- })
- .attr("stroke-width", 2.4)
-
- .attr("class", "node_path")
- .attr("id", function (d) {
- return "path_" + d.id;
- })
-
- .call(d3.drag()
- .on("start", dragstarted)
- .on("drag", dragged)
- .on("end", dragended));
-
- var figure_node = this.svg.selectAll('.node')
- .data(self.d3_graph.nodes
- .filter(this.node_filter_cb))
-
- .filter(function (d) {
- return self._node_property_by_type(d.info.type, 'image', d) != undefined
- });
-
- figure_node.append("svg:image")
- .attr("xlink:href", function (d) {
- return self._node_property_by_type(d.info.type, 'image', d)
- })
- .attr("x", function (d) {
- return -self._node_property_by_type(d.info.type, 'size', d) / 2
- })
- .attr("y", function (d) {
- return -self._node_property_by_type(d.info.type, 'size', d) / 2
- })
- .attr("width", function (d) {
- return self._node_property_by_type(d.info.type, 'size', d)
- })
- .attr("height", function (d) {
- return self._node_property_by_type(d.info.type, 'size', d)
- })
- .style("stroke", "black")
- .style("stroke-width", "1px")
-
- .attr("class", "node_path")
- .attr("id", function (d) {
- return "path_" + d.id;
- })
- .call(d3.drag()
- .on("start", dragstarted)
- .on("drag", dragged)
- .on("end", dragended));
-
- figure_node.append("svg:path")
- .attr("d", d3.symbol()
- .size(function (d) {
- return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d) + 7, 2) / 4;
- })
- .type(function (d) {
- return (self.get_d3_symbol('circle'));
- })
- )
- .style("fill", 'transparent')
- .attr("transform", function () {
- return "rotate(-45)";
-
- })
- .attr("stroke-width", 2.4)
-
- .attr("class", "hidden_circle")
- .attr("id", function (d) {
- return "path_" + d.id;
- })
-
- .call(d3.drag()
- .on("start", dragstarted)
- .on("drag", dragged)
- .on("end", dragended));
-
-
-
- this.node = this.svg.selectAll('.node')
- .data(self.d3_graph.nodes
- .filter(this.node_filter_cb)).selectAll("image, path, circle");
-
-
-
- this.node.on("contextmenu", self.behavioursOnEvents.nodes["contextmenu"])
- .on("mouseover", self.behavioursOnEvents.nodes["mouseover"])
- .on("mouseout", self.behavioursOnEvents.nodes["mouseout"])
- .on('click', self.behavioursOnEvents.nodes["click"])
- .on('dblclick', self.behavioursOnEvents.nodes["dblclick"]);
-
- this.link
- .on("contextmenu", self.behavioursOnEvents.links["contextmenu"])
- .on("mouseover", self.behavioursOnEvents.links["mouseover"])
- .on('click', self.behavioursOnEvents.links["click"])
- .on("mouseout", self.behavioursOnEvents.links["mouseout"]);
-
-
-
- this.text = this.svg.selectAll(".node")
- .data(self.d3_graph.nodes
- .filter(this.node_filter_cb))
- .append("svg:text")
- .attr("class", "nodetext")
- .attr("class", "cleanable")
- .attr("dy", function(d) {
- if (self._node_property_by_type(d.info.type, 'image', d) == undefined) {
- //shape
- return "-5"
- }
- else {
- //image
- return (-self._node_property_by_type(d.info.type, 'size', d)/2).toString()
- }
- })
- .attr("pointer-events", "none")
- .style("font-size", nominal_text_size + "px")
- .style("font-family", "Lucida Console")
- .style("fill", function (d) {
- return self._node_property_by_type(d.info.type, 'node_label_color', d);
- })
- .style("text-anchor", "middle")
- .text(function (d) {
- return d.id;
- });
-
-
-
- function dragstarted(d) {
- d.draggednode = true;
- if (!d3.event.active) self.force.alphaTarget(0.3).restart();
- d.fx = d.x;
- d.fy = d.y;
-
- }
-
- function dragged(d) {
- d.fx = d3.event.x;
- d.fy = d3.event.y;
- }
-
- function dragended(d) {
- d.draggednode = false;
- if (!d3.event.active) self.force.alphaTarget(0);
- if (self.forceSimulationActive) {
- d.fx = null;
- d.fy = null;
- } else {
- d.fx = d.x;
- d.fy = d.y;
- self.force.stop();
- self.forceSimulationActive = false;
- }
- }
-
-
- };
-
- /**
- * Start force layout on Graph.
- *
- */
- GraphEditor.prototype.startForce = function () {
- //this.force.stop();
- var self = this
- this.force
- .nodes(this.d3_graph.nodes)
- .on("tick", ticked);
-
-
- this.force
- .force("link")
- .links(this.d3_graph.links);
-
- function ticked() {
- self.node.attr("cx", function (d) {
- return d.x = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.width - self._node_property_by_type(d.info.type, 'size', d), d.x));
- })
- .attr("cy", function (d) {
- return d.y = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.height - self._node_property_by_type(d.info.type, 'size', d), d.y));
- });
-
- self.link.attr("d", function (d) {
- var dx = d.target.x - d.source.x,
- dy = d.target.y - d.source.y,
- dr = Math.sqrt(dx * dx + dy * dy);
- return "M" + d.source.x + "," + d.source.y + "," + d.target.x + "," + d.target.y;
- });
-
- self.node.attr("transform", function (d) {
- return "translate(" + d.x + "," + d.y + ")";
- });
- self.text.attr("transform", function (d) {
- var label_pos_y = d.y + self._node_property_by_type(d.info.type, 'size', d) + 10;
- return "translate(" + d.x + "," + label_pos_y + ")";
- });
- };
-
-
-
- };
-
- /**
- * This method attaches an event handler.
- * @param {String} Required. A String that specifies the name of the event.
- * @param {Function} Required. Specifies the function to run when the event occurs.
- * @returns {}
- */
- GraphEditor.prototype.addListener = function (event_name, cb) {
- this.eventHandler.addL(event_name, cb);
- }
-
- /**
- * This method removes an event handler that has been attached with the addListener() method.
- * @param {String} Required. A String that specifies the name of the event to remove.
- * @param {Function} Required. Specifies the function to remove.
- * @returns {}
- */
- GraphEditor.prototype.removeListener = function (event_name, cb) {
-
- }
-
-
- GraphEditor.prototype.setNodeClass = function (class_name, filter_cb) {
- log("setNodeClass");
- var self = this;
- this.svg.selectAll('.node').classed(class_name, false);
- this.svg.selectAll('.node')
- .classed(class_name, filter_cb);
- }
-
- GraphEditor.prototype.setLinkClass = function (class_name, filter_cb) {
- log("setLinkClass");
- var self = this;
- this.svg.selectAll('.link').classed(class_name, false);
- this.svg.selectAll('.link')
- .classed(class_name, filter_cb);
- }
-
- GraphEditor.prototype.showNodeInfo = function(args){
- this.addLinesToPopup(args['node_info'], "Info about node selected")
- this.handlePopupVisibility(true, 'right')
- }
- GraphEditor.prototype.addLinesToPopup = function(data, title) {
- var self = this;
- var index = 1;
- var translate_y = 0;
- var width_popup = 400;
- var height_popup = 0;
-
- d3.selectAll(".popupcleanable").remove(); // clean
-
- var popupbg = d3.select(".popup").append("rect")
- .attr("id", "popupbg")
- .attr("class", "popup bg popupcleanable cleanable")
- .attr("width", "400")
- .attr("height", "0")
- .attr("rx", 10) // set the x corner curve radius
- .attr("ry", 10); // set the y corner curve radius
-
-
- d3.select(".popup").append("svg:path")
- .attr("d", d3.symbol()
- .size(function (d) {
- return 80
- })
- .type(function (d) {
- console.log("popiup")
- return (self.get_d3_symbol());
- })
- )
- .style("fill", 'red')
- .attr("transform", function () {
- return "translate(380,15) rotate(-45)";
-
- })
- .attr("stroke-width", 2.4)
- .attr("id", "close_popup")
- .attr("class", "popupcleanable cleanable")
- .on("click", function(d) {
- self.handlePopupVisibility(false);
- });
-
- d3.select(".popup").append("text")
- .attr("class", "popup title popupcleanable cleanable")
- .attr("x", "10")
- .attr("y", "20")
- .text(title);
-
- for (var i in data) {
- //console.log(i, data, data[i])
- //var typeofvalue = typeof data[i];
- var record = data[i];
- index = this._addRecordToPopup(i, record,index)
-
- }
-
- };
-
- GraphEditor.prototype._addRecordToPopup = function (key, record, index, tab) {
- //console.log("_addRecordToPopup", key, record, index)
- var translate_y = 23 * index;
- var summary = d3.select(".popup").append("g")
- .attr("class", "popup summary d popupcleanable cleanable")
- .attr("transform", "translate(10 " + translate_y + ")");
- if(Object.prototype.toString.call( record ) !== '[object Array]'){ //is a record simple key:value
- //console.log(key, record)
- var summary_g = summary.append("g");
- summary_g.append("rect")
- .attr("class", "popup summary bg popupcleanable cleanable")
- .attr("width", "380")
- .attr("height", "20");
-
- summary_g.append("text")
- .attr("class", "popup summary popupcleanable cleanable")
- .attr("x", (tab)? tab: 10)
- .attr("y", "17")
- .attr("width", "100")
- .text(function(d){
- return key.toUpperCase() + ":";
- });
-
- summary_g.append("text")
- .attr("class", "popup summary popupcleanable cleanable")
- .attr("x", "370")
- .attr("y", "17")
- .attr("text-anchor", "end")
- .text(function(d){return record});
- }
- else {//is a record simple complex: have a list of sub record key:value
- //index ++;
- this._addRecordToPopup(key, "", index)
- for(var r in record){
- //console.log(i, r, record, record[r])
- for(var k in record[r]){
- //console.log(i, r, k, record[r][k])
- var curr_key = k;
- var recordValue = record[r][k]
-
- index ++;
- this._addRecordToPopup(curr_key, recordValue, index, 20)
- }
- }
-
- }
-
- translate_y = 30 * index++;
- d3.select('#popupbg').attr("height", translate_y);
- return index;
- };
-
-
-
- /**
- * Remove all the graph objects from the view
- */
- GraphEditor.prototype.cleanAll = function () {
- this.svg.selectAll('.cleanable').remove();
- };
-
- /**
- * Internal functions
- */
-
- GraphEditor.prototype._node_property_by_type = function (type, property, node) {
- //console.log(type, property, layer, group)
- var unrecognized = function (ui_prop, property) {
- return ui_prop['unrecognized'][property]
- };
-
- //type recognized
- if (this.type_property[type]) {
-
- if (this.type_property[type]['property']) {
- var filt_property = this.type_property[type]['property']
- return this.type_property[type][node.info[filt_property]][property]
- } else { // type without property spec
-
- return this.type_property[type][property]
-
- }
-
- } else { //type unrecognized
- return unrecognized(this.type_property, property)
- }
-
- };
-
- GraphEditor.prototype._link_property_by_type = function (type, property) {
- //log(type + "-" + property)
- if (this.type_property_link[type] != undefined && this.type_property_link[type][property] != undefined) {
- //if(property == "shape")
- // log("dentro" + this.type_property[type][property])
- return this.type_property_link[type][property];
- } else {
- return this.type_property_link['unrecognized'][property];
- }
-
- }
-
-
- /**
- *
- *
- *
- */
- GraphEditor.prototype._setupFiltersBehaviors = function (args) {
-
- var self = this;
-
- this.node_filter_cb = args.node_filter_cb || function (d) {
-
- var cond_view = true,
- cond_group = true;
- //log(d.info.type + " " + self.filter_parameters.node.type + " group: " + self.filter_parameters.node.group + "- " + d.info.group)
- // check filter by node type
- if (self.filter_parameters.node.type.length > 0) {
-
- if (self.filter_parameters.node.type.indexOf(d.info.type) < 0)
- cond_view = false;
- }
-
- // check filter by group
- if (self.filter_parameters.node.group.length > 0) {
- self.filter_parameters.node.group.forEach(function (group) {
- if (d.info.group.indexOf(group) < 0)
- cond_group = false;
- });
-
-
- }
-
-
- return cond_view && cond_group;
- };
-
- this.link_filter_cb = args.link_filter_cb || function (d) {
- var cond_view = true,
- cond_group = true;
-
- // check filter by view
- if (self.filter_parameters.link.view.length > 0) {
- self.filter_parameters.link.view.forEach(function (view) {
- if (d.view.indexOf(view) < 0)
- cond_view = false;
- });
- }
-
- // check filter by group
- if (self.filter_parameters.link.group.length > 0) {
- self.filter_parameters.link.group.forEach(function (group) {
- if (d.group.indexOf(group) < 0)
- cond_group = false;
- });
- }
- return cond_view && cond_group;
- };
-
- };
-
- /**
- *
- *
- */
- GraphEditor.prototype._setupBehaviorsOnEvents = function () {
- log("_setupBehaviorsOnEvents");
- var self = this;
- this.behavioursOnEvents = {
- 'nodes': {
- 'click': function (d) {
- d3.event.preventDefault();
- log('click', d);
- if (self.lastKeyDown == SHIFT_BUTTON && self._selected_node != undefined) {
- var source_id = self._selected_node.id;
- var target_id = d.id;
- log("--" + JSON.stringify(self.filter_parameters.link.view));
- var new_link = {
- source: source_id,
- target: target_id,
- view: self.filter_parameters.link.view[0],
- group: self.filter_parameters.link.group[0],
- };
- self.addLink(new_link);
- self._deselectAllNodes();
- } else {
- self._selectNodeExclusive(this, d);
- }
-
- },
- 'mouseover': function (d) {
-
- },
- 'mouseout': function (d) {},
- 'dblclick': function (d) {
- d3.event.preventDefault();
- log('dblclick');
- },
- 'contextmenu': function (d, i) {
- d3.event.preventDefault();
- log("contextmenu node");
- self.eventHandler.fire("right_click_node", d);
- }
- },
- 'links': {
- 'click': function (event) {
-
- },
- 'dblclick': function (event) {
-
- }
- }
- };
- };
-
- /**
- * Deselect previously selected nodes
- *
- */
- GraphEditor.prototype._deselectAllNodes = function () {
- log("_deselectAllNodes");
- this.node.classed("node_selected", false);
- this._selected_node = undefined;
- };
-
- GraphEditor.prototype._deselectAllLinks = function () {
- log("_deselectAllLinks");
- this.link.classed("link_selected", false).style('stroke-width', 2);
- this._selected_link = undefined;
- };
- /**
- * Select node in exclusive mode
- * @param {Object} Required. Element selected on click event
- */
- GraphEditor.prototype._selectNodeExclusive = function (node_instance, node_id) {
- log("_selectNodeExclusive ");
- var activeClass = "node_selected";
- var alreadyIsActive = d3.select(node_instance).classed(activeClass);
- this._deselectAllNodes();
- this._deselectAllLinks();
- d3.select(node_instance).classed(activeClass, !alreadyIsActive);
- this._selected_node = (alreadyIsActive) ? undefined : node_instance.__data__;
- };
-
- /**
- * Select node in exclusive mode
- * @param {Object} Required. Element selected on click event
- */
- GraphEditor.prototype._selectLinkExclusive = function (link_instance, link_id) {
- log("_selectLinkExclusive ");
- var activeClass = "link_selected";
- var alreadyIsActive = d3.select(link_instance).classed(activeClass);
- this._deselectAllNodes();
- this._deselectAllLinks();
- d3.select(link_instance).classed(activeClass, !alreadyIsActive);
- d3.select(link_instance).style('stroke-width', 4)
- this._selected_link = link_instance.__data__;
- };
-
- /**
- * Callback to resize SVG element on window resize
- */
- GraphEditor.prototype.resizeSvg = function (width, height) {
- log("resizeSvg");
- //log(event);
- this.width = width || this.width;
- this.height = height || this.height;
- this.svg.attr('width', width);
- this.svg.attr('height', height);
-
- }
-
- GraphEditor.prototype.handlePopupVisibility = function(visible, side) {
- var opacity = (visible) ? 1 : 0;
-
- var translate_op = (side == "left") ? "translate(50 50)" : "translate("+(this.width - 450).toString()+" 50)";
-
- if (!visible) {
- d3.selectAll(".popupcleanable").remove();
- d3.select(".popup")
- .attr("transform", "translate(-1 -1)");
- } else {
- d3.select(".popup")
- .attr("transform", translate_op);
- }
- d3.select(".popup").attr("opacity", opacity);
- };
-
- GraphEditor.prototype.refreshGraphParameters = function (graphParameters) {
- this.eventHandler.fire("refresh_graph_parameters", graphParameters);
- }
-
- /**
- * Log utility
- */
- function log(text) {
- if (DEBUG)
- console.log("::GraphEditor::", text);
- }
-
-
-
- return GraphEditor;
-
-
-}(this));
-
-if (typeof module === 'object') {
- module.exports = dreamer.GraphEditor;
-}
\ No newline at end of file
+++ /dev/null
-/*
- Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-if (typeof dreamer === 'undefined') {
- var dreamer = {};
-}
-var level = {}
-
-dreamer.GraphRequests = (function(global) {
- 'use strict';
-
- var DEBUG = true;
-
- GraphRequests.prototype.constructor = GraphRequests;
-
- /**
- * Constructor
- */
- function GraphRequests(args) {
-
-
- }
-
- GraphRequests.prototype.addNode = function(args, choice, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
-
- data = args_to_formdata(args, data);
-
- //FIXME questo metodo dovrebbere essere generico
- if(args.existing_element)
- data.append('existing_element', args.existing_element ? args.existing_element : false)
- //if (choice)
- // data.append('choice', choice);
- $.ajax({
- url: "addelement",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success();
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- };
-
- GraphRequests.prototype.removeNode = function(args, choice, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
-
- data = args_to_formdata(args, data);
-
- $.ajax({
- url: "removeelement",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success();
- },
- error: function(result) {
- if (error)
- error(result);
- }
- });
- };
-
- GraphRequests.prototype.getNodeOverview = function(args, success, error) {
-
- var params = jQuery.param(args)
- console.log("params", params)
- $.ajax({
- url: "overviewelement?"+params,
- type: 'GET',
- success: function(result) {
- if (success)
- success(result);
- },
- error: function(result) {
- if (error)
- error(result);
- }
- });
- };
-
- GraphRequests.prototype.addLink = function(args, choice, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- data = args_to_formdata(args, data);
-
- //data.append('destination', JSON.stringify(destination));
- if (choice)
- data.append('choice', choice);
- //if(link.desc_id)
- // data.append('element_desc_id', link.desc_id || '');
- $.ajax({
- url: "addlink",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success();
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- };
-
- GraphRequests.prototype.removeLink = function(args, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- data = args_to_formdata(args, data);
-
- $.ajax({
- url: "removelink",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success();
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- };
-
- //
- GraphRequests.prototype.getAvailableNodes = function(args, success, error){
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- $.ajax({
- url: "availablenodes?layer="+args.layer,
- type: 'GET',
- success: function(result) {
- if (success)
- success(result);
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- }
-
- GraphRequests.prototype.savePositions = function(positions, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- data.append('positions', JSON.stringify(positions));
- $.ajax({
- url: "positions",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success();
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- };
-
- /* START ETSI methods */
- GraphRequests.prototype.addVnffg = function(args, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- /* data.append('group_id', args.info.group[0]);
- data.append('element_id', args.id);
- data.append('element_type', args.info.type);*/
- data = args_to_formdata(args, data);
- $.ajax({
- url: "addelement",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success(result);
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- };
-
- GraphRequests.prototype.addNodeToVnffg = function(args, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- /* data.append('group_id', args.info.group[0]);
- data.append('element_id', args.id);
- data.append('element_type', args.info.type);
- data.append('vnffg_id', args.vnffgId);*/
- data = args_to_formdata(args, data);
-
- $.ajax({
- url: "addnodetovnffg",
- type: 'POST',
- data: data,
- cache: false,
- contentType: false,
- processData: false,
- success: function(result) {
- if (success)
- success(result);
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
- };
-
- GraphRequests.prototype.getUnusedVnf = function(nsd_id, success, error) {
- var data = new FormData();
- data.append('csrfmiddlewaretoken', this.getCookie('csrftoken'));
- $.ajax({
- url: "unusedvnf/" + nsd_id,
- type: 'GET',
- success: function(result) {
- if (success)
- success(result);
- },
- error: function(result) {
- if (error)
- error(result);
- log("some error: " + result);
- }
- });
-
- };
- /* END ETSI methods */
-
- GraphRequests.prototype.getCookie = function(name) {
- var cookieValue = null;
- if (document.cookie && document.cookie !== '') {
- var cookies = document.cookie.split(';');
- for (var i = 0; i < cookies.length; i++) {
- var cookie = jQuery.trim(cookies[i]);
- // Does this cookie string begin with the name we want?
- if (cookie.substring(0, name.length + 1) === (name + '=')) {
- cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
- break;
- }
- }
- }
- return cookieValue;
- };
-
- function args_to_formdata(args, form_data){
- for ( var key in args ) {
- form_data.append(key, args[key]);
- }
- return form_data;
- };
-
-
- /**
- * Log utility
- */
- function log(text) {
- if (DEBUG)
- console.log("::GraphRequests::", text);
- }
-
- return GraphRequests;
-
-
-}(this));
-
-if (typeof module === 'object') {
- module.exports = dreamer.GraphRequests;
-}
+++ /dev/null
-/*
- Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-if (typeof dreamer === 'undefined') {
- var dreamer = {};
-}
-var level = {}
-
-dreamer.ModelGraphEditor = (function (global) {
- 'use strict';
-
- var DEBUG = true;
- var SHIFT_BUTTON = 16;
- var IMAGE_PATH = "/static/assets/img/";
- var GUI_VERSION = "v1";
-
-
- ModelGraphEditor.prototype = new dreamer.GraphEditor();
- ModelGraphEditor.prototype.constructor = ModelGraphEditor;
- ModelGraphEditor.prototype.parent = dreamer.GraphEditor.prototype;
-
- /**
- * Constructor
- */
- function ModelGraphEditor(args) {
-
- log("Constructor");
-
- }
-
-
- ModelGraphEditor.prototype.init = function (args) {
- this.parent.init.call(this, args);
-
- if (args.gui_properties[GUI_VERSION] != undefined) {
- args.gui_properties = args.gui_properties[GUI_VERSION];
- }
-
- this.desc_id = args.desc_id || undefined;
- this.type_property = {};
- this.type_property["unrecognized"] = args.gui_properties["default"];
- this.type_property["unrecognized"]["default_node_label_color"] = args.gui_properties["default"]["label_color"];
- //this.type_property["unrecognized"]["shape"] = d3.symbolCross;
- this._edit_mode = (args.edit_mode != undefined) ? args.edit_mode : this._edit_mode;
-
- Object.keys(args.gui_properties["nodes"]).forEach(function (key, index) {
- console.log(key + " ####")
- this.type_property[key] = args.gui_properties["nodes"][key];
- if ( this.type_property[key]['property'] != undefined){
- for(var c_prop in this.type_property[key]){
- if(c_prop != 'property'){
-
- this.type_property[key][c_prop]['shape'] = this.parent.get_d3_symbol(this.type_property[key][c_prop]['shape']);
- if(this.type_property[key][c_prop]["image"] != undefined){
- this.type_property[key][c_prop]["image"] = IMAGE_PATH + this.type_property[key][c_prop]["image"]
- }
- }
- }
- }
- else{
-
- this.type_property[key]["shape"] = this.parent.get_d3_symbol(this.type_property[key]["shape"]);
- if (this.type_property[key]["image"] != undefined) {
- this.type_property[key]["image"] = IMAGE_PATH + this.type_property[key]["image"];
- }
- }
-
-
-
- }, this);
- if(args.gui_properties["edges"]){
- this.type_property_link = args.gui_properties["edges"];
- var self = this;
- var link_types = ['unrecognized'].concat(Object.keys(self.type_property_link))
- this.defs.selectAll("marker")
- .data(link_types)
- .enter()
- .append("svg:marker") // This section adds in the arrows
- .attr("id", function(d){
- return d;
- })
- .attr("viewBox", "-5 -5 10 10")
- .attr("refX", 13) /*must be smarter way to calculate shift*/
- .attr("refY", 0)
- .attr("markerUnits", "userSpaceOnUse")
- .attr("markerWidth", 12)
- .attr("markerHeight", 12)
- .attr("orient", "auto")
- .append("path")
- .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
- .attr('fill', function(d){
- return self.type_property_link[d].color;
- });
- }
-
- this.customBehavioursOnEvents = args.behaviorsOnEvents || undefined;
-
- var self = this;
- var data_url = (args.data_url) ? args.data_url : "graph_data/";
- if (!args.graph_data) {
- d3.json(data_url, function (error, data) {
- //console.log(JSON.stringify(data))
- self.d3_graph.nodes = data.vertices;
- self.d3_graph.links = data.edges;
- self.d3_graph.graph_parameters = data.graph_parameters;
- self.model = data.model;
- self.refreshGraphParameters(self.d3_graph.graph_parameters);
- self.refresh();
- self.startForce();
- //if(args.filter_base != undefined)
-
- setTimeout(function () {
- //self.handleForce(self.forceSimulationActive);
- //var f_t = {"node":{"type":[],"group":["vlan_r3u0"]},"link":{"group":["vlan_r3u0"],"view":[""]}}
- //var f_t ={"node":{"type":["vnf_vl","vnf_ext_cp","vnf_vdu_cp","vnf_vdu","vnf_click_vdu"],"group":["vlan_r3u0"]},"link":{"group":["vlan_r3u0"],"view":["vnf"]}}
- self.handleFiltersParams(args.filter_base);
- //self.handleFiltersParams(f_t);
- //console.log(JSON.stringify(args.filter_base))
- //console.log(self.d3_graph.nodes.length)
- //console.log(JSON.stringify(self.d3_graph.nodes))
- //self.d3_graph.nodes.forEach(function(key, index){
- //console.log(key, index);
- //})
- }, 500);
-
- });
- } else {
- this.updateData(args)
- }
- }
-
- /**
- * Update data of the graph.
- * @param {Object} Required. An object that specifies tha data of the new node.
- * @returns {boolean}
- */
- ModelGraphEditor.prototype.updateData = function (args) {
- console.log("updateData")
- this.d3_graph.nodes = args.graph_data.vertices;
- this.d3_graph.links = args.graph_data.edges;
- this.d3_graph.graph_parameters = args.graph_parameters;
- this.model = args.model;
- this.refreshGraphParameters(this.d3_graph.graph_parameters);
- this.refresh();
- this.startForce();
- //if(args.filter_base != undefined)
-
- //if(args.filter_base){
- var self = this;
- setTimeout(function () {
- self.handleForce(true);
- self.handleFiltersParams(args.filter_base);
- }, 500);
- //}
- }
-
- /**
- * Add a new node to the graph.
- * @param {Object} Required. An object that specifies tha data of the new node.
- * @returns {boolean}
- */
- ModelGraphEditor.prototype.addNode = function (node, success, error) {
- var self = this;
- var current_layer = self.getCurrentView();
- var node_type = node.info.type;
-
- if (self.model.layer[current_layer] && self.model.layer[current_layer].nodes[node_type] && self.model.layer[current_layer].nodes[node_type].addable) {
- if (self.model.layer[current_layer].nodes[node_type].addable.callback) {
- var c = self.model.callback[self.model.layer[current_layer].nodes[node_type].addable.callback].class;
- var controller = new dreamer[c]();
- controller[self.model.layer[current_layer].nodes[node_type].addable.callback](self, node, function () {
- self.parent.addNode.call(self, node);
- success && success();
- }, error);
-
- } else {
-
- log('addNode: callback undefined in model spec.');
- error && error("You can't add a " + node.info.type + ", callback undefined.");
- }
- } else {
- //FIXME Error handling????
- log("You can't add a " + node.info.type + " in a current layer " + current_layer);
- error && error("You can't add a " + node.info.type + " in a current layer " + current_layer);
- }
- };
-
-
-
- /**
- * Update the data properties of the node
- * @param {Object} Required. An object that specifies tha data of the node.
- * @returns {boolean}
- */
- ModelGraphEditor.prototype.updateDataNode = function (args) {
- //FIXME updating a node properties need commit to server side!
- this.parent.updateDataNode.call(this, args);
- };
-
- /**
- * Remove a node from graph and related links.
- * @param {String} Required. Id of node to remove.
- * @returns {boolean}
- */
- ModelGraphEditor.prototype.removeNode = function (node, success, error) {
- console.log('removeNode', JSON.stringify(node))
- var self = this;
- var current_layer = self.getCurrentView();
- var node_type = node.info.type;
- if (node.info.desc_id == undefined){
- node.info.desc_id = self.desc_id;
- }
- if (self.model.layer[current_layer] && self.model.layer[current_layer].nodes[node_type] && self.model.layer[current_layer].nodes[node_type].removable) {
- if (self.model.layer[current_layer].nodes[node_type].removable.callback) {
- var c = self.model.callback[self.model.layer[current_layer].nodes[node_type].removable.callback].class;
- var controller = new dreamer[c]();
- controller[self.model.layer[current_layer].nodes[node_type].removable.callback](self, node, function () {
- self.parent.removeNode.call(self, node);
- success && success();
- }, error);
- } else {
-
- log('removeNode: callback undefined in model spec.');
- error && error("You can't remove a " + node.info.type + ", callback undefined.");
- }
- } else {
- //FIXME we need to manage alert in a different way: FAILBACK
- log("You can't remove a " + node.info.type);
- error && error("You can't remove a " + node.info.type);
- }
- };
-
- /**
- * Add a new link to graph.
- * @param {Object} Required. An object that specifies tha data of the new Link.
- * @returns {boolean}
- */
- ModelGraphEditor.prototype.addLink = function (s, d, success, error) {
- var self = this;
- var source_id = s.id;
- var target_id = d.id;
- var source_type = s.info.type;
- var destination_type = d.info.type;
- var link = {
- source: s,
- target: d,
- view: this.filter_parameters.link.view[0],
- group: this.filter_parameters.link.group,
- desc_id: this.desc_id
- };
- log("addLink: " + JSON.stringify(link))
- var current_layer = self.getCurrentView()
- if (self.model.layer[current_layer].allowed_edges && self.model.layer[current_layer].allowed_edges[source_type] && self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type]) {
-
- if (self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].callback) {
- var callback = self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].callback;
- console.log(callback, self.model.callback)
- var direct_edge = 'direct_edge' in self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type] ? self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type]['direct_edge'] : false;
- link.directed_edge = direct_edge;
- var c = self.model.callback[callback].class;
- var controller = new dreamer[c]();
- controller[callback](self, link, function () {
- self._deselectAllNodes();
- self.parent.addLink.call(self, link);
- if (success)
- success();
- }, error);
- } else {
- log('addLink: callback undefined in model spec.');
- error && error("You can't add a link, callback undefined.");
- }
-
- } else {
- //FIXME we need to manage alert in a different way: FAILBACK
- log("You can't link a " + source_type + " with a " + destination_type);
-
- error && error("You can't link a " + source_type + " with a " + destination_type);
- }
- };
-
- /**
- * Remove a link from graph.
- * @param {String} Required. The identifier of link to remove.
- * @returns {boolean}
- */
- ModelGraphEditor.prototype.removeLink = function (link, success, error) {
- var self = this;
- var s = link.source;
- var d = link.target;
- var source_type = s.info.type;
- var destination_type = d.info.type;
- var current_layer = self.getCurrentView()
- if (self.model.layer[current_layer].allowed_edges && self.model.layer[current_layer].allowed_edges[source_type] && self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type] &&
- self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].removable
- ) {
- if (self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].removable.callback) {
- var callback = self.model.layer[current_layer].allowed_edges[source_type].destination[destination_type].removable.callback;
- var c = self.model.callback[callback].class;
- var controller = new dreamer[c]();
- controller[callback](self, link, function () {
- self._deselectAllNodes();
- self._deselectAllLinks();
- self.parent.removeLink.call(self, link.index);
- success && success();
- }, error);
- } else {
- log('removeLink: callback undefined in model spec.');
- error && error("You can't remove a link, callback undefined.");
- }
-
- } else {
- //FIXME we need to manage alert in a different way: FAILBACK
- log("You can't delete the link");
- error && error("You can't delete the link");
- }
-
-
- };
-
-
- ModelGraphEditor.prototype.savePositions = function (data) {
- var vertices = {}
- this.node.each(function (d) {
- vertices[d.id] = {};
- vertices[d.id]['x'] = d.x;
- vertices[d.id]['y'] = d.y;
- });
- new dreamer.GraphRequests().savePositions({
- 'vertices': vertices
- });
-
- };
-
- /**
- * Internal functions
- */
-
- /**
- *
- *
- */
-
- ModelGraphEditor.prototype._setupBehaviorsOnEvents = function (layer) {
-
- var self = this;
- var contextMenuLinksAction = [{
- title: 'Delete Link',
- action: function (elm, link, i) {
- self.removeLink(link, null, showAlert);
- },
- edit_mode: true
- }];
- var contextMenuNodesAction = [{
- title: 'Edit',
- action: function (elm, d, i) {
- if (d.info.type != undefined) {
- self.eventHandler.fire("edit_descriptor", self.project_id, d);
- }
- },
- nodes: [],
- edit_mode: true
- },
- {
- title: 'Delete',
- action: function (elm, d, i) {
- self.removeNode(d, null, showAlert);
- },
- edit_mode: true
- }
-
- ];
- if(this.customBehavioursOnEvents){
- contextMenuNodesAction = contextMenuNodesAction.concat(this.customBehavioursOnEvents['behaviors'].nodes);
- }
-
-
- if ( self.model && self.model.layer && self.model.layer[layer] && self.model.layer[layer].action && self.model.layer[layer].action.node) {
- for (var i in self.model.layer[layer].action.node) {
- var action = self.model.layer[layer].action.node[i]
- contextMenuNodesAction.push({
- title: action.title,
- action: function (elm, d, i) {
- var callback = action.callback;
- var c = self.model.callback[callback].class;
- var controller = new dreamer[c]();
- var args = {
- elm: elm,
- d: d,
- i: i
- }
-
- controller[callback](self, args);
- },
- edit_mode: (action.edit_mode != undefined) ? action.edit_mode: undefined
- });
- }
- }
-
- this.behavioursOnEvents = {
- 'nodes': {
- 'click': function (d) {
-
- d3.event.preventDefault();
-
- if (self._edit_mode && self.lastKeyDown == SHIFT_BUTTON && self._selected_node != undefined) {
- self.addLink(self._selected_node, d, null, showAlert);
- } else {
- self._selectNodeExclusive(this, d);
- }
-
- },
- 'mouseover': function (d) {
- self.link.style('stroke-width', function (l) {
- if (d === l.source || d === l.target)
- return 4;
- else
- return 2;
- });
- },
- 'mouseout': function (d) {
- self.link.style('stroke-width', 2);
- },
- 'contextmenu': d3.contextMenu(contextMenuNodesAction, {
- 'edit_mode': self._edit_mode,
- 'layer': layer,
- 'type_object': 'node'
- })
- },
- 'links': {
- 'click': function (d) {
- self._selectLinkExclusive(this, d);
-
- },
- 'dblclick': function (event) {
-
- },
- 'mouseover': function (d) {
- d3.select(this).style('stroke-width', 4);
- },
- 'mouseout': function (d) {
- if (d != self._selected_link)
- d3.select(this).style('stroke-width', 2);
- },
- 'contextmenu': d3.contextMenu(contextMenuLinksAction, {
- 'edit_mode': self._edit_mode,
- 'layer': layer,
- 'type_object': 'link'
- })
- }
- }
- };
-
- ModelGraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
-
- this.parent.handleFiltersParams.call(this, filtersParams, notFireEvent);
- this._setupBehaviorsOnEvents(filtersParams.link.view[0]);
- };
-
- ModelGraphEditor.prototype.getAvailableNodes = function () {
- log('getAvailableNodes');
- log(this.model)
- if (this.model && this.model.layer[this.getCurrentView()] != undefined)
- return this.model.layer[this.getCurrentView()].nodes;
- return [];
- }
-
-
- ModelGraphEditor.prototype.exploreLayer = function (args) {
-
- };
-
- ModelGraphEditor.prototype.getTypeProperty = function () {
- return this.type_property;
- };
-
- ModelGraphEditor.prototype.getCurrentGroup = function () {
- return this.filter_parameters.node.group[0];
-
- }
-
- ModelGraphEditor.prototype.getCurrentView = function () {
- return this.filter_parameters.link.view[0];
-
- }
- /**
- * Log utility
- */
- function log(text) {
- if (DEBUG)
- console.log("::ModelGraphEditor::", text);
- }
-
-
-
- return ModelGraphEditor;
-
-
-}(this));
-
-if (typeof module === 'object') {
- module.exports = dreamer.ModelGraphEditor;
-}
\ No newline at end of file