mirror of
https://github.com/ansible/awx.git
synced 2026-03-13 23:17:32 -02:30
Begins network-ui prototype integration into Tower UI.
* Moves network ui into a directive
* Adds awxNet prefix to network ui directives
* Adds a module to integrate the stand alone network UI with
Tower UI.
* Adds reconnectingwebsocket to webpack bundle
* Adds configuration for webpack
* Moves ngTouch and hamsterjs to webpack vendor bundle
* Moves angular to network UI vendor bundle
* Adds ui-router dependency
* Changes CSS to BEM style
* Adds unique id sequences for devices and links on Topology and interfaces on Device
* Adds group widget with move, resize, delete, and edit label support
This commit is contained in:
committed by
Ben Thomasson
parent
640e687f3e
commit
d0e402c39a
@@ -6,8 +6,8 @@ all: models admin
|
||||
|
||||
models:
|
||||
jinja2 templates/models.pyt designs/models.yml > models.py
|
||||
autopep8 -i models.py
|
||||
autopep8 -i models.py --ignore-local-config --max-line-length 160
|
||||
|
||||
admin:
|
||||
jinja2 templates/admin.pyt designs/models.yml > admin.py
|
||||
autopep8 -i admin.py
|
||||
autopep8 -i admin.py --ignore-local-config --max-line-length 160
|
||||
|
||||
@@ -14,9 +14,13 @@ from awx.network_ui.models import MessageType
|
||||
|
||||
from awx.network_ui.models import Interface
|
||||
|
||||
from awx.network_ui.models import Group
|
||||
|
||||
from awx.network_ui.models import GroupDevice
|
||||
|
||||
|
||||
class DeviceAdmin(admin.ModelAdmin):
|
||||
fields = ('topology', 'name', 'x', 'y', 'id', 'type',)
|
||||
fields = ('topology', 'name', 'x', 'y', 'id', 'type', 'interface_id_seq',)
|
||||
raw_id_fields = ('topology',)
|
||||
|
||||
|
||||
@@ -32,8 +36,8 @@ admin.site.register(Link, LinkAdmin)
|
||||
|
||||
|
||||
class TopologyAdmin(admin.ModelAdmin):
|
||||
fields = ('name', 'scale', 'panX', 'panY',)
|
||||
raw_id_fields = ()
|
||||
fields = ('name', 'scale', 'panX', 'panY', 'device_id_seq', 'link_id_seq', 'group_id_seq',)
|
||||
raw_id_fields = ('device_id_seq',)
|
||||
|
||||
|
||||
admin.site.register(Topology, TopologyAdmin)
|
||||
@@ -69,3 +73,19 @@ class InterfaceAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
admin.site.register(Interface, InterfaceAdmin)
|
||||
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
fields = ('id', 'name', 'x1', 'y1', 'x2', 'y2', 'topology',)
|
||||
raw_id_fields = ('id', 'y1', 'x2', 'topology',)
|
||||
|
||||
|
||||
admin.site.register(Group, GroupAdmin)
|
||||
|
||||
|
||||
class GroupDeviceAdmin(admin.ModelAdmin):
|
||||
fields = ('group', 'device',)
|
||||
raw_id_fields = ('group', 'device',)
|
||||
|
||||
|
||||
admin.site.register(GroupDevice, GroupDeviceAdmin)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
from channels import Group, Channel
|
||||
from channels.sessions import channel_session
|
||||
from awx.network_ui.models import Topology, Device, Link, Client, TopologyHistory, MessageType, Interface
|
||||
from awx.network_ui.models import Group as DeviceGroup
|
||||
from awx.network_ui.models import GroupDevice as GroupDeviceMap
|
||||
from awx.network_ui.serializers import yaml_serialize_topology
|
||||
import urlparse
|
||||
from django.db.models import Q
|
||||
@@ -9,10 +11,11 @@ from collections import defaultdict
|
||||
from django.conf import settings
|
||||
import math
|
||||
import random
|
||||
import logging
|
||||
|
||||
from awx.network_ui.utils import transform_dict
|
||||
from pprint import pprint
|
||||
import dpath.util
|
||||
from pprint import pformat
|
||||
|
||||
import json
|
||||
import time
|
||||
@@ -33,6 +36,8 @@ SPACING = 200
|
||||
RACK_SPACING = 50
|
||||
settings.RECORDING = False
|
||||
|
||||
logger = logging.getLogger("awx.network_ui.consumers")
|
||||
|
||||
|
||||
def circular_layout(topology_id):
|
||||
n = Device.objects.filter(topology_id=topology_id).count()
|
||||
@@ -172,22 +177,17 @@ def tier_layout(topology_id):
|
||||
edges[device_map[l.from_device.pk]].add(device_map[l.to_device.pk])
|
||||
edges[device_map[l.to_device.pk]].add(device_map[l.from_device.pk])
|
||||
|
||||
pprint(devices)
|
||||
|
||||
similar_connections = defaultdict(list)
|
||||
|
||||
for device, connections in edges.iteritems():
|
||||
similar_connections[tuple(connections)].append(device)
|
||||
|
||||
pprint(dict(**similar_connections))
|
||||
|
||||
for connections, from_devices in similar_connections.iteritems():
|
||||
if len(from_devices) > 0 and from_devices[0].role == "host":
|
||||
racks.append(from_devices)
|
||||
|
||||
pprint(racks)
|
||||
pprint(devices)
|
||||
|
||||
tiers = defaultdict(list)
|
||||
|
||||
for device in devices:
|
||||
@@ -211,8 +211,6 @@ def tier_layout(topology_id):
|
||||
for tier in tiers.values():
|
||||
tier.sort(key=lambda x: x.name)
|
||||
|
||||
pprint(tiers)
|
||||
|
||||
for device in devices:
|
||||
print device, getattr(device, 'tier', None)
|
||||
if getattr(device, 'tier', None) is None:
|
||||
@@ -227,7 +225,7 @@ def tier_layout(topology_id):
|
||||
x = 0 - (len(racks) * SPACING) / 2 + j * SPACING
|
||||
for i, device in enumerate(rack):
|
||||
device.x = x
|
||||
device.y = SPACING * 3 + i * RACK_SPACING
|
||||
device.y = SPACING * 3 + i * RACK_SPACING
|
||||
device.save()
|
||||
|
||||
send_snapshot(Group("topology-%s" % topology_id), topology_id)
|
||||
@@ -288,6 +286,9 @@ class _Persistence(object):
|
||||
d.y = device['y']
|
||||
d.type = device['type']
|
||||
d.save()
|
||||
(Topology.objects
|
||||
.filter(topology_id=topology_id, device_id_seq__lt=device['id'])
|
||||
.update(device_id_seq=device['id']))
|
||||
|
||||
def onDeviceDestroy(self, device, topology_id, client_id):
|
||||
Device.objects.filter(topology_id=topology_id, id=device['id']).delete()
|
||||
@@ -313,6 +314,11 @@ class _Persistence(object):
|
||||
topology_id=topology_id).pk,
|
||||
id=interface['id'],
|
||||
defaults=dict(name=interface['name']))
|
||||
(Device.objects
|
||||
.filter(id=interface['device_id'],
|
||||
topology_id=topology_id,
|
||||
interface_id_seq__lt=interface['id'])
|
||||
.update(interface_id_seq=interface['id']))
|
||||
|
||||
def onLinkCreate(self, link, topology_id, client_id):
|
||||
device_map = dict(Device.objects
|
||||
@@ -326,6 +332,9 @@ class _Persistence(object):
|
||||
id=link['from_interface_id']).pk,
|
||||
to_interface_id=Interface.objects.get(device_id=device_map[link['to_device_id']],
|
||||
id=link['to_interface_id']).pk)
|
||||
(Topology.objects
|
||||
.filter(topology_id=topology_id, link_id_seq__lt=link['id'])
|
||||
.update(link_id_seq=link['id']))
|
||||
|
||||
def onLinkDestroy(self, link, topology_id, client_id):
|
||||
device_map = dict(Device.objects
|
||||
@@ -370,13 +379,13 @@ class _Persistence(object):
|
||||
print "Unsupported message ", message['msg_type']
|
||||
|
||||
def onDeploy(self, message_value, topology_id, client_id):
|
||||
Group("workers").send({"text": json.dumps(["Deploy", topology_id, yaml_serialize_topology(topology_id)])})
|
||||
DeviceGroup("workers").send({"text": json.dumps(["Deploy", topology_id, yaml_serialize_topology(topology_id)])})
|
||||
|
||||
def onDestroy(self, message_value, topology_id, client_id):
|
||||
Group("workers").send({"text": json.dumps(["Destroy", topology_id])})
|
||||
DeviceGroup("workers").send({"text": json.dumps(["Destroy", topology_id])})
|
||||
|
||||
def onDiscover(self, message_value, topology_id, client_id):
|
||||
Group("workers").send({"text": json.dumps(["Discover", topology_id, yaml_serialize_topology(topology_id)])})
|
||||
DeviceGroup("workers").send({"text": json.dumps(["Discover", topology_id, yaml_serialize_topology(topology_id)])})
|
||||
|
||||
def onLayout(self, message_value, topology_id, client_id):
|
||||
# circular_layout(topology_id)
|
||||
@@ -408,6 +417,53 @@ class _Persistence(object):
|
||||
onMouseWheelEvent = write_event
|
||||
onKeyEvent = write_event
|
||||
|
||||
def onGroupCreate(self, group, topology_id, client_id):
|
||||
group = transform_dict(dict(x1='x1',
|
||||
y1='y1',
|
||||
x2='x2',
|
||||
y2='y2',
|
||||
name='name',
|
||||
id='id'), group)
|
||||
d, _ = DeviceGroup.objects.get_or_create(topology_id=topology_id, id=group['id'], defaults=group)
|
||||
d.x1 = group['x1']
|
||||
d.y1 = group['y1']
|
||||
d.x2 = group['x2']
|
||||
d.y2 = group['y2']
|
||||
d.save()
|
||||
(Topology.objects
|
||||
.filter(topology_id=topology_id, group_id_seq__lt=group['id'])
|
||||
.update(group_id_seq=group['id']))
|
||||
|
||||
def onGroupDestroy(self, group, topology_id, client_id):
|
||||
DeviceGroup.objects.filter(topology_id=topology_id, id=group['id']).delete()
|
||||
|
||||
def onGroupLabelEdit(self, group, topology_id, client_id):
|
||||
DeviceGroup.objects.filter(topology_id=topology_id, id=group['id']).update(name=group['name'])
|
||||
|
||||
def onGroupMove(self, group, topology_id, client_id):
|
||||
DeviceGroup.objects.filter(topology_id=topology_id, id=group['id']).update(x1=group['x1'],
|
||||
y1=group['y1'],
|
||||
x2=group['x2'],
|
||||
y2=group['y2'])
|
||||
|
||||
def onGroupMembership(self, group_membership, topology_id, client_id):
|
||||
members = set(group_membership['members'])
|
||||
group = DeviceGroup.objects.get(topology_id=topology_id, id=group_membership['id'])
|
||||
existing = set(GroupDeviceMap.objects.filter(group=group).values_list('device__id', flat=True))
|
||||
new = members - existing
|
||||
removed = existing - members
|
||||
|
||||
GroupDeviceMap.objects.filter(group__group_id=group.group_id,
|
||||
device__id__in=list(removed)).delete()
|
||||
|
||||
device_map = dict(Device.objects.filter(topology_id=topology_id, id__in=list(new)).values_list('id', 'device_id'))
|
||||
new_entries = []
|
||||
for i in new:
|
||||
new_entries.append(GroupDeviceMap(group=group,
|
||||
device_id=device_map[i]))
|
||||
if new_entries:
|
||||
GroupDeviceMap.objects.bulk_create(new_entries)
|
||||
|
||||
|
||||
persistence = _Persistence()
|
||||
|
||||
@@ -525,7 +581,9 @@ class _Discovery(object):
|
||||
|
||||
def onFacts(self, message, topology_id):
|
||||
send_updates = False
|
||||
print message['key']
|
||||
logger.info("onFacts message key %s", message['key'])
|
||||
logger.info("onFacts message %s", pformat(message))
|
||||
return
|
||||
name = message['key']
|
||||
device, created = Device.objects.get_or_create(topology_id=topology_id,
|
||||
name=name,
|
||||
@@ -538,11 +596,14 @@ class _Discovery(object):
|
||||
device.id = device.pk
|
||||
device.save()
|
||||
send_updates = True
|
||||
print "Created device ", device
|
||||
logger.info("onFacts Created device %s", device)
|
||||
|
||||
interfaces = dpath.util.get(message, '/value/ansible_local/lldp/lldp') or []
|
||||
try:
|
||||
interfaces = dpath.util.get(message, '/value/ansible_local/lldp/lldp')
|
||||
except KeyError:
|
||||
interfaces = []
|
||||
for interface in interfaces:
|
||||
pprint(interface)
|
||||
logger.info("onFacts %s: ", pformat(interface))
|
||||
for inner_interface in interface.get('interface', []):
|
||||
name = inner_interface.get('name')
|
||||
if not name:
|
||||
@@ -662,7 +723,10 @@ def ws_connect(message):
|
||||
name='name',
|
||||
panX='panX',
|
||||
panY='panY',
|
||||
scale='scale'), topology.__dict__)
|
||||
scale='scale',
|
||||
link_id_seq='link_id_seq',
|
||||
device_id_seq='device_id_seq',
|
||||
group_id_seq='group_id_seq'), topology.__dict__)
|
||||
|
||||
message.reply_channel.send({"text": json.dumps(["Topology", topology_data])})
|
||||
send_snapshot(message.reply_channel, topology_id)
|
||||
@@ -696,9 +760,19 @@ def send_snapshot(channel, topology_id):
|
||||
'to_device__id',
|
||||
'from_interface__id',
|
||||
'to_interface__id'))]
|
||||
groups = list(DeviceGroup.objects
|
||||
.filter(topology_id=topology_id).values())
|
||||
group_map = {g['id']: g for g in groups}
|
||||
for group_id, device_id in GroupDeviceMap.objects.filter(group__topology_id=topology_id).values_list('group__id', 'device__id'):
|
||||
if 'members' not in group_map[group_id]:
|
||||
group_map[group_id]['members'] = [device_id]
|
||||
else:
|
||||
group_map[group_id]['members'].append(device_id)
|
||||
|
||||
snapshot = dict(sender=0,
|
||||
devices=devices,
|
||||
links=links)
|
||||
links=links,
|
||||
groups=groups)
|
||||
channel.send({"text": json.dumps(["Snapshot", snapshot])})
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
app: prototype
|
||||
app: awx.network_ui
|
||||
external_models: []
|
||||
models:
|
||||
- display: name
|
||||
@@ -22,6 +22,9 @@ models:
|
||||
- len: 200
|
||||
name: type
|
||||
type: CharField
|
||||
- default: 0
|
||||
name: interface_id_seq
|
||||
type: IntegerField
|
||||
name: Device
|
||||
x: 348
|
||||
y: 124
|
||||
@@ -71,6 +74,17 @@ models:
|
||||
type: FloatField
|
||||
- name: panY
|
||||
type: FloatField
|
||||
- default: 0
|
||||
name: device_id_seq
|
||||
ref: Topology
|
||||
ref_field: device_id_seq
|
||||
type: IntegerField
|
||||
- default: 0
|
||||
name: link_id_seq
|
||||
type: IntegerField
|
||||
- default: 0
|
||||
name: group_id_seq
|
||||
type: IntegerField
|
||||
name: Topology
|
||||
x: 111
|
||||
y: 127
|
||||
@@ -135,6 +149,51 @@ models:
|
||||
name: Interface
|
||||
x: 600
|
||||
y: 243
|
||||
- fields:
|
||||
- name: group_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- name: id
|
||||
ref: Group
|
||||
ref_field: id
|
||||
type: IntegerField
|
||||
- len: 200
|
||||
name: name
|
||||
type: CharField
|
||||
- name: x1
|
||||
type: IntegerField
|
||||
- name: y1
|
||||
ref: Group
|
||||
ref_field: y1
|
||||
type: IntegerField
|
||||
- name: x2
|
||||
ref: Group
|
||||
ref_field: x2
|
||||
type: IntegerField
|
||||
- name: y2
|
||||
type: IntegerField
|
||||
- name: topology
|
||||
ref: Topology
|
||||
ref_field: topology_id
|
||||
type: ForeignKey
|
||||
name: Group
|
||||
x: 907
|
||||
y: 520
|
||||
- fields:
|
||||
- name: group_device_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- name: group
|
||||
ref: GroupDevice
|
||||
ref_field: group
|
||||
type: ForeignKey
|
||||
- name: device
|
||||
ref: Device
|
||||
ref_field: device_id
|
||||
type: ForeignKey
|
||||
name: GroupDevice
|
||||
x: 631
|
||||
y: 561
|
||||
modules: []
|
||||
view:
|
||||
panX: 213.72955551921206
|
||||
|
||||
29
awx/network_ui/migrations/0012_auto_20170706_1526.py
Normal file
29
awx/network_ui/migrations/0012_auto_20170706_1526.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0011_link_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='interface_id_seq',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='topology',
|
||||
name='device_id_seq',
|
||||
field=models.IntegerField(default=0, verbose_name=b'Topology'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='topology',
|
||||
name='link_id_seq',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
||||
39
awx/network_ui/migrations/0013_auto_20170710_1840.py
Normal file
39
awx/network_ui/migrations/0013_auto_20170710_1840.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0012_auto_20170706_1526'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Group',
|
||||
fields=[
|
||||
('group_id', models.AutoField(serialize=False, primary_key=True)),
|
||||
('id', models.IntegerField(verbose_name=b'Group')),
|
||||
('name', models.CharField(max_length=200)),
|
||||
('x1', models.IntegerField()),
|
||||
('y1', models.IntegerField(verbose_name=b'Group')),
|
||||
('x2', models.IntegerField(verbose_name=b'Group')),
|
||||
('y2', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GroupDevice',
|
||||
fields=[
|
||||
('group_device_id', models.AutoField(serialize=False, primary_key=True)),
|
||||
('device', models.ForeignKey(to='network_ui.Device')),
|
||||
('group', models.ForeignKey(to='network_ui.GroupDevice')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='topology',
|
||||
name='group_id_seq',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
||||
20
awx/network_ui/migrations/0014_group_topology.py
Normal file
20
awx/network_ui/migrations/0014_group_topology.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0013_auto_20170710_1840'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='group',
|
||||
name='topology',
|
||||
field=models.ForeignKey(default=1, to='network_ui.Topology'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
19
awx/network_ui/migrations/0015_auto_20170710_1937.py
Normal file
19
awx/network_ui/migrations/0015_auto_20170710_1937.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0014_group_topology'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='groupdevice',
|
||||
name='group',
|
||||
field=models.ForeignKey(to='network_ui.Group'),
|
||||
),
|
||||
]
|
||||
@@ -5,11 +5,12 @@ class Device(models.Model):
|
||||
|
||||
device_id = models.AutoField(primary_key=True,)
|
||||
topology = models.ForeignKey('Topology',)
|
||||
name = models.CharField(max_length=200, )
|
||||
name = models.CharField(max_length=200,)
|
||||
x = models.IntegerField()
|
||||
y = models.IntegerField()
|
||||
id = models.IntegerField()
|
||||
type = models.CharField(max_length=200, )
|
||||
type = models.CharField(max_length=200,)
|
||||
interface_id_seq = models.IntegerField(default=0)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
@@ -18,21 +19,24 @@ class Device(models.Model):
|
||||
class Link(models.Model):
|
||||
|
||||
link_id = models.AutoField(primary_key=True,)
|
||||
from_device = models.ForeignKey('Device', related_name='from_link', )
|
||||
to_device = models.ForeignKey('Device', related_name='to_link', )
|
||||
from_interface = models.ForeignKey('Interface', related_name='from_link', )
|
||||
to_interface = models.ForeignKey('Interface', related_name='to_link', )
|
||||
from_device = models.ForeignKey('Device', related_name='from_link',)
|
||||
to_device = models.ForeignKey('Device', related_name='to_link',)
|
||||
from_interface = models.ForeignKey('Interface', related_name='from_link',)
|
||||
to_interface = models.ForeignKey('Interface', related_name='to_link',)
|
||||
id = models.IntegerField()
|
||||
name = models.CharField(max_length=200, )
|
||||
name = models.CharField(max_length=200,)
|
||||
|
||||
|
||||
class Topology(models.Model):
|
||||
|
||||
topology_id = models.AutoField(primary_key=True,)
|
||||
name = models.CharField(max_length=200, )
|
||||
name = models.CharField(max_length=200,)
|
||||
scale = models.FloatField()
|
||||
panX = models.FloatField()
|
||||
panY = models.FloatField()
|
||||
device_id_seq = models.IntegerField('Topology', default=0)
|
||||
link_id_seq = models.IntegerField(default=0)
|
||||
group_id_seq = models.IntegerField(default=0)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
@@ -57,7 +61,7 @@ class TopologyHistory(models.Model):
|
||||
class MessageType(models.Model):
|
||||
|
||||
message_type_id = models.AutoField(primary_key=True,)
|
||||
name = models.CharField(max_length=200, )
|
||||
name = models.CharField(max_length=200,)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
@@ -67,8 +71,27 @@ class Interface(models.Model):
|
||||
|
||||
interface_id = models.AutoField(primary_key=True,)
|
||||
device = models.ForeignKey('Device',)
|
||||
name = models.CharField(max_length=200, )
|
||||
name = models.CharField(max_length=200,)
|
||||
id = models.IntegerField()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Group(models.Model):
|
||||
|
||||
group_id = models.AutoField(primary_key=True,)
|
||||
id = models.IntegerField('Group',)
|
||||
name = models.CharField(max_length=200,)
|
||||
x1 = models.IntegerField()
|
||||
y1 = models.IntegerField('Group',)
|
||||
x2 = models.IntegerField('Group',)
|
||||
y2 = models.IntegerField()
|
||||
topology = models.ForeignKey('Topology',)
|
||||
|
||||
|
||||
class GroupDevice(models.Model):
|
||||
|
||||
group_device_id = models.AutoField(primary_key=True,)
|
||||
group = models.ForeignKey('Group',)
|
||||
device = models.ForeignKey('Device',)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
|
||||
from awx.network_ui.models import Topology, Device, Link, Interface
|
||||
from awx.network_ui.models import Topology, Device, Link, Interface, Group, GroupDevice
|
||||
from django.db.models import Q
|
||||
import yaml
|
||||
import json
|
||||
from collections import defaultdict
|
||||
|
||||
NetworkAnnotatedInterface = Interface.objects.values('name',
|
||||
'id',
|
||||
@@ -17,13 +18,29 @@ NetworkAnnotatedInterface = Interface.objects.values('name',
|
||||
def topology_data(topology_id):
|
||||
|
||||
data = dict(devices=[],
|
||||
links=[])
|
||||
links=[],
|
||||
groups=[])
|
||||
|
||||
topology = Topology.objects.get(pk=topology_id)
|
||||
|
||||
data['name'] = topology.name
|
||||
data['topology_id'] = topology_id
|
||||
|
||||
groups = list(Group.objects.filter(topology_id=topology_id).values())
|
||||
group_devices = GroupDevice.objects.filter(group__topology_id=topology_id).values('group_id', 'device_id', 'device__name', 'group__name')
|
||||
group_device_map = defaultdict(list)
|
||||
|
||||
for group_device in group_devices:
|
||||
group_device_map[group_device['group_id']].append(group_device)
|
||||
|
||||
device_group_map = defaultdict(list)
|
||||
for group_device in group_devices:
|
||||
device_group_map[group_device['device_id']].append(group_device)
|
||||
|
||||
for group in groups:
|
||||
group['members'] = [x['device__name'] for x in group_device_map[group['group_id']]]
|
||||
data['groups'] = groups
|
||||
|
||||
links = list(Link.objects
|
||||
.filter(Q(from_device__topology_id=topology_id) |
|
||||
Q(to_device__topology_id=topology_id)))
|
||||
@@ -43,7 +60,8 @@ def topology_data(topology_id):
|
||||
x=device.x,
|
||||
y=device.y,
|
||||
id=device.id,
|
||||
interfaces=interfaces))
|
||||
interfaces=interfaces,
|
||||
groups=[x['group__name'] for x in device_group_map[device.device_id]]))
|
||||
|
||||
for link in links:
|
||||
data['links'].append(dict(from_device=link.from_device.name,
|
||||
@@ -54,7 +72,9 @@ def topology_data(topology_id):
|
||||
to_device_id=link.to_device.id,
|
||||
from_interface_id=link.from_interface.id,
|
||||
to_interface_id=link.to_interface.id,
|
||||
name=link.name,
|
||||
network=link.pk))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
||||
10
awx/network_ui/static/network_ui/CONTRIBUTING.md
Normal file
10
awx/network_ui/static/network_ui/CONTRIBUTING.md
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
To build the UI:
|
||||
|
||||
make
|
||||
|
||||
|
||||
To push the UI to tower code base:
|
||||
|
||||
make deploy
|
||||
@@ -1,15 +1,18 @@
|
||||
.PHONY: all main lint lessc
|
||||
.PHONY: all main lint lessc install simple-server deploy
|
||||
|
||||
all: clean lessc lint main istanbul
|
||||
all: clean install lessc lint main
|
||||
|
||||
clean:
|
||||
rm -rf src-instrumented
|
||||
rm -f js/bundle.js
|
||||
rm -f js/vendor.bundle.js
|
||||
rm -f css/style.css
|
||||
|
||||
main:
|
||||
webpack src/main.js js/bundle.js
|
||||
cp vendor/*.js js/
|
||||
install:
|
||||
npm i
|
||||
|
||||
main: install
|
||||
webpack
|
||||
|
||||
lint:
|
||||
jshint --verbose src/*js
|
||||
@@ -23,3 +26,11 @@ istanbul:
|
||||
cp index.html index-instrumented.html
|
||||
sed -i "s/bundle.js/bundle-instrumented.js/g" index-instrumented.html
|
||||
cp vendor/*.js js/
|
||||
|
||||
|
||||
simple-server:
|
||||
python -m SimpleHTTPServer
|
||||
|
||||
|
||||
deploy: main
|
||||
rsync -av src/ ../../../../awx/ui/client/src/network_ui/
|
||||
|
||||
98
awx/network_ui/static/network_ui/designs/group.yml
Normal file
98
awx/network_ui/static/network_ui/designs/group.yml
Normal file
@@ -0,0 +1,98 @@
|
||||
finite_state_machine_id: 102
|
||||
name: fsm
|
||||
states:
|
||||
- id: 1
|
||||
label: Resize
|
||||
x: 571
|
||||
y: 911
|
||||
- id: 2
|
||||
label: Start
|
||||
x: 744
|
||||
y: 69
|
||||
- id: 3
|
||||
label: CornerSelected
|
||||
x: 359
|
||||
y: 682
|
||||
- id: 4
|
||||
label: Selected1
|
||||
x: 839
|
||||
y: 640
|
||||
- id: 5
|
||||
label: Selected3
|
||||
x: 1528
|
||||
y: 360
|
||||
- id: 6
|
||||
label: Move
|
||||
x: 1297
|
||||
y: 786
|
||||
- id: 7
|
||||
label: Ready
|
||||
x: 740
|
||||
y: 324
|
||||
- id: 8
|
||||
label: EditLabel
|
||||
x: 1056
|
||||
y: 148
|
||||
- id: 9
|
||||
label: Selected2
|
||||
x: 1179
|
||||
y: 435
|
||||
- id: 10
|
||||
label: Placing
|
||||
x: 410
|
||||
y: 295
|
||||
transitions:
|
||||
- from_state: Ready
|
||||
label: onMouseDown
|
||||
to_state: Selected1
|
||||
- from_state: EditLabel
|
||||
label: onMouseDown
|
||||
to_state: Ready
|
||||
- from_state: Selected2
|
||||
label: onMouseDown
|
||||
to_state: Ready
|
||||
- from_state: Selected2
|
||||
label: onMouseDown
|
||||
to_state: Selected3
|
||||
- from_state: Selected1
|
||||
label: onMouseUp
|
||||
to_state: Selected2
|
||||
- from_state: Move
|
||||
label: onMouseUp
|
||||
to_state: Selected2
|
||||
- from_state: Selected1
|
||||
label: onMouseMove
|
||||
to_state: Move
|
||||
- from_state: Start
|
||||
label: start
|
||||
to_state: Ready
|
||||
- from_state: Ready
|
||||
label: onMouseDown
|
||||
to_state: CornerSelected
|
||||
- from_state: Selected3
|
||||
label: onMouseMove
|
||||
to_state: Move
|
||||
- from_state: Selected3
|
||||
label: onMouseUp
|
||||
to_state: EditLabel
|
||||
- from_state: Ready
|
||||
label: onNewGroup
|
||||
to_state: Placing
|
||||
- from_state: Placing
|
||||
label: onMouseDown
|
||||
to_state: CornerSelected
|
||||
- from_state: CornerSelected
|
||||
label: onMouseMove
|
||||
to_state: Resize
|
||||
- from_state: Resize
|
||||
label: onMouseUp
|
||||
to_state: Selected1
|
||||
- from_state: Selected2
|
||||
label: onNewGroup
|
||||
to_state: Ready
|
||||
- from_state: CornerSelected
|
||||
label: onMouseUp
|
||||
to_state: Selected1
|
||||
- from_state: Move
|
||||
label: onMouseDown
|
||||
to_state: Selected1
|
||||
25
awx/network_ui/static/network_ui/designs/hotkeys.yml
Normal file
25
awx/network_ui/static/network_ui/designs/hotkeys.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
finite_state_machine_id: 113
|
||||
name: hotkeys
|
||||
states:
|
||||
- id: 2
|
||||
label: Enabled
|
||||
x: 585
|
||||
y: 396
|
||||
- id: 1
|
||||
label: Start
|
||||
x: 585
|
||||
y: 160
|
||||
- id: 3
|
||||
label: Disabled
|
||||
x: 331
|
||||
y: 408
|
||||
transitions:
|
||||
- from_state: Enabled
|
||||
label: onDisable
|
||||
to_state: Disabled
|
||||
- from_state: Disabled
|
||||
label: onEnable
|
||||
to_state: Enabled
|
||||
- from_state: Start
|
||||
label: start
|
||||
to_state: Enabled
|
||||
@@ -1,36 +1,38 @@
|
||||
app: move
|
||||
panX: 285.92999999999995
|
||||
panY: -151.52999999999997
|
||||
scaleXY: 0.8700000000000001
|
||||
finite_state_machine_id: 106
|
||||
name: move
|
||||
states:
|
||||
- label: Start
|
||||
size: 100
|
||||
- id: 1
|
||||
label: Start
|
||||
x: 533
|
||||
y: 121
|
||||
- label: Ready
|
||||
size: 100
|
||||
- id: 2
|
||||
label: Ready
|
||||
x: 531
|
||||
y: 320
|
||||
- label: Selected1
|
||||
size: 100
|
||||
- id: 3
|
||||
label: Selected1
|
||||
x: 226
|
||||
y: 325
|
||||
- label: Selected2
|
||||
size: 100
|
||||
- id: 4
|
||||
label: Selected2
|
||||
x: 230
|
||||
y: 582
|
||||
- label: Move
|
||||
size: 100
|
||||
- id: 5
|
||||
label: Move
|
||||
x: -54
|
||||
y: 587
|
||||
- label: EditLabel
|
||||
size: 100
|
||||
x: 535.7126436781609
|
||||
y: 583.367816091954
|
||||
- label: Selected3
|
||||
size: 100
|
||||
x: 231.11494252873567
|
||||
y: 867.2758620689654
|
||||
- id: 6
|
||||
label: EditLabel
|
||||
x: 566
|
||||
y: 677
|
||||
- id: 7
|
||||
label: Selected3
|
||||
x: 175
|
||||
y: 886
|
||||
- id: 8
|
||||
label: Placing
|
||||
x: 92
|
||||
y: 82
|
||||
transitions:
|
||||
- from_state: Start
|
||||
label: start
|
||||
@@ -49,6 +51,9 @@ transitions:
|
||||
to_state: Ready
|
||||
- from_state: Move
|
||||
label: onMouseUp
|
||||
to_state: Selected1
|
||||
- from_state: EditLabel
|
||||
label: onKeyDown
|
||||
to_state: Selected2
|
||||
- from_state: EditLabel
|
||||
label: onMouseDown
|
||||
@@ -62,3 +67,21 @@ transitions:
|
||||
- from_state: Selected3
|
||||
label: onMouseUp
|
||||
to_state: EditLabel
|
||||
- from_state: Placing
|
||||
label: onMouseMove
|
||||
to_state: Move
|
||||
- from_state: Ready
|
||||
label: onNewDevice
|
||||
to_state: Placing
|
||||
- from_state: Placing
|
||||
label: onMouseDown
|
||||
to_state: Selected1
|
||||
- from_state: Move
|
||||
label: onMouseDown
|
||||
to_state: Selected1
|
||||
- from_state: Selected2
|
||||
label: onNewDevice
|
||||
to_state: Ready
|
||||
- from_state: Selected2
|
||||
label: onKeyDown
|
||||
to_state: Ready
|
||||
|
||||
15
awx/network_ui/static/network_ui/designs/null.yml
Normal file
15
awx/network_ui/static/network_ui/designs/null.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
finite_state_machine_id: 114
|
||||
name: fsm
|
||||
states:
|
||||
- id: 1
|
||||
label: Start
|
||||
x: 391
|
||||
y: 132
|
||||
- id: 2
|
||||
label: Ready
|
||||
x: 402
|
||||
y: 346
|
||||
transitions:
|
||||
- from_state: Start
|
||||
label: start
|
||||
to_state: Ready
|
||||
@@ -1,27 +1,43 @@
|
||||
app: time
|
||||
panX: 0
|
||||
panY: 0
|
||||
scaleXY: 1
|
||||
finite_state_machine_id: 89
|
||||
name: src/time
|
||||
states:
|
||||
- label: Start
|
||||
size: 100
|
||||
x: 634
|
||||
y: 117
|
||||
- label: Present
|
||||
size: 100
|
||||
x: 632
|
||||
y: 379
|
||||
- label: Past
|
||||
size: 100
|
||||
x: 367
|
||||
y: 369
|
||||
- id: 3
|
||||
label: Present
|
||||
x: 256
|
||||
y: 123
|
||||
- id: 2
|
||||
label: Start
|
||||
x: 245
|
||||
y: -161
|
||||
- id: 1
|
||||
label: Past
|
||||
x: -115
|
||||
y: 129
|
||||
transitions:
|
||||
- from_state: Past
|
||||
label: onMessage
|
||||
to_state: Present
|
||||
- from_state: Past
|
||||
label: onRedo
|
||||
to_state: Present
|
||||
- from_state: Past
|
||||
label: onMouseWheel
|
||||
to_state: Present
|
||||
- from_state: Past
|
||||
label: onKeyDown
|
||||
to_state: Present
|
||||
- from_state: Start
|
||||
label: start
|
||||
to_state: Present
|
||||
- from_state: Present
|
||||
label: onMessage
|
||||
to_state: Past
|
||||
- from_state: Present
|
||||
label: onUndo
|
||||
to_state: Past
|
||||
- from_state: Present
|
||||
label: onMouseWheel
|
||||
to_state: Past
|
||||
- from_state: Past
|
||||
label: onMouseWheel
|
||||
to_state: Present
|
||||
- from_state: Present
|
||||
label: onKeyDown
|
||||
to_state: Past
|
||||
|
||||
@@ -1,111 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="triangular">
|
||||
<html ng-app="networkUI">
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<script data-require="angular.js@1.6.2" src="js/angular.js" data-semver="1.6.2"></script>
|
||||
<script src="js/reconnecting-websocket.js"></script>
|
||||
<script src="js/bundle.js"></script>
|
||||
<script src="js/hamster.js"></script>
|
||||
<script src="js/ngTouch.js"></script>
|
||||
<script src="js/mousewheel.js"></script>
|
||||
<script src="js/vendor.bundle.js"></script>
|
||||
</head>
|
||||
<body ng-controller="MainCtrl" id="Main">
|
||||
<svg id="frame"
|
||||
ng-attr-height="{{graph.height}}"
|
||||
ng-attr-width="{{graph.width}}"
|
||||
ng-mousedown="onMouseDown($event)"
|
||||
ng-mouseup="onMouseUp($event)"
|
||||
ng-mouseenter="onMouseEnter($event)"
|
||||
ng-mouseleave="onMouseLeave($event)"
|
||||
ng-mousemove="onMouseMove($event)"
|
||||
ng-mouseover="onMouseOver($event)"
|
||||
ng-touchstart="onTouchStart($event)"
|
||||
ng-touchmove="onTouchMove($event)"
|
||||
ng-touchend="onTouchEnd($event)"
|
||||
ng-tap="onTap($event)"
|
||||
msd-wheel="onMouseWheel($event, $delta, $deltaX, $deltaY)">
|
||||
<defs>
|
||||
<filter x="0" y="0" width="1" height="1" id="selected">
|
||||
<feFlood flood-color="#b3d8fd"/>
|
||||
<feComposite in="SourceGraphic" operator="xor"/>
|
||||
</filter>
|
||||
<filter x="0" y="0" width="1" height="1" id="background">
|
||||
<feFlood flood-color="#ffffff"/>
|
||||
<feComposite in="SourceGraphic" operator="xor"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g transform="scale(1.0)" id="frame_g">
|
||||
<g ng-repeat="link in links">
|
||||
<g link></g>
|
||||
</g>
|
||||
<g ng-repeat="link in links">
|
||||
<g ng-if="link.selected || link.to_interface.selected || link.from_interface.selected" link></g>
|
||||
</g>
|
||||
<g ng-repeat="device in devices"
|
||||
ng-attr-transform="translate({{device.x}},{{device.y}})"
|
||||
ng-attr-class="{{device.type}}"
|
||||
ng-switch on="device.type">
|
||||
<g ng-switch-when="router"><!-- begin router -->
|
||||
<g router></g>
|
||||
</g> <!-- end router -->
|
||||
|
||||
<g ng-switch-when="switch"> <!-- begin switch -->
|
||||
<g switch> </g>
|
||||
</g> <!-- end switch -->
|
||||
|
||||
<g ng-switch-when="host"> <!-- begin host -->
|
||||
<g host> </g>
|
||||
|
||||
</g> <!-- end host -->
|
||||
|
||||
<g ng-switch-when="rack"> <!-- begin rack -->
|
||||
<g rack> </g>
|
||||
</g> <!-- end rack -->
|
||||
|
||||
<g ng-switch-default> <!-- begin default -->
|
||||
<g default></g>
|
||||
</g> <!-- end default -->
|
||||
<g status-light></g>
|
||||
<g task-status></g>
|
||||
</g> <!-- end devices -->
|
||||
<g ng-attr-transform="translate({{scaledX}},{{scaledY}})" ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug-cursor" >
|
||||
<line x1="-5" y1="0" x2="5" y2="0"/>
|
||||
<line x1="0" y1="-5" x2="0" y2="5"/>
|
||||
</g>
|
||||
<g quadrants>
|
||||
</g>
|
||||
</g>
|
||||
<g ng-if="!hide_buttons">
|
||||
<g> <!-- buttons -->
|
||||
<g ng-repeat="button in buttons"
|
||||
ng-attr-transform="translate({{button.x}},{{button.y}})"
|
||||
ng-attr-class="{{button.is_pressed ? 'button-pressed' : button.mouse_over ? 'button-hover' : 'button'}}">
|
||||
<g button></g>
|
||||
</g>
|
||||
</g> <!-- end buttons -->
|
||||
|
||||
<g> <!-- stencils -->
|
||||
<g ng-repeat="stencil in stencils"
|
||||
ng-attr-transform="translate({{stencil.x}},{{stencil.y}})"
|
||||
class="button">
|
||||
<g stencil></g>
|
||||
</g>
|
||||
</g> <!-- end stencils -->
|
||||
|
||||
<g> <!-- layers -->
|
||||
<g ng-repeat="layer in layers"
|
||||
ng-attr-transform="translate({{layer.x}},{{layer.y}})"
|
||||
class="button">
|
||||
<g layer> </g>
|
||||
</g>
|
||||
</g> <!-- end layers -->
|
||||
</g>
|
||||
<g debug></g>
|
||||
<g cursor></g>
|
||||
<g ng-repeat="touch in touches">
|
||||
<g touch></g>
|
||||
</g>
|
||||
</svg>
|
||||
<body>
|
||||
<awx-network-ui></awx-network-ui>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
12
awx/network_ui/static/network_ui/index2.html
Normal file
12
awx/network_ui/static/network_ui/index2.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="tower">
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<script src="js/vendor.bundle.js"></script>
|
||||
<script src="js/bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ui-view></ui-view>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,13 +9,18 @@
|
||||
"author": "Ben Thomasson",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"angular": "~1.6.2",
|
||||
"angular-ui-router": "",
|
||||
"webpack": "",
|
||||
"browserify": "",
|
||||
"inherits": "",
|
||||
"require": "",
|
||||
"jshint": "",
|
||||
"less": "",
|
||||
"mathjs": ""
|
||||
"mathjs": "",
|
||||
"reconnectingwebsocket": "^1.0.0",
|
||||
"hamsterjs": "~1.1.2",
|
||||
"angular-mousewheel": "~1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^3.17.1",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
python -m SimpleHTTPServer
|
||||
python -m SimpleHTTPServer 8080
|
||||
|
||||
5
awx/network_ui/static/network_ui/src/button.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/button.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function button () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/button.html' };
|
||||
}
|
||||
exports.button = button;
|
||||
@@ -55,8 +55,8 @@ _Start.prototype.start.transitions = ['Ready'];
|
||||
|
||||
_Clicked.prototype.start = function (controller) {
|
||||
|
||||
controller.scope.callback(controller.scope);
|
||||
controller.scope.is_pressed = false;
|
||||
controller.scope.callback(controller.scope);
|
||||
controller.changeState(Ready);
|
||||
};
|
||||
_Clicked.prototype.start.transitions = ['Ready'];
|
||||
|
||||
@@ -33,7 +33,7 @@ exports.ButtonPressed = ButtonPressed;
|
||||
_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
|
||||
|
||||
var i = 0;
|
||||
var buttons = controller.scope.buttons;
|
||||
var buttons = controller.scope.all_buttons;
|
||||
var button = null;
|
||||
for (i = 0; i < buttons.length; i++) {
|
||||
button = buttons[i];
|
||||
@@ -58,7 +58,7 @@ _Ready.prototype.onMouseMove = function (controller, msg_type, $event) {
|
||||
if (!controller.scope.hide_buttons) {
|
||||
|
||||
var i = 0;
|
||||
var buttons = controller.scope.buttons;
|
||||
var buttons = controller.scope.all_buttons;
|
||||
var button = null;
|
||||
for (i = 0; i < buttons.length; i++) {
|
||||
button = buttons[i];
|
||||
@@ -85,7 +85,7 @@ _Start.prototype.start.transitions = ['Ready'];
|
||||
_ButtonPressed.prototype.onMouseUp = function (controller, msg_type, $event) {
|
||||
|
||||
var i = 0;
|
||||
var buttons = controller.scope.buttons;
|
||||
var buttons = controller.scope.all_buttons;
|
||||
var button = null;
|
||||
for (i = 0; i < buttons.length; i++) {
|
||||
button = buttons[i];
|
||||
|
||||
5
awx/network_ui/static/network_ui/src/cursor.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/cursor.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function cursor () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/cursor.html' };
|
||||
}
|
||||
exports.cursor = cursor;
|
||||
6
awx/network_ui/static/network_ui/src/debug.directive.js
Normal file
6
awx/network_ui/static/network_ui/src/debug.directive.js
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
function debug () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/debug.html' };
|
||||
}
|
||||
|
||||
exports.debug = debug;
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
function defaultd () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/default.html' };
|
||||
}
|
||||
exports.defaultd = defaultd;
|
||||
5
awx/network_ui/static/network_ui/src/group.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/group.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function group () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/group.html' };
|
||||
}
|
||||
exports.group = group;
|
||||
527
awx/network_ui/static/network_ui/src/group.js
Normal file
527
awx/network_ui/static/network_ui/src/group.js
Normal file
@@ -0,0 +1,527 @@
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
var models = require('./models.js');
|
||||
var messages = require('./messages.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
|
||||
function _Resize () {
|
||||
this.name = 'Resize';
|
||||
}
|
||||
inherits(_Resize, _State);
|
||||
var Resize = new _Resize();
|
||||
exports.Resize = Resize;
|
||||
|
||||
function _Start () {
|
||||
this.name = 'Start';
|
||||
}
|
||||
inherits(_Start, _State);
|
||||
var Start = new _Start();
|
||||
exports.Start = Start;
|
||||
|
||||
function _CornerSelected () {
|
||||
this.name = 'CornerSelected';
|
||||
}
|
||||
inherits(_CornerSelected, _State);
|
||||
var CornerSelected = new _CornerSelected();
|
||||
exports.CornerSelected = CornerSelected;
|
||||
|
||||
function _Selected1 () {
|
||||
this.name = 'Selected1';
|
||||
}
|
||||
inherits(_Selected1, _State);
|
||||
var Selected1 = new _Selected1();
|
||||
exports.Selected1 = Selected1;
|
||||
|
||||
function _Selected3 () {
|
||||
this.name = 'Selected3';
|
||||
}
|
||||
inherits(_Selected3, _State);
|
||||
var Selected3 = new _Selected3();
|
||||
exports.Selected3 = Selected3;
|
||||
|
||||
function _Move () {
|
||||
this.name = 'Move';
|
||||
}
|
||||
inherits(_Move, _State);
|
||||
var Move = new _Move();
|
||||
exports.Move = Move;
|
||||
|
||||
function _Ready () {
|
||||
this.name = 'Ready';
|
||||
}
|
||||
inherits(_Ready, _State);
|
||||
var Ready = new _Ready();
|
||||
exports.Ready = Ready;
|
||||
|
||||
function _EditLabel () {
|
||||
this.name = 'EditLabel';
|
||||
}
|
||||
inherits(_EditLabel, _State);
|
||||
var EditLabel = new _EditLabel();
|
||||
exports.EditLabel = EditLabel;
|
||||
|
||||
function _Selected2 () {
|
||||
this.name = 'Selected2';
|
||||
}
|
||||
inherits(_Selected2, _State);
|
||||
var Selected2 = new _Selected2();
|
||||
exports.Selected2 = Selected2;
|
||||
|
||||
function _Placing () {
|
||||
this.name = 'Placing';
|
||||
}
|
||||
inherits(_Placing, _State);
|
||||
var Placing = new _Placing();
|
||||
exports.Placing = Placing;
|
||||
|
||||
|
||||
_Resize.prototype.onMouseUp = function (controller, msg_type, $event) {
|
||||
|
||||
controller.changeState(Selected1);
|
||||
controller.handle_message(msg_type, $event);
|
||||
|
||||
};
|
||||
_Resize.prototype.onMouseUp.transitions = ['Selected1'];
|
||||
|
||||
_Resize.prototype.onMouseMove = function (controller) {
|
||||
|
||||
var groups = controller.scope.selected_groups;
|
||||
|
||||
var diffX = controller.scope.scaledX - controller.scope.pressedScaledX;
|
||||
var diffY = controller.scope.scaledY - controller.scope.pressedScaledY;
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
var membership_old_new = [];
|
||||
var previous_x1, previous_y1, previous_x2, previous_y2;
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
previous_x1 = groups[i].x1;
|
||||
previous_y1 = groups[i].y1;
|
||||
previous_x2 = groups[i].x2;
|
||||
previous_y2 = groups[i].y2;
|
||||
if (groups[i].selected_corner === models.TOP_LEFT) {
|
||||
groups[i].x1 = groups[i].x1 + diffX;
|
||||
groups[i].y1 = groups[i].y1 + diffY;
|
||||
}
|
||||
if (groups[i].selected_corner === models.BOTTOM_RIGHT) {
|
||||
groups[i].x2 = groups[i].x2 + diffX;
|
||||
groups[i].y2 = groups[i].y2 + diffY;
|
||||
}
|
||||
if (groups[i].selected_corner === models.TOP_RIGHT) {
|
||||
groups[i].x2 = groups[i].x2 + diffX;
|
||||
groups[i].y1 = groups[i].y1 + diffY;
|
||||
}
|
||||
if (groups[i].selected_corner === models.BOTTOM_LEFT) {
|
||||
groups[i].x1 = groups[i].x1 + diffX;
|
||||
groups[i].y2 = groups[i].y2 + diffY;
|
||||
}
|
||||
|
||||
membership_old_new = groups[i].update_membership(controller.scope.devices);
|
||||
for(j = 0; j < membership_old_new[0].length; j++) {
|
||||
membership_old_new[0][j].selected = false;
|
||||
}
|
||||
for(j = 0; j < membership_old_new[1].length; j++) {
|
||||
membership_old_new[1][j].selected = true;
|
||||
}
|
||||
|
||||
controller.scope.send_control_message(new messages.GroupMove(controller.scope.client_id,
|
||||
groups[i].id,
|
||||
groups[i].x1,
|
||||
groups[i].y1,
|
||||
groups[i].x2,
|
||||
groups[i].y2,
|
||||
previous_x1,
|
||||
previous_y1,
|
||||
previous_x2,
|
||||
previous_y2));
|
||||
controller.scope.send_control_message(new messages.GroupMembership(controller.scope.client_id,
|
||||
groups[i].id,
|
||||
membership_old_new[2]));
|
||||
}
|
||||
controller.scope.pressedScaledX = controller.scope.scaledX;
|
||||
controller.scope.pressedScaledY = controller.scope.scaledY;
|
||||
};
|
||||
|
||||
_Resize.prototype.end = function (controller) {
|
||||
|
||||
var groups = controller.scope.selected_groups;
|
||||
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
for(j = 0; j < groups[i].devices.length; j++) {
|
||||
groups[i].devices[j].selected = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_Start.prototype.start.transitions = ['Ready'];
|
||||
|
||||
_CornerSelected.prototype.start = function (controller) {
|
||||
|
||||
var groups = controller.scope.selected_groups;
|
||||
var i = 0;
|
||||
var x = controller.scope.scaledX;
|
||||
var y = controller.scope.scaledY;
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
groups[i].selected_corner = groups[i].select_corner(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
_CornerSelected.prototype.onMouseMove = function (controller) {
|
||||
|
||||
controller.changeState(Resize);
|
||||
};
|
||||
_CornerSelected.prototype.onMouseMove.transitions = ['Resize'];
|
||||
|
||||
_CornerSelected.prototype.onMouseUp = function (controller, msg_type, $event) {
|
||||
|
||||
controller.changeState(Selected1);
|
||||
controller.handle_message(msg_type, $event);
|
||||
};
|
||||
_CornerSelected.prototype.onMouseUp.transitions = ['Selected1'];
|
||||
|
||||
|
||||
|
||||
_Selected1.prototype.onMouseMove = function (controller) {
|
||||
|
||||
controller.changeState(Move);
|
||||
|
||||
};
|
||||
_Selected1.prototype.onMouseMove.transitions = ['Move'];
|
||||
|
||||
_Selected1.prototype.onMouseUp = function (controller) {
|
||||
|
||||
controller.changeState(Selected2);
|
||||
};
|
||||
_Selected1.prototype.onMouseUp.transitions = ['Selected2'];
|
||||
|
||||
|
||||
|
||||
_Selected3.prototype.onMouseMove = function (controller) {
|
||||
|
||||
controller.changeState(Move);
|
||||
|
||||
};
|
||||
_Selected3.prototype.onMouseMove.transitions = ['Move'];
|
||||
|
||||
_Selected3.prototype.onMouseUp = function (controller) {
|
||||
|
||||
controller.changeState(EditLabel);
|
||||
|
||||
};
|
||||
_Selected3.prototype.onMouseUp.transitions = ['EditLabel'];
|
||||
|
||||
|
||||
_Move.prototype.onMouseMove = function (controller) {
|
||||
|
||||
var groups = controller.scope.selected_groups;
|
||||
|
||||
var diffX = controller.scope.scaledX - controller.scope.pressedScaledX;
|
||||
var diffY = controller.scope.scaledY - controller.scope.pressedScaledY;
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
var membership_old_new = [];
|
||||
var previous_x1, previous_y1, previous_x2, previous_y2;
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
previous_x1 = groups[i].x1;
|
||||
previous_y1 = groups[i].y1;
|
||||
previous_x2 = groups[i].x2;
|
||||
previous_y2 = groups[i].y2;
|
||||
groups[i].x1 = groups[i].x1 + diffX;
|
||||
groups[i].y1 = groups[i].y1 + diffY;
|
||||
groups[i].x2 = groups[i].x2 + diffX;
|
||||
groups[i].y2 = groups[i].y2 + diffY;
|
||||
|
||||
membership_old_new = groups[i].update_membership(controller.scope.devices);
|
||||
for(j = 0; j < membership_old_new[0].length; j++) {
|
||||
membership_old_new[0][j].selected = false;
|
||||
}
|
||||
for(j = 0; j < membership_old_new[1].length; j++) {
|
||||
membership_old_new[1][j].selected = true;
|
||||
}
|
||||
|
||||
controller.scope.send_control_message(new messages.GroupMove(controller.scope.client_id,
|
||||
groups[i].id,
|
||||
groups[i].x1,
|
||||
groups[i].y1,
|
||||
groups[i].x2,
|
||||
groups[i].y2,
|
||||
previous_x1,
|
||||
previous_y1,
|
||||
previous_x2,
|
||||
previous_y2));
|
||||
controller.scope.send_control_message(new messages.GroupMembership(controller.scope.client_id,
|
||||
groups[i].id,
|
||||
membership_old_new[2]));
|
||||
}
|
||||
controller.scope.pressedScaledX = controller.scope.scaledX;
|
||||
controller.scope.pressedScaledY = controller.scope.scaledY;
|
||||
};
|
||||
|
||||
_Move.prototype.onMouseUp = function (controller) {
|
||||
|
||||
controller.changeState(Selected2);
|
||||
|
||||
};
|
||||
_Move.prototype.onMouseUp.transitions = ['Selected2'];
|
||||
|
||||
_Move.prototype.onMouseDown = function (controller) {
|
||||
|
||||
controller.changeState(Selected1);
|
||||
};
|
||||
_Move.prototype.onMouseDown.transitions = ['Selected1'];
|
||||
|
||||
_Move.prototype.end = function (controller) {
|
||||
|
||||
var groups = controller.scope.selected_groups;
|
||||
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
for(j = 0; j < groups[i].devices.length; j++) {
|
||||
groups[i].devices[j].selected = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_Ready.prototype.onMouseMove = function (controller, msg_type, $event) {
|
||||
|
||||
if (controller.scope.hide_groups) {
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
|
||||
for (i = 0; i < controller.scope.groups.length; i++) {
|
||||
controller.scope.groups[i].update_hightlighted(controller.scope.scaledX, controller.scope.scaledY);
|
||||
}
|
||||
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
};
|
||||
|
||||
|
||||
_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
|
||||
|
||||
if (controller.scope.hide_groups) {
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
var i = 0;
|
||||
|
||||
for (i = 0; i < controller.scope.groups.length; i++) {
|
||||
controller.scope.groups[i].selected = false;
|
||||
}
|
||||
controller.scope.selected_groups = [];
|
||||
|
||||
for (i = 0; i < controller.scope.groups.length; i++) {
|
||||
if (controller.scope.groups[i].has_corner_selected(controller.scope.scaledX, controller.scope.scaledY)) {
|
||||
controller.scope.clear_selections();
|
||||
if (controller.scope.selected_groups.indexOf(controller.scope.groups[i]) === -1) {
|
||||
controller.scope.selected_groups.push(controller.scope.groups[i]);
|
||||
}
|
||||
controller.scope.groups[i].selected = true;
|
||||
controller.changeState(CornerSelected);
|
||||
controller.scope.pressedX = controller.scope.mouseX;
|
||||
controller.scope.pressedY = controller.scope.mouseY;
|
||||
controller.scope.pressedScaledX = controller.scope.scaledX;
|
||||
controller.scope.pressedScaledY = controller.scope.scaledY;
|
||||
|
||||
return;
|
||||
} else if (controller.scope.groups[i].is_selected(controller.scope.scaledX, controller.scope.scaledY)) {
|
||||
controller.scope.clear_selections();
|
||||
if (controller.scope.selected_groups.indexOf(controller.scope.groups[i]) === -1) {
|
||||
controller.scope.selected_groups.push(controller.scope.groups[i]);
|
||||
}
|
||||
controller.scope.groups[i].selected = true;
|
||||
controller.changeState(Selected1);
|
||||
controller.scope.pressedX = controller.scope.mouseX;
|
||||
controller.scope.pressedY = controller.scope.mouseY;
|
||||
controller.scope.pressedScaledX = controller.scope.scaledX;
|
||||
controller.scope.pressedScaledY = controller.scope.scaledY;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
|
||||
};
|
||||
_Ready.prototype.onMouseDown.transitions = ['Selected1', 'CornerSelected'];
|
||||
|
||||
|
||||
_Ready.prototype.onNewGroup = function (controller) {
|
||||
controller.scope.hide_groups = false;
|
||||
controller.changeState(Placing);
|
||||
};
|
||||
_Ready.prototype.onNewGroup.transitions = ['Placing'];
|
||||
|
||||
|
||||
|
||||
_EditLabel.prototype.start = function (controller) {
|
||||
controller.scope.selected_groups[0].edit_label = true;
|
||||
};
|
||||
|
||||
_EditLabel.prototype.end = function (controller) {
|
||||
controller.scope.selected_groups[0].edit_label = false;
|
||||
};
|
||||
|
||||
|
||||
_EditLabel.prototype.onMouseDown = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_EditLabel.prototype.onMouseDown.transitions = ['Ready'];
|
||||
|
||||
|
||||
_EditLabel.prototype.onKeyDown = function (controller, msg_type, $event) {
|
||||
//Key codes found here:
|
||||
//https://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
|
||||
var item = controller.scope.selected_groups[0];
|
||||
var previous_name = item.name;
|
||||
if ($event.keyCode === 8 || $event.keyCode === 46) { //Delete
|
||||
item.name = item.name.slice(0, -1);
|
||||
} else if ($event.keyCode >= 48 && $event.keyCode <=90) { //Alphanumeric
|
||||
item.name += $event.key;
|
||||
} else if ($event.keyCode >= 186 && $event.keyCode <=222) { //Punctuation
|
||||
item.name += $event.key;
|
||||
} else if ($event.keyCode === 13) { //Enter
|
||||
controller.changeState(Selected2);
|
||||
} else if ($event.keyCode === 32) { //Space
|
||||
item.name += " ";
|
||||
} else {
|
||||
console.log($event.keyCode);
|
||||
}
|
||||
controller.scope.send_control_message(new messages.GroupLabelEdit(controller.scope.client_id,
|
||||
item.id,
|
||||
item.name,
|
||||
previous_name));
|
||||
};
|
||||
_EditLabel.prototype.onKeyDown.transitions = ['Selected2'];
|
||||
|
||||
_Selected2.prototype.onNewGroup = function (controller, msg_type, $event) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
controller.handle_message(msg_type, $event);
|
||||
|
||||
};
|
||||
_Selected2.prototype.onNewGroup.transitions = ['Ready'];
|
||||
|
||||
|
||||
_Selected2.prototype.onMouseDown = function (controller, msg_type, $event) {
|
||||
|
||||
controller.scope.pressedX = controller.scope.mouseX;
|
||||
controller.scope.pressedY = controller.scope.mouseY;
|
||||
controller.scope.pressedScaledX = controller.scope.scaledX;
|
||||
controller.scope.pressedScaledY = controller.scope.scaledY;
|
||||
|
||||
|
||||
|
||||
var groups = controller.scope.selected_groups;
|
||||
controller.scope.selected_groups = [];
|
||||
var i = 0;
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
if (groups[i].has_corner_selected(controller.scope.scaledX, controller.scope.scaledY)) {
|
||||
controller.scope.selected_groups = [];
|
||||
break;
|
||||
}
|
||||
else if (groups[i].is_selected(controller.scope.scaledX, controller.scope.scaledY)) {
|
||||
if (controller.scope.selected_groups.indexOf(groups[i]) === -1) {
|
||||
controller.scope.selected_groups.push(groups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (controller.scope.selected_groups.length > 0) {
|
||||
controller.changeState(Selected3);
|
||||
} else {
|
||||
controller.changeState(Ready);
|
||||
controller.handle_message(msg_type, $event);
|
||||
}
|
||||
|
||||
};
|
||||
_Selected2.prototype.onMouseDown.transitions = ['Ready', 'Selected3'];
|
||||
|
||||
|
||||
_Selected2.prototype.onKeyDown = function (controller, msg_type, $event) {
|
||||
|
||||
if ($event.keyCode === 8) {
|
||||
//Delete
|
||||
controller.changeState(Ready);
|
||||
|
||||
var i = 0;
|
||||
var index = -1;
|
||||
var groups = controller.scope.selected_groups;
|
||||
controller.scope.selected_groups = [];
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
index = controller.scope.groups.indexOf(groups[i]);
|
||||
if (index !== -1) {
|
||||
groups[i].selected = false;
|
||||
groups[i].remote_selected = false;
|
||||
controller.scope.groups.splice(index, 1);
|
||||
}
|
||||
controller.scope.send_control_message(new messages.GroupDestroy(controller.scope.client_id,
|
||||
groups[i].id,
|
||||
groups[i].x1,
|
||||
groups[i].y1,
|
||||
groups[i].x2,
|
||||
groups[i].y2,
|
||||
groups[i].name));
|
||||
}
|
||||
}
|
||||
};
|
||||
_Selected2.prototype.onKeyDown.transitions = ['Ready'];
|
||||
|
||||
|
||||
_Placing.prototype.onMouseDown = function (controller) {
|
||||
|
||||
var scope = controller.scope;
|
||||
var group = null;
|
||||
|
||||
scope.pressedX = scope.mouseX;
|
||||
scope.pressedY = scope.mouseY;
|
||||
scope.pressedScaledX = scope.scaledX;
|
||||
scope.pressedScaledY = scope.scaledY;
|
||||
|
||||
scope.clear_selections();
|
||||
|
||||
var id = scope.group_id_seq();
|
||||
|
||||
group = new models.Group(id,
|
||||
"Group" + id,
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
false);
|
||||
scope.send_control_message(new messages.GroupCreate(scope.client_id,
|
||||
group.id,
|
||||
group.x1,
|
||||
group.y1,
|
||||
group.x2,
|
||||
group.y2,
|
||||
group.name));
|
||||
|
||||
scope.groups.push(group);
|
||||
scope.selected_groups.push(group);
|
||||
group.selected = true;
|
||||
group.selected_corner = models.BOTTOM_RIGHT;
|
||||
|
||||
controller.changeState(Resize);
|
||||
};
|
||||
_Placing.prototype.onMouseDown.transitions = ['CornerSelected'];
|
||||
|
||||
5
awx/network_ui/static/network_ui/src/host.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/host.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function host () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/host.html' };
|
||||
}
|
||||
exports.host = host;
|
||||
104
awx/network_ui/static/network_ui/src/hotkeys.fsm.js
Normal file
104
awx/network_ui/static/network_ui/src/hotkeys.fsm.js
Normal file
@@ -0,0 +1,104 @@
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
var messages = require('./messages.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
|
||||
function _Enabled () {
|
||||
this.name = 'Enabled';
|
||||
}
|
||||
inherits(_Enabled, _State);
|
||||
var Enabled = new _Enabled();
|
||||
exports.Enabled = Enabled;
|
||||
|
||||
function _Start () {
|
||||
this.name = 'Start';
|
||||
}
|
||||
inherits(_Start, _State);
|
||||
var Start = new _Start();
|
||||
exports.Start = Start;
|
||||
|
||||
function _Disabled () {
|
||||
this.name = 'Disabled';
|
||||
}
|
||||
inherits(_Disabled, _State);
|
||||
var Disabled = new _Disabled();
|
||||
exports.Disabled = Disabled;
|
||||
|
||||
|
||||
|
||||
|
||||
_Enabled.prototype.onDisable = function (controller) {
|
||||
|
||||
controller.changeState(Disabled);
|
||||
|
||||
};
|
||||
_Enabled.prototype.onDisable.transitions = ['Disabled'];
|
||||
|
||||
|
||||
_Enabled.prototype.onKeyDown = function(controller, msg_type, $event) {
|
||||
|
||||
var scope = controller.scope;
|
||||
|
||||
if ($event.key === 'l') {
|
||||
scope.first_controller.handle_message("NewLink", $event);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event.key === 'd') {
|
||||
scope.debug.hidden = !scope.debug.hidden;
|
||||
return;
|
||||
}
|
||||
if ($event.key === 'p') {
|
||||
scope.cursor.hidden = !scope.cursor.hidden;
|
||||
return;
|
||||
}
|
||||
if ($event.key === 'b') {
|
||||
scope.hide_buttons = !scope.hide_buttons;
|
||||
return;
|
||||
}
|
||||
if ($event.key === 'i') {
|
||||
scope.hide_interfaces = !scope.hide_interfaces;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event.key === 'r') {
|
||||
scope.first_controller.handle_message("NewDevice", new messages.NewDevice("router"));
|
||||
return;
|
||||
}
|
||||
else if ($event.key === 's') {
|
||||
scope.first_controller.handle_message("NewDevice", new messages.NewDevice("switch"));
|
||||
return;
|
||||
}
|
||||
else if ($event.key === 'a') {
|
||||
scope.first_controller.handle_message("NewDevice", new messages.NewDevice("rack"));
|
||||
return;
|
||||
}
|
||||
else if ($event.key === 'h') {
|
||||
scope.first_controller.handle_message("NewDevice", new messages.NewDevice("host"));
|
||||
return;
|
||||
}
|
||||
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
};
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
controller.changeState(Enabled);
|
||||
|
||||
};
|
||||
_Start.prototype.start.transitions = ['Enabled'];
|
||||
|
||||
|
||||
|
||||
_Disabled.prototype.onEnable = function (controller) {
|
||||
|
||||
controller.changeState(Enabled);
|
||||
|
||||
};
|
||||
_Disabled.prototype.onEnable.transitions = ['Enabled'];
|
||||
|
||||
|
||||
5
awx/network_ui/static/network_ui/src/layer.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/layer.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function layer () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/layer.html' };
|
||||
}
|
||||
exports.layer = layer;
|
||||
5
awx/network_ui/static/network_ui/src/link.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/link.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function link () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/link.html' };
|
||||
}
|
||||
exports.link = link;
|
||||
@@ -46,19 +46,11 @@ exports.Selecting = Selecting;
|
||||
|
||||
|
||||
|
||||
_Ready.prototype.onKeyDown = function(controller, msg_type, $event) {
|
||||
|
||||
if ($event.key === 'l') {
|
||||
controller.handle_message("NewLink", $event);
|
||||
}
|
||||
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
};
|
||||
|
||||
_Ready.prototype.onNewLink = function (controller) {
|
||||
_Ready.prototype.onNewLink = function (controller, msg_type, message) {
|
||||
|
||||
controller.scope.clear_selections();
|
||||
controller.changeState(Selecting);
|
||||
controller.next_controller.handle_message(msg_type, message);
|
||||
};
|
||||
|
||||
|
||||
@@ -90,10 +82,10 @@ _Connecting.prototype.onMouseUp = function (controller) {
|
||||
if (selected_device !== null) {
|
||||
controller.scope.new_link.to_device = selected_device;
|
||||
i = controller.scope.new_link.to_device.interface_seq();
|
||||
to_device_interface = new models.Interface(i, "swp" + i);
|
||||
to_device_interface = new models.Interface(i, "eth" + i);
|
||||
controller.scope.new_link.to_device.interfaces.push(to_device_interface);
|
||||
i = controller.scope.new_link.from_device.interface_seq();
|
||||
from_device_interface = new models.Interface(i, "swp" + i);
|
||||
from_device_interface = new models.Interface(i, "eth" + i);
|
||||
controller.scope.new_link.from_device.interfaces.push(from_device_interface);
|
||||
to_device_interface.link = controller.scope.new_link;
|
||||
from_device_interface.link = controller.scope.new_link;
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
var app = require('./app.js');
|
||||
exports.app = app;
|
||||
var networkUI = require('./network.ui.app.js');
|
||||
var tower = require('./tower.app.js');
|
||||
exports.networkUI = networkUI.networkUI;
|
||||
exports.tower = tower.tower;
|
||||
|
||||
@@ -101,13 +101,15 @@ function LinkCreate(sender, id, from_device_id, to_device_id, from_interface_id,
|
||||
}
|
||||
exports.LinkCreate = LinkCreate;
|
||||
|
||||
function LinkDestroy(sender, id, from_id, to_id) {
|
||||
function LinkDestroy(sender, id, from_device_id, to_device_id, from_interface_id, to_interface_id, name) {
|
||||
this.msg_type = "LinkDestroy";
|
||||
this.id = id;
|
||||
this.sender = sender;
|
||||
this.from_id = from_id;
|
||||
this.to_id = to_id;
|
||||
this.name = '';
|
||||
this.name = name;
|
||||
this.from_device_id = from_device_id;
|
||||
this.to_device_id = to_device_id;
|
||||
this.from_interface_id = from_interface_id;
|
||||
this.to_interface_id = to_interface_id;
|
||||
}
|
||||
exports.LinkDestroy = LinkDestroy;
|
||||
|
||||
@@ -239,3 +241,79 @@ function ViewPort(sender, scale, panX, panY) {
|
||||
this.panY = panY;
|
||||
}
|
||||
exports.ViewPort = ViewPort;
|
||||
|
||||
function NewDevice(type) {
|
||||
this.type = type;
|
||||
}
|
||||
exports.NewDevice = NewDevice;
|
||||
|
||||
function GroupMove(sender, id, x1, y1, x2, y2, previous_x1, previous_y1, previous_x2, previous_y2) {
|
||||
this.msg_type = "GroupMove";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
this.previous_x1 = previous_x1;
|
||||
this.previous_y1 = previous_y1;
|
||||
this.previous_x2 = previous_x2;
|
||||
this.previous_y2 = previous_y2;
|
||||
}
|
||||
exports.GroupMove = GroupMove;
|
||||
|
||||
function GroupCreate(sender, id, x1, y1, x2, y2, name) {
|
||||
this.msg_type = "GroupCreate";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
this.name = name;
|
||||
}
|
||||
exports.GroupCreate = GroupCreate;
|
||||
|
||||
function GroupDestroy(sender, id, previous_x1, previous_y1, previous_x2, previous_y2, previous_name, previous_type) {
|
||||
this.msg_type = "GroupDestroy";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
this.previous_x1 = previous_x1;
|
||||
this.previous_y1 = previous_y1;
|
||||
this.previous_x2 = previous_x2;
|
||||
this.previous_y2 = previous_y2;
|
||||
this.previous_name = previous_name;
|
||||
this.previous_type = previous_type;
|
||||
}
|
||||
exports.GroupDestroy = GroupDestroy;
|
||||
|
||||
function GroupLabelEdit(sender, id, name, previous_name) {
|
||||
this.msg_type = "GroupLabelEdit";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.previous_name = previous_name;
|
||||
}
|
||||
exports.GroupLabelEdit = GroupLabelEdit;
|
||||
|
||||
function GroupSelected(sender, id) {
|
||||
this.msg_type = "GroupSelected";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
}
|
||||
exports.GroupSelected = GroupSelected;
|
||||
|
||||
function GroupUnSelected(sender, id) {
|
||||
this.msg_type = "GroupUnSelected";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
}
|
||||
exports.GroupUnSelected = GroupUnSelected;
|
||||
|
||||
function GroupMembership(sender, id, members) {
|
||||
this.msg_type = "GroupMembership";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
this.members = members;
|
||||
}
|
||||
exports.GroupMembership = GroupMembership;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var fsm = require('./fsm.js');
|
||||
var button = require('./button.js');
|
||||
var util = require('./util.js');
|
||||
var inherits = require('inherits');
|
||||
|
||||
function Device(id, name, x, y, type) {
|
||||
this.id = id;
|
||||
@@ -267,6 +268,36 @@ Button.prototype.is_selected = function (x, y) {
|
||||
|
||||
};
|
||||
|
||||
|
||||
function ToggleButton(name, x, y, width, height, toggle_callback, untoggle_callback, default_toggled) {
|
||||
this.name = name;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.callback = this.toggle;
|
||||
this.is_pressed = default_toggled;
|
||||
this.toggled = default_toggled;
|
||||
this.toggle_callback = toggle_callback;
|
||||
this.untoggle_callback = untoggle_callback;
|
||||
this.mouse_over = false;
|
||||
this.fsm = new fsm.FSMController(this, button.Start, null);
|
||||
}
|
||||
inherits(ToggleButton, Button);
|
||||
exports.ToggleButton = ToggleButton;
|
||||
|
||||
ToggleButton.prototype.toggle = function () {
|
||||
this.toggled = !this.toggled;
|
||||
this.is_pressed = this.toggled;
|
||||
|
||||
if (this.toggled) {
|
||||
this.toggle_callback();
|
||||
} else {
|
||||
this.untoggle_callback();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function Task(id, name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -276,3 +307,189 @@ function Task(id, name) {
|
||||
exports.Task = Task;
|
||||
|
||||
Task.prototype.describeArc = util.describeArc;
|
||||
|
||||
|
||||
function Group(id, name, x1, y1, x2, y2, selected) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
this.selected = selected;
|
||||
this.highlighted = false;
|
||||
this.fsm = null;
|
||||
this.selected_corner = null;
|
||||
this.devices = [];
|
||||
}
|
||||
exports.Group = Group;
|
||||
|
||||
Group.prototype.update_hightlighted = function (x, y) {
|
||||
|
||||
this.highlighted = this.is_highlighted(x, y);
|
||||
};
|
||||
|
||||
Group.prototype.is_highlighted = function (x, y) {
|
||||
|
||||
return (x > this.left_extent() &&
|
||||
x < this.right_extent() &&
|
||||
y > this.top_extent() &&
|
||||
y < this.bottom_extent());
|
||||
|
||||
};
|
||||
|
||||
var TOP_LEFT = 0;
|
||||
exports.TOP_LEFT = TOP_LEFT;
|
||||
var TOP_RIGHT = 1;
|
||||
exports.TOP_RIGHT = TOP_RIGHT;
|
||||
var BOTTOM_LEFT = 2;
|
||||
exports.BOTTOM_LEFT = BOTTOM_LEFT;
|
||||
var BOTTOM_RIGHT = 3;
|
||||
exports.BOTTOM_RIGHT = BOTTOM_RIGHT;
|
||||
|
||||
Group.prototype.has_corner_selected = function (x, y) {
|
||||
|
||||
if (x > this.left_extent() &&
|
||||
x < this.left_extent() + 10 &&
|
||||
y > this.top_extent() &&
|
||||
y < this.top_extent() + 10) {
|
||||
return true;
|
||||
}
|
||||
if (x > this.left_extent() &&
|
||||
x < this.left_extent() + 10 &&
|
||||
y > this.bottom_extent() - 10 &&
|
||||
y < this.bottom_extent()) {
|
||||
return true;
|
||||
}
|
||||
if (x > this.right_extent() - 10 &&
|
||||
x < this.right_extent() &&
|
||||
y > this.bottom_extent() - 10 &&
|
||||
y < this.bottom_extent()) {
|
||||
return true;
|
||||
}
|
||||
if (x > this.right_extent() - 10 &&
|
||||
x < this.right_extent() &&
|
||||
y > this.top_extent() &&
|
||||
y < this.top_extent() + 10) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Group.prototype.select_corner = function (x, y) {
|
||||
|
||||
var corners = [[util.distance(this.x1, this.y1, x, y), TOP_LEFT],
|
||||
[util.distance(this.x2, this.y2, x, y), BOTTOM_RIGHT],
|
||||
[util.distance(this.x1, this.y2, x, y), BOTTOM_LEFT],
|
||||
[util.distance(this.x2, this.y1, x, y), TOP_RIGHT]];
|
||||
|
||||
console.log(corners);
|
||||
|
||||
corners.sort(function(a, b) {
|
||||
return a[0] - b[0];
|
||||
});
|
||||
|
||||
if (corners[0][0] > 30) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return corners[0][1];
|
||||
};
|
||||
|
||||
Group.prototype.is_selected = function (x, y) {
|
||||
|
||||
if (util.pDistance(x,
|
||||
y,
|
||||
this.left_extent(),
|
||||
this.top_extent(),
|
||||
this.left_extent(),
|
||||
this.bottom_extent()) < 10) {
|
||||
return true;
|
||||
}
|
||||
if (util.pDistance(x,
|
||||
y,
|
||||
this.left_extent(),
|
||||
this.top_extent(),
|
||||
this.right_extent(),
|
||||
this.top_extent()) < 10) {
|
||||
return true;
|
||||
}
|
||||
if (util.pDistance(x,
|
||||
y,
|
||||
this.left_extent(),
|
||||
this.top_extent(),
|
||||
this.right_extent(),
|
||||
this.top_extent()) < 40 && y > this.top_extent()) {
|
||||
return true;
|
||||
}
|
||||
if (util.pDistance(x,
|
||||
y,
|
||||
this.right_extent(),
|
||||
this.bottom_extent(),
|
||||
this.right_extent(),
|
||||
this.top_extent()) < 10) {
|
||||
return true;
|
||||
}
|
||||
if (util.pDistance(x,
|
||||
y,
|
||||
this.right_extent(),
|
||||
this.bottom_extent(),
|
||||
this.left_extent(),
|
||||
this.bottom_extent()) < 10) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Group.prototype.width = function (scaledX) {
|
||||
var x2 = this.x2 !== null ? this.x2 : scaledX;
|
||||
return Math.abs(this.x1 - x2);
|
||||
};
|
||||
|
||||
Group.prototype.height = function (scaledY) {
|
||||
var y2 = this.y2 !== null ? this.y2 : scaledY;
|
||||
return Math.abs(this.y1 - y2);
|
||||
};
|
||||
|
||||
Group.prototype.top_extent = function (scaledY) {
|
||||
var y2 = this.y2 !== null ? this.y2 : scaledY;
|
||||
return (this.y1 < y2? this.y1 : y2);
|
||||
};
|
||||
|
||||
Group.prototype.left_extent = function (scaledX) {
|
||||
var x2 = this.x2 !== null ? this.x2 : scaledX;
|
||||
return (this.x1 < x2? this.x1 : x2);
|
||||
};
|
||||
|
||||
Group.prototype.bottom_extent = function (scaledY) {
|
||||
var y2 = this.y2 !== null ? this.y2 : scaledY;
|
||||
return (this.y1 > y2? this.y1 : y2);
|
||||
};
|
||||
|
||||
Group.prototype.right_extent = function (scaledX) {
|
||||
var x2 = this.x2 !== null ? this.x2 : scaledX;
|
||||
return (this.x1 > x2? this.x1 : x2);
|
||||
};
|
||||
|
||||
Group.prototype.update_membership = function (devices) {
|
||||
var i = 0;
|
||||
var y1 = this.top_extent();
|
||||
var x1 = this.left_extent();
|
||||
var y2 = this.bottom_extent();
|
||||
var x2 = this.right_extent();
|
||||
var old_devices = this.devices;
|
||||
var device_ids = [];
|
||||
this.devices = [];
|
||||
for (i = 0; i < devices.length; i++) {
|
||||
if (devices[i].x > x1 &&
|
||||
devices[i].y > y1 &&
|
||||
devices[i].x < x2 &&
|
||||
devices[i].y < y2) {
|
||||
this.devices.push(devices[i]);
|
||||
device_ids.push(devices[i].id);
|
||||
}
|
||||
}
|
||||
return [old_devices, this.devices, device_ids];
|
||||
};
|
||||
|
||||
@@ -59,6 +59,75 @@ inherits(_EditLabel, _State);
|
||||
var EditLabel = new _EditLabel();
|
||||
exports.EditLabel = EditLabel;
|
||||
|
||||
|
||||
function _Placing () {
|
||||
this.name = 'Placing';
|
||||
}
|
||||
inherits(_Placing, _State);
|
||||
var Placing = new _Placing();
|
||||
exports.Placing = Placing;
|
||||
|
||||
_Ready.prototype.onNewDevice = function (controller, msg_type, message) {
|
||||
|
||||
var scope = controller.scope;
|
||||
var device = null;
|
||||
var id = null;
|
||||
|
||||
scope.pressedX = scope.mouseX;
|
||||
scope.pressedY = scope.mouseY;
|
||||
scope.pressedScaledX = scope.scaledX;
|
||||
scope.pressedScaledY = scope.scaledY;
|
||||
|
||||
scope.clear_selections();
|
||||
|
||||
if (message.type === "router") {
|
||||
id = controller.scope.device_id_seq();
|
||||
device = new models.Device(id,
|
||||
"Router" + id,
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"router");
|
||||
}
|
||||
else if (message.type === "switch") {
|
||||
id = controller.scope.device_id_seq();
|
||||
device = new models.Device(id,
|
||||
"Switch" + id,
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"switch");
|
||||
}
|
||||
else if (message.type === "rack") {
|
||||
id = controller.scope.device_id_seq();
|
||||
device = new models.Device(id,
|
||||
"Rack" + id,
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"rack");
|
||||
}
|
||||
else if (message.type === "host") {
|
||||
id = controller.scope.device_id_seq();
|
||||
device = new models.Device(id,
|
||||
"Host" + id,
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"host");
|
||||
}
|
||||
|
||||
if (device !== null) {
|
||||
scope.devices.push(device);
|
||||
scope.send_control_message(new messages.DeviceCreate(scope.client_id,
|
||||
device.id,
|
||||
device.x,
|
||||
device.y,
|
||||
device.name,
|
||||
device.type));
|
||||
scope.selected_devices.push(device);
|
||||
device.selected = true;
|
||||
controller.changeState(Placing);
|
||||
}
|
||||
};
|
||||
_Ready.prototype.onNewDevice.transitions = ['Placing'];
|
||||
|
||||
_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
|
||||
|
||||
var last_selected = controller.scope.select_items($event.shiftKey);
|
||||
@@ -78,53 +147,6 @@ _Ready.prototype.onMouseDown.transitions = ['Selected1'];
|
||||
_Ready.prototype.onTouchStart = _Ready.prototype.onMouseDown;
|
||||
|
||||
|
||||
_Ready.prototype.onKeyDown = function(controller, msg_type, $event) {
|
||||
|
||||
var scope = controller.scope;
|
||||
var device = null;
|
||||
|
||||
if ($event.key === 'r') {
|
||||
device = new models.Device(controller.scope.device_id_seq(),
|
||||
"Router",
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"router");
|
||||
}
|
||||
else if ($event.key === 's') {
|
||||
device = new models.Device(controller.scope.device_id_seq(),
|
||||
"Switch",
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"switch");
|
||||
}
|
||||
else if ($event.key === 'a') {
|
||||
device = new models.Device(controller.scope.device_id_seq(),
|
||||
"Rack",
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"rack");
|
||||
}
|
||||
else if ($event.key === 'h') {
|
||||
device = new models.Device(controller.scope.device_id_seq(),
|
||||
"Host",
|
||||
scope.scaledX,
|
||||
scope.scaledY,
|
||||
"host");
|
||||
}
|
||||
|
||||
if (device !== null) {
|
||||
scope.devices.push(device);
|
||||
scope.send_control_message(new messages.DeviceCreate(scope.client_id,
|
||||
device.id,
|
||||
device.x,
|
||||
device.y,
|
||||
device.name,
|
||||
device.type));
|
||||
}
|
||||
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
};
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
@@ -133,6 +155,12 @@ _Start.prototype.start = function (controller) {
|
||||
_Start.prototype.start.transitions = ['Ready'];
|
||||
|
||||
|
||||
_Selected2.prototype.onNewDevice = function (controller, msg_type, message) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
controller.handle_message(msg_type, message);
|
||||
};
|
||||
_Selected2.prototype.onNewDevice.transitions = ['Ready'];
|
||||
|
||||
_Selected2.prototype.onMouseDown = function (controller, msg_type, $event) {
|
||||
|
||||
@@ -182,9 +210,25 @@ _Selected2.prototype.onKeyDown = function (controller, msg_type, $event) {
|
||||
var j = 0;
|
||||
var index = -1;
|
||||
var devices = controller.scope.selected_devices;
|
||||
var links = controller.scope.selected_links;
|
||||
var all_links = controller.scope.links.slice();
|
||||
controller.scope.selected_devices = [];
|
||||
controller.scope.selected_links = [];
|
||||
for (i = 0; i < links.length; i++) {
|
||||
index = controller.scope.links.indexOf(links[i]);
|
||||
if (index !== -1) {
|
||||
links[i].selected = false;
|
||||
links[i].remote_selected = false;
|
||||
controller.scope.links.splice(index, 1);
|
||||
controller.scope.send_control_message(new messages.LinkDestroy(controller.scope.client_id,
|
||||
links[i].id,
|
||||
links[i].from_device.id,
|
||||
links[i].to_device.id,
|
||||
links[i].from_interface.id,
|
||||
links[i].to_interface.id,
|
||||
links[i].name));
|
||||
}
|
||||
}
|
||||
for (i = 0; i < devices.length; i++) {
|
||||
index = controller.scope.devices.indexOf(devices[i]);
|
||||
if (index !== -1) {
|
||||
@@ -207,6 +251,8 @@ _Selected2.prototype.onKeyDown = function (controller, msg_type, $event) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
};
|
||||
_Selected2.prototype.onKeyDown.transitions = ['Ready'];
|
||||
|
||||
@@ -234,12 +280,14 @@ _Selected1.prototype.onMouseDown = util.noop;
|
||||
_Move.prototype.onMouseMove = function (controller) {
|
||||
|
||||
var devices = controller.scope.selected_devices;
|
||||
var groups = controller.scope.groups;
|
||||
|
||||
var diffX = controller.scope.scaledX - controller.scope.pressedScaledX;
|
||||
var diffY = controller.scope.scaledY - controller.scope.pressedScaledY;
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
var previous_x, previous_y;
|
||||
var membership_old_new;
|
||||
for (i = 0; i < devices.length; i++) {
|
||||
previous_x = devices[i].x;
|
||||
previous_y = devices[i].y;
|
||||
@@ -261,6 +309,15 @@ _Move.prototype.onMouseMove = function (controller) {
|
||||
}
|
||||
controller.scope.pressedScaledX = controller.scope.scaledX;
|
||||
controller.scope.pressedScaledY = controller.scope.scaledY;
|
||||
|
||||
|
||||
//TODO: Improve the performance of this code from O(n^2) to O(n) or better
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
membership_old_new = groups[i].update_membership(controller.scope.devices);
|
||||
controller.scope.send_control_message(new messages.GroupMembership(controller.scope.client_id,
|
||||
groups[i].id,
|
||||
membership_old_new[2]));
|
||||
}
|
||||
};
|
||||
|
||||
_Move.prototype.onTouchMove = _Move.prototype.onMouseMove;
|
||||
@@ -268,13 +325,19 @@ _Move.prototype.onTouchMove = _Move.prototype.onMouseMove;
|
||||
|
||||
_Move.prototype.onMouseUp = function (controller, msg_type, $event) {
|
||||
|
||||
controller.changeState(Selected2);
|
||||
controller.changeState(Selected1);
|
||||
controller.handle_message(msg_type, $event);
|
||||
};
|
||||
_Move.prototype.onMouseUp.transitions = ['Selected2'];
|
||||
_Move.prototype.onMouseUp.transitions = ['Selected1'];
|
||||
|
||||
_Move.prototype.onTouchEnd = _Move.prototype.onMouseUp;
|
||||
|
||||
_Move.prototype.onMouseDown = function (controller) {
|
||||
|
||||
controller.changeState(Selected1);
|
||||
};
|
||||
_Move.prototype.onMouseDown.transitions = ['Selected1'];
|
||||
|
||||
_Selected3.prototype.onMouseUp = function (controller) {
|
||||
controller.changeState(EditLabel);
|
||||
};
|
||||
@@ -346,3 +409,20 @@ _EditLabel.prototype.onKeyDown = function (controller, msg_type, $event) {
|
||||
}
|
||||
};
|
||||
_EditLabel.prototype.onKeyDown.transitions = ['Selected2'];
|
||||
|
||||
|
||||
_Placing.prototype.onMouseDown = function (controller) {
|
||||
|
||||
controller.changeState(Selected1);
|
||||
|
||||
};
|
||||
_Placing.prototype.onMouseDown.transitions = ['Selected1'];
|
||||
|
||||
_Placing.prototype.onMouseMove = function (controller) {
|
||||
|
||||
controller.changeState(Move);
|
||||
|
||||
};
|
||||
_Placing.prototype.onMouseMove.transitions = ['Move'];
|
||||
|
||||
|
||||
|
||||
46
awx/network_ui/static/network_ui/src/network.ui.app.js
Normal file
46
awx/network_ui/static/network_ui/src/network.ui.app.js
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
//console.log = function () { };
|
||||
var angular = require('angular');
|
||||
var NetworkUIController = require('./network.ui.controller.js');
|
||||
var cursor = require('./cursor.directive.js');
|
||||
var touch = require('./touch.directive.js');
|
||||
var router = require('./router.directive.js');
|
||||
var switchd = require('./switch.directive.js');
|
||||
var host = require('./host.directive.js');
|
||||
var link = require('./link.directive.js');
|
||||
var rack = require('./rack.directive.js');
|
||||
var group = require('./group.directive.js');
|
||||
var defaultd = require('./default.directive.js');
|
||||
var quadrants = require('./quadrants.directive.js');
|
||||
var stencil = require('./stencil.directive.js');
|
||||
var layer = require('./layer.directive.js');
|
||||
var button = require('./button.directive.js');
|
||||
var statusLight = require('./status.light.directive.js');
|
||||
var taskStatus = require('./task.status.directive.js');
|
||||
var debug = require('./debug.directive.js');
|
||||
var awxNetworkUI = require('./network.ui.directive.js');
|
||||
|
||||
var networkUI = angular.module('networkUI', [
|
||||
'monospaced.mousewheel',
|
||||
'ngTouch'
|
||||
])
|
||||
.controller('NetworkUIController', NetworkUIController.NetworkUIController)
|
||||
.directive('awxNetCursor', cursor.cursor)
|
||||
.directive('awxNetTouch', touch.touch)
|
||||
.directive('awxNetDebug', debug.debug)
|
||||
.directive('awxNetRouter', router.router)
|
||||
.directive('awxNetSwitch', switchd.switchd)
|
||||
.directive('awxNetHost', host.host)
|
||||
.directive('awxNetLink', link.link)
|
||||
.directive('awxNetRack', rack.rack)
|
||||
.directive('awxNetGroup', group.group)
|
||||
.directive('awxNetDefault', defaultd.defaultd)
|
||||
.directive('awxNetQuadrants', quadrants.quadrants)
|
||||
.directive('awxNetStencil', stencil.stencil)
|
||||
.directive('awxNetLayer', layer.layer)
|
||||
.directive('awxNetButton', button.button)
|
||||
.directive('awxNetStatusLight', statusLight.statusLight)
|
||||
.directive('awxNetTaskStatus', taskStatus.taskStatus)
|
||||
.directive('awxNetworkUi', awxNetworkUI.awxNetworkUI);
|
||||
|
||||
exports.networkUI = networkUI;
|
||||
@@ -1,28 +1,41 @@
|
||||
|
||||
|
||||
//console.log = function () { };
|
||||
var app = angular.module('triangular', ['monospaced.mousewheel', 'ngTouch']);
|
||||
var angular = require('angular');
|
||||
var fsm = require('./fsm.js');
|
||||
var null_fsm = require('./null.fsm.js');
|
||||
var hotkeys = require('./hotkeys.fsm.js');
|
||||
var view = require('./view.js');
|
||||
var move = require('./move.js');
|
||||
var link = require('./link.js');
|
||||
var group = require('./group.js');
|
||||
var buttons = require('./buttons.js');
|
||||
var time = require('./time.js');
|
||||
var util = require('./util.js');
|
||||
var models = require('./models.js');
|
||||
var messages = require('./messages.js');
|
||||
var svg_crowbar = require('../vendor/svg-crowbar.js');
|
||||
var ReconnectingWebSocket = require('reconnectingwebsocket');
|
||||
|
||||
app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
var NetworkUIController = function($scope, $document, $location, $window) {
|
||||
|
||||
window.scope = $scope;
|
||||
|
||||
$scope.api_token = '';
|
||||
$scope.disconnected = false;
|
||||
|
||||
$scope.topology_id = $location.search().topology_id || 0;
|
||||
// Create a web socket to connect to the backend server
|
||||
$scope.control_socket = new window.ReconnectingWebSocket("ws://" + window.location.host + "/network_ui/topology?topology_id=" + $scope.topology_id,
|
||||
|
||||
if (!$scope.disconnected) {
|
||||
$scope.control_socket = new ReconnectingWebSocket("ws://" + window.location.host + "/network_ui/topology?topology_id=" + $scope.topology_id,
|
||||
null,
|
||||
{debug: false, reconnectInterval: 300});
|
||||
} else {
|
||||
$scope.control_socket = {
|
||||
on_message: util.noop
|
||||
};
|
||||
}
|
||||
$scope.history = [];
|
||||
$scope.client_id = 0;
|
||||
$scope.onMouseDownResult = "";
|
||||
@@ -48,11 +61,15 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.selected_links = [];
|
||||
$scope.selected_interfaces = [];
|
||||
$scope.selected_items = [];
|
||||
$scope.selected_groups = [];
|
||||
$scope.new_link = null;
|
||||
$scope.view_controller = new fsm.FSMController($scope, view.Start, null);
|
||||
$scope.null_controller = new fsm.FSMController($scope, null_fsm.Start, null);
|
||||
$scope.hotkeys_controller = new fsm.FSMController($scope, hotkeys.Start, $scope.null_controller);
|
||||
$scope.view_controller = new fsm.FSMController($scope, view.Start, $scope.hotkeys_controller);
|
||||
$scope.move_controller = new fsm.FSMController($scope, move.Start, $scope.view_controller);
|
||||
$scope.link_controller = new fsm.FSMController($scope, link.Start, $scope.move_controller);
|
||||
$scope.buttons_controller = new fsm.FSMController($scope, buttons.Start, $scope.link_controller);
|
||||
$scope.group_controller = new fsm.FSMController($scope, group.Start, $scope.link_controller);
|
||||
$scope.buttons_controller = new fsm.FSMController($scope, buttons.Start, $scope.group_controller);
|
||||
$scope.time_controller = new fsm.FSMController($scope, time.Start, $scope.buttons_controller);
|
||||
$scope.first_controller = $scope.time_controller;
|
||||
$scope.last_key = "";
|
||||
@@ -62,12 +79,15 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
|
||||
$scope.debug = {'hidden': true};
|
||||
$scope.hide_buttons = false;
|
||||
$scope.hide_links = false;
|
||||
$scope.hide_interfaces = false;
|
||||
$scope.hide_groups = false;
|
||||
$scope.graph = {'width': window.innerWidth,
|
||||
'right_column': window.innerWidth - 300,
|
||||
'height': window.innerHeight};
|
||||
$scope.device_id_seq = util.natural_numbers(0);
|
||||
$scope.link_id_seq = util.natural_numbers(0);
|
||||
$scope.group_id_seq = util.natural_numbers(0);
|
||||
$scope.message_id_seq = util.natural_numbers(0);
|
||||
$scope.time_pointer = -1;
|
||||
$scope.frame = 0;
|
||||
@@ -75,25 +95,10 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.replay = false;
|
||||
$scope.touch_data = {};
|
||||
$scope.touches = [];
|
||||
|
||||
|
||||
$scope.devices = [
|
||||
];
|
||||
|
||||
$scope.stencils = [
|
||||
//{"name": "router", "size":50, 'x':10, 'y':100},
|
||||
//{"name": "switch", "size":50, 'x':10, 'y':160},
|
||||
//{"name": "rack", "size":50, 'x':10, 'y':220},
|
||||
];
|
||||
|
||||
$scope.layers = [
|
||||
//{"name": "Layer 3", "size":60, 'x':window.innerWidth - 70, 'y':10},
|
||||
//{"name": "Layer 2", "size":60, 'x':window.innerWidth - 70, 'y':80},
|
||||
//{"name": "Layer 1", "size":60, 'x':window.innerWidth - 70, 'y':150},
|
||||
];
|
||||
|
||||
$scope.links = [
|
||||
];
|
||||
$scope.devices = [];
|
||||
$scope.stencils = [];
|
||||
$scope.links = [];
|
||||
$scope.groups = [];
|
||||
|
||||
|
||||
|
||||
@@ -117,10 +122,12 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
var j = 0;
|
||||
var devices = $scope.devices;
|
||||
var links = $scope.links;
|
||||
var groups = $scope.groups;
|
||||
$scope.selected_items = [];
|
||||
$scope.selected_devices = [];
|
||||
$scope.selected_links = [];
|
||||
$scope.selected_interfaces = [];
|
||||
$scope.selected_groups = [];
|
||||
for (i = 0; i < devices.length; i++) {
|
||||
for (j = 0; j < devices[i].interfaces.length; j++) {
|
||||
devices[i].interfaces[j].selected = false;
|
||||
@@ -136,6 +143,9 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
}
|
||||
links[i].selected = false;
|
||||
}
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
groups[i].selected = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.select_items = function (multiple_selection) {
|
||||
@@ -426,43 +436,96 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.onDiscoverButton = function (button) {
|
||||
console.log(button.name);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/12/launch/", true);
|
||||
xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/7/launch/", true);
|
||||
xhr.onload = function () {
|
||||
console.log(xhr.readyState);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
console.error(xhr.statusText);
|
||||
};
|
||||
xhr.setRequestHeader('Authorization', 'Token ' + $scope.api_token);
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
$scope.onConfigureButton = function (button) {
|
||||
console.log(button.name);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/11/launch/", true);
|
||||
xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/9/launch/", true);
|
||||
xhr.onload = function () {
|
||||
console.log(xhr.readyState);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
console.error(xhr.statusText);
|
||||
};
|
||||
xhr.setRequestHeader('Authorization', 'Token ' + $scope.api_token);
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
$scope.onTogglePhysical = function () {
|
||||
$scope.hide_links = false;
|
||||
};
|
||||
|
||||
$scope.onUnTogglePhysical = function () {
|
||||
$scope.hide_links = true;
|
||||
};
|
||||
|
||||
$scope.onToggleGroup = function () {
|
||||
$scope.hide_groups = false;
|
||||
};
|
||||
|
||||
$scope.onUnToggleGroup = function () {
|
||||
$scope.hide_groups = true;
|
||||
$scope.group_controller.changeState(group.Ready);
|
||||
};
|
||||
|
||||
// Buttons
|
||||
|
||||
$scope.buttons = [
|
||||
new models.Button("Deploy", 10, 10, 60, 50, $scope.onDeployButton),
|
||||
new models.Button("Destroy", 80, 10, 60, 50, $scope.onDestroyButton),
|
||||
new models.Button("Record", 150, 10, 60, 50, $scope.onRecordButton),
|
||||
new models.Button("Export", 220, 10, 60, 50, $scope.onExportButton),
|
||||
new models.Button("Discover", 290, 10, 80, 50, $scope.onDiscoverButton),
|
||||
new models.Button("Layout", 380, 10, 60, 50, $scope.onLayoutButton),
|
||||
new models.Button("Configure", 450, 10, 80, 50, $scope.onConfigureButton)
|
||||
new models.Button("DEPLOY", 10, 10, 70, 30, $scope.onDeployButton),
|
||||
new models.Button("DESTROY", 90, 10, 80, 30, $scope.onDestroyButton),
|
||||
new models.Button("RECORD", 180, 10, 80, 30, $scope.onRecordButton),
|
||||
new models.Button("EXPORT", 270, 10, 70, 30, $scope.onExportButton),
|
||||
new models.Button("DISCOVER", 350, 10, 80, 30, $scope.onDiscoverButton),
|
||||
new models.Button("LAYOUT", 440, 10, 70, 30, $scope.onLayoutButton),
|
||||
new models.Button("CONFIGURE", 520, 10, 90, 30, $scope.onConfigureButton)
|
||||
];
|
||||
|
||||
var LAYERS_X = 160;
|
||||
|
||||
$scope.layers = [
|
||||
new models.ToggleButton("APPLICATION", $scope.graph.width - LAYERS_X, 10, 120, 30, util.noop, util.noop, true),
|
||||
new models.ToggleButton("PRESENTATION", $scope.graph.width - LAYERS_X, 50, 120, 30, util.noop, util.noop, true),
|
||||
new models.ToggleButton("SESSION", $scope.graph.width - LAYERS_X, 90, 120, 30, util.noop, util.noop, true),
|
||||
new models.ToggleButton("TRANSPORT", $scope.graph.width - LAYERS_X, 130, 120, 30, util.noop, util.noop, true),
|
||||
new models.ToggleButton("NETWORK", $scope.graph.width - LAYERS_X, 170, 120, 30, util.noop, util.noop, true),
|
||||
new models.ToggleButton("DATA-LINK", $scope.graph.width - LAYERS_X, 210, 120, 30, util.noop, util.noop, true),
|
||||
new models.ToggleButton("PHYSICAL",
|
||||
$scope.graph.width - LAYERS_X, 250, 120, 30,
|
||||
$scope.onTogglePhysical,
|
||||
$scope.onUnTogglePhysical,
|
||||
true),
|
||||
new models.ToggleButton("GROUP",
|
||||
$scope.graph.width - LAYERS_X, 290, 120, 30,
|
||||
$scope.onToggleGroup,
|
||||
$scope.onUnToggleGroup,
|
||||
true)
|
||||
];
|
||||
|
||||
var STENCIL_X = 10;
|
||||
var STENCIL_Y = 100;
|
||||
var STENCIL_SPACING = 40;
|
||||
|
||||
$scope.stencils = [
|
||||
new models.Button("Switch", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 0, 70, 30, function () {$scope.first_controller.handle_message("NewDevice", new messages.NewDevice("switch"));}),
|
||||
new models.Button("Router", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 1, 70, 30, function () {$scope.first_controller.handle_message("NewDevice", new messages.NewDevice("router"));}),
|
||||
new models.Button("Host", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 2, 70, 30, function () {$scope.first_controller.handle_message("NewDevice", new messages.NewDevice("host"));}),
|
||||
new models.Button("Link", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 3, 70, 30, function () { $scope.first_controller.handle_message("NewLink");}),
|
||||
new models.Button("Group", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 4, 70, 30, function () { $scope.first_controller.handle_message("NewGroup");}),
|
||||
];
|
||||
|
||||
$scope.all_buttons = [];
|
||||
$scope.all_buttons.extend($scope.buttons);
|
||||
$scope.all_buttons.extend($scope.layers);
|
||||
$scope.all_buttons.extend($scope.stencils);
|
||||
|
||||
$scope.onTaskStatus = function(data) {
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
@@ -513,18 +576,42 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
var k = 0;
|
||||
var device = null;
|
||||
var keys = null;
|
||||
var peers = null;
|
||||
var ptm = null;
|
||||
var intf = null;
|
||||
for (i = 0; i < $scope.devices.length; i++) {
|
||||
device = $scope.devices[i];
|
||||
if (device.name === data.key) {
|
||||
keys = Object.keys(data.value.ansible_local.ptm);
|
||||
for (j = 0; j < keys.length; j++) {
|
||||
ptm = data.value.ansible_local.ptm[keys[j]];
|
||||
for (k = 0; k < device.interfaces.length; k++) {
|
||||
intf = device.interfaces[k];
|
||||
if (intf.name === ptm.port) {
|
||||
intf.link.status = ptm['cbl status'] === 'pass';
|
||||
|
||||
//Check PTM
|
||||
if (data.value.ansible_local !== undefined &&
|
||||
data.value.ansible_local.ptm !== undefined) {
|
||||
keys = Object.keys(data.value.ansible_local.ptm);
|
||||
for (j = 0; j < keys.length; j++) {
|
||||
ptm = data.value.ansible_local.ptm[keys[j]];
|
||||
for (k = 0; k < device.interfaces.length; k++) {
|
||||
intf = device.interfaces[k];
|
||||
if (intf.name === ptm.port) {
|
||||
intf.link.status = ptm['cbl status'] === 'pass';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check LLDP
|
||||
if (data.value.ansible_net_neighbors !== undefined) {
|
||||
keys = Object.keys(data.value.ansible_net_neighbors);
|
||||
for (j = 0; j < keys.length; j++) {
|
||||
peers = data.value.ansible_net_neighbors[keys[j]];
|
||||
for (k = 0; k < peers.length; k++) {
|
||||
intf = $scope.getDeviceInterface(device.name, keys[j]);
|
||||
if (intf !== null && intf.link !== null) {
|
||||
if (intf.link.to_interface === intf) {
|
||||
intf.link.status = ($scope.getDeviceInterface(peers[k].host, peers[k].port) === intf.link.from_interface);
|
||||
} else {
|
||||
intf.link.status = ($scope.getDeviceInterface(peers[k].host, peers[k].port) === intf.link.to_interface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -534,6 +621,34 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
$scope.getDevice = function(name) {
|
||||
|
||||
var i = 0;
|
||||
for (i = 0; i < $scope.devices.length; i++) {
|
||||
if ($scope.devices[i].name === name) {
|
||||
return $scope.devices[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.getDeviceInterface = function(device_name, interface_name) {
|
||||
|
||||
var i = 0;
|
||||
var k = 0;
|
||||
for (i = 0; i < $scope.devices.length; i++) {
|
||||
if ($scope.devices[i].name === device_name) {
|
||||
for (k = 0; k < $scope.devices[i].interfaces.length; k++) {
|
||||
if ($scope.devices[i].interfaces[k].name === interface_name) {
|
||||
return $scope.devices[i].interfaces[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.onDeviceCreate = function(data) {
|
||||
$scope.create_device(data);
|
||||
};
|
||||
@@ -583,6 +698,16 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.forGroup = function(group_id, data, fn) {
|
||||
var i = 0;
|
||||
for (i = 0; i < $scope.groups.length; i++) {
|
||||
if ($scope.groups[i].id === group_id) {
|
||||
fn($scope.groups[i], data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.onDeviceLabelEdit = function(data) {
|
||||
$scope.edit_device_label(data);
|
||||
};
|
||||
@@ -674,7 +799,7 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
link = $scope.links[i];
|
||||
if (link.id === data.id &&
|
||||
link.from_device.id === data.from_device_id &&
|
||||
link.to_device.id === data.to_device_id &&
|
||||
link.to_device.id === data.to_device_id &&
|
||||
link.to_interface.id === data.to_interface_id &&
|
||||
link.from_interface.id === data.from_interface_id) {
|
||||
link.from_interface.link = null;
|
||||
@@ -751,6 +876,17 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.onGroupLabelEdit = function(data) {
|
||||
$scope.edit_group_label(data);
|
||||
};
|
||||
|
||||
$scope.edit_group_label = function(data) {
|
||||
$scope.forGroup(data.id, data, function(group, data) {
|
||||
group.name = data.name;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.redo = function(type_data) {
|
||||
var type = type_data[0];
|
||||
var data = type_data[1];
|
||||
@@ -831,6 +967,9 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.panX = data.panX;
|
||||
$scope.panY = data.panX;
|
||||
$scope.current_scale = data.scale;
|
||||
$scope.link_id_seq = util.natural_numbers(data.link_id_seq);
|
||||
$scope.group_id_seq = util.natural_numbers(data.group_id_seq);
|
||||
$scope.device_id_seq = util.natural_numbers(data.device_id_seq);
|
||||
$location.search({topology_id: data.topology_id});
|
||||
};
|
||||
|
||||
@@ -863,7 +1002,6 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
|
||||
$scope.onSnapshot = function (data) {
|
||||
|
||||
|
||||
//Erase the existing state
|
||||
$scope.devices = [];
|
||||
$scope.links = [];
|
||||
@@ -878,11 +1016,13 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
var new_intf = null;
|
||||
var max_device_id = null;
|
||||
var max_link_id = null;
|
||||
var max_group_id = null;
|
||||
var min_x = null;
|
||||
var min_y = null;
|
||||
var max_x = null;
|
||||
var max_y = null;
|
||||
var new_link = null;
|
||||
var new_group = null;
|
||||
|
||||
//Build the devices
|
||||
for (i = 0; i < data.devices.length; i++) {
|
||||
@@ -907,6 +1047,7 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
device.x,
|
||||
device.y,
|
||||
device.type);
|
||||
new_device.interface_seq = util.natural_numbers(device.interface_id_seq);
|
||||
$scope.devices.push(new_device);
|
||||
device_map[device.id] = new_device;
|
||||
device_interface_map[device.id] = {};
|
||||
@@ -938,6 +1079,28 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
device_interface_map[link.to_device_id][link.to_interface_id].link = new_link;
|
||||
}
|
||||
|
||||
//Build the groups
|
||||
var group = null;
|
||||
for (i = 0; i < data.groups.length; i++) {
|
||||
group = data.groups[i];
|
||||
if (max_group_id === null || group.id > max_group_id) {
|
||||
max_group_id = group.id;
|
||||
}
|
||||
new_group = new models.Group(group.id,
|
||||
group.name,
|
||||
group.x1,
|
||||
group.y1,
|
||||
group.x2,
|
||||
group.y2,
|
||||
false);
|
||||
if (group.members !== undefined) {
|
||||
for (j=0; j < group.members.length; j++) {
|
||||
new_group.devices.push(device_map[group.members[j]]);
|
||||
}
|
||||
}
|
||||
$scope.groups.push(new_group);
|
||||
}
|
||||
|
||||
var diff_x;
|
||||
var diff_y;
|
||||
|
||||
@@ -969,6 +1132,10 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
if (max_link_id !== null) {
|
||||
$scope.link_id_seq = util.natural_numbers(max_link_id);
|
||||
}
|
||||
//Update the group_id_seq to be greater than all group ids to prevent duplicate ids.
|
||||
if (max_group_id !== null) {
|
||||
$scope.group_id_seq = util.natural_numbers(max_group_id);
|
||||
}
|
||||
|
||||
$scope.updateInterfaceDots();
|
||||
};
|
||||
@@ -1034,7 +1201,11 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
}
|
||||
}
|
||||
var data = messages.serialize(message);
|
||||
$scope.control_socket.send(data);
|
||||
if (!$scope.disconnected) {
|
||||
$scope.control_socket.send(data);
|
||||
} else {
|
||||
console.log(data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1047,6 +1218,8 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.graph.right_column = $window.innerWidth - 300;
|
||||
$scope.graph.height = $window.innerHeight;
|
||||
|
||||
$scope.update_size();
|
||||
|
||||
// manuall $digest required as resize event
|
||||
// is outside of angular
|
||||
$scope.$digest();
|
||||
@@ -1057,68 +1230,21 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
|
||||
$scope.frame = Math.floor(window.performance.now());
|
||||
$scope.$apply();
|
||||
}, 17);
|
||||
});
|
||||
|
||||
app.directive('cursor', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/cursor.html' };
|
||||
});
|
||||
console.log("Network UI started");
|
||||
|
||||
app.directive('touch', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/touch.html' };
|
||||
});
|
||||
$scope.$on('$destroy', function () {
|
||||
console.log("Network UI stopping");
|
||||
$document.unbind('keydown', $scope.onKeyDown);
|
||||
});
|
||||
|
||||
app.directive('debug', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/debug.html' };
|
||||
});
|
||||
$scope.update_size = function () {
|
||||
var i = 0;
|
||||
for (i = 0; i < $scope.layers.length; i++) {
|
||||
$scope.layers[i].x = $scope.graph.width - 140;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
app.directive('router', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/router.html' };
|
||||
});
|
||||
|
||||
app.directive('switch', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/switch.html' };
|
||||
});
|
||||
|
||||
app.directive('host', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/host.html' };
|
||||
});
|
||||
|
||||
app.directive('link', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/link.html' };
|
||||
});
|
||||
|
||||
app.directive('rack', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/rack.html' };
|
||||
});
|
||||
|
||||
app.directive('default', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/default.html' };
|
||||
});
|
||||
|
||||
app.directive('quadrants', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/quadrants.html' };
|
||||
});
|
||||
|
||||
app.directive('stencil', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/stencil.html' };
|
||||
});
|
||||
|
||||
app.directive('layer', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/layer.html' };
|
||||
});
|
||||
|
||||
app.directive('button', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/button.html' };
|
||||
});
|
||||
|
||||
app.directive('statusLight', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/status_light.html' };
|
||||
});
|
||||
|
||||
app.directive('taskStatus', function() {
|
||||
return { restrict: 'A', templateUrl: 'widgets/task_status.html' };
|
||||
});
|
||||
|
||||
|
||||
|
||||
exports.app = app;
|
||||
exports.NetworkUIController = NetworkUIController;
|
||||
console.log("Network UI loaded");
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
function awxNetworkUI () {
|
||||
return { restrict: 'E', templateUrl: '/static/network_ui/widgets/network_ui.html' };
|
||||
}
|
||||
exports.awxNetworkUI = awxNetworkUI;
|
||||
35
awx/network_ui/static/network_ui/src/null.fsm.js
Normal file
35
awx/network_ui/static/network_ui/src/null.fsm.js
Normal file
@@ -0,0 +1,35 @@
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
|
||||
function _Start () {
|
||||
this.name = 'Start';
|
||||
}
|
||||
inherits(_Start, _State);
|
||||
var Start = new _Start();
|
||||
exports.Start = Start;
|
||||
|
||||
function _Ready () {
|
||||
this.name = 'Ready';
|
||||
}
|
||||
inherits(_Ready, _State);
|
||||
var Ready = new _Ready();
|
||||
exports.Ready = Ready;
|
||||
|
||||
|
||||
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_Start.prototype.start.transitions = ['Ready'];
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
function quadrants () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/quadrants.html' };
|
||||
}
|
||||
exports.quadrants = quadrants;
|
||||
5
awx/network_ui/static/network_ui/src/rack.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/rack.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function rack () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/rack.html' };
|
||||
}
|
||||
exports.rack = rack;
|
||||
5
awx/network_ui/static/network_ui/src/router.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/router.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function router () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/router.html' };
|
||||
}
|
||||
exports.router = router;
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
function statusLight () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/status_light.html' };
|
||||
}
|
||||
exports.statusLight = statusLight;
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
function stencil () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/stencil.html' };
|
||||
}
|
||||
exports.stencil = stencil;
|
||||
@@ -1,4 +1,17 @@
|
||||
/* Put your css in here */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(/static/assets/OpenSans-Regular.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: bold;
|
||||
font-weight: 600;
|
||||
src: url(/static/assets/OpenSans-Bold.ttf);
|
||||
}
|
||||
|
||||
@selected-red: #c9232c;
|
||||
@selected-mango: #ff5850;
|
||||
@@ -8,308 +21,469 @@
|
||||
@dark-widget-detail: #707070;
|
||||
@widget-body: #D7D7D7;
|
||||
@link: #D7D7D7;
|
||||
@group: #707070;
|
||||
@debug-copynot: rgb(77,200,242);
|
||||
@button-body: #f6f6f6;
|
||||
@button-body: #ffffff;
|
||||
@button-text: #707070;
|
||||
@button-outline: #b4b6b4;
|
||||
@button-body-hover: #dfdfdf;
|
||||
@button-body-pressed: #d5d5d5;
|
||||
@button-outline: #b7b7b7;
|
||||
@button-body-hover: #f2f2f2;
|
||||
@button-body-pressed: #848992;
|
||||
@button-text-pressed: #ffffff;
|
||||
@green: #5CB85C;
|
||||
@red: #D9534F;
|
||||
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: yellow;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
svg {
|
||||
.NetworkUI {
|
||||
background-color: @light-background;
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
svg text {
|
||||
.NetworkUI__text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
.debug text {
|
||||
.NetworkUI__debug-text {
|
||||
fill: @debug-copynot;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
|
||||
line.selected {
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
line.remote-selected {
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
line.selected-conflict {
|
||||
stroke: @selected-red;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
svg rect.debug {
|
||||
.NetworkUI--debug {
|
||||
fill-opacity: 0;
|
||||
stroke: @debug-copynot;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
svg line.link {
|
||||
|
||||
.NetworkUI__link--selected {
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
.NetworkUI__link--remote-selected {
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
.NetworkUI__link--selected-conflict {
|
||||
stroke: @selected-red;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
|
||||
.NetworkUI__link {
|
||||
stroke: @link;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
svg line.link-pass {
|
||||
.NetworkUI__link--link-pass {
|
||||
stroke: @green;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
svg line.link-fail {
|
||||
.NetworkUI__link--link-fail {
|
||||
stroke: @red;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
svg line.cursor {
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
svg line.debug {
|
||||
.NetworkUI__link--debug {
|
||||
stroke: @debug-copynot;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.debug-cursor line {
|
||||
.NetworkUI__cursor {
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.NetworkUI__debug-cursor {
|
||||
stroke: @debug-copynot;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
.NetworkUI--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.router circle {
|
||||
.NetworkUI__router {
|
||||
fill: @widget-body;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.router circle.selected {
|
||||
.NetworkUI__router--selected {
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.router circle.remote-selected {
|
||||
.NetworkUI__router--remote-selected {
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.router circle.selected-conflict {
|
||||
.NetworkUI__router--selected-conflict {
|
||||
stroke: @selected-red;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.router line {
|
||||
.NetworkUI__router line {
|
||||
stroke: @light-widget-detail;
|
||||
stroke-width: 20;
|
||||
}
|
||||
|
||||
.router polygon {
|
||||
.NetworkUI__router polygon {
|
||||
fill: @light-widget-detail;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.switch rect {
|
||||
.NetworkUI__router-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
|
||||
.NetworkUI__router-text--selected {
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
|
||||
.NetworkUI__switch {
|
||||
fill: @widget-body;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.switch rect.selected {
|
||||
.NetworkUI__switch--selected {
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.switch rect.remote-selected {
|
||||
.NetworkUI__switch--remote-selected {
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.switch rect.selected-conflict {
|
||||
.NetworkUI__switch--selected-conflict {
|
||||
stroke: @selected-red;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.switch line {
|
||||
.NetworkUI__switch line {
|
||||
stroke: @light-widget-detail;
|
||||
stroke-width: 20;
|
||||
}
|
||||
|
||||
.switch polygon {
|
||||
.NetworkUI__switch polygon {
|
||||
fill: @light-widget-detail;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.rack rect {
|
||||
.NetworkUI__switch-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
|
||||
.NetworkUI__switch-text--selected {
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
.NetworkUI__rack {
|
||||
fill: @widget-body;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.rack rect.background {
|
||||
.NetworkUI__rack-background {
|
||||
fill: @light-background;
|
||||
stroke: @light-background;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.rack rect.selected {
|
||||
.NetworkUI__rack--selected {
|
||||
fill: @selected-blue;
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.rack rect.remote-selected {
|
||||
.NetworkUI__rack--remote-selected {
|
||||
fill: @selected-mango;
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.rack rect.selected-conflict {
|
||||
.NetworkUI__rack--selected-conflict {
|
||||
fill: @selected-red;
|
||||
stroke: @selected-red;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.rack line {
|
||||
.NetworkUI__rack line {
|
||||
stroke: @light-widget-detail;
|
||||
stroke-width: 20;
|
||||
}
|
||||
|
||||
.rack circle {
|
||||
.NetworkUI__rack circle {
|
||||
fill: @light-widget-detail;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.button rect {
|
||||
.NetworkUI__button {
|
||||
fill: @button-body;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.button text {
|
||||
.NetworkUI__button-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.button-pressed rect {
|
||||
.NetworkUI__button--button-pressed {
|
||||
fill: @button-body-pressed;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.button-pressed text {
|
||||
fill: @button-text;
|
||||
.NetworkUI__button-text--button-pressed {
|
||||
fill: @button-text-pressed;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.button-hover rect {
|
||||
.NetworkUI__button--button-hover {
|
||||
fill: @button-body-hover;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.button-hover text {
|
||||
.NetworkUI__button-text--button-hover {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.host rect {
|
||||
.NetworkUI__layer {
|
||||
fill: @button-body;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.NetworkUI__layer-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.NetworkUI__layer--button-pressed {
|
||||
fill: @button-body-pressed;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.NetworkUI__layer-text--button-pressed {
|
||||
fill: @button-text-pressed;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.NetworkUI__layer--button-hover {
|
||||
fill: @button-body-hover;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.NetworkUI__layer-text--button-hover {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.NetworkUI__stencil {
|
||||
fill: @button-body;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.NetworkUI__stencil-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.NetworkUI__stencil--button-pressed {
|
||||
fill: @button-body-pressed;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.NetworkUI__stencil-text--button-pressed {
|
||||
fill: @button-text-pressed;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.NetworkUI__stencil--button-hover {
|
||||
fill: @button-body-hover;
|
||||
stroke: @button-outline;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.NetworkUI__stencil-text--button-hover {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.NetworkUI__host {
|
||||
fill: @widget-body;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.host rect.background {
|
||||
.NetworkUI__host-background {
|
||||
fill: @light-background;
|
||||
stroke: @light-background;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.host rect.selected {
|
||||
.NetworkUI__host--selected {
|
||||
fill: @selected-blue;
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.host rect.remote-selected {
|
||||
.NetworkUI__host--remote-selected {
|
||||
fill: @selected-mango;
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.host rect.selected-conflict {
|
||||
.NetworkUI__host--selected-conflict {
|
||||
fill: @selected-red;
|
||||
stroke: @selected-red;
|
||||
stroke-width: 10;
|
||||
}
|
||||
|
||||
.host line {
|
||||
.NetworkUI__host line {
|
||||
stroke: @light-widget-detail;
|
||||
stroke-width: 20;
|
||||
}
|
||||
|
||||
.host circle {
|
||||
.NetworkUI__host circle {
|
||||
fill: @light-widget-detail;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
circle.status {
|
||||
|
||||
.NetworkUI__host-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
|
||||
.NetworkUI__host-text--selected {
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
.NetworkUI__status {
|
||||
fill: @widget-body;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
circle.pass {
|
||||
.NetworkUI__status--pass {
|
||||
fill: @green;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
circle.fail {
|
||||
.NetworkUI__status--fail {
|
||||
fill: @red;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
path.status {
|
||||
.NetworkUI__status-path {
|
||||
fill: none;
|
||||
stroke: @dark-widget-detail;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
circle.debug {
|
||||
.NetworkUI__circle-debug {
|
||||
fill: @debug-copynot;
|
||||
}
|
||||
|
||||
|
||||
circle.interface {
|
||||
.NetworkUI__interface {
|
||||
fill: @dark-widget-detail;
|
||||
}
|
||||
|
||||
circle.selected {
|
||||
.NetworkUI__interface--selected {
|
||||
fill: @selected-blue;
|
||||
}
|
||||
|
||||
text.interface {
|
||||
.NetworkUI__interface-text {
|
||||
fill: @button-text;
|
||||
font-size: 8px;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
.touch circle {
|
||||
|
||||
.NetworkUI__interface-text--selected {
|
||||
font-size: 8px;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
.NetworkUI__link-text {
|
||||
fill: @button-text;
|
||||
font-size: 8px;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
.NetworkUI__touch {
|
||||
stroke: @debug-copynot;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.NetworkUI__group--selected {
|
||||
stroke: @selected-blue;
|
||||
stroke-width: 6;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.NetworkUI__group--remote-selected {
|
||||
stroke: @selected-mango;
|
||||
stroke-width: 6;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.NetworkUI__group--selected-conflict {
|
||||
stroke: @selected-red;
|
||||
stroke-width: 6;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.NetworkUI__group {
|
||||
stroke: @group;
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.NetworkUI__group--debug {
|
||||
stroke: @debug-copynot;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.NetworkUI__group-text {
|
||||
fill: @button-text;
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
|
||||
.NetworkUI__group-text--selected {
|
||||
font-family: 'Open Sans';
|
||||
}
|
||||
|
||||
5
awx/network_ui/static/network_ui/src/switch.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/switch.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function switchd () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/switch.html' };
|
||||
}
|
||||
exports.switchd = switchd;
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
function taskStatus () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/task_status.html' };
|
||||
}
|
||||
exports.taskStatus = taskStatus;
|
||||
@@ -45,6 +45,7 @@ _Past.prototype.onMessage = function(controller, msg_type, message) {
|
||||
'DeviceDestroy',
|
||||
'DeviceMove',
|
||||
'DeviceLabelEdit',
|
||||
'GroupLabelEdit',
|
||||
'LinkLabelEdit',
|
||||
'InterfaceLabelEdit',
|
||||
'InterfaceCreate',
|
||||
@@ -98,6 +99,7 @@ _Past.prototype.onRedo = function(controller, msg_type, message) {
|
||||
}
|
||||
}
|
||||
};
|
||||
_Past.prototype.onRedo.transitions = ['Present'];
|
||||
|
||||
_Past.prototype.onCoverageRequest = function(controller) {
|
||||
controller.scope.send_coverage();
|
||||
@@ -195,7 +197,7 @@ _Past.prototype.onMouseWheel = function (controller, msg_type, message) {
|
||||
}
|
||||
|
||||
};
|
||||
_Past.prototype.onMouseWheel.transitions = ['Past'];
|
||||
_Past.prototype.onMouseWheel.transitions = ['Present'];
|
||||
|
||||
_Past.prototype.onKeyDown = function(controller, msg_type, $event) {
|
||||
|
||||
@@ -217,7 +219,7 @@ _Past.prototype.onKeyDown = function(controller, msg_type, $event) {
|
||||
controller.next_controller.handle_message(msg_type, $event);
|
||||
}
|
||||
};
|
||||
_Past.prototype.onKeyDown.transitions = ['Past'];
|
||||
_Past.prototype.onKeyDown.transitions = ['Present'];
|
||||
|
||||
|
||||
_Past.prototype.undo = function(controller) {
|
||||
@@ -271,18 +273,18 @@ _Present.prototype.onMessage = function(controller, msg_type, message) {
|
||||
'DeviceDestroy',
|
||||
'DeviceMove',
|
||||
'DeviceLabelEdit',
|
||||
'LinkLabelEdit',
|
||||
'InterfaceLabelEdit',
|
||||
'GroupLabelEdit',
|
||||
'InterfaceCreate',
|
||||
'InterfaceLabelEdit',
|
||||
'LinkCreate',
|
||||
'LinkDestroy',
|
||||
'LinkLabelEdit',
|
||||
'Snapshot'].indexOf(type) !== -1) {
|
||||
|
||||
controller.scope.history.push(message.data);
|
||||
}
|
||||
controller.handle_message(type, data);
|
||||
};
|
||||
_Present.prototype.onMessage.transitions = ['Past'];
|
||||
|
||||
_Present.prototype.onMultipleMessage = function(controller, msg_type, message) {
|
||||
|
||||
@@ -342,6 +344,11 @@ _Present.prototype.onDeviceLabelEdit = function(controller, msg_type, message) {
|
||||
controller.scope.onDeviceLabelEdit(message);
|
||||
}
|
||||
};
|
||||
_Present.prototype.onGroupLabelEdit = function(controller, msg_type, message) {
|
||||
if (message.sender !== controller.scope.client_id) {
|
||||
controller.scope.onGroupLabelEdit(message);
|
||||
}
|
||||
};
|
||||
_Present.prototype.onLinkLabelEdit = function(controller, msg_type, message) {
|
||||
if (message.sender !== controller.scope.client_id) {
|
||||
controller.scope.onLinkLabelEdit(message);
|
||||
@@ -369,6 +376,7 @@ _Present.prototype.onUndo = function(controller, msg_type, message) {
|
||||
controller.changeState(Past);
|
||||
}
|
||||
};
|
||||
_Present.prototype.onUndo.transitions = ['Past'];
|
||||
_Present.prototype.onSnapshot = function(controller, msg_type, message) {
|
||||
if (message.sender !== controller.scope.client_id) {
|
||||
controller.scope.onSnapshot(message);
|
||||
@@ -471,7 +479,6 @@ _Present.prototype.onMouseWheelEvent = function(controller, msg_type, message) {
|
||||
controller.scope.onKeyDown(message);
|
||||
}
|
||||
};
|
||||
_Present.prototype.onMessage.transitions = ['Past'];
|
||||
|
||||
_Present.prototype.onMouseWheel = function (controller, msg_type, message) {
|
||||
|
||||
|
||||
5
awx/network_ui/static/network_ui/src/touch.directive.js
Normal file
5
awx/network_ui/static/network_ui/src/touch.directive.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
function touch () {
|
||||
return { restrict: 'A', templateUrl: '/static/network_ui/widgets/touch.html' };
|
||||
}
|
||||
exports.touch = touch;
|
||||
28
awx/network_ui/static/network_ui/src/tower.app.js
Normal file
28
awx/network_ui/static/network_ui/src/tower.app.js
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
var angular = require('angular');
|
||||
var ui_router = require('angular-ui-router');
|
||||
|
||||
var tower = angular.module('tower', ['networkUI', 'ui.router']);
|
||||
|
||||
tower.config(function($stateProvider, $urlRouterProvider) {
|
||||
|
||||
$urlRouterProvider.otherwise('/index');
|
||||
|
||||
$stateProvider
|
||||
.state({
|
||||
name: 'index',
|
||||
url: '/index',
|
||||
template: '<a href="#!/topology">Topology</a>'
|
||||
});
|
||||
|
||||
$stateProvider
|
||||
.state({
|
||||
name: 'topology',
|
||||
url: '/topology',
|
||||
template: "<awx-network-ui></awx-network-ui>"
|
||||
});
|
||||
});
|
||||
|
||||
exports.tower = tower;
|
||||
exports.ui_router = ui_router;
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
Array.prototype.extend = function (other_array) {
|
||||
/* you should include a test to check whether other_array really is an array */
|
||||
var i = 0;
|
||||
for (i = 0; i < other_array.length; i++) {
|
||||
this.push(other_array[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var math = require('mathjs');
|
||||
|
||||
function noop () {
|
||||
@@ -10,6 +19,13 @@ function natural_numbers (start) {
|
||||
}
|
||||
exports.natural_numbers = natural_numbers;
|
||||
|
||||
|
||||
function distance (x1, y1, x2, y2) {
|
||||
|
||||
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
||||
}
|
||||
exports.distance = distance;
|
||||
|
||||
// polarToCartesian
|
||||
// @wdebeaum, @opsb
|
||||
// from http://stackoverflow.com/questions/5736398/how-to-calculate-the-svg-path-for-an-arc-of-a-circle
|
||||
|
||||
@@ -86,28 +86,6 @@ _Ready.prototype.onMouseWheel = function (controller, msg_type, $event) {
|
||||
controller.handle_message(msg_type, $event);
|
||||
};
|
||||
|
||||
_Ready.prototype.onKeyDown = function(controller, msg_type, $event) {
|
||||
|
||||
var scope = controller.scope;
|
||||
|
||||
if ($event.key === 'd') {
|
||||
scope.debug.hidden = !scope.debug.hidden;
|
||||
return;
|
||||
}
|
||||
if ($event.key === 'p') {
|
||||
scope.cursor.hidden = !scope.cursor.hidden;
|
||||
return;
|
||||
}
|
||||
if ($event.key === 'b') {
|
||||
scope.hide_buttons = !scope.hide_buttons;
|
||||
return;
|
||||
}
|
||||
if ($event.key === 'i') {
|
||||
scope.hide_interfaces = !scope.hide_interfaces;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
@@ -127,6 +105,7 @@ _Scale.prototype.onMouseWheel = function (controller, msg_type, message) {
|
||||
controller.scope.updatePanAndScale();
|
||||
controller.changeState(Ready);
|
||||
};
|
||||
_Scale.prototype.onMouseWheel.transitions = ['Ready'];
|
||||
|
||||
|
||||
_Pressed.prototype.onMouseUp = function (controller) {
|
||||
@@ -134,6 +113,7 @@ _Pressed.prototype.onMouseUp = function (controller) {
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_Pressed.prototype.onMouseUp.transitions = ['Ready'];
|
||||
|
||||
_Pressed.prototype.onTouchEnd = _Pressed.prototype.onMouseUp;
|
||||
|
||||
@@ -142,6 +122,7 @@ _Pressed.prototype.onMouseMove = function (controller, msg_type, $event) {
|
||||
controller.changeState(Pan);
|
||||
controller.handle_message(msg_type, $event);
|
||||
};
|
||||
_Pressed.prototype.onMouseMove.transitions = ['Pan'];
|
||||
|
||||
_Pressed.prototype.onTouchMove = _Pressed.prototype.onMouseMove;
|
||||
|
||||
@@ -186,5 +167,6 @@ _Pan.prototype.onMouseUp = function (controller) {
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_Pan.prototype.onMouseUp.transitions = ['Ready'];
|
||||
|
||||
_Pan.prototype.onTouchEnd = _Pan.prototype.onMouseUp;
|
||||
|
||||
@@ -5,22 +5,22 @@ function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
{%for state, functions in states%}
|
||||
function _{{state}} () {
|
||||
this.name = '{{state}}';
|
||||
{%for state in states%}
|
||||
function _{{state.label}} () {
|
||||
this.name = '{{state.label}}';
|
||||
}
|
||||
inherits(_{{state}}, _State);
|
||||
var {{state}} = new _{{state}}();
|
||||
exports.{{state}} = {{state}};
|
||||
inherits(_{{state.label}}, _State);
|
||||
var {{state.label}} = new _{{state.label}}();
|
||||
exports.{{state.label}} = {{state.label}};
|
||||
{%endfor%}
|
||||
|
||||
{%for state, functions in states%}
|
||||
{%for fn, transitions in functions%}
|
||||
_{{state}}.prototype.{{fn}} = function (controller) {
|
||||
{%for state in states%}
|
||||
{%for fn, transitions in state.functions%}
|
||||
_{{state.label}}.prototype.{{fn}} = function (controller) {
|
||||
{%for tn in transitions %}
|
||||
controller.changeState({{tn.to_state}});
|
||||
{%endfor%}
|
||||
};
|
||||
_{{state}}.prototype.{{fn}}.transitions = [{%for t in transitions%}'{{t.to_state}}'{% if not loop.last%}, {%endif%}{%endfor%}];
|
||||
_{{state.label}}.prototype.{{fn}}.transitions = [{%for t in transitions%}'{{t.to_state}}'{% if not loop.last%}, {%endif%}{%endfor%}];
|
||||
{%endfor%}
|
||||
{%endfor%}
|
||||
|
||||
61
awx/network_ui/static/network_ui/tools/transform_fsm.py
Executable file
61
awx/network_ui/static/network_ui/tools/transform_fsm.py
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Usage:
|
||||
transform_fsm [options] <input> <output>
|
||||
|
||||
Options:
|
||||
-h, --help Show this page
|
||||
--debug Show debug logging
|
||||
--verbose Show verbose logging
|
||||
"""
|
||||
from docopt import docopt
|
||||
import logging
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger('transform_fsm')
|
||||
|
||||
|
||||
def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
parsed_args = docopt(__doc__, args)
|
||||
if parsed_args['--debug']:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
elif parsed_args['--verbose']:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
with open(parsed_args['<input>']) as f:
|
||||
data = yaml.load(f.read())
|
||||
|
||||
state_map = dict()
|
||||
|
||||
for state in data['states']:
|
||||
state_map[state['label']] = state
|
||||
state['functions'] = dict()
|
||||
|
||||
for transition in data['transitions']:
|
||||
state = state_map.get(transition['from_state'], dict(label=transition['from_state'], functions=dict()))
|
||||
state_map[transition['from_state']] = state
|
||||
if state not in data['states']:
|
||||
data['states'].append(state)
|
||||
function_transitions = state['functions'].get(transition['label'], list())
|
||||
function_transitions.append(dict(to_state=transition['to_state']))
|
||||
state['functions'][transition['label']] = function_transitions
|
||||
|
||||
for state in data['states']:
|
||||
state['functions'] = sorted(state['functions'].items())
|
||||
|
||||
with open(parsed_args['<output>'], 'w') as f:
|
||||
f.write(yaml.safe_dump(data, default_flow_style=False))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
@@ -20,7 +20,7 @@ angular.module("ngTouch", [])
|
||||
}
|
||||
|
||||
}]
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive("ngTouchmove", function () {
|
||||
return {
|
||||
@@ -44,7 +44,7 @@ angular.module("ngTouch", [])
|
||||
}
|
||||
|
||||
}]
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive("ngTouchend", function () {
|
||||
return {
|
||||
@@ -58,7 +58,7 @@ angular.module("ngTouch", [])
|
||||
}
|
||||
|
||||
}]
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive("ngTap", function () {
|
||||
return {
|
||||
@@ -66,14 +66,14 @@ angular.module("ngTouch", [])
|
||||
|
||||
var moved = false;
|
||||
$element.bind("touchstart", onTouchStart);
|
||||
function onTouchStart(event) {
|
||||
function onTouchStart() {
|
||||
$element.bind("touchmove", onTouchMove);
|
||||
$element.bind("touchend", onTouchEnd);
|
||||
}
|
||||
function onTouchMove(event) {
|
||||
function onTouchMove() {
|
||||
moved = true;
|
||||
}
|
||||
function onTouchEnd(event) {
|
||||
function onTouchEnd() {
|
||||
$element.unbind("touchmove", onTouchMove);
|
||||
$element.unbind("touchend", onTouchEnd);
|
||||
if (!moved) {
|
||||
@@ -83,5 +83,5 @@ angular.module("ngTouch", [])
|
||||
}
|
||||
|
||||
}]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,365 +0,0 @@
|
||||
// MIT License:
|
||||
//
|
||||
// Copyright (c) 2010-2012, Joe Walnes
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
/**
|
||||
* This behaves like a WebSocket in every way, except if it fails to connect,
|
||||
* or it gets disconnected, it will repeatedly poll until it successfully connects
|
||||
* again.
|
||||
*
|
||||
* It is API compatible, so when you have:
|
||||
* ws = new WebSocket('ws://....');
|
||||
* you can replace with:
|
||||
* ws = new ReconnectingWebSocket('ws://....');
|
||||
*
|
||||
* The event stream will typically look like:
|
||||
* onconnecting
|
||||
* onopen
|
||||
* onmessage
|
||||
* onmessage
|
||||
* onclose // lost connection
|
||||
* onconnecting
|
||||
* onopen // sometime later...
|
||||
* onmessage
|
||||
* onmessage
|
||||
* etc...
|
||||
*
|
||||
* It is API compatible with the standard WebSocket API, apart from the following members:
|
||||
*
|
||||
* - `bufferedAmount`
|
||||
* - `extensions`
|
||||
* - `binaryType`
|
||||
*
|
||||
* Latest version: https://github.com/joewalnes/reconnecting-websocket/
|
||||
* - Joe Walnes
|
||||
*
|
||||
* Syntax
|
||||
* ======
|
||||
* var socket = new ReconnectingWebSocket(url, protocols, options);
|
||||
*
|
||||
* Parameters
|
||||
* ==========
|
||||
* url - The url you are connecting to.
|
||||
* protocols - Optional string or array of protocols.
|
||||
* options - See below
|
||||
*
|
||||
* Options
|
||||
* =======
|
||||
* Options can either be passed upon instantiation or set after instantiation:
|
||||
*
|
||||
* var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
|
||||
*
|
||||
* or
|
||||
*
|
||||
* var socket = new ReconnectingWebSocket(url);
|
||||
* socket.debug = true;
|
||||
* socket.reconnectInterval = 4000;
|
||||
*
|
||||
* debug
|
||||
* - Whether this instance should log debug messages. Accepts true or false. Default: false.
|
||||
*
|
||||
* automaticOpen
|
||||
* - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
|
||||
*
|
||||
* reconnectInterval
|
||||
* - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000.
|
||||
*
|
||||
* maxReconnectInterval
|
||||
* - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000.
|
||||
*
|
||||
* reconnectDecay
|
||||
* - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5.
|
||||
*
|
||||
* timeoutInterval
|
||||
* - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000.
|
||||
*
|
||||
*/
|
||||
(function (global, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([], factory);
|
||||
} else if (typeof module !== 'undefined' && module.exports){
|
||||
module.exports = factory();
|
||||
} else {
|
||||
global.ReconnectingWebSocket = factory();
|
||||
}
|
||||
})(this, function () {
|
||||
|
||||
if (!('WebSocket' in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
function ReconnectingWebSocket(url, protocols, options) {
|
||||
|
||||
// Default settings
|
||||
var settings = {
|
||||
|
||||
/** Whether this instance should log debug messages. */
|
||||
debug: false,
|
||||
|
||||
/** Whether or not the websocket should attempt to connect immediately upon instantiation. */
|
||||
automaticOpen: true,
|
||||
|
||||
/** The number of milliseconds to delay before attempting to reconnect. */
|
||||
reconnectInterval: 1000,
|
||||
/** The maximum number of milliseconds to delay a reconnection attempt. */
|
||||
maxReconnectInterval: 30000,
|
||||
/** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */
|
||||
reconnectDecay: 1.5,
|
||||
|
||||
/** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */
|
||||
timeoutInterval: 2000,
|
||||
|
||||
/** The maximum number of reconnection attempts to make. Unlimited if null. */
|
||||
maxReconnectAttempts: null,
|
||||
|
||||
/** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
|
||||
binaryType: 'blob'
|
||||
}
|
||||
if (!options) { options = {}; }
|
||||
|
||||
// Overwrite and define settings with options if they exist.
|
||||
for (var key in settings) {
|
||||
if (typeof options[key] !== 'undefined') {
|
||||
this[key] = options[key];
|
||||
} else {
|
||||
this[key] = settings[key];
|
||||
}
|
||||
}
|
||||
|
||||
// These should be treated as read-only properties
|
||||
|
||||
/** The URL as resolved by the constructor. This is always an absolute URL. Read only. */
|
||||
this.url = url;
|
||||
|
||||
/** The number of attempted reconnects since starting, or the last successful connection. Read only. */
|
||||
this.reconnectAttempts = 0;
|
||||
|
||||
/**
|
||||
* The current state of the connection.
|
||||
* Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
|
||||
* Read only.
|
||||
*/
|
||||
this.readyState = WebSocket.CONNECTING;
|
||||
|
||||
/**
|
||||
* A string indicating the name of the sub-protocol the server selected; this will be one of
|
||||
* the strings specified in the protocols parameter when creating the WebSocket object.
|
||||
* Read only.
|
||||
*/
|
||||
this.protocol = null;
|
||||
|
||||
// Private state variables
|
||||
|
||||
var self = this;
|
||||
var ws;
|
||||
var forcedClose = false;
|
||||
var timedOut = false;
|
||||
var eventTarget = document.createElement('div');
|
||||
|
||||
// Wire up "on*" properties as event handlers
|
||||
|
||||
eventTarget.addEventListener('open', function(event) { self.onopen(event); });
|
||||
eventTarget.addEventListener('close', function(event) { self.onclose(event); });
|
||||
eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
|
||||
eventTarget.addEventListener('message', function(event) { self.onmessage(event); });
|
||||
eventTarget.addEventListener('error', function(event) { self.onerror(event); });
|
||||
|
||||
// Expose the API required by EventTarget
|
||||
|
||||
this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
|
||||
this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
|
||||
this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
|
||||
|
||||
/**
|
||||
* This function generates an event that is compatible with standard
|
||||
* compliant browsers and IE9 - IE11
|
||||
*
|
||||
* This will prevent the error:
|
||||
* Object doesn't support this action
|
||||
*
|
||||
* http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
|
||||
* @param s String The name that the event should use
|
||||
* @param args Object an optional object that the event will use
|
||||
*/
|
||||
function generateEvent(s, args) {
|
||||
var evt = document.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(s, false, false, args);
|
||||
return evt;
|
||||
};
|
||||
|
||||
this.open = function (reconnectAttempt) {
|
||||
ws = new WebSocket(self.url, protocols || []);
|
||||
ws.binaryType = this.binaryType;
|
||||
|
||||
if (reconnectAttempt) {
|
||||
if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
eventTarget.dispatchEvent(generateEvent('connecting'));
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
|
||||
}
|
||||
|
||||
var localWs = ws;
|
||||
var timeout = setTimeout(function() {
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
|
||||
}
|
||||
timedOut = true;
|
||||
localWs.close();
|
||||
timedOut = false;
|
||||
}, self.timeoutInterval);
|
||||
|
||||
ws.onopen = function(event) {
|
||||
clearTimeout(timeout);
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'onopen', self.url);
|
||||
}
|
||||
self.protocol = ws.protocol;
|
||||
self.readyState = WebSocket.OPEN;
|
||||
self.reconnectAttempts = 0;
|
||||
var e = generateEvent('open');
|
||||
e.isReconnect = reconnectAttempt;
|
||||
reconnectAttempt = false;
|
||||
eventTarget.dispatchEvent(e);
|
||||
};
|
||||
|
||||
ws.onclose = function(event) {
|
||||
clearTimeout(timeout);
|
||||
ws = null;
|
||||
if (forcedClose) {
|
||||
self.readyState = WebSocket.CLOSED;
|
||||
eventTarget.dispatchEvent(generateEvent('close'));
|
||||
} else {
|
||||
self.readyState = WebSocket.CONNECTING;
|
||||
var e = generateEvent('connecting');
|
||||
e.code = event.code;
|
||||
e.reason = event.reason;
|
||||
e.wasClean = event.wasClean;
|
||||
eventTarget.dispatchEvent(e);
|
||||
if (!reconnectAttempt && !timedOut) {
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'onclose', self.url);
|
||||
}
|
||||
eventTarget.dispatchEvent(generateEvent('close'));
|
||||
}
|
||||
|
||||
var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
|
||||
setTimeout(function() {
|
||||
self.reconnectAttempts++;
|
||||
self.open(true);
|
||||
}, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
|
||||
}
|
||||
};
|
||||
ws.onmessage = function(event) {
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
|
||||
}
|
||||
var e = generateEvent('message');
|
||||
e.data = event.data;
|
||||
eventTarget.dispatchEvent(e);
|
||||
};
|
||||
ws.onerror = function(event) {
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
|
||||
}
|
||||
eventTarget.dispatchEvent(generateEvent('error'));
|
||||
};
|
||||
}
|
||||
|
||||
// Whether or not to create a websocket upon instantiation
|
||||
if (this.automaticOpen == true) {
|
||||
this.open(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmits data to the server over the WebSocket connection.
|
||||
*
|
||||
* @param data a text string, ArrayBuffer or Blob to send to the server.
|
||||
*/
|
||||
this.send = function(data) {
|
||||
if (ws) {
|
||||
if (self.debug || ReconnectingWebSocket.debugAll) {
|
||||
console.debug('ReconnectingWebSocket', 'send', self.url, data);
|
||||
}
|
||||
return ws.send(data);
|
||||
} else {
|
||||
throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the WebSocket connection or connection attempt, if any.
|
||||
* If the connection is already CLOSED, this method does nothing.
|
||||
*/
|
||||
this.close = function(code, reason) {
|
||||
// Default CLOSE_NORMAL code
|
||||
if (typeof code == 'undefined') {
|
||||
code = 1000;
|
||||
}
|
||||
forcedClose = true;
|
||||
if (ws) {
|
||||
ws.close(code, reason);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Additional public API method to refresh the connection if still open (close, re-open).
|
||||
* For example, if the app suspects bad data / missed heart beats, it can try to refresh.
|
||||
*/
|
||||
this.refresh = function() {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An event listener to be called when the WebSocket connection's readyState changes to OPEN;
|
||||
* this indicates that the connection is ready to send and receive data.
|
||||
*/
|
||||
ReconnectingWebSocket.prototype.onopen = function(event) {};
|
||||
/** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */
|
||||
ReconnectingWebSocket.prototype.onclose = function(event) {};
|
||||
/** An event listener to be called when a connection begins being attempted. */
|
||||
ReconnectingWebSocket.prototype.onconnecting = function(event) {};
|
||||
/** An event listener to be called when a message is received from the server. */
|
||||
ReconnectingWebSocket.prototype.onmessage = function(event) {};
|
||||
/** An event listener to be called when an error occurs. */
|
||||
ReconnectingWebSocket.prototype.onerror = function(event) {};
|
||||
|
||||
/**
|
||||
* Whether all instances of ReconnectingWebSocket should log debug messages.
|
||||
* Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true.
|
||||
*/
|
||||
ReconnectingWebSocket.debugAll = false;
|
||||
|
||||
ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;
|
||||
ReconnectingWebSocket.OPEN = WebSocket.OPEN;
|
||||
ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;
|
||||
ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;
|
||||
|
||||
return ReconnectingWebSocket;
|
||||
});
|
||||
@@ -14,13 +14,13 @@ function svg_crowbar () {
|
||||
xmlns: "http://www.w3.org/2000/xmlns/",
|
||||
xlink: "http://www.w3.org/1999/xlink",
|
||||
svg: "http://www.w3.org/2000/svg"
|
||||
}
|
||||
};
|
||||
|
||||
initialize();
|
||||
|
||||
function initialize() {
|
||||
var documents = [window.document],
|
||||
SVGSources = [];
|
||||
SVGSources = [],
|
||||
iframes = document.querySelectorAll("iframe"),
|
||||
objects = document.querySelectorAll("object");
|
||||
|
||||
@@ -30,7 +30,7 @@ function svg_crowbar () {
|
||||
documents.push(el.contentDocument);
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err)
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ function svg_crowbar () {
|
||||
documents.push(el.contentDocument);
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err)
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -50,8 +50,8 @@ function svg_crowbar () {
|
||||
// because of prototype on NYT pages
|
||||
for (var i = 0; i < newSources.length; i++) {
|
||||
SVGSources.push(newSources[i]);
|
||||
};
|
||||
})
|
||||
}
|
||||
});
|
||||
if (SVGSources.length > 1) {
|
||||
createPopover(SVGSources);
|
||||
} else if (SVGSources.length > 0) {
|
||||
@@ -72,7 +72,7 @@ function svg_crowbar () {
|
||||
s2.left += 38;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
var buttonsContainer = document.createElement("div");
|
||||
@@ -80,9 +80,9 @@ function svg_crowbar () {
|
||||
|
||||
buttonsContainer.setAttribute("class", "svg-crowbar");
|
||||
buttonsContainer.style["z-index"] = 1e7;
|
||||
buttonsContainer.style["position"] = "absolute";
|
||||
buttonsContainer.style["top"] = 0;
|
||||
buttonsContainer.style["left"] = 0;
|
||||
buttonsContainer.style.position = "absolute";
|
||||
buttonsContainer.style.top = 0;
|
||||
buttonsContainer.style.left = 0;
|
||||
|
||||
|
||||
|
||||
@@ -90,40 +90,40 @@ function svg_crowbar () {
|
||||
body.appendChild(background);
|
||||
|
||||
background.setAttribute("class", "svg-crowbar");
|
||||
background.style["background"] = "rgba(255, 255, 255, 0.7)";
|
||||
background.style["position"] = "fixed";
|
||||
background.style["left"] = 0;
|
||||
background.style["top"] = 0;
|
||||
background.style["width"] = "100%";
|
||||
background.style["height"] = "100%";
|
||||
background.style.background = "rgba(255, 255, 255, 0.7)";
|
||||
background.style.position = "fixed";
|
||||
background.style.left = 0;
|
||||
background.style.top = 0;
|
||||
background.style.width = "100%";
|
||||
background.style.height = "100%";
|
||||
|
||||
sources.forEach(function(d, i) {
|
||||
var buttonWrapper = document.createElement("div");
|
||||
buttonsContainer.appendChild(buttonWrapper);
|
||||
buttonWrapper.setAttribute("class", "svg-crowbar");
|
||||
buttonWrapper.style["position"] = "absolute";
|
||||
buttonWrapper.style["top"] = (d.top + document.body.scrollTop) + "px";
|
||||
buttonWrapper.style["left"] = (document.body.scrollLeft + d.left) + "px";
|
||||
buttonWrapper.style["padding"] = "4px";
|
||||
buttonWrapper.style.position = "absolute";
|
||||
buttonWrapper.style.top = (d.top + document.body.scrollTop) + "px";
|
||||
buttonWrapper.style.left = (document.body.scrollLeft + d.left) + "px";
|
||||
buttonWrapper.style.padding = "4px";
|
||||
buttonWrapper.style["border-radius"] = "3px";
|
||||
buttonWrapper.style["color"] = "white";
|
||||
buttonWrapper.style.color = "white";
|
||||
buttonWrapper.style["text-align"] = "center";
|
||||
buttonWrapper.style["font-family"] = "'Helvetica Neue'";
|
||||
buttonWrapper.style["background"] = "rgba(0, 0, 0, 0.8)";
|
||||
buttonWrapper.style.background = "rgba(0, 0, 0, 0.8)";
|
||||
buttonWrapper.style["box-shadow"] = "0px 4px 18px rgba(0, 0, 0, 0.4)";
|
||||
buttonWrapper.style["cursor"] = "move";
|
||||
buttonWrapper.style.cursor = "move";
|
||||
buttonWrapper.textContent = "SVG #" + i + ": " + (d.id ? "#" + d.id : "") + (d.class ? "." + d.class : "");
|
||||
|
||||
var button = document.createElement("button");
|
||||
buttonWrapper.appendChild(button);
|
||||
button.setAttribute("data-source-id", i)
|
||||
button.style["width"] = "150px";
|
||||
button.setAttribute("data-source-id", i);
|
||||
button.style.width = "150px";
|
||||
button.style["font-size"] = "12px";
|
||||
button.style["line-height"] = "1.4em";
|
||||
button.style["margin"] = "5px 0 0 0";
|
||||
button.style.margin = "5px 0 0 0";
|
||||
button.textContent = "Download";
|
||||
|
||||
button.onclick = function(el) {
|
||||
button.onclick = function() {
|
||||
// console.log(el, d, i, sources)
|
||||
download(d);
|
||||
};
|
||||
@@ -155,7 +155,7 @@ function svg_crowbar () {
|
||||
svg.insertBefore(defsEl, svg.firstChild); //TODO .insert("defs", ":first-child")
|
||||
// defsEl.setAttribute("class", "svg-crowbar");
|
||||
|
||||
var styleEl = document.createElement("style")
|
||||
var styleEl = document.createElement("style");
|
||||
defsEl.appendChild(styleEl);
|
||||
styleEl.setAttribute("type", "text/css");
|
||||
|
||||
@@ -207,7 +207,7 @@ function svg_crowbar () {
|
||||
a.setAttribute("class", "svg-crowbar");
|
||||
a.setAttribute("download", filename + ".svg");
|
||||
a.setAttribute("href", url);
|
||||
a.style["display"] = "none";
|
||||
a.style.display = "none";
|
||||
a.click();
|
||||
|
||||
setTimeout(function() {
|
||||
|
||||
19
awx/network_ui/static/network_ui/webpack.config.js
Normal file
19
awx/network_ui/static/network_ui/webpack.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
var webpack = require('webpack');
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: "./src/main.js",
|
||||
vendor: ["angular",
|
||||
"angular-ui-router",
|
||||
"hamsterjs",
|
||||
"angular-mousewheel",
|
||||
"reconnectingwebsocket"],
|
||||
},
|
||||
output: {
|
||||
path: __dirname + "/js",
|
||||
filename: "bundle.js",
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({Hamster: 'hamsterjs'}),
|
||||
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' })
|
||||
]
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
|
||||
|
||||
<rect x=0
|
||||
<rect ng-attr-class="{{button.is_pressed ? 'NetworkUI__button--button-pressed' : button.mouse_over ? 'NetworkUI__button--button-hover' : 'NetworkUI__button'}}"
|
||||
x=0
|
||||
y=0
|
||||
ng-attr-width={{button.width}}
|
||||
ng-attr-height={{button.height}}
|
||||
rx=3></rect>
|
||||
<text ng-attr-x="{{button.width/2}}"
|
||||
rx=5></rect>
|
||||
<text ng-attr-class="{{button.is_pressed ? 'NetworkUI__button-text--button-pressed' : button.mouse_over ? 'NetworkUI__button-text--button-hover' : 'NetworkUI__button-text'}}"
|
||||
ng-attr-x="{{button.width/2}}"
|
||||
ng-attr-y="{{button.height/2}}"
|
||||
dy=".3em"
|
||||
text-anchor="middle">{{button.name}}</text>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<g ng-attr-transform="translate({{cursor.x}},{{cursor.y}})" ng-attr-class="{{cursor.hidden && 'hidden' || ''}}" >
|
||||
<line x1="-15" y1="0" x2="15" y2="0" class="cursor"/>
|
||||
<line x1="0" y1="-15" x2="0" y2="15" class="cursor"/>
|
||||
<g ng-attr-transform="translate({{cursor.x}},{{cursor.y}})" ng-attr-class="{{cursor.hidden && 'NetworkUI--hidden' || ''}}" >
|
||||
<line x1="-15" y1="0" x2="15" y2="0" class="NetworkUI__cursor"/>
|
||||
<line x1="0" y1="-15" x2="0" y2="15" class="NetworkUI__cursor"/>
|
||||
</g>
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
|
||||
|
||||
<g ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug">
|
||||
<text ng-attr-x="{{graph.right_column}}" y="15">width: {{graph.width}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="35">height: {{graph.height}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="55">rc: {{graph.right_column}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="75">Mouse down: {{onMouseDownResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="95">Mouse up: {{onMouseUpResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="115">Mouse move: {{onMouseMoveResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="135">Mouse over: {{onMouseOverResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="155">Mouse enter: {{onMouseEnterResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="175">Mouse leave: {{onMouseLeaveResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="195">Current scale: {{current_scale.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="215">Pan X: {{panX.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="235">Pan Y: {{panY.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="255">View State: {{view_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="275">Mouse X: {{mouseX.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="295">Mouse Y: {{mouseY.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="315">Scaled X: {{scaledX.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="335">Scaled Y: {{scaledY.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="355">Key: {{last_key}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="375">Key Code: {{last_key_code}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="395">Move State: {{move_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="415">Selected devices: {{selected_devices.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="435">Selected links: {{selected_links.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="455">Link State: {{link_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="475">Buttons State: {{buttons_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="495">Time State: {{time_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="515">Time Pointer: {{time_pointer}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="535">History: {{history.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="555">Touch Data: {{touch_data.xb}} {{touch_data.yb}} {{touch_data.d}}</text>
|
||||
<g ng-attr-class="{{debug.hidden && 'NetworkUI--hidden' || ''}}">
|
||||
<text ng-attr-x="{{graph.right_column}}" y="115" class="NetworkUI__debug-text">width: {{graph.width}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="135" class="NetworkUI__debug-text">height: {{graph.height}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="155" class="NetworkUI__debug-text">rc: {{graph.right_column}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="175" class="NetworkUI__debug-text">Mouse down: {{onMouseDownResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="195" class="NetworkUI__debug-text">Mouse up: {{onMouseUpResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="215" class="NetworkUI__debug-text">Mouse move: {{onMouseMoveResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="235" class="NetworkUI__debug-text">Mouse over: {{onMouseOverResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="255" class="NetworkUI__debug-text">Mouse enter: {{onMouseEnterResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="275" class="NetworkUI__debug-text">Mouse leave: {{onMouseLeaveResult}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="295" class="NetworkUI__debug-text">Current scale: {{current_scale.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="315" class="NetworkUI__debug-text">Pan X: {{panX.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="335" class="NetworkUI__debug-text">Pan Y: {{panY.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="355" class="NetworkUI__debug-text">View State: {{view_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="375" class="NetworkUI__debug-text">Mouse X: {{mouseX.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="395" class="NetworkUI__debug-text">Mouse Y: {{mouseY.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="415" class="NetworkUI__debug-text">Scaled X: {{scaledX.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="435" class="NetworkUI__debug-text">Scaled Y: {{scaledY.toFixed(2)}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="455" class="NetworkUI__debug-text">Key: {{last_key}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="475" class="NetworkUI__debug-text">Key Code: {{last_key_code}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="495" class="NetworkUI__debug-text">Move State: {{move_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="515" class="NetworkUI__debug-text">Selected devices: {{selected_devices.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="535" class="NetworkUI__debug-text">Selected links: {{selected_links.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="555" class="NetworkUI__debug-text">Link State: {{link_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="575" class="NetworkUI__debug-text">Buttons State: {{buttons_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="595" class="NetworkUI__debug-text">Time State: {{time_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="615" class="NetworkUI__debug-text">Time Pointer: {{time_pointer}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="635" class="NetworkUI__debug-text">History: {{history.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="655" class="NetworkUI__debug-text">Touch Data: {{touch_data.xb}} {{touch_data.yb}} {{touch_data.d}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="675" class="NetworkUI__debug-text">Group State: {{group_controller.state.name}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="695" class="NetworkUI__debug-text">Selected groups: {{selected_groups.length}}</text>
|
||||
<text ng-attr-x="{{graph.right_column}}" y="715" class="NetworkUI__debug-text">Hotkeys State: {{hotkeys_controller.state.name}}</text>
|
||||
</g>
|
||||
|
||||
70
awx/network_ui/static/network_ui/widgets/group.html
Normal file
70
awx/network_ui/static/network_ui/widgets/group.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<g ng-if="!hide_groups">
|
||||
<rect ng-attr-width="{{group.width(scaledX)}}"
|
||||
ng-attr-height="{{group.height(scaledY)}}"
|
||||
ng-attr-x="{{group.left_extent(scaledX)}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY)}}"
|
||||
ng-attr-class="{{group.selected && 'NetworkUI__group--selected' || 'NetworkUI--hidden'}}"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.left_extent(scaledX)}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY)}}"
|
||||
ng-attr-class="{{group.selected && 'NetworkUI__group--selected' || 'NetworkUI--hidden'}}"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.right_extent(scaledX) - 10}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY)}}"
|
||||
ng-attr-class="{{group.selected && 'NetworkUI__group--selected' || 'NetworkUI--hidden'}}"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.right_extent(scaledX) - 10}}"
|
||||
ng-attr-y="{{group.bottom_extent(scaledY) - 10}}"
|
||||
ng-attr-class="{{group.selected && 'NetworkUI__group--selected' || 'NetworkUI--hidden'}}"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.left_extent(scaledX)}}"
|
||||
ng-attr-y="{{group.bottom_extent(scaledY) - 10}}"
|
||||
ng-attr-class="{{group.selected && 'NetworkUI__group--selected' || 'NetworkUI--hidden'}}"/>
|
||||
|
||||
<g ng-if="group.highlighted || group.selected">
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.left_extent(scaledX)}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY)}}"
|
||||
class="NetworkUI__group"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.right_extent(scaledX) - 10}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY)}}"
|
||||
class="NetworkUI__group"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.right_extent(scaledX) - 10}}"
|
||||
ng-attr-y="{{group.bottom_extent(scaledY) - 10}}"
|
||||
class="NetworkUI__group"/>
|
||||
|
||||
<rect ng-attr-width="10"
|
||||
ng-attr-height="10"
|
||||
ng-attr-x="{{group.left_extent(scaledX)}}"
|
||||
ng-attr-y="{{group.bottom_extent(scaledY) - 10}}"
|
||||
class="NetworkUI__group"/>
|
||||
</g>
|
||||
|
||||
<rect ng-attr-width="{{group.width(scaledX)}}"
|
||||
ng-attr-height="{{group.height(scaledY)}}"
|
||||
ng-attr-x="{{group.left_extent(scaledX)}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY)}}"
|
||||
class="NetworkUI__group"/>
|
||||
|
||||
<text ng-attr-class="{{group.selected && ! group.edit_label ? 'NetworkUI__group-text--selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="left"
|
||||
ng-attr-x="{{group.left_extent(scaledX) + 20}}"
|
||||
ng-attr-y="{{group.top_extent(scaledY) + 32}}"> {{group.name}} </text>
|
||||
<text class="NetworkUI__group-text" text-anchor="left" ng-attr-x="{{group.left_extent(scaledX) + 20}}" ng-attr-y="{{group.top_extent(scaledY) + 32}}">{{group.name}}{{group.edit_label?'_':''}}</text>
|
||||
</g>
|
||||
@@ -1,26 +1,29 @@
|
||||
|
||||
|
||||
<g ng-if="!debug.hidden">
|
||||
<line ng-attr-x1="{{-50 - 10}}"
|
||||
ng-attr-y1="0"
|
||||
ng-attr-x2="{{50 + 10}}"
|
||||
ng-attr-y2="0"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></line>
|
||||
<line ng-attr-x1="0"
|
||||
ng-attr-y1="{{-15 - 10}}"
|
||||
ng-attr-x2="0"
|
||||
ng-attr-y2="{{15 + 10}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></line>
|
||||
<rect ng-attr-x="{{-50}}"
|
||||
ng-attr-y="{{-15}}"
|
||||
ng-attr-width="{{50 * 2}}"
|
||||
ng-attr-height="{{15 * 2}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></rect>
|
||||
</g>
|
||||
<g class="NetworkUI__host">
|
||||
<rect
|
||||
x="-52"
|
||||
y="-17"
|
||||
ng-attr-width="{{100 + 4}}"
|
||||
ng-attr-height="{{30 + 4}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'selected-conflict' : device.selected ? 'selected' : 'remote-selected' : 'hidden'}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'NetworkUI__host--selected-conflict' : device.selected ? 'NetworkUI__host--selected' : 'NetworkUI__host--remote-selected' : 'NetworkUI--hidden'}}"
|
||||
rx=10>
|
||||
</rect>
|
||||
<rect
|
||||
@@ -32,11 +35,12 @@
|
||||
</rect>
|
||||
<circle cx="30" cy="0" r=7 />
|
||||
</circle>
|
||||
</g>
|
||||
<g ng-show="current_scale > 0.5">
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'NetworkUI__host-text--selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
x="0"
|
||||
y="5"> {{device.name}} </text>
|
||||
<text text-anchor="middle" x="0" y="5">{{device.name}}{{device.edit_label?'_':''}}</text>
|
||||
<text class="NetworkUI__host-text" text-anchor="middle" x="0" y="5">{{device.name}}{{device.edit_label?'_':''}}</text>
|
||||
</g>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
|
||||
|
||||
<rect x=0
|
||||
<rect ng-attr-class="{{layer.is_pressed ? 'NetworkUI__layer--button-pressed' : layer.mouse_over ? 'NetworkUI__layer--button-hover' : 'NetworkUI__layer'}}"
|
||||
x=0
|
||||
y=0
|
||||
ng-attr-width={{layer.size}}
|
||||
ng-attr-height={{layer.size}}
|
||||
rx=3></rect>
|
||||
<text ng-attr-x="{{layer.size/2}}"
|
||||
ng-attr-y="{{layer.size/2}}"
|
||||
ng-attr-width={{layer.width}}
|
||||
ng-attr-height={{layer.height}}
|
||||
rx=5></rect>
|
||||
<text ng-attr-class="{{layer.is_pressed ? 'NetworkUI__layer-text--button-pressed' : layer.mouse_over ? 'NetworkUI__layer-text--button-hover' : 'NetworkUI__layer-text'}}"
|
||||
ng-attr-x="{{layer.width/2}}"
|
||||
ng-attr-y="{{layer.height/2}}"
|
||||
dy=".3em"
|
||||
text-anchor="middle">{{layer.name}}</text>
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
ng-attr-y1="{{link.from_device.y}}"
|
||||
ng-attr-x2="{{link.to_device !== null ? link.to_device.x : scaledX}}"
|
||||
ng-attr-y2="{{link.to_device !== null ? link.to_device.y : scaledY}}"
|
||||
ng-attr-class="{{link.selected && 'selected' || 'hidden'}}"/>
|
||||
ng-attr-class="{{link.selected && 'NetworkUI__link--selected' || 'NetworkUI--hidden'}}"/>
|
||||
<line ng-attr-x1="{{link.from_device.x}}"
|
||||
ng-attr-y1="{{link.from_device.y}}"
|
||||
ng-attr-x2="{{link.to_device !== null ? link.to_device.x : scaledX}}"
|
||||
ng-attr-y2="{{link.to_device !== null ? link.to_device.y : scaledY}}"
|
||||
class="{{link.status === null ? 'link' : link.status ? 'link-pass' : 'link-fail'}}"/>
|
||||
class="{{link.status === null ? 'NetworkUI__link' : link.status ? 'NetworkUI__link--link-pass' : 'NetworkUI__link--link-fail'}}"/>
|
||||
<g ng-if="!debug.hidden && current_scale > 0.5">
|
||||
<line ng-if="link.to_device !== null && link.plength(scaledX, scaledY) < 100"
|
||||
ng-attr-x1="{{link.pDistanceLine(scaledX, scaledY).x2}}"
|
||||
ng-attr-y1="{{link.pDistanceLine(scaledX, scaledY).y2}}"
|
||||
ng-attr-x2="{{scaledX}}"
|
||||
ng-attr-y2="{{scaledY}}"
|
||||
ng-attr-class="debug" />
|
||||
ng-attr-class="NetworkUI__link--debug" />
|
||||
<g ng-if="link.to_device !== null" ng-attr-transform="translate({{link.to_device.x}},
|
||||
{{link.to_device.y}})
|
||||
rotate({{link.slope()}})
|
||||
@@ -22,13 +22,13 @@
|
||||
<circle ng-attr-cx="0"
|
||||
ng-attr-cy="0"
|
||||
r=10
|
||||
class="debug" ></circle>
|
||||
class="NetworkUI__circle-debug" ></circle>
|
||||
</g>
|
||||
<g ng-if="link.to_device !== null" ng-attr-transform="translate({{link.to_device.x}},
|
||||
{{link.to_device.y}})
|
||||
rotate({{link.slope()}})
|
||||
translate({{link.length()/2}}, 0)">
|
||||
<line x1="0" y1=-20 x2=0 y2=20 class="debug"/>
|
||||
<line x1="0" y1=-20 x2=0 y2=20 class="NetworkUI__link--debug"/>
|
||||
</g>
|
||||
<g ng-if="link.to_device !== null" ng-attr-transform="translate({{link.to_device.x}},
|
||||
{{link.to_device.y}})
|
||||
@@ -37,7 +37,7 @@
|
||||
<circle ng-attr-cx="0"
|
||||
ng-attr-cy="0"
|
||||
r=10
|
||||
class="debug" ></circle>
|
||||
class="NetworkUI__circle-debug" ></circle>
|
||||
</g>
|
||||
<g ng-if="link.to_device !== null" ng-attr-transform="translate({{link.from_device.x}},
|
||||
{{link.from_device.y}})
|
||||
@@ -46,7 +46,7 @@
|
||||
<circle ng-attr-cx="0"
|
||||
ng-attr-cy="0"
|
||||
r=10
|
||||
class="debug" ></circle>
|
||||
class="NetworkUI__circle-debug" ></circle>
|
||||
</g>
|
||||
</g>
|
||||
<g ng-if="link.to_device !== null" ng-attr-transform="translate({{link.to_device.x}},
|
||||
@@ -67,59 +67,61 @@
|
||||
rotate({{-link.slope()}})
|
||||
translate(0, -5)
|
||||
">
|
||||
<text ng-attr-class="{{link.selected && ! link.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{link.selected && ! link.edit_label ? 'selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
font-size="8"
|
||||
x="0"
|
||||
y="0"> {{link.name}}</text>
|
||||
<text text-anchor="middle" x="0" y="0">{{link.name}}{{link.edit_label?'_':''}}</text>
|
||||
<text class="NetworkUI__link-text" text-anchor="middle" x="0" y="0">{{link.name}}{{link.edit_label?'_':''}}</text>
|
||||
</g>
|
||||
|
||||
<g ng-if="current_scale > 1.0 && link.to_device !== null"
|
||||
ng-attr-transform="translate({{link.from_device.x}},
|
||||
{{link.from_device.y}})
|
||||
rotate({{link.slope()}})
|
||||
translate({{-link.from_interface.dot_d - 20}}, 0)
|
||||
translate({{-link.from_interface.dot_d - 25}}, 0)
|
||||
rotate({{-link.slope()}})
|
||||
">
|
||||
<text ng-attr-class="interface {{link.from_interface.selected && ! link.from_interface.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{link.from_interface.selected && ! link.from_interface.edit_label ? 'NetworkUI__interface-text--selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
font-size="8"
|
||||
x="0"
|
||||
y="0"> {{link.from_interface.name}}</text>
|
||||
<text class="interface" text-anchor="middle" x="0" y="0">{{link.from_interface.name}}{{link.from_interface.edit_label ?'_':''}}</text>
|
||||
<text class="NetworkUI__interface-text" text-anchor="middle" x="0" y="0">{{link.from_interface.name}}{{link.from_interface.edit_label ?'_':''}}</text>
|
||||
</g>
|
||||
|
||||
<g ng-if="current_scale > 1.0 && link.to_device !== null"
|
||||
ng-attr-transform="translate({{link.from_device.x}},
|
||||
{{link.from_device.y}})
|
||||
rotate({{link.slope()}})
|
||||
translate({{-link.length() + link.to_interface.dot_d + 20}}, 0)
|
||||
translate({{-link.length() + link.to_interface.dot_d + 25}}, 0)
|
||||
rotate({{-link.slope()}})
|
||||
">
|
||||
<text ng-attr-class="interface {{link.to_interface.selected && ! link.to_interface.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{link.to_interface.selected && ! link.to_interface.edit_label ? 'NetworkUI__interface-text--selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
x="0"
|
||||
y="0"> {{link.to_interface.name}}</text>
|
||||
<text class="interface" text-anchor="middle" x="0" y="0">{{link.to_interface.name}}{{link.to_interface.edit_label?'_':''}}</text>
|
||||
<text class="NetworkUI__interface-text" text-anchor="middle" x="0" y="0">{{link.to_interface.name}}{{link.to_interface.edit_label?'_':''}}</text>
|
||||
</g>
|
||||
<g ng-if="current_scale > 1.0 && link.to_device !== null">
|
||||
<circle ng-attr-cx="{{link.from_interface.dot_x}}"
|
||||
ng-attr-cy="{{link.from_interface.dot_y}}"
|
||||
r=14
|
||||
ng-attr-class="{{link.from_interface.selected && ! link.from_interface.edit_label ? 'selected' : 'hidden'}}" ></circle>
|
||||
ng-attr-class="{{link.from_interface.selected ? 'NetworkUI__interface--selected' : 'NetworkUI--hidden'}}" ></circle>
|
||||
<circle ng-attr-cx="{{link.from_interface.dot_x}}"
|
||||
ng-attr-cy="{{link.from_interface.dot_y}}"
|
||||
r=10
|
||||
class="interface" ></circle>
|
||||
class="NetworkUI__interface" ></circle>
|
||||
<circle ng-attr-cx="{{link.to_interface.dot_x}}"
|
||||
ng-attr-cy="{{link.to_interface.dot_y}}"
|
||||
r=14
|
||||
ng-attr-class="{{link.to_interface.selected && ! link.to_interface.edit_label ? 'selected' : 'hidden'}}" ></circle>
|
||||
ng-attr-class="{{link.to_interface.selected ? 'NetworkUI__interface--selected' : 'NetworkUI--hidden'}}" ></circle>
|
||||
<circle ng-attr-cx="{{link.to_interface.dot_x}}"
|
||||
ng-attr-cy="{{link.to_interface.dot_y}}"
|
||||
r=10
|
||||
class="interface" ></circle>
|
||||
class="NetworkUI__interface" ></circle>
|
||||
</g>
|
||||
</g> <!-- end hide_interfaces -->
|
||||
|
||||
102
awx/network_ui/static/network_ui/widgets/network_ui.html
Normal file
102
awx/network_ui/static/network_ui/widgets/network_ui.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<div ng-controller="NetworkUIController">
|
||||
<svg id="frame" class="NetworkUI"
|
||||
ng-attr-height="{{graph.height}}"
|
||||
ng-attr-width="{{graph.width}}"
|
||||
ng-mousedown="onMouseDown($event)"
|
||||
ng-mouseup="onMouseUp($event)"
|
||||
ng-mouseenter="onMouseEnter($event)"
|
||||
ng-mouseleave="onMouseLeave($event)"
|
||||
ng-mousemove="onMouseMove($event)"
|
||||
ng-mouseover="onMouseOver($event)"
|
||||
ng-touchstart="onTouchStart($event)"
|
||||
ng-touchmove="onTouchMove($event)"
|
||||
ng-touchend="onTouchEnd($event)"
|
||||
ng-tap="onTap($event)"
|
||||
msd-wheel="onMouseWheel($event, $delta, $deltaX, $deltaY)">
|
||||
<defs>
|
||||
<filter x="0" y="0" width="1" height="1" id="selected">
|
||||
<feFlood flood-color="#b3d8fd"/>
|
||||
<feComposite in="SourceGraphic" operator="xor"/>
|
||||
</filter>
|
||||
<filter x="0" y="0" width="1" height="1" id="background">
|
||||
<feFlood flood-color="#ffffff"/>
|
||||
<feComposite in="SourceGraphic" operator="xor"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g transform="scale(1.0)" id="frame_g">
|
||||
<g ng-if="!hide_links">
|
||||
<g ng-repeat="link in links">
|
||||
<g awx-net-link></g>
|
||||
</g>
|
||||
</g>
|
||||
<g ng-repeat="link in links">
|
||||
<g ng-if="link.selected || link.to_interface.selected || link.from_interface.selected" link></g>
|
||||
</g>
|
||||
<g ng-repeat="device in devices"
|
||||
ng-attr-transform="translate({{device.x}},{{device.y}})"
|
||||
ng-attr-class="{{device.type}}"
|
||||
ng-switch on="device.type">
|
||||
<g ng-switch-when="router"><!-- begin router -->
|
||||
<g awx-net-router></g>
|
||||
</g> <!-- end router -->
|
||||
|
||||
<g ng-switch-when="switch"> <!-- begin switch -->
|
||||
<g awx-net-switch> </g>
|
||||
</g> <!-- end switch -->
|
||||
|
||||
<g ng-switch-when="host"> <!-- begin host -->
|
||||
<g awx-net-host> </g>
|
||||
|
||||
</g> <!-- end host -->
|
||||
|
||||
<g ng-switch-when="rack"> <!-- begin rack -->
|
||||
<g awx-net-rack> </g>
|
||||
</g> <!-- end rack -->
|
||||
|
||||
<g ng-switch-default> <!-- begin default -->
|
||||
<g awx-net-default></g>
|
||||
</g> <!-- end default -->
|
||||
<g awx-net-status-light></g>
|
||||
<g awx-net-task-status></g>
|
||||
</g> <!-- end devices -->
|
||||
<g ng-repeat="group in groups">
|
||||
<g awx-net-group></g>
|
||||
</g>
|
||||
<g ng-attr-transform="translate({{scaledX}},{{scaledY}})" ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug-cursor" >
|
||||
<line x1="-5" y1="0" x2="5" y2="0"/>
|
||||
<line x1="0" y1="-5" x2="0" y2="5"/>
|
||||
</g>
|
||||
<g awx-net-quadrants>
|
||||
</g>
|
||||
</g>
|
||||
<g ng-if="!hide_buttons">
|
||||
<g> <!-- buttons -->
|
||||
<g ng-repeat="button in buttons"
|
||||
ng-attr-transform="translate({{button.x}},{{button.y}})">
|
||||
<g awx-net-button></g>
|
||||
</g>
|
||||
</g> <!-- end buttons -->
|
||||
|
||||
<g> <!-- stencils -->
|
||||
<g ng-repeat="stencil in stencils"
|
||||
ng-attr-transform="translate({{stencil.x}},{{stencil.y}})"
|
||||
class="button">
|
||||
<g awx-net-stencil></g>
|
||||
</g>
|
||||
</g> <!-- end stencils -->
|
||||
|
||||
<g> <!-- layers -->
|
||||
<g ng-repeat="layer in layers"
|
||||
ng-attr-transform="translate({{layer.x}},{{layer.y}})"
|
||||
class="button">
|
||||
<g awx-net-layer> </g>
|
||||
</g>
|
||||
</g> <!-- end layers -->
|
||||
</g>
|
||||
<g awx-net-debug></g>
|
||||
<g awx-net-cursor></g>
|
||||
<g ng-repeat="touch in touches">
|
||||
<g awx-net-touch></g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -2,9 +2,9 @@
|
||||
ng-attr-y1="0"
|
||||
ng-attr-x2="100000"
|
||||
ng-attr-y2="0"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
ng-attr-class="{{debug.hidden && 'NetworkUI--hidden' || 'NetworkUI--debug'}}" />
|
||||
<line ng-attr-x1="0"
|
||||
ng-attr-y1="-100000"
|
||||
ng-attr-x2="0"
|
||||
ng-attr-y2="100000"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
ng-attr-class="{{debug.hidden && 'NetworkUI--hidden' || 'NetworkUI--debug'}}" />
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
|
||||
|
||||
<g ng-if="!debug.hidden">
|
||||
<line ng-attr-x1="{{-50 - 10}}"
|
||||
ng-attr-y1="0"
|
||||
ng-attr-x2="{{50 + 10}}"
|
||||
ng-attr-y2="0"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></line>
|
||||
<line ng-attr-x1="0"
|
||||
ng-attr-y1="{{-50 - 10}}"
|
||||
ng-attr-x2="0"
|
||||
ng-attr-y2="{{50 + 10}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></line>
|
||||
<rect ng-attr-x="{{-50}}"
|
||||
ng-attr-y="{{-50}}"
|
||||
ng-attr-width="{{50 * 2}}"
|
||||
ng-attr-height="{{50 * 2}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></rect>
|
||||
</g>
|
||||
<rect
|
||||
x="-52"
|
||||
y="-52"
|
||||
ng-attr-width="{{100 + 4}}"
|
||||
ng-attr-height="{{100 + 4}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'selected-conflict' : device.selected ? 'selected' : 'remote-selected' : 'hidden'}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'NetworkUI__rack--selected-conflict' : device.selected ? 'NetworkUI__rack--selected' : 'NetworkUI__rack--remote-selected' : 'NetworkUI--hidden'}}"
|
||||
rx=10>
|
||||
</rect>
|
||||
<g class="NetworkUI__rack">
|
||||
<rect
|
||||
x="-50"
|
||||
y="-50"
|
||||
ng-attr-width="{{100}}"
|
||||
ng-attr-height="{{100}}"
|
||||
rx=10
|
||||
class="background">
|
||||
class="NetworkUI__rack-background">
|
||||
</rect>
|
||||
<rect
|
||||
x="-50"
|
||||
@@ -56,8 +59,9 @@
|
||||
<circle cx="30" cy="0" r=7 />
|
||||
<circle cx="30" cy="35" r=7 />
|
||||
</circle>
|
||||
</g>
|
||||
<g ng-show="current_scale > 0.5">
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
x="0"
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
|
||||
|
||||
<g ng-if="!debug.hidden">
|
||||
<line ng-attr-x1="{{-50 - 10}}"
|
||||
ng-attr-y1="0"
|
||||
ng-attr-x2="{{50 + 10}}"
|
||||
ng-attr-y2="0"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></line>
|
||||
<line ng-attr-x1="0"
|
||||
ng-attr-y1="{{-50 - 10}}"
|
||||
ng-attr-x2="0"
|
||||
ng-attr-y2="{{50 + 10}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></line>
|
||||
<rect ng-attr-x="{{-50}}"
|
||||
ng-attr-y="{{-50}}"
|
||||
ng-attr-width="{{50 * 2}}"
|
||||
ng-attr-height="{{50 * 2}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
class="NetworkUI--debug"></rect>
|
||||
</g>
|
||||
<g class="NetworkUI__router">
|
||||
<circle
|
||||
cx="0"
|
||||
cy="0"
|
||||
ng-attr-r="{{50 + 2}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'selected-conflict' : device.selected ? 'selected' : 'remote-selected' : 'hidden'}}">
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'NetworkUI__router--selected-conflict' : device.selected ? 'NetworkUI__router--selected' : 'NetworkUI__router--remote-selected' : 'NetworkUI--hidden'}}">
|
||||
</circle>
|
||||
<circle
|
||||
cx="0"
|
||||
@@ -48,12 +51,13 @@
|
||||
<polygon points="0 0, 10 10, 0 20" ng-attr-transform="rotate(90) translate(-22, -20) scale(2.0)" />
|
||||
<polygon points="0 0, 10 10, 0 20" ng-attr-transform="rotate(270) translate(-22, -20) scale(2.0)"/>
|
||||
</g>
|
||||
</g>
|
||||
<g ng-show="current_scale > 0.5">
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'NetworkUI__router-text--selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
x="0"
|
||||
y="0"> {{device.name}}</text>
|
||||
<text text-anchor="middle" x="0" y="0">{{device.name}}{{device.edit_label?'_':''}}</text>
|
||||
<text class="NetworkUI__router-text" text-anchor="middle" x="0" y="0">{{device.name}}{{device.edit_label?'_':''}}</text>
|
||||
</g>
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<g ng-if="device.working">
|
||||
<path ng-attr-transform="translate({{-device.width}}, {{-device.height}}) rotate({{frame/3}})" class="status" ng-attr-d="{{device.describeArc(0, 0, 10, 0, 270)}}"/>
|
||||
<path ng-attr-transform="translate({{-device.width}}, {{-device.height}}) rotate({{frame/3}})" class="NetworkUI__status-path" ng-attr-d="{{device.describeArc(0, 0, 10, 0, 270)}}"/>
|
||||
</g>
|
||||
<g ng-if="!device.working">
|
||||
<circle ng-attr-cx="{{-device.width}}"
|
||||
ng-attr-cy="{{-device.height}}"
|
||||
r=10
|
||||
ng-attr-class="{{device.status === null ? 'hidden' : device.status ? 'pass': 'fail'}} status">
|
||||
ng-attr-class="{{device.status === null ? 'NetworkUI--hidden' : device.status ? 'NetworkUI__status--pass': 'NetworkUI__status--fail'}}">
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
|
||||
|
||||
<rect x=0
|
||||
<rect ng-attr-class="{{stencil.is_pressed ? 'NetworkUI__stencil--button-pressed' : stencil.mouse_over ? 'NetworkUI__stencil--button-hover' : 'NetworkUI__stencil'}}"
|
||||
x=0
|
||||
y=0
|
||||
ng-attr-width={{stencil.size}}
|
||||
ng-attr-height={{stencil.size}}
|
||||
rx=3></rect>
|
||||
<text ng-attr-x="{{stencil.size/2}}"
|
||||
ng-attr-y="{{stencil.size/2}}"
|
||||
ng-attr-width={{stencil.width}}
|
||||
ng-attr-height={{stencil.height}}
|
||||
rx=5></rect>
|
||||
<text ng-attr-class="{{stencil.is_pressed ? 'NetworkUI__stencil-text--button-pressed' : stencil.mouse_over ? 'NetworkUI__stencil-text--button-hover' : 'NetworkUI__stencil-text'}}"
|
||||
ng-attr-x="{{stencil.width/2}}"
|
||||
ng-attr-y="{{stencil.height/2}}"
|
||||
dy=".3em"
|
||||
text-anchor="middle">{{stencil.name}}</text>
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
|
||||
|
||||
<line ng-attr-x1="{{-50 - 10}}"
|
||||
ng-attr-y1="0"
|
||||
ng-attr-x2="{{50 + 10}}"
|
||||
ng-attr-y2="0"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
<line ng-attr-x1="0"
|
||||
ng-attr-y1="{{-50 - 10}}"
|
||||
ng-attr-x2="0"
|
||||
ng-attr-y2="{{50 + 10}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
<rect ng-attr-x="{{-50}}"
|
||||
ng-attr-y="{{-50}}"
|
||||
ng-attr-width="{{50 * 2}}"
|
||||
ng-attr-height="{{50 * 2}}"
|
||||
ng-attr-class="{{debug.hidden && 'hidden' || ''}} debug" />
|
||||
<g ng-if="!debug.hidden">
|
||||
<line ng-attr-x1="{{-50 - 10}}"
|
||||
ng-attr-y1="0"
|
||||
ng-attr-x2="{{50 + 10}}"
|
||||
ng-attr-y2="0"
|
||||
class="NetworkUI--debug"></line>
|
||||
<line ng-attr-x1="0"
|
||||
ng-attr-y1="{{-50 - 10}}"
|
||||
ng-attr-x2="0"
|
||||
ng-attr-y2="{{50 + 10}}"
|
||||
class="NetworkUI--debug"></line>
|
||||
<rect ng-attr-x="{{-50}}"
|
||||
ng-attr-y="{{-50}}"
|
||||
ng-attr-width="{{50 * 2}}"
|
||||
ng-attr-height="{{50 * 2}}"
|
||||
class="NetworkUI--debug"></rect>
|
||||
</g>
|
||||
<rect
|
||||
x="-52"
|
||||
y="-52"
|
||||
ng-attr-width="{{100 + 4}}"
|
||||
ng-attr-height="{{100 + 4}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'selected-conflict' : device.selected ? 'selected' : 'remote-selected' : 'hidden'}}"
|
||||
ng-attr-class="{{device.selected || device.remote_selected ? device.selected && device.remote_selected ? 'NetworkUI__switch--selected-conflict' : device.selected ? 'NetworkUI__switch--selected' : 'NetworkUI__switch--remote-selected' : 'NetworkUI--hidden'}}"
|
||||
rx=10>
|
||||
</rect>
|
||||
|
||||
<g class="NetworkUI__switch">
|
||||
<rect
|
||||
x="-50"
|
||||
y="-50"
|
||||
@@ -51,11 +53,12 @@
|
||||
ng-attr-x2="-38"
|
||||
ng-attr-y2="-14"/>
|
||||
<polygon points="0 0, 10 10, 0 20" ng-attr-transform="rotate(180) translate(28, -6) scale(2.0)"/>
|
||||
</g>
|
||||
<g ng-show="current_scale > 0.5">
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'selected' : 'hidden'}}"
|
||||
<text ng-attr-class="{{device.selected && ! device.edit_label ? 'NetworkUI__switch-text--selected' : 'NetworkUI--hidden'}}"
|
||||
filter="url(#selected)"
|
||||
text-anchor="middle"
|
||||
x="0"
|
||||
y="0"> {{device.name}} </text>
|
||||
<text text-anchor="middle" x="0" y="0">{{device.name}}{{device.edit_label?'_':''}}</text>
|
||||
<text class="NetworkUI__switch-text" text-anchor="middle" x="0" y="0">{{device.name}}{{device.edit_label?'_':''}}</text>
|
||||
</g>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<g ng-attr-transform="translate({{-device.width}}, {{-device.height}})">
|
||||
<g ng-repeat="task in device.tasks | limitTo: -7">
|
||||
<g ng-if="task.working && current_scale > 0.5">
|
||||
<path ng-attr-transform="translate({{$index * 12 + 17}}, -5 ) rotate({{frame/3}})" class="status" ng-attr-d="{{task.describeArc(0, 0, 5, 0, 270)}}"/>
|
||||
<path ng-attr-transform="translate({{$index * 12 + 17}}, -5 ) rotate({{frame/3}})" class="NetworkUI__status-path" ng-attr-d="{{task.describeArc(0, 0, 5, 0, 270)}}"/>
|
||||
</g>
|
||||
<g ng-if="!task.working && current_scale > 0.5">
|
||||
<circle ng-attr-cx="{{$index * 12 + 17}}"
|
||||
ng-attr-cy="-5"
|
||||
r=5
|
||||
ng-attr-class="{{task.status === null ? 'hidden' : task.status ? 'pass': 'fail'}} status"
|
||||
ng-attr-class="{{task.status === null ? 'NetworkUI--hidden' : task.status ? 'NetworkUI__status--pass': 'NetworkUI__status--fail'}}"
|
||||
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<g ng-attr-transform="translate({{touch.screenX}},{{touch.screenY}})" ng-attr-class="touch {{touch.hidden && 'hidden' || ''}}" >
|
||||
<circle cx=0 cy=0 r=40></circle>
|
||||
<g ng-attr-transform="translate({{touch.screenX}},{{touch.screenY}})">
|
||||
<circle ng-attr-class="{{touch.hidden && 'NetworkUI--hidden' || 'NetworkUI__touch'}}" cx=0 cy=0 r=40></circle>
|
||||
</g>
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.db import models
|
||||
{%for model in models%}
|
||||
class {{model.name}}(models.Model):
|
||||
|
||||
{%for field in model.fields%}{{field.name}} = models.{{field.type}}( {%if field.ref%}'{{field.ref}}', {%endif%}{%if field.pk%}primary_key=True, {%endif%} {%if field.len%}max_length={{field.len}}, {%endif%}{%if field.related_name%}related_name='{{field.related_name}}', {%endif%}{%if field.default is defined%}default={{field.default}}{%endif%})
|
||||
{%for field in model.fields%}{{field.name}} = models.{{field.type}}({%if field.ref%}'{{field.ref}}',{%endif%}{%if field.pk%}primary_key=True,{%endif%}{%if field.len%}max_length={{field.len}},{%endif%}{%if field.related_name%}related_name='{{field.related_name}}',{%endif%}{%if field.default is defined%}default={{field.default}}{%endif%})
|
||||
{%endfor%}
|
||||
|
||||
{%if model.display%}
|
||||
|
||||
Reference in New Issue
Block a user